1 import { CircularArray
, DEFAULT_CIRCULAR_ARRAY_SIZE
} from
'./CircularArray';
2 import CommandStatistics
, { CommandStatisticsData
, PerfEntry
} from
'../types/CommandStatistics';
3 import { IncomingRequestCommand
, RequestCommand
} from
'../types/ocpp/Requests';
4 import { PerformanceEntry
, PerformanceObserver
, performance
} from
'perf_hooks';
6 import Configuration from
'./Configuration';
7 import { MessageType
} from
'../types/ocpp/MessageType';
8 import Utils from
'./Utils';
9 import logger from
'./Logger';
11 export default class PerformanceStatistics
{
12 private objId
: string;
13 private commandsStatistics
: CommandStatistics
;
15 public constructor(objId
: string) {
17 this.commandsStatistics
= { id
: this.objId
? this.objId
: 'Object id not specified', commandsStatisticsData
: {} };
20 public static initFunctionPerformanceObserver(performanceStatistics
: PerformanceStatistics
): PerformanceObserver
{
21 const performanceObserver
= new PerformanceObserver((list
, observer
) => {
22 performanceStatistics
.logPerformance(list
.getEntries()[0]);
23 observer
.disconnect();
25 performanceObserver
.observe({ entryTypes
: ['function'] });
26 return performanceObserver
;
29 public static timedFunction(method
: (...optionalParams
: any[]) => any): (...optionalParams
: any[]) => any {
30 return performance
.timerify(method
);
33 public addMessage(command
: RequestCommand
| IncomingRequestCommand
, messageType
: MessageType
): void {
34 switch (messageType
) {
35 case MessageType
.CALL_MESSAGE
:
36 if (this.commandsStatistics
.commandsStatisticsData
[command
] && this.commandsStatistics
.commandsStatisticsData
[command
].countRequest
) {
37 this.commandsStatistics
.commandsStatisticsData
[command
].countRequest
++;
39 this.commandsStatistics
.commandsStatisticsData
[command
] = {} as CommandStatisticsData
;
40 this.commandsStatistics
.commandsStatisticsData
[command
].countRequest
= 1;
43 case MessageType
.CALL_RESULT_MESSAGE
:
44 if (this.commandsStatistics
.commandsStatisticsData
[command
]) {
45 if (this.commandsStatistics
.commandsStatisticsData
[command
].countResponse
) {
46 this.commandsStatistics
.commandsStatisticsData
[command
].countResponse
++;
48 this.commandsStatistics
.commandsStatisticsData
[command
].countResponse
= 1;
51 this.commandsStatistics
.commandsStatisticsData
[command
] = {} as CommandStatisticsData
;
52 this.commandsStatistics
.commandsStatisticsData
[command
].countResponse
= 1;
55 case MessageType
.CALL_ERROR_MESSAGE
:
56 if (this.commandsStatistics
.commandsStatisticsData
[command
]) {
57 if (this.commandsStatistics
.commandsStatisticsData
[command
].countError
) {
58 this.commandsStatistics
.commandsStatisticsData
[command
].countError
++;
60 this.commandsStatistics
.commandsStatisticsData
[command
].countError
= 1;
63 this.commandsStatistics
.commandsStatisticsData
[command
] = {} as CommandStatisticsData
;
64 this.commandsStatistics
.commandsStatisticsData
[command
].countError
= 1;
68 logger
.error(`${this.logPrefix()} wrong message type ${messageType}`);
73 public logPerformance(entry
: PerformanceEntry
): void {
74 this.addPerformanceTimer(entry
.name
, entry
.duration
);
75 const perfEntry
: PerfEntry
= {
77 entryType
: entry
.entryType
,
78 startTime
: entry
.startTime
,
79 duration
: entry
.duration
81 logger
.debug(`${this.logPrefix()} Method or function '${entry.name}' performance entry: %j`, perfEntry
);
84 public start(): void {
85 this.displayInterval();
88 private display(): void {
89 logger
.info(this.logPrefix() + ' %j', this.commandsStatistics
);
92 private displayInterval(): void {
93 if (Configuration
.getStatisticsDisplayInterval() > 0) {
96 }, Configuration
.getStatisticsDisplayInterval() * 1000);
97 logger
.info(this.logPrefix() + ' displayed every ' + Utils
.secondsToHHMMSS(Configuration
.getStatisticsDisplayInterval()));
101 private median(dataSet
: number[]): number {
102 if (Array.isArray(dataSet
) && dataSet
.length
=== 1) {
105 const sortedDataSet
= dataSet
.slice().sort();
106 const middleIndex
= Math.floor(sortedDataSet
.length
/ 2);
107 if (sortedDataSet
.length
% 2) {
108 return sortedDataSet
[middleIndex
/ 2];
110 return (sortedDataSet
[(middleIndex
- 1)] + sortedDataSet
[middleIndex
]) / 2;
113 private stdDeviation(dataSet
: number[]): number {
114 let totalDataSet
= 0;
115 for (const data
of dataSet
) {
116 totalDataSet
+= data
;
118 const dataSetMean
= totalDataSet
/ dataSet
.length
;
119 let totalGeometricDeviation
= 0;
120 for (const data
of dataSet
) {
121 const deviation
= data
- dataSetMean
;
122 totalGeometricDeviation
+= deviation
* deviation
;
124 return Math.sqrt(totalGeometricDeviation
/ dataSet
.length
);
127 private addPerformanceTimer(name
: string, duration
: number): void {
130 startATGTransaction
: 'StartATGTransaction',
131 stopATGTransaction
: 'StartATGTransaction'
133 if (MAP_NAME
[name
]) {
134 name
= MAP_NAME
[name
] as string;
136 // Initialize command statistics
137 if (!this.commandsStatistics
.commandsStatisticsData
[name
]) {
138 this.commandsStatistics
.commandsStatisticsData
[name
] = {} as CommandStatisticsData
;
140 // Update current statistics timers
141 this.commandsStatistics
.commandsStatisticsData
[name
].countTimeMeasurement
= this.commandsStatistics
.commandsStatisticsData
[name
].countTimeMeasurement
? this.commandsStatistics
.commandsStatisticsData
[name
].countTimeMeasurement
+ 1 : 1;
142 this.commandsStatistics
.commandsStatisticsData
[name
].currentTimeMeasurement
= duration
;
143 this.commandsStatistics
.commandsStatisticsData
[name
].minTimeMeasurement
= this.commandsStatistics
.commandsStatisticsData
[name
].minTimeMeasurement
? (this.commandsStatistics
.commandsStatisticsData
[name
].minTimeMeasurement
> duration
? duration
: this.commandsStatistics
.commandsStatisticsData
[name
].minTimeMeasurement
) : duration
;
144 this.commandsStatistics
.commandsStatisticsData
[name
].maxTimeMeasurement
= this.commandsStatistics
.commandsStatisticsData
[name
].maxTimeMeasurement
? (this.commandsStatistics
.commandsStatisticsData
[name
].maxTimeMeasurement
< duration
? duration
: this.commandsStatistics
.commandsStatisticsData
[name
].maxTimeMeasurement
) : duration
;
145 this.commandsStatistics
.commandsStatisticsData
[name
].totalTimeMeasurement
= this.commandsStatistics
.commandsStatisticsData
[name
].totalTimeMeasurement
? this.commandsStatistics
.commandsStatisticsData
[name
].totalTimeMeasurement
+ duration
: duration
;
146 this.commandsStatistics
.commandsStatisticsData
[name
].avgTimeMeasurement
= this.commandsStatistics
.commandsStatisticsData
[name
].totalTimeMeasurement
/ this.commandsStatistics
.commandsStatisticsData
[name
].countTimeMeasurement
;
147 Array.isArray(this.commandsStatistics
.commandsStatisticsData
[name
].timeMeasurementSeries
) ? this.commandsStatistics
.commandsStatisticsData
[name
].timeMeasurementSeries
.push(duration
) : this.commandsStatistics
.commandsStatisticsData
[name
].timeMeasurementSeries
= new CircularArray
<number>(DEFAULT_CIRCULAR_ARRAY_SIZE
, duration
);
148 this.commandsStatistics
.commandsStatisticsData
[name
].medTimeMeasurement
= this.median(this.commandsStatistics
.commandsStatisticsData
[name
].timeMeasurementSeries
);
149 this.commandsStatistics
.commandsStatisticsData
[name
].stdDevTimeMeasurement
= this.stdDeviation(this.commandsStatistics
.commandsStatisticsData
[name
].timeMeasurementSeries
);
152 private logPrefix(): string {
153 return Utils
.logPrefix(` ${this.objId} | Performance statistics`);