Commit | Line | Data |
---|---|---|
6bf6769e | 1 | import CommandStatistics, { CommandStatisticsData, PerfEntry } from '../types/CommandStatistics'; |
c0560973 | 2 | import { IncomingRequestCommand, RequestCommand } from '../types/ocpp/Requests'; |
63b48f77 | 3 | |
edfb206c | 4 | import CircularArray from './CircularArray'; |
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 | |
3f40bc9c | 11 | export default class Statistics { |
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: | |
c0560973 | 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); |
6bf6769e JB |
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; | |
c0560973 | 67 | logger.info(`${this.logPrefix()} object ${className} method(s) performance entry: %j`, perfEntry); |
7dde0b73 JB |
68 | } |
69 | ||
418106c8 | 70 | public start(): void { |
c0560973 | 71 | this.displayInterval(); |
136c90ba JB |
72 | } |
73 | ||
c0560973 JB |
74 | private display(): void { |
75 | logger.info(this.logPrefix() + ' %j', this.commandsStatistics); | |
7dde0b73 JB |
76 | } |
77 | ||
c0560973 | 78 | private displayInterval(): void { |
c6b89400 | 79 | if (Configuration.getStatisticsDisplayInterval() > 0) { |
7dde0b73 | 80 | setInterval(() => { |
c0560973 | 81 | this.display(); |
7dde0b73 | 82 | }, Configuration.getStatisticsDisplayInterval() * 1000); |
c0560973 | 83 | logger.info(this.logPrefix() + ' displayed every ' + Utils.secondsToHHMMSS(Configuration.getStatisticsDisplayInterval())); |
7dde0b73 JB |
84 | } |
85 | } | |
86 | ||
6bf6769e JB |
87 | private median(dataSet: number[]): number { |
88 | if (Array.isArray(dataSet) && dataSet.length === 1) { | |
89 | return dataSet[0]; | |
90 | } | |
91 | const sortedDataSet = dataSet.slice().sort(); | |
92 | const middleIndex = Math.floor(sortedDataSet.length / 2); | |
93 | if (sortedDataSet.length % 2) { | |
94 | return sortedDataSet[middleIndex / 2]; | |
95 | } | |
96 | return (sortedDataSet[(middleIndex - 1)] + sortedDataSet[middleIndex]) / 2; | |
97 | } | |
98 | ||
aeada1fa JB |
99 | private stdDeviation(dataSet: number[]): number { |
100 | let totalDataSet = 0; | |
101 | for (const data of dataSet) { | |
102 | totalDataSet += data; | |
103 | } | |
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; | |
109 | } | |
110 | return Math.sqrt(totalGeometricDeviation / dataSet.length); | |
111 | } | |
112 | ||
d9f60ba1 | 113 | private addPerformanceTimer(command: RequestCommand | IncomingRequestCommand, duration: number): void { |
7ec46a9a JB |
114 | // Map to proper command name |
115 | const MAPCOMMAND = { | |
116 | sendMeterValues: 'MeterValues', | |
117 | startTransaction: 'StartTransaction', | |
118 | stopTransaction: 'StopTransaction', | |
119 | }; | |
120 | if (MAPCOMMAND[command]) { | |
d9f60ba1 | 121 | command = MAPCOMMAND[command] as RequestCommand | IncomingRequestCommand; |
7ec46a9a JB |
122 | } |
123 | // Initialize command statistics | |
418106c8 JB |
124 | if (!this.commandsStatistics.commandsStatisticsData[command]) { |
125 | this.commandsStatistics.commandsStatisticsData[command] = {} as CommandStatisticsData; | |
7ec46a9a JB |
126 | } |
127 | // Update current statistics timers | |
418106c8 JB |
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); | |
aeada1fa | 136 | this.commandsStatistics.commandsStatisticsData[command].stdDevTimeMeasurement = this.stdDeviation(this.commandsStatistics.commandsStatisticsData[command].timeMeasurementSeries); |
7ec46a9a JB |
137 | } |
138 | ||
c0560973 | 139 | private logPrefix(): string { |
418106c8 | 140 | return Utils.logPrefix(` ${this.objId} Statistics:`); |
6af9012e | 141 | } |
7dde0b73 | 142 | } |