Commit | Line | Data |
---|---|---|
ef72d3f5 | 1 | import { CircularArray, DEFAULT_CIRCULAR_ARRAY_SIZE } from './CircularArray'; |
6bf6769e | 2 | import CommandStatistics, { CommandStatisticsData, PerfEntry } from '../types/CommandStatistics'; |
c0560973 | 3 | import { IncomingRequestCommand, RequestCommand } from '../types/ocpp/Requests'; |
63b48f77 | 4 | |
6af9012e | 5 | import Configuration from './Configuration'; |
d2a64eb5 | 6 | import { MessageType } from '../types/ocpp/MessageType'; |
e118beaa | 7 | import { PerformanceEntry } from 'perf_hooks'; |
6af9012e JB |
8 | import Utils from './Utils'; |
9 | import logger from './Logger'; | |
7dde0b73 | 10 | |
54b1efe0 | 11 | export default class PerformanceStatistics { |
418106c8 | 12 | private objId: string; |
ad2f27c3 | 13 | private commandsStatistics: CommandStatistics; |
560bcf5b | 14 | |
c0560973 JB |
15 | public constructor(objId: string) { |
16 | this.objId = objId; | |
8434025b | 17 | this.commandsStatistics = { id: this.objId ? this.objId : 'Object id not specified', commandsStatisticsData: {} }; |
560bcf5b JB |
18 | } |
19 | ||
418106c8 | 20 | public addMessage(command: RequestCommand | IncomingRequestCommand, messageType: MessageType): void { |
7f134aca | 21 | switch (messageType) { |
d2a64eb5 | 22 | case MessageType.CALL_MESSAGE: |
418106c8 JB |
23 | if (this.commandsStatistics.commandsStatisticsData[command] && this.commandsStatistics.commandsStatisticsData[command].countRequest) { |
24 | this.commandsStatistics.commandsStatisticsData[command].countRequest++; | |
7dde0b73 | 25 | } else { |
418106c8 JB |
26 | this.commandsStatistics.commandsStatisticsData[command] = {} as CommandStatisticsData; |
27 | this.commandsStatistics.commandsStatisticsData[command].countRequest = 1; | |
7f134aca JB |
28 | } |
29 | break; | |
d2a64eb5 | 30 | case MessageType.CALL_RESULT_MESSAGE: |
418106c8 JB |
31 | if (this.commandsStatistics.commandsStatisticsData[command]) { |
32 | if (this.commandsStatistics.commandsStatisticsData[command].countResponse) { | |
33 | this.commandsStatistics.commandsStatisticsData[command].countResponse++; | |
7f134aca | 34 | } else { |
418106c8 | 35 | this.commandsStatistics.commandsStatisticsData[command].countResponse = 1; |
7f134aca JB |
36 | } |
37 | } else { | |
418106c8 JB |
38 | this.commandsStatistics.commandsStatisticsData[command] = {} as CommandStatisticsData; |
39 | this.commandsStatistics.commandsStatisticsData[command].countResponse = 1; | |
7dde0b73 | 40 | } |
7f134aca | 41 | break; |
d2a64eb5 | 42 | case MessageType.CALL_ERROR_MESSAGE: |
418106c8 JB |
43 | if (this.commandsStatistics.commandsStatisticsData[command]) { |
44 | if (this.commandsStatistics.commandsStatisticsData[command].countError) { | |
45 | this.commandsStatistics.commandsStatisticsData[command].countError++; | |
7f134aca | 46 | } else { |
418106c8 | 47 | this.commandsStatistics.commandsStatisticsData[command].countError = 1; |
7f134aca JB |
48 | } |
49 | } else { | |
418106c8 JB |
50 | this.commandsStatistics.commandsStatisticsData[command] = {} as CommandStatisticsData; |
51 | this.commandsStatistics.commandsStatisticsData[command].countError = 1; | |
7f134aca JB |
52 | } |
53 | break; | |
54 | default: | |
54b1efe0 | 55 | logger.error(`${this.logPrefix()} wrong message type ${messageType}`); |
7f134aca | 56 | break; |
7dde0b73 JB |
57 | } |
58 | } | |
59 | ||
418106c8 | 60 | public logPerformance(entry: PerformanceEntry, className: string): void { |
d9f60ba1 | 61 | this.addPerformanceTimer(entry.name as RequestCommand | IncomingRequestCommand, entry.duration); |
9fbc92f7 JB |
62 | const perfEntry: PerfEntry = { |
63 | name: entry.name, | |
64 | entryType: entry.entryType, | |
65 | startTime: entry.startTime, | |
66 | duration: entry.duration | |
67 | } ; | |
54b1efe0 | 68 | logger.info(`${this.logPrefix()} ${className} method(s) entry: %j`, perfEntry); |
7dde0b73 JB |
69 | } |
70 | ||
418106c8 | 71 | public start(): void { |
c0560973 | 72 | this.displayInterval(); |
136c90ba JB |
73 | } |
74 | ||
c0560973 JB |
75 | private display(): void { |
76 | logger.info(this.logPrefix() + ' %j', this.commandsStatistics); | |
7dde0b73 JB |
77 | } |
78 | ||
c0560973 | 79 | private displayInterval(): void { |
c6b89400 | 80 | if (Configuration.getStatisticsDisplayInterval() > 0) { |
7dde0b73 | 81 | setInterval(() => { |
c0560973 | 82 | this.display(); |
7dde0b73 | 83 | }, Configuration.getStatisticsDisplayInterval() * 1000); |
c0560973 | 84 | logger.info(this.logPrefix() + ' displayed every ' + Utils.secondsToHHMMSS(Configuration.getStatisticsDisplayInterval())); |
7dde0b73 JB |
85 | } |
86 | } | |
87 | ||
6bf6769e JB |
88 | private median(dataSet: number[]): number { |
89 | if (Array.isArray(dataSet) && dataSet.length === 1) { | |
90 | return dataSet[0]; | |
91 | } | |
92 | const sortedDataSet = dataSet.slice().sort(); | |
93 | const middleIndex = Math.floor(sortedDataSet.length / 2); | |
94 | if (sortedDataSet.length % 2) { | |
95 | return sortedDataSet[middleIndex / 2]; | |
96 | } | |
97 | return (sortedDataSet[(middleIndex - 1)] + sortedDataSet[middleIndex]) / 2; | |
98 | } | |
99 | ||
aeada1fa JB |
100 | private stdDeviation(dataSet: number[]): number { |
101 | let totalDataSet = 0; | |
102 | for (const data of dataSet) { | |
103 | totalDataSet += data; | |
104 | } | |
105 | const dataSetMean = totalDataSet / dataSet.length; | |
106 | let totalGeometricDeviation = 0; | |
107 | for (const data of dataSet) { | |
108 | const deviation = data - dataSetMean; | |
109 | totalGeometricDeviation += deviation * deviation; | |
110 | } | |
111 | return Math.sqrt(totalGeometricDeviation / dataSet.length); | |
112 | } | |
113 | ||
d9f60ba1 | 114 | private addPerformanceTimer(command: RequestCommand | IncomingRequestCommand, duration: number): void { |
7ec46a9a | 115 | // Map to proper command name |
f0c6ed89 | 116 | const MAP_COMMAND = { |
bb80986b | 117 | sendMeterValues: RequestCommand.METER_VALUES, |
b57115d8 JB |
118 | startTransaction: RequestCommand.START_TRANSACTION, |
119 | stopTransaction: RequestCommand.STOP_TRANSACTION, | |
7ec46a9a | 120 | }; |
f0c6ed89 JB |
121 | if (MAP_COMMAND[command]) { |
122 | command = MAP_COMMAND[command] as RequestCommand | IncomingRequestCommand; | |
7ec46a9a JB |
123 | } |
124 | // Initialize command statistics | |
418106c8 JB |
125 | if (!this.commandsStatistics.commandsStatisticsData[command]) { |
126 | this.commandsStatistics.commandsStatisticsData[command] = {} as CommandStatisticsData; | |
7ec46a9a JB |
127 | } |
128 | // Update current statistics timers | |
418106c8 JB |
129 | this.commandsStatistics.commandsStatisticsData[command].countTimeMeasurement = this.commandsStatistics.commandsStatisticsData[command].countTimeMeasurement ? this.commandsStatistics.commandsStatisticsData[command].countTimeMeasurement + 1 : 1; |
130 | this.commandsStatistics.commandsStatisticsData[command].currentTimeMeasurement = duration; | |
131 | this.commandsStatistics.commandsStatisticsData[command].minTimeMeasurement = this.commandsStatistics.commandsStatisticsData[command].minTimeMeasurement ? (this.commandsStatistics.commandsStatisticsData[command].minTimeMeasurement > duration ? duration : this.commandsStatistics.commandsStatisticsData[command].minTimeMeasurement) : duration; | |
132 | this.commandsStatistics.commandsStatisticsData[command].maxTimeMeasurement = this.commandsStatistics.commandsStatisticsData[command].maxTimeMeasurement ? (this.commandsStatistics.commandsStatisticsData[command].maxTimeMeasurement < duration ? duration : this.commandsStatistics.commandsStatisticsData[command].maxTimeMeasurement) : duration; | |
133 | this.commandsStatistics.commandsStatisticsData[command].totalTimeMeasurement = this.commandsStatistics.commandsStatisticsData[command].totalTimeMeasurement ? this.commandsStatistics.commandsStatisticsData[command].totalTimeMeasurement + duration : duration; | |
134 | this.commandsStatistics.commandsStatisticsData[command].avgTimeMeasurement = this.commandsStatistics.commandsStatisticsData[command].totalTimeMeasurement / this.commandsStatistics.commandsStatisticsData[command].countTimeMeasurement; | |
ef72d3f5 | 135 | Array.isArray(this.commandsStatistics.commandsStatisticsData[command].timeMeasurementSeries) ? this.commandsStatistics.commandsStatisticsData[command].timeMeasurementSeries.push(duration) : this.commandsStatistics.commandsStatisticsData[command].timeMeasurementSeries = new CircularArray<number>(DEFAULT_CIRCULAR_ARRAY_SIZE, duration); |
418106c8 | 136 | this.commandsStatistics.commandsStatisticsData[command].medTimeMeasurement = this.median(this.commandsStatistics.commandsStatisticsData[command].timeMeasurementSeries); |
aeada1fa | 137 | this.commandsStatistics.commandsStatisticsData[command].stdDevTimeMeasurement = this.stdDeviation(this.commandsStatistics.commandsStatisticsData[command].timeMeasurementSeries); |
7ec46a9a JB |
138 | } |
139 | ||
c0560973 | 140 | private logPrefix(): string { |
54b1efe0 | 141 | return Utils.logPrefix(` ${this.objId} | Performance statistics`); |
6af9012e | 142 | } |
7dde0b73 | 143 | } |