From: Jérôme Benoit Date: Thu, 16 Sep 2021 21:23:36 +0000 (+0200) Subject: Fix ATG execution time accuracy. X-Git-Tag: v1.1.1~1 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=7d75bee1fb4f5946cba9bd453604ae9c359595c1;p=e-mobility-charging-stations-simulator.git Fix ATG execution time accuracy. And parallel run on each CS connectors Signed-off-by: Jérôme Benoit --- diff --git a/src/charging-station/AutomaticTransactionGenerator.ts b/src/charging-station/AutomaticTransactionGenerator.ts index 04f05da5..963042a4 100644 --- a/src/charging-station/AutomaticTransactionGenerator.ts +++ b/src/charging-station/AutomaticTransactionGenerator.ts @@ -10,6 +10,8 @@ import logger from '../utils/Logger'; export default class AutomaticTransactionGenerator { public timeToStop: boolean; + private startDate!: Date; + private stopDate!: Date; private chargingStation: ChargingStation; constructor(chargingStation: ChargingStation) { @@ -17,29 +19,27 @@ export default class AutomaticTransactionGenerator { this.timeToStop = true; } - public async start(): Promise { + public start(): void { + this.startDate = new Date(); + this.stopDate = new Date(this.startDate.getTime() + (this.chargingStation.stationInfo?.AutomaticTransactionGenerator?.stopAfterHours ?? Constants.CHARGING_STATION_ATG_DEFAULT_STOP_AFTER_HOURS) * 3600 * 1000); this.timeToStop = false; - if (this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours && - this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours > 0) { - // eslint-disable-next-line @typescript-eslint/no-misused-promises - setTimeout(async (): Promise => { - await this.stop(); - }, this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours * 3600 * 1000); - } for (const connector in this.chargingStation.connectors) { if (Utils.convertToInt(connector) > 0) { - await this.startConnector(Utils.convertToInt(connector)); + // Avoid hogging the event loop with a busy loop + setImmediate(() => { + this.startOnConnector(Utils.convertToInt(connector)).catch(() => { /* This is intentional */ }); + }); } } - logger.info(this.logPrefix() + ' ATG started and will stop in ' + Utils.secondsToHHMMSS(this.chargingStation.stationInfo.AutomaticTransactionGenerator.stopAfterHours * 3600)); + logger.info(this.logPrefix() + ' started and will run for ' + Utils.milliSecondsToHHMMSS(this.stopDate.getTime() - this.startDate.getTime())); } public async stop(reason: StopTransactionReason = StopTransactionReason.NONE): Promise { - logger.info(this.logPrefix() + ' ATG OVER => STOPPING ALL TRANSACTIONS'); + logger.info(this.logPrefix() + ' over. Stopping all transactions'); for (const connector in this.chargingStation.connectors) { const transactionId = this.chargingStation.getConnector(Utils.convertToInt(connector)).transactionId; if (this.chargingStation.getConnector(Utils.convertToInt(connector)).transactionStarted) { - logger.info(this.logPrefix(Utils.convertToInt(connector)) + ' ATG OVER. Stop transaction ' + transactionId.toString()); + logger.info(this.logPrefix(Utils.convertToInt(connector)) + ' over. Stop transaction ' + transactionId.toString()); await this.chargingStation.ocppRequestService.sendStopTransaction(transactionId, this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId), this.chargingStation.getTransactionIdTag(transactionId), reason); } @@ -47,10 +47,13 @@ export default class AutomaticTransactionGenerator { this.timeToStop = true; } - private async startConnector(connectorId: number): Promise { - do { - if (this.timeToStop) { - logger.error(this.logPrefix(connectorId) + ' Entered in transaction loop while a request to stop it was made'); + private async startOnConnector(connectorId: number): Promise { + logger.info(this.logPrefix(connectorId) + ' started on connector'); + let transactionSkip = 0; + let totalTransactionSkip = 0; + while (!this.timeToStop) { + if ((new Date()) > this.stopDate) { + await this.stop(); break; } if (!this.chargingStation.isRegistered()) { @@ -74,11 +77,11 @@ export default class AutomaticTransactionGenerator { } const wait = Utils.getRandomInt(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDelayBetweenTwoTransactions, this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDelayBetweenTwoTransactions) * 1000; - logger.info(this.logPrefix(connectorId) + ' wait for ' + Utils.milliSecondsToHHMMSS(wait)); + logger.info(this.logPrefix(connectorId) + ' waiting for ' + Utils.milliSecondsToHHMMSS(wait)); await Utils.sleep(wait); const start = Math.random(); - let skip = 0; if (start < this.chargingStation.stationInfo.AutomaticTransactionGenerator.probabilityOfStart) { + transactionSkip = 0; // Start transaction const startResponse = await this.startTransaction(connectorId); if (startResponse?.idTagInfo?.status !== AuthorizationStatus.ACCEPTED) { @@ -97,14 +100,14 @@ export default class AutomaticTransactionGenerator { } } } else { - skip++; - logger.info(this.logPrefix(connectorId) + ' transaction skipped ' + skip.toString()); + transactionSkip++; + totalTransactionSkip++; + logger.info(this.logPrefix(connectorId) + ' skipped transaction ' + transactionSkip.toString() + '/' + totalTransactionSkip.toString()); } - } while (!this.timeToStop); - logger.info(this.logPrefix(connectorId) + ' ATG STOPPED on the connector'); + } + logger.info(this.logPrefix(connectorId) + ' stopped on connector'); } - // eslint-disable-next-line consistent-this private async startTransaction(connectorId: number): Promise { const measureId = 'StartTransaction with ATG'; const beginId = PerformanceStatistics.beginMeasure(measureId); @@ -136,7 +139,6 @@ export default class AutomaticTransactionGenerator { return startResponse; } - // eslint-disable-next-line consistent-this private async stopTransaction(connectorId: number): Promise { const measureId = 'StopTransaction with ATG'; const beginId = PerformanceStatistics.beginMeasure(measureId); diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 67bf3b62..185eab5e 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -225,17 +225,17 @@ export default class ChargingStation { public getSampledValueTemplate(connectorId: number, measurand: MeterValueMeasurand = MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER, phase?: MeterValuePhase): SampledValueTemplate | undefined { if (!Constants.SUPPORTED_MEASURANDS.includes(measurand)) { - logger.warn(`${this.logPrefix()} Trying to get unsupported MeterValues measurand ${measurand} ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); + logger.warn(`${this.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); return; } if (measurand !== MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER && !this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(measurand)) { - logger.debug(`${this.logPrefix()} Trying to get MeterValues measurand ${measurand} ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId} not found in '${StandardParametersKey.MeterValuesSampledData}' OCPP parameter`); + logger.debug(`${this.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId} not found in '${StandardParametersKey.MeterValuesSampledData}' OCPP parameter`); return; } const sampledValueTemplates: SampledValueTemplate[] = this.getConnector(connectorId).MeterValues; for (let index = 0; !Utils.isEmptyArray(sampledValueTemplates) && index < sampledValueTemplates.length; index++) { if (!Constants.SUPPORTED_MEASURANDS.includes(sampledValueTemplates[index]?.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER)) { - logger.warn(`${this.logPrefix()} Unsupported MeterValues measurand ${measurand} ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); + logger.warn(`${this.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); } else if (phase && sampledValueTemplates[index]?.phase === phase && sampledValueTemplates[index]?.measurand === measurand && this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(measurand)) { return sampledValueTemplates[index]; @@ -248,11 +248,11 @@ export default class ChargingStation { } } if (measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) { - const errorMsg = `${this.logPrefix()} Missing MeterValues for default measurand ${measurand} in template on connectorId ${connectorId}`; + const errorMsg = `${this.logPrefix()} Missing MeterValues for default measurand '${measurand}' in template on connectorId ${connectorId}`; logger.error(errorMsg); throw new Error(errorMsg); } - logger.debug(`${this.logPrefix()} No MeterValues for measurand ${measurand} ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); + logger.debug(`${this.logPrefix()} No MeterValues for measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); } public getAutomaticTransactionGeneratorRequireAuthorize(): boolean { @@ -866,8 +866,7 @@ export default class ChargingStation { this.automaticTransactionGeneration = new AutomaticTransactionGenerator(this); } if (this.automaticTransactionGeneration.timeToStop) { - // The ATG might sleep - this.automaticTransactionGeneration.start().catch(() => { }); + this.automaticTransactionGeneration.start(); } } } diff --git a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts index 006088a2..1a0190e5 100644 --- a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts @@ -120,7 +120,6 @@ export default class OCPP16RequestService extends OCPPRequestService { } } - // eslint-disable-next-line consistent-this public async sendMeterValues(connectorId: number, transactionId: number, interval: number, debug = false): Promise { try { const meterValue: OCPP16MeterValue = { diff --git a/src/utils/Constants.ts b/src/utils/Constants.ts index 593aa42a..e6c28221 100644 --- a/src/utils/Constants.ts +++ b/src/utils/Constants.ts @@ -32,6 +32,7 @@ export default class Constants { static readonly CHARGING_STATION_DEFAULT_RESET_TIME = 60000; // Ms static readonly CHARGING_STATION_ATG_WAIT_TIME = 2000; // Ms static readonly CHARGING_STATION_ATG_INITIALIZATION_TIME = 1000; // Ms + static readonly CHARGING_STATION_ATG_DEFAULT_STOP_AFTER_HOURS = 0.25; // Hours static readonly TRANSACTION_DEFAULT_IDTAG = '00000000';