X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStation.ts;h=92f15e4c20f7c71fdd5f1fffe55723eec306532e;hb=fc28853f03af7666e2f55a244b787157fddadfbc;hp=645c3f736b004be126891afc3f8bb8b2318c85dc;hpb=56d09fd7cdb49418af30cef6b9a47ea5007fb044;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 645c3f73..92f15e4c 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -10,11 +10,11 @@ import merge from 'just-merge'; import WebSocket, { type RawData } from 'ws'; import { - AuthorizedTagsCache, AutomaticTransactionGenerator, ChargingStationConfigurationUtils, ChargingStationUtils, ChargingStationWorkerBroadcastChannel, + IdTagsCache, MessageChannelUtils, SharedLRUCache, } from './internal'; @@ -47,7 +47,7 @@ import { type ChargingStationOcppConfiguration, type ChargingStationTemplate, ConnectorPhaseRotation, - ConnectorStatus, + type ConnectorStatus, ConnectorStatusEnum, CurrentType, type ErrorCallback, @@ -74,7 +74,6 @@ import { RegistrationStatusEnumType, RequestCommand, type Response, - type ResponseCallback, StandardParametersKey, type StatusNotificationRequest, type StatusNotificationResponse, @@ -104,7 +103,7 @@ export class ChargingStation { public stationInfo!: ChargingStationInfo; public started: boolean; public starting: boolean; - public authorizedTagsCache: AuthorizedTagsCache; + public idTagsCache: IdTagsCache; public automaticTransactionGenerator!: AutomaticTransactionGenerator | undefined; public ocppConfiguration!: ChargingStationOcppConfiguration | undefined; public wsConnection!: WebSocket | null; @@ -123,7 +122,6 @@ export class ChargingStation { private ocppIncomingRequestService!: OCPPIncomingRequestService; private readonly messageBuffer: Set; private configuredSupervisionUrl!: URL; - private configuredSupervisionUrlIndex!: number; private wsConnectionRestarted: boolean; private autoReconnectRetryCount: number; private templateFileWatcher!: fs.FSWatcher | undefined; @@ -143,7 +141,7 @@ export class ChargingStation { this.requests = new Map(); this.messageBuffer = new Set(); this.sharedLRUCache = SharedLRUCache.getInstance(); - this.authorizedTagsCache = AuthorizedTagsCache.getInstance(); + this.idTagsCache = IdTagsCache.getInstance(); this.chargingStationWorkerBroadcastChannel = new ChargingStationWorkerBroadcastChannel(this); this.initialize(); @@ -152,7 +150,8 @@ export class ChargingStation { private get wsConnectionUrl(): URL { return new URL( `${ - this.getSupervisionUrlOcppConfiguration() + this.getSupervisionUrlOcppConfiguration() && + Utils.isNotEmptyString(this.getSupervisionUrlOcppKey()) ? ChargingStationConfigurationUtils.getConfigurationKey( this, this.getSupervisionUrlOcppKey() @@ -165,20 +164,17 @@ export class ChargingStation { public logPrefix = (): string => { return Utils.logPrefix( ` ${ - (Utils.isNotEmptyString(this?.stationInfo?.chargingStationId) && - this?.stationInfo?.chargingStationId) ?? - ChargingStationUtils.getChargingStationId(this.index, this.getTemplateFromFile()) ?? - '' + (Utils.isNotEmptyString(this?.stationInfo?.chargingStationId) + ? this?.stationInfo?.chargingStationId + : ChargingStationUtils.getChargingStationId(this.index, this.getTemplateFromFile())) ?? + 'Error at building log prefix' } |` ); }; - public hasAuthorizedTags(): boolean { - return Utils.isNotEmptyArray( - this.authorizedTagsCache.getAuthorizedTags( - ChargingStationUtils.getAuthorizationFile(this.stationInfo) - ) - ); + public hasIdTags(): boolean { + const idTagsFile = ChargingStationUtils.getIdTagsFile(this.stationInfo); + return Utils.isNotEmptyArray(this.idTagsCache.getIdTags(idTagsFile)); } public getEnableStatistics(): boolean { @@ -387,12 +383,49 @@ export class ChargingStation { return localAuthListEnabled ? Utils.convertToBoolean(localAuthListEnabled.value) : false; } - public startHeartbeat(): void { + public getHeartbeatInterval(): number { + const HeartbeatInterval = ChargingStationConfigurationUtils.getConfigurationKey( + this, + StandardParametersKey.HeartbeatInterval + ); + if (HeartbeatInterval) { + return Utils.convertToInt(HeartbeatInterval.value) * 1000; + } + const HeartBeatInterval = ChargingStationConfigurationUtils.getConfigurationKey( + this, + StandardParametersKey.HeartBeatInterval + ); + if (HeartBeatInterval) { + return Utils.convertToInt(HeartBeatInterval.value) * 1000; + } + this.stationInfo?.autoRegister === false && + logger.warn( + `${this.logPrefix()} Heartbeat interval configuration key not set, using default value: ${ + Constants.DEFAULT_HEARTBEAT_INTERVAL + }` + ); + return Constants.DEFAULT_HEARTBEAT_INTERVAL; + } + + public setSupervisionUrl(url: string): void { if ( - this.getHeartbeatInterval() && - this.getHeartbeatInterval() > 0 && - !this.heartbeatSetInterval + this.getSupervisionUrlOcppConfiguration() && + Utils.isNotEmptyString(this.getSupervisionUrlOcppKey()) ) { + ChargingStationConfigurationUtils.setConfigurationKeyValue( + this, + this.getSupervisionUrlOcppKey(), + url + ); + } else { + this.stationInfo.supervisionUrls = url; + this.saveStationInfo(); + this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl(); + } + } + + public startHeartbeat(): void { + if (this.getHeartbeatInterval() > 0 && !this.heartbeatSetInterval) { this.heartbeatSetInterval = setInterval(() => { this.ocppRequestService .requestHandler(this, RequestCommand.HEARTBEAT) @@ -416,11 +449,7 @@ export class ChargingStation { ); } else { logger.error( - `${this.logPrefix()} Heartbeat interval set to ${ - this.getHeartbeatInterval() - ? Utils.formatDurationMilliSeconds(this.getHeartbeatInterval()) - : this.getHeartbeatInterval() - }, not starting the heartbeat` + `${this.logPrefix()} Heartbeat interval set to ${this.getHeartbeatInterval()}, not starting the heartbeat` ); } } @@ -496,9 +525,7 @@ export class ChargingStation { logger.error( `${this.logPrefix()} Charging station ${ StandardParametersKey.MeterValueSampleInterval - } configuration set to ${ - interval ? Utils.formatDurationMilliSeconds(interval) : interval - }, not sending MeterValues` + } configuration set to ${interval}, not sending MeterValues` ); } } @@ -625,7 +652,7 @@ export class ChargingStation { } public openWSConnection( - options: WsOptions = this.stationInfo?.wsOptions ?? Constants.EMPTY_OBJECT, + options: WsOptions = this.stationInfo?.wsOptions ?? {}, params: { closeOpened?: boolean; terminateOpened?: boolean } = { closeOpened: false, terminateOpened: false, @@ -769,7 +796,7 @@ export class ChargingStation { private flushMessageBuffer(): void { if (this.messageBuffer.size > 0) { - this.messageBuffer.forEach((message) => { + for (const message of this.messageBuffer.values()) { let beginId: string; let commandName: RequestCommand; const [messageType] = JSON.parse(message) as OutgoingRequest | Response | ErrorResponse; @@ -786,7 +813,7 @@ export class ChargingStation { )} payload sent: ${message}` ); this.messageBuffer.delete(message); - }); + } } } @@ -839,18 +866,10 @@ export class ChargingStation { logger.error(`${this.logPrefix()} ${errorMsg}`); throw new BaseError(errorMsg); } - // Deprecation template keys section - ChargingStationUtils.warnDeprecatedTemplateKey( - stationTemplate, - 'supervisionUrl', + ChargingStationUtils.warnTemplateKeysDeprecation( this.templateFile, - this.logPrefix(), - "Use 'supervisionUrls' instead" - ); - ChargingStationUtils.convertDeprecatedTemplateKey( stationTemplate, - 'supervisionUrl', - 'supervisionUrls' + this.logPrefix() ); const stationInfo: ChargingStationInfo = ChargingStationUtils.stationTemplateToStationInfo(stationTemplate); @@ -894,7 +913,7 @@ export class ChargingStation { }, reset: true, }, - stationTemplate?.firmwareUpgrade ?? Constants.EMPTY_OBJECT + stationTemplate?.firmwareUpgrade ?? {} ); stationInfo.resetTime = !Utils.isNullOrUndefined(stationTemplate?.resetTime) ? stationTemplate.resetTime * 1000 @@ -943,7 +962,10 @@ export class ChargingStation { private getStationInfo(): ChargingStationInfo { const stationInfoFromTemplate: ChargingStationInfo = this.getStationInfoFromTemplate(); const stationInfoFromFile: ChargingStationInfo | undefined = this.getStationInfoFromFile(); - // Priority: charging station info from template > charging station info from configuration file > charging station info attribute + // Priority: + // 1. charging station info from template + // 2. charging station info from configuration file + // 3. charging station info attribute if (stationInfoFromFile?.templateHash === stationInfoFromTemplate.templateHash) { if (this.stationInfo?.infoHash === stationInfoFromFile?.infoHash) { return this.stationInfo; @@ -985,6 +1007,24 @@ export class ChargingStation { `${ChargingStationUtils.getHashId(this.index, this.getTemplateFromFile())}.json` ); this.stationInfo = this.getStationInfo(); + if ( + this.stationInfo.firmwareStatus === FirmwareStatus.Installing && + Utils.isNotEmptyString(this.stationInfo.firmwareVersion) && + Utils.isNotEmptyString(this.stationInfo.firmwareVersionPattern) + ) { + const patternGroup: number | undefined = + this.stationInfo.firmwareUpgrade?.versionUpgrade?.patternGroup ?? + this.stationInfo.firmwareVersion?.split('.').length; + const match = this.stationInfo?.firmwareVersion + ?.match(new RegExp(this.stationInfo.firmwareVersionPattern)) + ?.slice(1, patternGroup + 1); + const patchLevelIndex = match.length - 1; + match[patchLevelIndex] = ( + Utils.convertToInt(match[patchLevelIndex]) + + this.stationInfo.firmwareUpgrade?.versionUpgrade?.step + ).toString(); + this.stationInfo.firmwareVersion = match?.join('.'); + } this.saveStationInfo(); // Avoid duplication of connectors related information in RAM delete this.stationInfo?.Connectors; @@ -1011,24 +1051,6 @@ export class ChargingStation { status: RegistrationStatusEnumType.ACCEPTED, }; } - if ( - this.stationInfo.firmwareStatus === FirmwareStatus.Installing && - Utils.isNotEmptyString(this.stationInfo.firmwareVersion) && - Utils.isNotEmptyString(this.stationInfo.firmwareVersionPattern) - ) { - const patternGroup: number | undefined = - this.stationInfo.firmwareUpgrade?.versionUpgrade?.patternGroup ?? - this.stationInfo.firmwareVersion?.split('.').length; - const match = this.stationInfo?.firmwareVersion - ?.match(new RegExp(this.stationInfo.firmwareVersionPattern)) - ?.slice(1, patternGroup + 1); - const patchLevelIndex = match.length - 1; - match[patchLevelIndex] = ( - Utils.convertToInt(match[patchLevelIndex]) + - this.stationInfo.firmwareUpgrade?.versionUpgrade?.step - ).toString(); - this.stationInfo.firmwareVersion = match?.join('.'); - } } private initializeOcppServices(): void { @@ -1083,6 +1105,7 @@ export class ChargingStation { } if ( this.getSupervisionUrlOcppConfiguration() && + Utils.isNotEmptyString(this.getSupervisionUrlOcppKey()) && !ChargingStationConfigurationUtils.getConfigurationKey(this, this.getSupervisionUrlOcppKey()) ) { ChargingStationConfigurationUtils.addConfigurationKey( @@ -1093,6 +1116,7 @@ export class ChargingStation { ); } else if ( !this.getSupervisionUrlOcppConfiguration() && + Utils.isNotEmptyString(this.getSupervisionUrlOcppKey()) && ChargingStationConfigurationUtils.getConfigurationKey(this, this.getSupervisionUrlOcppKey()) ) { ChargingStationConfigurationUtils.deleteConfigurationKey( @@ -1299,8 +1323,7 @@ export class ChargingStation { } if ( connectorId > 0 && - (this.getConnectorStatus(connectorId)?.transactionStarted === undefined || - this.getConnectorStatus(connectorId)?.transactionStarted === null) + Utils.isNullOrUndefined(this.getConnectorStatus(connectorId)?.transactionStarted) ) { this.initializeConnectorStatus(connectorId); } @@ -1358,7 +1381,7 @@ export class ChargingStation { fs.mkdirSync(path.dirname(this.configurationFile), { recursive: true }); } const configurationData: ChargingStationConfiguration = - Utils.cloneObject(this.getConfigurationFromFile()) ?? Constants.EMPTY_OBJECT; + Utils.cloneObject(this.getConfigurationFromFile()) ?? {}; this.ocppConfiguration?.configurationKey && (configurationData.configurationKey = this.ocppConfiguration.configurationKey); this.stationInfo && (configurationData.stationInfo = this.stationInfo); @@ -1510,7 +1533,7 @@ export class ChargingStation { messageType )} is not an array`, undefined, - cachedRequest as unknown as JsonType + cachedRequest as JsonType ); } @@ -1824,15 +1847,7 @@ export class ChargingStation { // Set default status connectorStatus = ConnectorStatusEnum.Available; } - await this.ocppRequestService.requestHandler< - StatusNotificationRequest, - StatusNotificationResponse - >( - this, - RequestCommand.STATUS_NOTIFICATION, - OCPPServiceUtils.buildStatusNotificationRequest(this, connectorId, connectorStatus) - ); - this.getConnectorStatus(connectorId).status = connectorStatus; + await OCPPServiceUtils.sendAndSetConnectorStatus(this, connectorId, connectorStatus); } if (this.stationInfo?.firmwareStatus === FirmwareStatus.Installing) { await this.ocppRequestService.requestHandler< @@ -1914,11 +1929,7 @@ export class ChargingStation { ); } else { logger.error( - `${this.logPrefix()} WebSocket ping interval set to ${ - webSocketPingInterval - ? Utils.formatDurationSeconds(webSocketPingInterval) - : webSocketPingInterval - }, not starting the WebSocket ping` + `${this.logPrefix()} WebSocket ping interval set to ${webSocketPingInterval}, not starting the WebSocket ping` ); } } @@ -1926,17 +1937,17 @@ export class ChargingStation { private stopWebSocketPing(): void { if (this.webSocketPingSetInterval) { clearInterval(this.webSocketPingSetInterval); + delete this.webSocketPingSetInterval; } } private getConfiguredSupervisionUrl(): URL { const supervisionUrls = this.stationInfo?.supervisionUrls ?? Configuration.getSupervisionUrls(); if (Utils.isNotEmptyArray(supervisionUrls)) { + let configuredSupervisionUrlIndex: number; switch (Configuration.getSupervisionUrlDistribution()) { case SupervisionUrlDistribution.RANDOM: - this.configuredSupervisionUrlIndex = Math.floor( - Utils.secureRandom() * supervisionUrls.length - ); + configuredSupervisionUrlIndex = Math.floor(Utils.secureRandom() * supervisionUrls.length); break; case SupervisionUrlDistribution.ROUND_ROBIN: case SupervisionUrlDistribution.CHARGING_STATION_AFFINITY: @@ -1949,41 +1960,18 @@ export class ChargingStation { SupervisionUrlDistribution.CHARGING_STATION_AFFINITY }` ); - this.configuredSupervisionUrlIndex = (this.index - 1) % supervisionUrls.length; + configuredSupervisionUrlIndex = (this.index - 1) % supervisionUrls.length; break; } - return new URL(supervisionUrls[this.configuredSupervisionUrlIndex]); + return new URL(supervisionUrls[configuredSupervisionUrlIndex]); } return new URL(supervisionUrls as string); } - private getHeartbeatInterval(): number { - const HeartbeatInterval = ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.HeartbeatInterval - ); - if (HeartbeatInterval) { - return Utils.convertToInt(HeartbeatInterval.value) * 1000; - } - const HeartBeatInterval = ChargingStationConfigurationUtils.getConfigurationKey( - this, - StandardParametersKey.HeartBeatInterval - ); - if (HeartBeatInterval) { - return Utils.convertToInt(HeartBeatInterval.value) * 1000; - } - this.stationInfo?.autoRegister === false && - logger.warn( - `${this.logPrefix()} Heartbeat interval configuration key not set, using default value: ${ - Constants.DEFAULT_HEARTBEAT_INTERVAL - }` - ); - return Constants.DEFAULT_HEARTBEAT_INTERVAL; - } - private stopHeartbeat(): void { if (this.heartbeatSetInterval) { clearInterval(this.heartbeatSetInterval); + delete this.heartbeatSetInterval; } } @@ -2038,7 +2026,7 @@ export class ChargingStation { ); this.openWSConnection( { - ...(this.stationInfo?.wsOptions ?? Constants.EMPTY_OBJECT), + ...(this.stationInfo?.wsOptions ?? {}), handshakeTimeout: reconnectTimeout, }, { closeOpened: true }