From: Jérôme Benoit Date: Wed, 11 Nov 2020 00:53:59 +0000 (+0100) Subject: More typing. X-Git-Tag: v1.0.1-0~207 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=e118beaab2fe63c663d5a969085c682ca05b6554;p=e-mobility-charging-stations-simulator.git More typing. Signed-off-by: Jérôme Benoit --- diff --git a/docker/config.json b/docker/config.json index 6535a074..02ec2060 100644 --- a/docker/config.json +++ b/docker/config.json @@ -11,27 +11,27 @@ "stationTemplateURLs": [ { "file": "./src/assets/station-templates/siemens.station-template.json", - "numberOfStation": 0 + "numberOfStations": 0 }, { "file": "./src/assets/station-templates/keba.station-template.json", - "numberOfStation": 0 + "numberOfStations": 0 }, { "file": "./src/assets/station-templates/abb.station-template.json", - "numberOfStation": 0 + "numberOfStations": 0 }, { "file": "./src/assets/station-templates/evlink.station-template.json", - "numberOfStation": 0 + "numberOfStations": 0 }, { "file": "./src/assets/station-templates/schneider.station-template.json", - "numberOfStation": 0 + "numberOfStations": 0 }, { "file": "./src/assets/station-templates/virtual-simple-atg.station-template.json", - "numberOfStation": 10 + "numberOfStations": 10 } ], "consoleLog": false, diff --git a/src/assets/config-template.json b/src/assets/config-template.json index 69659bbc..d55855fe 100644 --- a/src/assets/config-template.json +++ b/src/assets/config-template.json @@ -2,32 +2,32 @@ "supervisionURLs": [ "ws://localhost:8010/OCPP16/5be7fb271014d90008992f06" ], - "statisticsDisplayInterval": 60, "autoReconnectTimeout": 10, "autoReconnectMaxRetries": 10, + "statisticsDisplayInterval": 60, "distributeStationToTenantEqually": true, "useWorkerPool": false, "workerPoolSize": 16, "stationTemplateURLs": [ { "file": "./src/assets/station-templates/siemens.station-template.json", - "numberOfStation": 1 + "numberOfStations": 1 }, { "file": "./src/assets/station-templates/keba.station-template.json", - "numberOfStation": 2 + "numberOfStations": 2 }, { "file": "./src/assets/station-templates/abb.station-template.json", - "numberOfStation": 2 + "numberOfStations": 2 }, { "file": "./src/assets/station-templates/evlink.station-template.json", - "numberOfStation": 4 + "numberOfStations": 4 }, { "file": "./src/assets/station-templates/schneider.station-template.json", - "numberOfStation": 1 + "numberOfStations": 1 } ], "logFile": "combined.log", diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 0a0043c9..9dcea851 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -1,3 +1,4 @@ +import ChargingStationConfiguration, { ConfigurationKey } from '../types/ChargingStationConfiguration'; import Connectors, { Connector } from '../types/Connectors'; import MeterValue, { MeterValueLocation, MeterValueMeasurand, MeterValuePhase, MeterValueUnit } from '../types/MeterValue'; import { PerformanceObserver, performance } from 'perf_hooks'; @@ -21,9 +22,15 @@ export default class ChargingStation { private _index: number; private _stationTemplateFile: string; private _stationInfo; - private _bootNotificationMessage; + private _bootNotificationMessage: { + chargePointModel: string, + chargePointVendor: string, + chargeBoxSerialNumber?: string, + firmwareVersion?: string, + }; + private _connectors: Connectors; - private _configuration; + private _configuration: ChargingStationConfiguration; private _connectorsConfigurationHash: string; private _supervisionUrl: string; private _wsConnectionUrl: string; @@ -32,7 +39,7 @@ export default class ChargingStation { private _autoReconnectRetryCount: number; private _autoReconnectMaxRetries: number; private _autoReconnectTimeout: number; - private _requests; + private _requests: { [id: string]: [(payload?, requestPayload?) => void, (error?: OCPPError) => void, object] }; private _messageQueue: any[]; private _automaticTransactionGeneration: AutomaticTransactionGenerator; private _authorizedTags: string[]; @@ -169,8 +176,8 @@ export default class ChargingStation { return Utils.logPrefix(` ${this._stationInfo.name}:`); } - _getConfiguration() { - return this._stationInfo.Configuration ? this._stationInfo.Configuration : {}; + _getConfiguration(): ChargingStationConfiguration { + return this._stationInfo.Configuration ? this._stationInfo.Configuration : {} as ChargingStationConfiguration; } _getAuthorizationFile(): string { @@ -292,9 +299,9 @@ export default class ChargingStation { // Get a random url indexUrl = Math.floor(Math.random() * supervisionUrls.length); } - return supervisionUrls[indexUrl]; + return supervisionUrls[indexUrl] as string; } - return supervisionUrls; + return supervisionUrls as string; } _getAuthorizeRemoteTxRequests(): boolean { @@ -478,7 +485,7 @@ export default class ChargingStation { logger.error(`${this._logPrefix()} Socket: connection retry with timeout ${this._autoReconnectTimeout}ms`); this._autoReconnectRetryCount++; setTimeout(() => { - logger.error(this._logPrefix() + ' Socket: reconnecting try #' + this._autoReconnectRetryCount); + logger.error(this._logPrefix() + ' Socket: reconnecting try #' + this._autoReconnectRetryCount.toString()); this.start(); }, this._autoReconnectTimeout); } else if (this._autoReconnectTimeout !== 0 || this._autoReconnectMaxRetries !== -1) { @@ -703,7 +710,7 @@ export default class ChargingStation { ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: voltageMeasurandValue.toString() }, }); for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) { - const voltageValue = Utils.convertToInt(sampledValues.sampledValue[sampledValues.sampledValue.length - 1].value); + const voltageValue = Utils.convertToFloat(sampledValues.sampledValue[sampledValues.sampledValue.length - 1].value); let phaseValue: string; if (voltageValue >= 0 && voltageValue <= 250) { phaseValue = `L${phase}-N`; @@ -720,7 +727,7 @@ export default class ChargingStation { }); } // Power.Active.Import measurand - } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.POWER_ACTIVE_EXPORT && self._getConfigurationKey('MeterValuesSampledData').value.includes(MeterValueMeasurand.POWER_ACTIVE_EXPORT)) { + } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.POWER_ACTIVE_IMPORT && self._getConfigurationKey('MeterValuesSampledData').value.includes(MeterValueMeasurand.POWER_ACTIVE_IMPORT)) { // FIXME: factor out powerDivider checks if (Utils.isUndefined(self._stationInfo.powerDivider)) { const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`; @@ -891,14 +898,14 @@ export default class ChargingStation { } } - async sendError(messageId, err: Error | OCPPError, commandName): Promise { + async sendError(messageId: string, err: Error | OCPPError, commandName: string): Promise { // Check exception type: only OCPP error are accepted const error = err instanceof OCPPError ? err : new OCPPError(Constants.OCPP_ERROR_INTERNAL_ERROR, err.message, err.stack && err.stack); // Send error return this.sendMessage(messageId, error, Constants.OCPP_JSON_CALL_ERROR_MESSAGE, commandName); } - async sendMessage(messageId, commandParams, messageType = Constants.OCPP_JSON_CALL_RESULT_MESSAGE, commandName: string): Promise { + async sendMessage(messageId: string, commandParams, messageType = Constants.OCPP_JSON_CALL_RESULT_MESSAGE, commandName: string): Promise { // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; // Send a message through wsConnection @@ -974,7 +981,7 @@ export default class ChargingStation { logger.debug(`${self._logPrefix()} Error %j occurred when calling command %s with parameters %j`, error, commandName, commandParams); // Build Exception // eslint-disable-next-line no-empty-function - self._requests[messageId] = [() => { }, () => { }, '']; // Properly format the request + self._requests[messageId] = [() => { }, () => { }, {}]; // Properly format the request // Send error reject(error); } @@ -1046,7 +1053,7 @@ export default class ChargingStation { } const configuredMeterValueSampleInterval = this._getConfigurationKey('MeterValueSampleInterval'); this._startMeterValues(requestPayload.connectorId, - configuredMeterValueSampleInterval ? configuredMeterValueSampleInterval.value * 1000 : 60000); + configuredMeterValueSampleInterval ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000 : 60000); } else { logger.error(this._logPrefix() + ' Starting transaction id ' + payload.transactionId + ' REJECTED with status ' + payload.idTagInfo.status + ', idTag ' + requestPayload.idTag); this._resetTransactionOnConnector(requestPayload.connectorId); @@ -1090,7 +1097,7 @@ export default class ChargingStation { logger.debug(this._logPrefix() + ' Heartbeat response received: %j to Heartbeat request: %j', payload, requestPayload); } - async handleRequest(messageId, commandName, commandPayload): Promise { + async handleRequest(messageId: string, commandName: string, commandPayload): Promise { let response; // Call if (typeof this['handleRequest' + commandName] === 'function') { @@ -1124,7 +1131,7 @@ export default class ChargingStation { return Constants.OCPP_RESPONSE_ACCEPTED; } - _getConfigurationKey(key: string) { + _getConfigurationKey(key: string): ConfigurationKey { return this._configuration.configurationKey.find((configElement) => configElement.key === key); } @@ -1149,9 +1156,9 @@ export default class ChargingStation { } } - handleRequestGetConfiguration(commandPayload) { - const configurationKey = []; - const unknownKey = []; + handleRequestGetConfiguration(commandPayload): { configurationKey: ConfigurationKey[]; unknownKey: string[] } { + const configurationKey: ConfigurationKey[] = []; + const unknownKey: string[] = []; if (Utils.isEmptyArray(commandPayload.key)) { for (const configuration of this._configuration.configurationKey) { if (Utils.isUndefined(configuration.visible)) { diff --git a/src/start.ts b/src/start.ts index baef77d1..61a6b5f9 100644 --- a/src/start.ts +++ b/src/start.ts @@ -1,4 +1,5 @@ import Configuration from './utils/Configuration'; +import { StationTemplateURL } from './types/ConfigurationData'; import Utils from './utils/Utils'; import Wrk from './charging-station/Worker'; import logger from './utils/Logger'; @@ -10,11 +11,11 @@ class Bootstrap { let numStationsTotal = 0; // Start each ChargingStation object in a worker thread if (Configuration.getStationTemplateURLs()) { - Configuration.getStationTemplateURLs().forEach((stationURL) => { + Configuration.getStationTemplateURLs().forEach((stationURL: StationTemplateURL) => { try { - const nbStation = stationURL.numberOfStation ? stationURL.numberOfStation : 0; - numStationsTotal += nbStation; - for (let index = 1; index <= nbStation; index++) { + const nbStations = stationURL.numberOfStations ? stationURL.numberOfStations : 0; + numStationsTotal += nbStations; + for (let index = 1; index <= nbStations; index++) { const worker = new Wrk('./dist/charging-station/StationWorker.js', { index, templateFile: stationURL.file, diff --git a/src/types/ChargingStationConfiguration.ts b/src/types/ChargingStationConfiguration.ts new file mode 100644 index 00000000..b39a5954 --- /dev/null +++ b/src/types/ChargingStationConfiguration.ts @@ -0,0 +1,11 @@ +export interface ConfigurationKey { + key: string; + readonly?: boolean; + value: string; + visible?: boolean; + reboot?: boolean; +} + +export default interface ChargingStationConfiguration { + configurationKey: ConfigurationKey[]; +} diff --git a/src/types/CommandStatisticsData.ts b/src/types/CommandStatisticsData.ts new file mode 100644 index 00000000..edc92cc0 --- /dev/null +++ b/src/types/CommandStatisticsData.ts @@ -0,0 +1,10 @@ +export default interface CommandStatisticsData { + countRequest: number; + countResponse: number; + countError: number; + countTime: number; + minTime: number; + maxTime: number; + totalTime: number; + avgTime: number; +} diff --git a/src/types/ConfigurationData.ts b/src/types/ConfigurationData.ts new file mode 100644 index 00000000..29a443aa --- /dev/null +++ b/src/types/ConfigurationData.ts @@ -0,0 +1,20 @@ +export interface StationTemplateURL { + file: string; + numberOfStations: number; +} + +export default interface ConfigurationData { + supervisionURLs?: string[]; + stationTemplateURLs: StationTemplateURL[]; + statisticsDisplayInterval?: number; + autoReconnectTimeout?: number; + autoReconnectMaxRetries?: number; + distributeStationToTenantEqually?: boolean; + useWorkerPool?: boolean; + workerPoolSize?: number; + logFormat?: string; + logLevel?: string; + logFile?: string; + errorFile?: string; + consoleLog?: boolean; +} diff --git a/src/utils/Configuration.ts b/src/utils/Configuration.ts index 995ab5d3..d15f9053 100644 --- a/src/utils/Configuration.ts +++ b/src/utils/Configuration.ts @@ -1,13 +1,15 @@ +import ConfigurationData, { StationTemplateURL } from '../types/ConfigurationData'; + import Utils from './Utils'; import fs from 'fs'; export default class Configuration { - static configuration; + static configuration: ConfigurationData; // Read the config file - static getConfig() { + static getConfig(): ConfigurationData { if (!Configuration.configuration) { - Configuration.configuration = JSON.parse(fs.readFileSync('./src/assets/config.json', 'utf8')); + Configuration.configuration = JSON.parse(fs.readFileSync('./src/assets/config.json', 'utf8')) as ConfigurationData; } return Configuration.configuration; } @@ -27,7 +29,7 @@ export default class Configuration { return Utils.objectHasOwnProperty(Configuration.getConfig(), 'autoReconnectMaxRetries') ? Configuration.getConfig().autoReconnectMaxRetries : -1; } - static getStationTemplateURLs(): any[] { + static getStationTemplateURLs(): StationTemplateURL[] { // Read conf return Configuration.getConfig().stationTemplateURLs; } @@ -60,7 +62,7 @@ export default class Configuration { return Utils.objectHasOwnProperty(Configuration.getConfig(), 'errorFile') ? Configuration.getConfig().errorFile : 'error.log'; } - static getSupervisionURLs(): string { + static getSupervisionURLs(): string[] { // Read conf return Configuration.getConfig().supervisionURLs; } diff --git a/src/utils/Statistics.ts b/src/utils/Statistics.ts index 6adfaef4..f2bfee4f 100644 --- a/src/utils/Statistics.ts +++ b/src/utils/Statistics.ts @@ -1,15 +1,19 @@ +import CommandStatisticsData from '../types/CommandStatisticsData'; import Configuration from './Configuration'; import Constants from './Constants'; +import { PerformanceEntry } from 'perf_hooks'; import Utils from './Utils'; import logger from './Logger'; export default class Statistics { private static instance: Statistics; - private _statistics; private _objName: string; + private _commandsStatistics: { + [command: string]: CommandStatisticsData + }; private constructor() { - this._statistics = {}; + this._commandsStatistics = {}; } set objName(objName: string) { @@ -26,35 +30,35 @@ export default class Statistics { addMessage(command: string, messageType: number): void { switch (messageType) { case Constants.OCPP_JSON_CALL_MESSAGE: - if (this._statistics[command] && this._statistics[command].countRequest) { - this._statistics[command].countRequest++; + if (this._commandsStatistics[command] && this._commandsStatistics[command].countRequest) { + this._commandsStatistics[command].countRequest++; } else { - this._statistics[command] = {}; - this._statistics[command].countRequest = 1; + this._commandsStatistics[command] = {} as CommandStatisticsData; + this._commandsStatistics[command].countRequest = 1; } break; case Constants.OCPP_JSON_CALL_RESULT_MESSAGE: - if (this._statistics[command]) { - if (this._statistics[command].countResponse) { - this._statistics[command].countResponse++; + if (this._commandsStatistics[command]) { + if (this._commandsStatistics[command].countResponse) { + this._commandsStatistics[command].countResponse++; } else { - this._statistics[command].countResponse = 1; + this._commandsStatistics[command].countResponse = 1; } } else { - this._statistics[command] = {}; - this._statistics[command].countResponse = 1; + this._commandsStatistics[command] = {} as CommandStatisticsData; + this._commandsStatistics[command].countResponse = 1; } break; case Constants.OCPP_JSON_CALL_ERROR_MESSAGE: - if (this._statistics[command]) { - if (this._statistics[command].countError) { - this._statistics[command].countError++; + if (this._commandsStatistics[command]) { + if (this._commandsStatistics[command].countError) { + this._commandsStatistics[command].countError++; } else { - this._statistics[command].countError = 1; + this._commandsStatistics[command].countError = 1; } } else { - this._statistics[command] = {}; - this._statistics[command].countError = 1; + this._commandsStatistics[command] = {} as CommandStatisticsData; + this._commandsStatistics[command].countError = 1; } break; default: @@ -71,27 +75,27 @@ export default class Statistics { stopTransaction: 'StopTransaction', }; if (MAPCOMMAND[command]) { - command = MAPCOMMAND[command]; + command = MAPCOMMAND[command] as string; } // Initialize command statistics - if (!this._statistics[command]) { - this._statistics[command] = {}; + if (!this._commandsStatistics[command]) { + this._commandsStatistics[command] = {} as CommandStatisticsData; } // Update current statistics timers - this._statistics[command].countTime = this._statistics[command].countTime ? this._statistics[command].countTime + 1 : 1; - this._statistics[command].minTime = this._statistics[command].minTime ? (this._statistics[command].minTime > duration ? duration : this._statistics[command].minTime) : duration; - this._statistics[command].maxTime = this._statistics[command].maxTime ? (this._statistics[command].maxTime < duration ? duration : this._statistics[command].maxTime) : duration; - this._statistics[command].totalTime = this._statistics[command].totalTime ? this._statistics[command].totalTime + duration : duration; - this._statistics[command].avgTime = this._statistics[command].totalTime / this._statistics[command].countTime; + this._commandsStatistics[command].countTime = this._commandsStatistics[command].countTime ? this._commandsStatistics[command].countTime + 1 : 1; + this._commandsStatistics[command].minTime = this._commandsStatistics[command].minTime ? (this._commandsStatistics[command].minTime > duration ? duration : this._commandsStatistics[command].minTime) : duration; + this._commandsStatistics[command].maxTime = this._commandsStatistics[command].maxTime ? (this._commandsStatistics[command].maxTime < duration ? duration : this._commandsStatistics[command].maxTime) : duration; + this._commandsStatistics[command].totalTime = this._commandsStatistics[command].totalTime ? this._commandsStatistics[command].totalTime + duration : duration; + this._commandsStatistics[command].avgTime = this._commandsStatistics[command].totalTime / this._commandsStatistics[command].countTime; } - logPerformance(entry, className: string): void { + logPerformance(entry: PerformanceEntry, className: string): void { this.addPerformanceTimer(entry.name, entry.duration); logger.info(`${this._logPrefix()} class->${className}, method->${entry.name}, duration->${entry.duration}`); } _display(): void { - logger.info(this._logPrefix() + ' %j', this._statistics); + logger.info(this._logPrefix() + ' %j', this._commandsStatistics); } _displayInterval(): void { @@ -99,7 +103,7 @@ export default class Statistics { setInterval(() => { this._display(); }, Configuration.getStatisticsDisplayInterval() * 1000); - logger.info(this._logPrefix() + ' displayed every ' + Configuration.getStatisticsDisplayInterval() + 's'); + logger.info(this._logPrefix() + ' displayed every ' + Configuration.getStatisticsDisplayInterval().toString() + 's'); } }