Finish circular array implementation.
[e-mobility-charging-stations-simulator.git] / src / utils / Statistics.ts
CommitLineData
6bf6769e 1import CommandStatistics, { CommandStatisticsData, PerfEntry } from '../types/CommandStatistics';
63b48f77 2
edfb206c 3import CircularArray from './CircularArray';
6af9012e 4import Configuration from './Configuration';
7f134aca 5import Constants from './Constants';
e118beaa 6import { PerformanceEntry } from 'perf_hooks';
6af9012e
JB
7import Utils from './Utils';
8import logger from './Logger';
7dde0b73 9
3f40bc9c 10export default class Statistics {
6af9012e 11 private static instance: Statistics;
6af9012e 12 private _objName: string;
63b48f77 13 private _commandsStatistics: CommandStatistics;
560bcf5b 14
6af9012e 15 private constructor() {
63b48f77 16 this._commandsStatistics = {} as CommandStatistics;
7dde0b73
JB
17 }
18
6af9012e 19 set objName(objName: string) {
560bcf5b
JB
20 this._objName = objName;
21 }
22
6af9012e 23 static getInstance(): Statistics {
560bcf5b
JB
24 if (!Statistics.instance) {
25 Statistics.instance = new Statistics();
26 }
27 return Statistics.instance;
28 }
29
7f134aca
JB
30 addMessage(command: string, messageType: number): void {
31 switch (messageType) {
32 case Constants.OCPP_JSON_CALL_MESSAGE:
e118beaa
JB
33 if (this._commandsStatistics[command] && this._commandsStatistics[command].countRequest) {
34 this._commandsStatistics[command].countRequest++;
7dde0b73 35 } else {
e118beaa
JB
36 this._commandsStatistics[command] = {} as CommandStatisticsData;
37 this._commandsStatistics[command].countRequest = 1;
7f134aca
JB
38 }
39 break;
40 case Constants.OCPP_JSON_CALL_RESULT_MESSAGE:
e118beaa
JB
41 if (this._commandsStatistics[command]) {
42 if (this._commandsStatistics[command].countResponse) {
43 this._commandsStatistics[command].countResponse++;
7f134aca 44 } else {
e118beaa 45 this._commandsStatistics[command].countResponse = 1;
7f134aca
JB
46 }
47 } else {
e118beaa
JB
48 this._commandsStatistics[command] = {} as CommandStatisticsData;
49 this._commandsStatistics[command].countResponse = 1;
7dde0b73 50 }
7f134aca
JB
51 break;
52 case Constants.OCPP_JSON_CALL_ERROR_MESSAGE:
e118beaa
JB
53 if (this._commandsStatistics[command]) {
54 if (this._commandsStatistics[command].countError) {
55 this._commandsStatistics[command].countError++;
7f134aca 56 } else {
e118beaa 57 this._commandsStatistics[command].countError = 1;
7f134aca
JB
58 }
59 } else {
e118beaa
JB
60 this._commandsStatistics[command] = {} as CommandStatisticsData;
61 this._commandsStatistics[command].countError = 1;
7f134aca
JB
62 }
63 break;
64 default:
65 logger.error(`${this._logPrefix()} Wrong message type ${messageType}`);
66 break;
7dde0b73
JB
67 }
68 }
69
e118beaa 70 logPerformance(entry: PerformanceEntry, className: string): void {
7dde0b73 71 this.addPerformanceTimer(entry.name, entry.duration);
6bf6769e
JB
72 const perfEntry: PerfEntry = {} as PerfEntry;
73 perfEntry.name = entry.name;
74 perfEntry.entryType = entry.entryType;
75 perfEntry.startTime = entry.startTime;
76 perfEntry.duration = entry.duration;
77 logger.info(`${this._logPrefix()} object ${className} method performance entry: %j`, perfEntry);
7dde0b73
JB
78 }
79
6af9012e 80 _display(): void {
e118beaa 81 logger.info(this._logPrefix() + ' %j', this._commandsStatistics);
7dde0b73
JB
82 }
83
6af9012e 84 _displayInterval(): void {
c6b89400 85 if (Configuration.getStatisticsDisplayInterval() > 0) {
7dde0b73
JB
86 setInterval(() => {
87 this._display();
88 }, Configuration.getStatisticsDisplayInterval() * 1000);
7ec46a9a 89 logger.info(this._logPrefix() + ' displayed every ' + Utils.secondsToHHMMSS(Configuration.getStatisticsDisplayInterval()));
7dde0b73
JB
90 }
91 }
92
6af9012e 93 start(): void {
7dde0b73
JB
94 this._displayInterval();
95 }
6af9012e 96
6bf6769e
JB
97 private median(dataSet: number[]): number {
98 if (Array.isArray(dataSet) && dataSet.length === 1) {
99 return dataSet[0];
100 }
101 const sortedDataSet = dataSet.slice().sort();
102 const middleIndex = Math.floor(sortedDataSet.length / 2);
103 if (sortedDataSet.length % 2) {
104 return sortedDataSet[middleIndex / 2];
105 }
106 return (sortedDataSet[(middleIndex - 1)] + sortedDataSet[middleIndex]) / 2;
107 }
108
7ec46a9a
JB
109 private addPerformanceTimer(command: string, duration: number): void {
110 // Map to proper command name
111 const MAPCOMMAND = {
112 sendMeterValues: 'MeterValues',
113 startTransaction: 'StartTransaction',
114 stopTransaction: 'StopTransaction',
115 };
116 if (MAPCOMMAND[command]) {
117 command = MAPCOMMAND[command] as string;
118 }
119 // Initialize command statistics
120 if (!this._commandsStatistics[command]) {
121 this._commandsStatistics[command] = {} as CommandStatisticsData;
122 }
123 // Update current statistics timers
4a71152b 124 this._commandsStatistics[command].countTimeMeasurement = this._commandsStatistics[command].countTimeMeasurement ? this._commandsStatistics[command].countTimeMeasurement + 1 : 1;
6bf6769e
JB
125 this._commandsStatistics[command].currentTimeMeasurement = duration;
126 this._commandsStatistics[command].minTimeMeasurement = this._commandsStatistics[command].minTimeMeasurement ? (this._commandsStatistics[command].minTimeMeasurement > duration ? duration : this._commandsStatistics[command].minTimeMeasurement) : duration;
127 this._commandsStatistics[command].maxTimeMeasurement = this._commandsStatistics[command].maxTimeMeasurement ? (this._commandsStatistics[command].maxTimeMeasurement < duration ? duration : this._commandsStatistics[command].maxTimeMeasurement) : duration;
128 this._commandsStatistics[command].totalTimeMeasurement = this._commandsStatistics[command].totalTimeMeasurement ? this._commandsStatistics[command].totalTimeMeasurement + duration : duration;
129 this._commandsStatistics[command].avgTimeMeasurement = this._commandsStatistics[command].totalTimeMeasurement / this._commandsStatistics[command].countTimeMeasurement;
edfb206c 130 Array.isArray(this._commandsStatistics[command].timeMeasurementSeries) ? this._commandsStatistics[command].timeMeasurementSeries.push(duration) : this._commandsStatistics[command].timeMeasurementSeries = [duration] as CircularArray<number>;
6bf6769e 131 this._commandsStatistics[command].medTimeMeasurement = this.median(this._commandsStatistics[command].timeMeasurementSeries);
7ec46a9a
JB
132 }
133
6af9012e
JB
134 private _logPrefix(): string {
135 return Utils.logPrefix(` ${this._objName} Statistics:`);
136 }
7dde0b73 137}