1 import CommandStatistics
, { CommandStatisticsData
, PerfEntry
} from
'../types/CommandStatistics';
2 import { IncomingRequestCommand
, RequestCommand
} from
'../types/ocpp/Requests';
4 import CircularArray from
'./CircularArray';
5 import Configuration from
'./Configuration';
6 import { MessageType
} from
'../types/ocpp/MessageType';
7 import { PerformanceEntry
} from
'perf_hooks';
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 addMessage(command
: RequestCommand
| IncomingRequestCommand
, messageType
: MessageType
): void {
21 switch (messageType
) {
22 case MessageType
.CALL_MESSAGE
:
23 if (this.commandsStatistics
.commandsStatisticsData
[command
] && this.commandsStatistics
.commandsStatisticsData
[command
].countRequest
) {
24 this.commandsStatistics
.commandsStatisticsData
[command
].countRequest
++;
26 this.commandsStatistics
.commandsStatisticsData
[command
] = {} as CommandStatisticsData
;
27 this.commandsStatistics
.commandsStatisticsData
[command
].countRequest
= 1;
30 case MessageType
.CALL_RESULT_MESSAGE
:
31 if (this.commandsStatistics
.commandsStatisticsData
[command
]) {
32 if (this.commandsStatistics
.commandsStatisticsData
[command
].countResponse
) {
33 this.commandsStatistics
.commandsStatisticsData
[command
].countResponse
++;
35 this.commandsStatistics
.commandsStatisticsData
[command
].countResponse
= 1;
38 this.commandsStatistics
.commandsStatisticsData
[command
] = {} as CommandStatisticsData
;
39 this.commandsStatistics
.commandsStatisticsData
[command
].countResponse
= 1;
42 case MessageType
.CALL_ERROR_MESSAGE
:
43 if (this.commandsStatistics
.commandsStatisticsData
[command
]) {
44 if (this.commandsStatistics
.commandsStatisticsData
[command
].countError
) {
45 this.commandsStatistics
.commandsStatisticsData
[command
].countError
++;
47 this.commandsStatistics
.commandsStatisticsData
[command
].countError
= 1;
50 this.commandsStatistics
.commandsStatisticsData
[command
] = {} as CommandStatisticsData
;
51 this.commandsStatistics
.commandsStatisticsData
[command
].countError
= 1;
55 logger
.error(`${this.logPrefix()} wrong message type ${messageType}`);
60 public logPerformance(entry
: PerformanceEntry
, className
: string): void {
61 this.addPerformanceTimer(entry
.name
as RequestCommand
| IncomingRequestCommand
, entry
.duration
);
62 const perfEntry
: PerfEntry
= {} as PerfEntry
;
63 perfEntry
.name
= entry
.name
;
64 perfEntry
.entryType
= entry
.entryType
;
65 perfEntry
.startTime
= entry
.startTime
;
66 perfEntry
.duration
= entry
.duration
;
67 logger
.info(`${this.logPrefix()} ${className} method(s) entry: %j`, perfEntry
);
70 public start(): void {
71 this.displayInterval();
74 private display(): void {
75 logger
.info(this.logPrefix() + ' %j', this.commandsStatistics
);
78 private displayInterval(): void {
79 if (Configuration
.getStatisticsDisplayInterval() > 0) {
82 }, Configuration
.getStatisticsDisplayInterval() * 1000);
83 logger
.info(this.logPrefix() + ' displayed every ' + Utils
.secondsToHHMMSS(Configuration
.getStatisticsDisplayInterval()));
87 private median(dataSet
: number[]): number {
88 if (Array.isArray(dataSet
) && dataSet
.length
=== 1) {
91 const sortedDataSet
= dataSet
.slice().sort();
92 const middleIndex
= Math.floor(sortedDataSet
.length
/ 2);
93 if (sortedDataSet
.length
% 2) {
94 return sortedDataSet
[middleIndex
/ 2];
96 return (sortedDataSet
[(middleIndex
- 1)] + sortedDataSet
[middleIndex
]) / 2;
99 private stdDeviation(dataSet
: number[]): number {
100 let totalDataSet
= 0;
101 for (const data
of dataSet
) {
102 totalDataSet
+= data
;
104 const dataSetMean
= totalDataSet
/ dataSet
.length
;
105 let totalGeometricDeviation
= 0;
106 for (const data
of dataSet
) {
107 const deviation
= data
- dataSetMean
;
108 totalGeometricDeviation
+= deviation
* deviation
;
110 return Math.sqrt(totalGeometricDeviation
/ dataSet
.length
);
113 private addPerformanceTimer(command
: RequestCommand
| IncomingRequestCommand
, duration
: number): void {
114 // Map to proper command name
116 sendMeterValues
: RequestCommand
.METERVALUES
,
117 startTransaction
: RequestCommand
.START_TRANSACTION
,
118 stopTransaction
: RequestCommand
.STOP_TRANSACTION
,
120 if (MAPCOMMAND
[command
]) {
121 command
= MAPCOMMAND
[command
] as RequestCommand
| IncomingRequestCommand
;
123 // Initialize command statistics
124 if (!this.commandsStatistics
.commandsStatisticsData
[command
]) {
125 this.commandsStatistics
.commandsStatisticsData
[command
] = {} as CommandStatisticsData
;
127 // Update current statistics timers
128 this.commandsStatistics
.commandsStatisticsData
[command
].countTimeMeasurement
= this.commandsStatistics
.commandsStatisticsData
[command
].countTimeMeasurement
? this.commandsStatistics
.commandsStatisticsData
[command
].countTimeMeasurement
+ 1 : 1;
129 this.commandsStatistics
.commandsStatisticsData
[command
].currentTimeMeasurement
= duration
;
130 this.commandsStatistics
.commandsStatisticsData
[command
].minTimeMeasurement
= this.commandsStatistics
.commandsStatisticsData
[command
].minTimeMeasurement
? (this.commandsStatistics
.commandsStatisticsData
[command
].minTimeMeasurement
> duration
? duration
: this.commandsStatistics
.commandsStatisticsData
[command
].minTimeMeasurement
) : duration
;
131 this.commandsStatistics
.commandsStatisticsData
[command
].maxTimeMeasurement
= this.commandsStatistics
.commandsStatisticsData
[command
].maxTimeMeasurement
? (this.commandsStatistics
.commandsStatisticsData
[command
].maxTimeMeasurement
< duration
? duration
: this.commandsStatistics
.commandsStatisticsData
[command
].maxTimeMeasurement
) : duration
;
132 this.commandsStatistics
.commandsStatisticsData
[command
].totalTimeMeasurement
= this.commandsStatistics
.commandsStatisticsData
[command
].totalTimeMeasurement
? this.commandsStatistics
.commandsStatisticsData
[command
].totalTimeMeasurement
+ duration
: duration
;
133 this.commandsStatistics
.commandsStatisticsData
[command
].avgTimeMeasurement
= this.commandsStatistics
.commandsStatisticsData
[command
].totalTimeMeasurement
/ this.commandsStatistics
.commandsStatisticsData
[command
].countTimeMeasurement
;
134 Array.isArray(this.commandsStatistics
.commandsStatisticsData
[command
].timeMeasurementSeries
) ? this.commandsStatistics
.commandsStatisticsData
[command
].timeMeasurementSeries
.push(duration
) : this.commandsStatistics
.commandsStatisticsData
[command
].timeMeasurementSeries
= [duration
] as CircularArray
<number>;
135 this.commandsStatistics
.commandsStatisticsData
[command
].medTimeMeasurement
= this.median(this.commandsStatistics
.commandsStatisticsData
[command
].timeMeasurementSeries
);
136 this.commandsStatistics
.commandsStatisticsData
[command
].stdDevTimeMeasurement
= this.stdDeviation(this.commandsStatistics
.commandsStatisticsData
[command
].timeMeasurementSeries
);
139 private logPrefix(): string {
140 return Utils
.logPrefix(` ${this.objId} | Performance statistics`);