From be4c670224282bebd69a75fec8fa45b7666634ff Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Wed, 26 Jul 2023 07:48:33 +0200 Subject: [PATCH] refactor: improve time handling code MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .../AutomaticTransactionGenerator.ts | 18 +++++++++------ src/charging-station/ChargingStation.ts | 17 +++++++------- .../ChargingStationWorkerBroadcastChannel.ts | 4 +++- .../ocpp/1.6/OCPP16IncomingRequestService.ts | 22 ++++++++++--------- .../ocpp/1.6/OCPP16ResponseService.ts | 3 ++- src/performance/PerformanceStatistics.ts | 4 +++- src/utils/Utils.ts | 12 ++++++++-- test/utils/Utils.test.ts | 8 +++++++ 8 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/charging-station/AutomaticTransactionGenerator.ts b/src/charging-station/AutomaticTransactionGenerator.ts index 578f47e9..5b27a258 100644 --- a/src/charging-station/AutomaticTransactionGenerator.ts +++ b/src/charging-station/AutomaticTransactionGenerator.ts @@ -2,6 +2,8 @@ import { AsyncResource } from 'node:async_hooks'; +import { hoursToMilliseconds, secondsToMilliseconds } from 'date-fns'; + import type { ChargingStation } from './ChargingStation'; import { checkChargingStation } from './ChargingStationUtils'; import { IdTagsCache } from './IdTagsCache'; @@ -241,13 +243,14 @@ export class AutomaticTransactionGenerator extends AsyncResource { await sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME); } while (!this.chargingStation?.ocppRequestService); } - const wait = + const wait = secondsToMilliseconds( getRandomInteger( this.chargingStation.getAutomaticTransactionGeneratorConfiguration() .maxDelayBetweenTwoTransactions, this.chargingStation.getAutomaticTransactionGeneratorConfiguration() .minDelayBetweenTwoTransactions, - ) * 1000; + ), + ); logger.info(`${this.logPrefix(connectorId)} waiting for ${formatDurationMilliSeconds(wait)}`); await sleep(wait); const start = secureRandom(); @@ -260,11 +263,12 @@ export class AutomaticTransactionGenerator extends AsyncResource { const startResponse = await this.startTransaction(connectorId); if (startResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { // Wait until end of transaction - const waitTrxEnd = + const waitTrxEnd = secondsToMilliseconds( getRandomInteger( this.chargingStation.getAutomaticTransactionGeneratorConfiguration().maxDuration, this.chargingStation.getAutomaticTransactionGeneratorConfiguration().minDuration, - ) * 1000; + ), + ); logger.info( `${this.logPrefix(connectorId)} transaction started with id ${this.chargingStation .getConnectorStatus(connectorId) @@ -320,9 +324,9 @@ export class AutomaticTransactionGenerator extends AsyncResource { this.connectorsStatus.get(connectorId)!.startDate = new Date(); this.connectorsStatus.get(connectorId)!.stopDate = new Date( this.connectorsStatus.get(connectorId)!.startDate!.getTime() + - this.chargingStation.getAutomaticTransactionGeneratorConfiguration().stopAfterHours * - 3600 * - 1000 - + hoursToMilliseconds( + this.chargingStation.getAutomaticTransactionGeneratorConfiguration().stopAfterHours, + ) - previousRunDuration, ); this.connectorsStatus.get(connectorId)!.start = true; diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 7e444c54..70390b15 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -14,6 +14,7 @@ import { dirname, join } from 'node:path'; import { URL } from 'node:url'; import { parentPort } from 'node:worker_threads'; +import { millisecondsToSeconds, secondsToMilliseconds } from 'date-fns'; import merge from 'just-merge'; import { type RawData, WebSocket } from 'ws'; @@ -516,11 +517,11 @@ export class ChargingStation { public getHeartbeatInterval(): number { const HeartbeatInterval = getConfigurationKey(this, StandardParametersKey.HeartbeatInterval); if (HeartbeatInterval) { - return convertToInt(HeartbeatInterval.value) * 1000; + return secondsToMilliseconds(convertToInt(HeartbeatInterval.value)); } const HeartBeatInterval = getConfigurationKey(this, StandardParametersKey.HeartBeatInterval); if (HeartBeatInterval) { - return convertToInt(HeartBeatInterval.value) * 1000; + return secondsToMilliseconds(convertToInt(HeartBeatInterval.value)); } this.stationInfo?.autoRegister === false && logger.warn( @@ -777,7 +778,7 @@ export class ChargingStation { terminateOpened: false, }, ): void { - options = { handshakeTimeout: this.getConnectionTimeout() * 1000, ...options }; + options = { handshakeTimeout: secondsToMilliseconds(this.getConnectionTimeout()), ...options }; params = { ...{ closeOpened: false, terminateOpened: false }, ...params }; if (this.started === false && this.starting === false) { logger.warn( @@ -1226,7 +1227,7 @@ export class ChargingStation { stationTemplate?.firmwareUpgrade ?? {}, ); stationInfo.resetTime = !isNullOrUndefined(stationTemplate?.resetTime) - ? stationTemplate.resetTime! * 1000 + ? secondsToMilliseconds(stationTemplate.resetTime!) : Constants.CHARGING_STATION_DEFAULT_RESET_TIME; stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo); return stationInfo; @@ -1340,7 +1341,7 @@ export class ChargingStation { if (this.stationInfo?.autoRegister === true) { this.bootNotificationResponse = { currentTime: new Date(), - interval: this.getHeartbeatInterval() / 1000, + interval: millisecondsToSeconds(this.getHeartbeatInterval()), status: RegistrationStatusEnumType.ACCEPTED, }; } @@ -1826,7 +1827,7 @@ export class ChargingStation { this.getRegistrationMaxRetries() !== -1 && ++registrationRetryCount; await sleep( this?.bootNotificationResponse?.interval - ? this.bootNotificationResponse.interval * 1000 + ? secondsToMilliseconds(this.bootNotificationResponse.interval) : Constants.DEFAULT_BOOT_NOTIFICATION_INTERVAL, ); } @@ -2280,7 +2281,7 @@ export class ChargingStation { if (this.isWebSocketConnectionOpened() === true) { this.wsConnection?.ping(); } - }, webSocketPingInterval * 1000); + }, secondsToMilliseconds(webSocketPingInterval)); logger.info( `${this.logPrefix()} WebSocket ping started every ${formatDurationSeconds( webSocketPingInterval, @@ -2378,7 +2379,7 @@ export class ChargingStation { ++this.autoReconnectRetryCount; const reconnectDelay = this.getReconnectExponentialDelay() ? exponentialDelay(this.autoReconnectRetryCount) - : this.getConnectionTimeout() * 1000; + : secondsToMilliseconds(this.getConnectionTimeout()); const reconnectDelayWithdraw = 1000; const reconnectTimeout = reconnectDelay && reconnectDelay - reconnectDelayWithdraw > 0 diff --git a/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts b/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts index 422c964b..7c1a2820 100644 --- a/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts +++ b/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts @@ -1,3 +1,5 @@ +import { secondsToMilliseconds } from 'date-fns'; + import { WorkerBroadcastChannel } from './WorkerBroadcastChannel'; import { BaseError, type OCPPError } from '../../exception'; import { @@ -193,7 +195,7 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne this.chargingStation.getConnectorStatus(requestPayload!.connectorId!)! .transactionId!, configuredMeterValueSampleInterval - ? convertToInt(configuredMeterValueSampleInterval.value) * 1000 + ? secondsToMilliseconds(convertToInt(configuredMeterValueSampleInterval.value)) : Constants.DEFAULT_METER_VALUES_INTERVAL, ), ], diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index c7337ab6..921c8f04 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -6,6 +6,7 @@ import { URL, fileURLToPath } from 'node:url'; import type { JSONSchemaType } from 'ajv'; import { Client, type FTPResponse } from 'basic-ftp'; +import { secondsToMilliseconds } from 'date-fns'; import { create } from 'tar'; import { OCPP16Constants } from './OCPP16Constants'; @@ -673,7 +674,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return OCPP16Constants.OCPP_RESPONSE_REJECTED; } const startDate = new Date(); - const endDate = new Date(startDate.getTime() + commandPayload.duration * 1000); + const endDate = new Date(startDate.getTime() + secondsToMilliseconds(commandPayload.duration)); let compositeSchedule: OCPP16ChargingSchedule | undefined; for (const chargingProfile of chargingStation.getConnectorStatus(commandPayload.connectorId)! .chargingProfiles!) { @@ -1156,7 +1157,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation.stationInfo?.firmwareUpgrade?.failureStatus === OCPP16FirmwareStatus.DownloadFailed ) { - await sleep(getRandomInteger(maxDelay, minDelay) * 1000); + await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))); await chargingStation.ocppRequestService.requestHandler< OCPP16FirmwareStatusNotificationRequest, OCPP16FirmwareStatusNotificationResponse @@ -1167,7 +1168,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation.stationInfo?.firmwareUpgrade?.failureStatus; return; } - await sleep(getRandomInteger(maxDelay, minDelay) * 1000); + await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))); await chargingStation.ocppRequestService.requestHandler< OCPP16FirmwareStatusNotificationRequest, OCPP16FirmwareStatusNotificationResponse @@ -1180,12 +1181,12 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { do { const runningTransactions = chargingStation.getNumberOfRunningTransactions(); if (runningTransactions > 0) { - const waitTime = 15 * 1000; + const waitTime = secondsToMilliseconds(15); logger.debug( `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: - ${runningTransactions} transaction(s) in progress, waiting ${ - waitTime / 1000 - } seconds before continuing firmware update simulation`, + ${runningTransactions} transaction(s) in progress, waiting ${formatDurationMilliSeconds( + waitTime, + )} before continuing firmware update simulation`, ); await sleep(waitTime); transactionsStarted = true; @@ -1223,7 +1224,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { transactionsStarted = false; } } while (transactionsStarted); - !wasTransactionsStarted && (await sleep(getRandomInteger(maxDelay, minDelay) * 1000)); + !wasTransactionsStarted && + (await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay)))); if (checkChargingStation(chargingStation, chargingStation.logPrefix()) === false) { return; } @@ -1238,7 +1240,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingStation.stationInfo?.firmwareUpgrade?.failureStatus === OCPP16FirmwareStatus.InstallationFailed ) { - await sleep(getRandomInteger(maxDelay, minDelay) * 1000); + await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))); await chargingStation.ocppRequestService.requestHandler< OCPP16FirmwareStatusNotificationRequest, OCPP16FirmwareStatusNotificationResponse @@ -1250,7 +1252,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return; } if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) { - await sleep(getRandomInteger(maxDelay, minDelay) * 1000); + await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))); await chargingStation.reset(OCPP16StopTransactionReason.REBOOT); } } diff --git a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts index 3e718285..3816c54f 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts @@ -3,6 +3,7 @@ import { parentPort } from 'node:worker_threads'; import type { JSONSchemaType } from 'ajv'; +import { secondsToMilliseconds } from 'date-fns'; import { OCPP16ServiceUtils } from './OCPP16ServiceUtils'; import { @@ -665,7 +666,7 @@ export class OCPP16ResponseService extends OCPPResponseService { chargingStation.startMeterValues( transactionConnectorId, configuredMeterValueSampleInterval - ? convertToInt(configuredMeterValueSampleInterval.value) * 1000 + ? secondsToMilliseconds(convertToInt(configuredMeterValueSampleInterval.value)) : Constants.DEFAULT_METER_VALUES_INTERVAL, ); } else { diff --git a/src/performance/PerformanceStatistics.ts b/src/performance/PerformanceStatistics.ts index 8451a193..c5748922 100644 --- a/src/performance/PerformanceStatistics.ts +++ b/src/performance/PerformanceStatistics.ts @@ -4,6 +4,8 @@ import { type PerformanceEntry, PerformanceObserver, performance } from 'node:pe import type { URL } from 'node:url'; import { parentPort } from 'node:worker_threads'; +import { secondsToMilliseconds } from 'date-fns'; + import { ConfigurationSection, type IncomingRequestCommand, @@ -185,7 +187,7 @@ export class PerformanceStatistics { if (logStatisticsInterval > 0 && !this.displayInterval) { this.displayInterval = setInterval(() => { this.logStatistics(); - }, logStatisticsInterval * 1000); + }, secondsToMilliseconds(logStatisticsInterval)); logger.info( `${this.logPrefix()} logged every ${formatDurationSeconds(logStatisticsInterval)}`, ); diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 48d0587c..dd6591f8 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -3,10 +3,13 @@ import { inspect } from 'node:util'; import { formatDuration, + hoursToMinutes, + hoursToSeconds, isDate, millisecondsToHours, millisecondsToMinutes, millisecondsToSeconds, + minutesToSeconds, secondsToMilliseconds, } from 'date-fns'; import clone from 'just-clone'; @@ -36,9 +39,14 @@ export const formatDurationMilliSeconds = (duration: number): string => { duration = convertToInt(duration); const days = Math.floor(duration / (24 * 3600 * 1000)); const hours = Math.floor(millisecondsToHours(duration) - days * 24); - const minutes = Math.floor(millisecondsToMinutes(duration) - days * 24 * 60 - hours * 60); + const minutes = Math.floor( + millisecondsToMinutes(duration) - days * 24 * 60 - hoursToMinutes(hours), + ); const seconds = Math.floor( - millisecondsToSeconds(duration) - days * 24 * 3600 - hours * 3600 - minutes * 60, + millisecondsToSeconds(duration) - + days * 24 * 3600 - + hoursToSeconds(hours) - + minutesToSeconds(minutes), ); return formatDuration({ days, diff --git a/test/utils/Utils.test.ts b/test/utils/Utils.test.ts index 5b9f8151..2b6df881 100644 --- a/test/utils/Utils.test.ts +++ b/test/utils/Utils.test.ts @@ -1,3 +1,4 @@ +import { hoursToMilliseconds } from 'date-fns'; import { expect } from 'expect'; import { Constants } from '../../src/utils/Constants'; @@ -7,6 +8,7 @@ import { convertToDate, convertToFloat, convertToInt, + formatDurationMilliSeconds, generateUUID, getRandomFloat, getRandomInteger, @@ -48,6 +50,12 @@ describe('Utils test suite', () => { expect(end - start).toBeGreaterThanOrEqual(1000); }); + it('Verify formatDurationMilliSeconds()', () => { + expect(formatDurationMilliSeconds(0)).toBe(''); + expect(formatDurationMilliSeconds(1000)).toBe('1 second'); + expect(formatDurationMilliSeconds(hoursToMilliseconds(4380))).toBe('182 days 12 hours'); + }); + it('Verify isValidDate()', () => { expect(isValidDate(undefined)).toBe(false); expect(isValidDate(null)).toBe(false); -- 2.34.1