X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fcharging-station%2FChargingStation.ts;h=70390b152dd9356219c9ba2d2ced00a9d9c1c002;hb=be4c670224282bebd69a75fec8fa45b7666634ff;hp=d0c7b0780119e528ab5c6a019563f7177dc4c7ff;hpb=42371a2ed3ad187c91417fa759fd260ae86dee7c;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index d0c7b078..70390b15 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -14,12 +14,18 @@ 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'; import { AutomaticTransactionGenerator } from './AutomaticTransactionGenerator'; import { ChargingStationWorkerBroadcastChannel } from './broadcast-channel/ChargingStationWorkerBroadcastChannel'; -import { ChargingStationConfigurationUtils } from './ChargingStationConfigurationUtils'; +import { + addConfigurationKey, + deleteConfigurationKey, + getConfigurationKey, + setConfigurationKeyValue, +} from './ChargingStationConfigurationUtils'; import { buildConnectorsMap, checkConnectorsConfiguration, @@ -172,6 +178,7 @@ export class ChargingStation { private configurationFileHash!: string; private connectorsConfigurationHash!: string; private evsesConfigurationHash!: string; + private automaticTransactionGeneratorConfiguration?: AutomaticTransactionGeneratorConfiguration; private ocppIncomingRequestService!: OCPPIncomingRequestService; private readonly messageBuffer: Set; private configuredSupervisionUrl!: URL; @@ -212,16 +219,8 @@ export class ChargingStation { `${ this.getSupervisionUrlOcppConfiguration() && isNotEmptyString(this.getSupervisionUrlOcppKey()) && - isNotEmptyString( - ChargingStationConfigurationUtils.getConfigurationKey( - this, - this.getSupervisionUrlOcppKey(), - )?.value, - ) - ? ChargingStationConfigurationUtils.getConfigurationKey( - this, - this.getSupervisionUrlOcppKey(), - )!.value + isNotEmptyString(getConfigurationKey(this, this.getSupervisionUrlOcppKey())?.value) + ? getConfigurationKey(this, this.getSupervisionUrlOcppKey())!.value : this.configuredSupervisionUrl.href }/${this.stationInfo.chargingStationId}`, ); @@ -363,7 +362,10 @@ export class ChargingStation { public getMaximumPower(stationInfo?: ChargingStationInfo): number { const localStationInfo = stationInfo ?? this.stationInfo; // eslint-disable-next-line @typescript-eslint/dot-notation - return (localStationInfo['maxPower'] as number) ?? localStationInfo.maximumPower; + return ( + (localStationInfo['maxPower' as keyof ChargingStationInfo] as number) ?? + localStationInfo.maximumPower + ); } public getConnectorMaximumAvailablePower(connectorId: number): number { @@ -497,7 +499,7 @@ export class ChargingStation { } public getAuthorizeRemoteTxRequests(): boolean { - const authorizeRemoteTxRequests = ChargingStationConfigurationUtils.getConfigurationKey( + const authorizeRemoteTxRequests = getConfigurationKey( this, StandardParametersKey.AuthorizeRemoteTxRequests, ); @@ -505,7 +507,7 @@ export class ChargingStation { } public getLocalAuthListEnabled(): boolean { - const localAuthListEnabled = ChargingStationConfigurationUtils.getConfigurationKey( + const localAuthListEnabled = getConfigurationKey( this, StandardParametersKey.LocalAuthListEnabled, ); @@ -513,19 +515,13 @@ export class ChargingStation { } public getHeartbeatInterval(): number { - const HeartbeatInterval = ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.HeartbeatInterval, - ); + const HeartbeatInterval = getConfigurationKey(this, StandardParametersKey.HeartbeatInterval); if (HeartbeatInterval) { - return convertToInt(HeartbeatInterval.value) * 1000; + return secondsToMilliseconds(convertToInt(HeartbeatInterval.value)); } - const HeartBeatInterval = ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.HeartBeatInterval, - ); + const HeartBeatInterval = getConfigurationKey(this, StandardParametersKey.HeartBeatInterval); if (HeartBeatInterval) { - return convertToInt(HeartBeatInterval.value) * 1000; + return secondsToMilliseconds(convertToInt(HeartBeatInterval.value)); } this.stationInfo?.autoRegister === false && logger.warn( @@ -541,11 +537,7 @@ export class ChargingStation { this.getSupervisionUrlOcppConfiguration() && isNotEmptyString(this.getSupervisionUrlOcppKey()) ) { - ChargingStationConfigurationUtils.setConfigurationKeyValue( - this, - this.getSupervisionUrlOcppKey(), - url, - ); + setConfigurationKeyValue(this, this.getSupervisionUrlOcppKey(), url); } else { this.stationInfo.supervisionUrls = url; this.saveStationInfo(); @@ -601,14 +593,14 @@ export class ChargingStation { public startMeterValues(connectorId: number, interval: number): void { if (connectorId === 0) { logger.error( - `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId.toString()}`, + `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId}`, ); return; } if (!this.getConnectorStatus(connectorId)) { logger.error( `${this.logPrefix()} Trying to start MeterValues on non existing connector id - ${connectorId.toString()}`, + ${connectorId}`, ); return; } @@ -700,6 +692,7 @@ export class ChargingStation { this.idTagsCache.deleteIdTags(getIdTagsFile(this.stationInfo)!); // Restart the ATG this.stopAutomaticTransactionGenerator(); + delete this.automaticTransactionGeneratorConfiguration; if (this.getAutomaticTransactionGeneratorConfiguration()?.enable === true) { this.startAutomaticTransactionGenerator(); } @@ -768,7 +761,7 @@ export class ChargingStation { } public hasFeatureProfile(featureProfile: SupportedFeatureProfiles): boolean | undefined { - return ChargingStationConfigurationUtils.getConfigurationKey( + return getConfigurationKey( this, StandardParametersKey.SupportedFeatureProfiles, )?.value?.includes(featureProfile); @@ -785,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( @@ -856,25 +849,28 @@ export class ChargingStation { } public getAutomaticTransactionGeneratorConfiguration(): AutomaticTransactionGeneratorConfiguration { - let automaticTransactionGeneratorConfiguration: - | AutomaticTransactionGeneratorConfiguration - | undefined; - const automaticTransactionGeneratorConfigurationFromFile = - this.getConfigurationFromFile()?.automaticTransactionGenerator; - if ( - this.getAutomaticTransactionGeneratorPersistentConfiguration() && - automaticTransactionGeneratorConfigurationFromFile - ) { - automaticTransactionGeneratorConfiguration = - automaticTransactionGeneratorConfigurationFromFile; - } else { - automaticTransactionGeneratorConfiguration = - this.getTemplateFromFile()?.AutomaticTransactionGenerator; + if (isNullOrUndefined(this.automaticTransactionGeneratorConfiguration)) { + let automaticTransactionGeneratorConfiguration: + | AutomaticTransactionGeneratorConfiguration + | undefined; + const automaticTransactionGeneratorConfigurationFromFile = + this.getConfigurationFromFile()?.automaticTransactionGenerator; + if ( + this.getAutomaticTransactionGeneratorPersistentConfiguration() && + automaticTransactionGeneratorConfigurationFromFile + ) { + automaticTransactionGeneratorConfiguration = + automaticTransactionGeneratorConfigurationFromFile; + } else { + automaticTransactionGeneratorConfiguration = + this.getTemplateFromFile()?.AutomaticTransactionGenerator; + } + this.automaticTransactionGeneratorConfiguration = { + ...Constants.DEFAULT_ATG_CONFIGURATION, + ...automaticTransactionGeneratorConfiguration, + }; } - return { - ...Constants.DEFAULT_ATG_CONFIGURATION, - ...automaticTransactionGeneratorConfiguration, - }; + return this.automaticTransactionGeneratorConfiguration!; } public getAutomaticTransactionGeneratorStatuses(): Status[] | undefined { @@ -945,10 +941,7 @@ export class ChargingStation { public getReservationOnConnectorId0Enabled(): boolean { return convertToBoolean( - ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.ReserveConnectorZeroSupported, - )!.value, + getConfigurationKey(this, StandardParametersKey.ReserveConnectorZeroSupported)!.value, ); } @@ -1006,14 +999,14 @@ export class ChargingStation { if (this.hasEvses) { for (const evseStatus of this.evses.values()) { for (const connectorStatus of evseStatus.connectors.values()) { - if (connectorStatus?.reservation?.[filterKey] === value) { + if (connectorStatus?.reservation?.[filterKey as keyof Reservation] === value) { return connectorStatus.reservation; } } } } else { for (const connectorStatus of this.connectors.values()) { - if (connectorStatus?.reservation?.[filterKey] === value) { + if (connectorStatus?.reservation?.[filterKey as keyof Reservation] === value) { return connectorStatus.reservation; } } @@ -1033,32 +1026,38 @@ export class ChargingStation { public startReservationExpirationSetInterval(customInterval?: number): void { const interval = customInterval ?? Constants.DEFAULT_RESERVATION_EXPIRATION_OBSERVATION_INTERVAL; - logger.info( - `${this.logPrefix()} Reservation expiration date interval is set to ${interval} - and starts on charging station now`, - ); if (interval > 0) { - // eslint-disable-next-line @typescript-eslint/no-misused-promises - this.reservationExpirationSetInterval = setInterval(async (): Promise => { - const now = new Date(); + logger.info( + `${this.logPrefix()} Reservation expiration date checks started every ${formatDurationMilliSeconds( + interval, + )}`, + ); + this.reservationExpirationSetInterval = setInterval((): void => { + const currentDate = new Date(); if (this.hasEvses) { for (const evseStatus of this.evses.values()) { for (const connectorStatus of evseStatus.connectors.values()) { - if (connectorStatus.reservation!.expiryDate < now) { - await this.removeReservation( - connectorStatus.reservation!, + if ( + connectorStatus.reservation && + connectorStatus.reservation.expiryDate < currentDate + ) { + this.removeReservation( + connectorStatus.reservation, ReservationTerminationReason.EXPIRED, - ); + ).catch(Constants.EMPTY_FUNCTION); } } } } else { for (const connectorStatus of this.connectors.values()) { - if (connectorStatus.reservation!.expiryDate < now) { - await this.removeReservation( - connectorStatus.reservation!, + if ( + connectorStatus.reservation && + connectorStatus.reservation.expiryDate < currentDate + ) { + this.removeReservation( + connectorStatus.reservation, ReservationTerminationReason.EXPIRED, - ); + ).catch(Constants.EMPTY_FUNCTION); } } } @@ -1228,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; @@ -1342,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, }; } @@ -1373,37 +1372,18 @@ export class ChargingStation { } private initializeOcppConfiguration(): void { - if ( - !ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.HeartbeatInterval, - ) - ) { - ChargingStationConfigurationUtils.addConfigurationKey( - this, - StandardParametersKey.HeartbeatInterval, - '0', - ); + if (!getConfigurationKey(this, StandardParametersKey.HeartbeatInterval)) { + addConfigurationKey(this, StandardParametersKey.HeartbeatInterval, '0'); } - if ( - !ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.HeartBeatInterval, - ) - ) { - ChargingStationConfigurationUtils.addConfigurationKey( - this, - StandardParametersKey.HeartBeatInterval, - '0', - { visible: false }, - ); + if (!getConfigurationKey(this, StandardParametersKey.HeartBeatInterval)) { + addConfigurationKey(this, StandardParametersKey.HeartBeatInterval, '0', { visible: false }); } if ( this.getSupervisionUrlOcppConfiguration() && isNotEmptyString(this.getSupervisionUrlOcppKey()) && - !ChargingStationConfigurationUtils.getConfigurationKey(this, this.getSupervisionUrlOcppKey()) + !getConfigurationKey(this, this.getSupervisionUrlOcppKey()) ) { - ChargingStationConfigurationUtils.addConfigurationKey( + addConfigurationKey( this, this.getSupervisionUrlOcppKey(), this.configuredSupervisionUrl.href, @@ -1412,22 +1392,15 @@ export class ChargingStation { } else if ( !this.getSupervisionUrlOcppConfiguration() && isNotEmptyString(this.getSupervisionUrlOcppKey()) && - ChargingStationConfigurationUtils.getConfigurationKey(this, this.getSupervisionUrlOcppKey()) + getConfigurationKey(this, this.getSupervisionUrlOcppKey()) ) { - ChargingStationConfigurationUtils.deleteConfigurationKey( - this, - this.getSupervisionUrlOcppKey(), - { save: false }, - ); + deleteConfigurationKey(this, this.getSupervisionUrlOcppKey(), { save: false }); } if ( isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) && - !ChargingStationConfigurationUtils.getConfigurationKey( - this, - this.stationInfo.amperageLimitationOcppKey!, - ) + !getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!) ) { - ChargingStationConfigurationUtils.addConfigurationKey( + addConfigurationKey( this, this.stationInfo.amperageLimitationOcppKey!, ( @@ -1435,43 +1408,28 @@ export class ChargingStation { ).toString(), ); } - if ( - !ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.SupportedFeatureProfiles, - ) - ) { - ChargingStationConfigurationUtils.addConfigurationKey( + if (!getConfigurationKey(this, StandardParametersKey.SupportedFeatureProfiles)) { + addConfigurationKey( this, StandardParametersKey.SupportedFeatureProfiles, `${SupportedFeatureProfiles.Core},${SupportedFeatureProfiles.FirmwareManagement},${SupportedFeatureProfiles.LocalAuthListManagement},${SupportedFeatureProfiles.SmartCharging},${SupportedFeatureProfiles.RemoteTrigger}`, ); } - ChargingStationConfigurationUtils.addConfigurationKey( + addConfigurationKey( this, StandardParametersKey.NumberOfConnectors, this.getNumberOfConnectors().toString(), { readonly: true }, { overwrite: true }, ); - if ( - !ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.MeterValuesSampledData, - ) - ) { - ChargingStationConfigurationUtils.addConfigurationKey( + if (!getConfigurationKey(this, StandardParametersKey.MeterValuesSampledData)) { + addConfigurationKey( this, StandardParametersKey.MeterValuesSampledData, MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER, ); } - if ( - !ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.ConnectorPhaseRotation, - ) - ) { + if (!getConfigurationKey(this, StandardParametersKey.ConnectorPhaseRotation)) { const connectorsPhaseRotation: string[] = []; if (this.hasEvses) { for (const evseStatus of this.evses.values()) { @@ -1488,47 +1446,25 @@ export class ChargingStation { ); } } - ChargingStationConfigurationUtils.addConfigurationKey( + addConfigurationKey( this, StandardParametersKey.ConnectorPhaseRotation, connectorsPhaseRotation.toString(), ); } - if ( - !ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.AuthorizeRemoteTxRequests, - ) - ) { - ChargingStationConfigurationUtils.addConfigurationKey( - this, - StandardParametersKey.AuthorizeRemoteTxRequests, - 'true', - ); - } - if ( - !ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.LocalAuthListEnabled, - ) && - ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.SupportedFeatureProfiles, - )?.value?.includes(SupportedFeatureProfiles.LocalAuthListManagement) - ) { - ChargingStationConfigurationUtils.addConfigurationKey( - this, - StandardParametersKey.LocalAuthListEnabled, - 'false', - ); + if (!getConfigurationKey(this, StandardParametersKey.AuthorizeRemoteTxRequests)) { + addConfigurationKey(this, StandardParametersKey.AuthorizeRemoteTxRequests, 'true'); } if ( - !ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.ConnectionTimeOut, + !getConfigurationKey(this, StandardParametersKey.LocalAuthListEnabled) && + getConfigurationKey(this, StandardParametersKey.SupportedFeatureProfiles)?.value?.includes( + SupportedFeatureProfiles.LocalAuthListManagement, ) ) { - ChargingStationConfigurationUtils.addConfigurationKey( + addConfigurationKey(this, StandardParametersKey.LocalAuthListEnabled, 'false'); + } + if (!getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)) { + addConfigurationKey( this, StandardParametersKey.ConnectionTimeOut, Constants.DEFAULT_CONNECTION_TIMEOUT.toString(), @@ -1761,8 +1697,9 @@ export class ChargingStation { if (!existsSync(dirname(this.configurationFile))) { mkdirSync(dirname(this.configurationFile), { recursive: true }); } - let configurationData: ChargingStationConfiguration = - cloneObject(this.getConfigurationFromFile()!) ?? {}; + let configurationData: ChargingStationConfiguration = this.getConfigurationFromFile() + ? cloneObject(this.getConfigurationFromFile()!) + : {}; if (this.getStationInfoPersistentConfiguration() && this.stationInfo) { configurationData.stationInfo = this.stationInfo; } else { @@ -1890,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, ); } @@ -2164,19 +2101,10 @@ export class ChargingStation { // 0 for disabling private getConnectionTimeout(): number { - if ( - ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.ConnectionTimeOut, - ) - ) { + if (getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)) { return ( - parseInt( - ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.ConnectionTimeOut, - )!.value!, - ) ?? Constants.DEFAULT_CONNECTION_TIMEOUT + parseInt(getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)!.value!) ?? + Constants.DEFAULT_CONNECTION_TIMEOUT ); } return Constants.DEFAULT_CONNECTION_TIMEOUT; @@ -2219,17 +2147,11 @@ export class ChargingStation { private getAmperageLimitation(): number | undefined { if ( isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) && - ChargingStationConfigurationUtils.getConfigurationKey( - this, - this.stationInfo.amperageLimitationOcppKey!, - ) + getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!) ) { return ( convertToInt( - ChargingStationConfigurationUtils.getConfigurationKey( - this, - this.stationInfo.amperageLimitationOcppKey!, - )?.value, + getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!)?.value, ) / getAmperageLimitationUnitDivider(this.stationInfo) ); } @@ -2348,23 +2270,18 @@ export class ChargingStation { } private startWebSocketPing(): void { - const webSocketPingInterval: number = ChargingStationConfigurationUtils.getConfigurationKey( + const webSocketPingInterval: number = getConfigurationKey( this, StandardParametersKey.WebSocketPingInterval, ) - ? convertToInt( - ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.WebSocketPingInterval, - )?.value, - ) + ? convertToInt(getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval)?.value) : 0; if (webSocketPingInterval > 0 && !this.webSocketPingSetInterval) { this.webSocketPingSetInterval = setInterval(() => { if (this.isWebSocketConnectionOpened() === true) { this.wsConnection?.ping(); } - }, webSocketPingInterval * 1000); + }, secondsToMilliseconds(webSocketPingInterval)); logger.info( `${this.logPrefix()} WebSocket ping started every ${formatDurationSeconds( webSocketPingInterval, @@ -2462,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