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) {
16 this.initFunctionPerformanceObserver();
18 this.commandsStatistics
= { id
: this.objId
? this.objId
: 'Object id not specified', commandsStatisticsData
: {} };
21 public static timedFunction(method
: (...optionalParams
: any[]) => any): (...optionalParams
: any[]) => any {
22 return performance
.timerify(method
);
25 public addMessage(command
: RequestCommand
| IncomingRequestCommand
, messageType
: MessageType
): void {
26 switch (messageType
) {
27 case MessageType
.CALL_MESSAGE
:
28 if (this.commandsStatistics
.commandsStatisticsData
[command
] && this.commandsStatistics
.commandsStatisticsData
[command
].countRequest
) {
29 this.commandsStatistics
.commandsStatisticsData
[command
].countRequest
++;
31 this.commandsStatistics
.commandsStatisticsData
[command
] = {} as CommandStatisticsData
;
32 this.commandsStatistics
.commandsStatisticsData
[command
].countRequest
= 1;
35 case MessageType
.CALL_RESULT_MESSAGE
:
36 if (this.commandsStatistics
.commandsStatisticsData
[command
]) {
37 if (this.commandsStatistics
.commandsStatisticsData
[command
].countResponse
) {
38 this.commandsStatistics
.commandsStatisticsData
[command
].countResponse
++;
40 this.commandsStatistics
.commandsStatisticsData
[command
].countResponse
= 1;
43 this.commandsStatistics
.commandsStatisticsData
[command
] = {} as CommandStatisticsData
;
44 this.commandsStatistics
.commandsStatisticsData
[command
].countResponse
= 1;
47 case MessageType
.CALL_ERROR_MESSAGE
:
48 if (this.commandsStatistics
.commandsStatisticsData
[command
]) {
49 if (this.commandsStatistics
.commandsStatisticsData
[command
].countError
) {
50 this.commandsStatistics
.commandsStatisticsData
[command
].countError
++;
52 this.commandsStatistics
.commandsStatisticsData
[command
].countError
= 1;
55 this.commandsStatistics
.commandsStatisticsData
[command
] = {} as CommandStatisticsData
;
56 this.commandsStatistics
.commandsStatisticsData
[command
].countError
= 1;
60 logger
.error(`${this.logPrefix()} wrong message type ${messageType}`);
65 public logPerformance(entry
: PerformanceEntry
): void {
66 this.addPerformanceTimer(entry
.name
, entry
.duration
);
67 const perfEntry
: PerfEntry
= {
69 entryType
: entry
.entryType
,
70 startTime
: entry
.startTime
,
71 duration
: entry
.duration
73 logger
.debug(`${this.logPrefix()} method or function '${entry.name}' performance entry: %j`, perfEntry
);
76 public start(): void {
77 this.displayInterval();
80 private initFunctionPerformanceObserver(): void {
81 const performanceObserver
= new PerformanceObserver((list
, observer
) => {
82 this.logPerformance(list
.getEntries()[0]);
83 observer
.disconnect();
85 performanceObserver
.observe({ entryTypes
: ['function'] });
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`);