-// Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
+// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
-import { CircularArray, DEFAULT_CIRCULAR_ARRAY_SIZE } from '../utils/CircularArray';
-import { IncomingRequestCommand, RequestCommand } from '../types/ocpp/Requests';
-import { PerformanceEntry, PerformanceObserver, performance } from 'perf_hooks';
-import Statistics, { StatisticsData, TimeSeries } from '../types/Statistics';
+import { type PerformanceEntry, PerformanceObserver, performance } from 'node:perf_hooks';
+import type { URL } from 'node:url';
+import { parentPort } from 'node:worker_threads';
-import { ChargingStationWorkerMessageEvents } from '../types/ChargingStationWorker';
-import Configuration from '../utils/Configuration';
-import { MessageType } from '../types/ocpp/MessageType';
-import { URL } from 'url';
-import Utils from '../utils/Utils';
-import logger from '../utils/Logger';
-import { parentPort } from 'worker_threads';
+import { MessageChannelUtils } from '../charging-station';
+import {
+ type IncomingRequestCommand,
+ MessageType,
+ type RequestCommand,
+ type Statistics,
+ type TimeSeries,
+} from '../types';
+import { CircularArray, Configuration, Constants, Utils, logger } from '../utils';
-export default class PerformanceStatistics {
+export class PerformanceStatistics {
private static readonly instances: Map<string, PerformanceStatistics> = new Map<
string,
PerformanceStatistics
>();
+
private readonly objId: string;
private readonly objName: string;
- private performanceObserver: PerformanceObserver;
+ private performanceObserver!: PerformanceObserver;
private readonly statistics: Statistics;
- private displayInterval: NodeJS.Timeout;
+ private displayInterval!: NodeJS.Timeout;
private constructor(objId: string, objName: string, uri: URL) {
this.objId = objId;
name: this.objName ?? 'Object name not specified',
uri: uri.toString(),
createdAt: new Date(),
- statisticsData: new Map<string, Partial<StatisticsData>>(),
+ statisticsData: new Map(),
};
}
- public static getInstance(objId: string, objName: string, uri: URL): PerformanceStatistics {
+ public static getInstance(
+ objId: string,
+ objName: string,
+ uri: URL
+ ): PerformanceStatistics | undefined {
if (!PerformanceStatistics.instances.has(objId)) {
PerformanceStatistics.instances.set(objId, new PerformanceStatistics(objId, objName, uri));
}
}
public static beginMeasure(id: string): string {
- const markId = `${id.charAt(0).toUpperCase() + id.slice(1)}~${Utils.generateUUID()}`;
+ const markId = `${id.charAt(0).toUpperCase()}${id.slice(1)}~${Utils.generateUUID()}`;
performance.mark(markId);
return markId;
}
public static endMeasure(name: string, markId: string): void {
performance.measure(name, markId);
performance.clearMarks(markId);
+ performance.clearMeasures(name);
}
public addRequestStatistic(
) {
this.statistics.statisticsData.get(command).countRequest++;
} else {
- this.statistics.statisticsData.set(
- command,
- Object.assign({ countRequest: 1 }, this.statistics.statisticsData.get(command))
- );
+ this.statistics.statisticsData.set(command, {
+ ...this.statistics.statisticsData.get(command),
+ countRequest: 1,
+ });
}
break;
case MessageType.CALL_RESULT_MESSAGE:
) {
this.statistics.statisticsData.get(command).countResponse++;
} else {
- this.statistics.statisticsData.set(
- command,
- Object.assign({ countResponse: 1 }, this.statistics.statisticsData.get(command))
- );
+ this.statistics.statisticsData.set(command, {
+ ...this.statistics.statisticsData.get(command),
+ countResponse: 1,
+ });
}
break;
case MessageType.CALL_ERROR_MESSAGE:
) {
this.statistics.statisticsData.get(command).countError++;
} else {
- this.statistics.statisticsData.set(
- command,
- Object.assign({ countError: 1 }, this.statistics.statisticsData.get(command))
- );
+ this.statistics.statisticsData.set(command, {
+ ...this.statistics.statisticsData.get(command),
+ countError: 1,
+ });
}
break;
default:
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
logger.error(`${this.logPrefix()} wrong message type ${messageType}`);
break;
}
}
public stop(): void {
- if (this.displayInterval) {
- clearInterval(this.displayInterval);
- }
+ this.stopLogStatisticsInterval();
performance.clearMarks();
+ performance.clearMeasures();
this.performanceObserver?.disconnect();
}
}
private initializePerformanceObserver(): void {
- this.performanceObserver = new PerformanceObserver((list) => {
- const lastPerformanceEntry = list.getEntries()[0];
+ this.performanceObserver = new PerformanceObserver((performanceObserverList) => {
+ const lastPerformanceEntry = performanceObserverList.getEntries()[0];
+ // logger.debug(
+ // `${this.logPrefix()} '${lastPerformanceEntry.name}' performance entry: %j`,
+ // lastPerformanceEntry
+ // );
this.addPerformanceEntryToStatistics(lastPerformanceEntry);
- logger.debug(
- `${this.logPrefix()} '${lastPerformanceEntry.name}' performance entry: %j`,
- lastPerformanceEntry
- );
});
this.performanceObserver.observe({ entryTypes: ['measure'] });
}
private logStatistics(): void {
- logger.info(this.logPrefix() + ' %j', this.statistics);
+ logger.info(`${this.logPrefix()}`, {
+ ...this.statistics,
+ statisticsData: Utils.JSONStringifyWithMapSupport(this.statistics.statisticsData),
+ });
}
private startLogStatisticsInterval(): void {
- if (Configuration.getLogStatisticsInterval() > 0) {
+ const logStatisticsInterval = Configuration.getLogStatisticsInterval();
+ if (logStatisticsInterval > 0 && !this.displayInterval) {
this.displayInterval = setInterval(() => {
this.logStatistics();
- }, Configuration.getLogStatisticsInterval() * 1000);
+ }, logStatisticsInterval * 1000);
+ logger.info(
+ `${this.logPrefix()} logged every ${Utils.formatDurationSeconds(logStatisticsInterval)}`
+ );
+ } else if (this.displayInterval) {
logger.info(
- this.logPrefix() +
- ' logged every ' +
- Utils.formatDurationSeconds(Configuration.getLogStatisticsInterval())
+ `${this.logPrefix()} already logged every ${Utils.formatDurationSeconds(
+ logStatisticsInterval
+ )}`
);
} else {
logger.info(
- this.logPrefix() +
- ' log interval is set to ' +
- Configuration.getLogStatisticsInterval().toString() +
- '. Not logging statistics'
+ `${this.logPrefix()} log interval is set to ${logStatisticsInterval?.toString()}. Not logging statistics`
);
}
}
+ private stopLogStatisticsInterval(): void {
+ if (this.displayInterval) {
+ clearInterval(this.displayInterval);
+ delete this.displayInterval;
+ }
+ }
+
private median(dataSet: number[]): number {
- if (Array.isArray(dataSet) && dataSet.length === 1) {
+ if (Array.isArray(dataSet) === true && dataSet.length === 1) {
return dataSet[0];
}
const sortedDataSet = dataSet.slice().sort((a, b) => a - b);
}
private addPerformanceEntryToStatistics(entry: PerformanceEntry): void {
- let entryName = entry.name;
- // Rename entry name
- const MAP_NAME: Record<string, string> = {};
- if (MAP_NAME[entryName]) {
- entryName = MAP_NAME[entryName];
- }
+ const entryName = entry.name;
// Initialize command statistics
if (!this.statistics.statisticsData.has(entryName)) {
this.statistics.statisticsData.set(entryName, {});
this.statistics.statisticsData.get(entryName).avgTimeMeasurement =
this.statistics.statisticsData.get(entryName).totalTimeMeasurement /
this.statistics.statisticsData.get(entryName).countTimeMeasurement;
- Array.isArray(this.statistics.statisticsData.get(entryName).timeMeasurementSeries)
+ this.statistics.statisticsData.get(entryName)?.timeMeasurementSeries instanceof CircularArray
? this.statistics.statisticsData
.get(entryName)
- .timeMeasurementSeries.push({ timestamp: entry.startTime, value: entry.duration })
+ ?.timeMeasurementSeries?.push({ timestamp: entry.startTime, value: entry.duration })
: (this.statistics.statisticsData.get(entryName).timeMeasurementSeries =
- new CircularArray<TimeSeries>(DEFAULT_CIRCULAR_ARRAY_SIZE, {
+ new CircularArray<TimeSeries>(Constants.DEFAULT_CIRCULAR_BUFFER_CAPACITY, {
timestamp: entry.startTime,
value: entry.duration,
}));
)
);
if (Configuration.getPerformanceStorage().enabled) {
- parentPort.postMessage({
- id: ChargingStationWorkerMessageEvents.PERFORMANCE_STATISTICS,
- data: this.statistics,
- });
+ parentPort?.postMessage(
+ MessageChannelUtils.buildPerformanceStatisticsMessage(this.statistics)
+ );
}
}
return timeSeries.map((timeSeriesItem) => timeSeriesItem.value);
}
- private logPrefix(): string {
+ private logPrefix = (): string => {
return Utils.logPrefix(` ${this.objName} | Performance statistics`);
- }
+ };
}