From cda5d0fb59316951530288baebfaa181261295cb Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sat, 29 Apr 2023 10:21:15 +0200 Subject: [PATCH] fix: untangle connectors/evses init from station info one MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- src/charging-station/ChargingStation.ts | 130 +++++++++--------- src/charging-station/ChargingStationUtils.ts | 131 ++++++++++++------- 2 files changed, 150 insertions(+), 111 deletions(-) diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index b27f5b2e..419e89e3 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -128,6 +128,7 @@ export class ChargingStation { private wsConnectionRestarted: boolean; private autoReconnectRetryCount: number; private templateFileWatcher!: fs.FSWatcher | undefined; + private templateFileHash!: string; private readonly sharedLRUCache: SharedLRUCache; private webSocketPingSetInterval!: NodeJS.Timeout; private readonly chargingStationWorkerBroadcastChannel: ChargingStationWorkerBroadcastChannel; @@ -265,6 +266,9 @@ export class ChargingStation { public getNumberOfConnectors(): number { if (this.hasEvses) { + if (this.evses.size === 0) { + throw new BaseError('Evses not initialized, cannot get number of connectors'); + } let numberOfConnectors = 0; for (const [evseId, evseStatus] of this.evses) { if (evseId > 0) { @@ -273,10 +277,16 @@ export class ChargingStation { } return numberOfConnectors; } + if (this.connectors.size === 0) { + throw new BaseError('Connectors not initialized, cannot get number of connectors'); + } return this.connectors.has(0) ? this.connectors.size - 1 : this.connectors.size; } public getNumberOfEvses(): number { + if (this.evses.size === 0) { + throw new BaseError('Evses not initialized, cannot get number of evses'); + } return this.evses.has(0) ? this.evses.size - 1 : this.evses.size; } @@ -643,7 +653,7 @@ export class ChargingStation { this.templateFile } file have changed, reload` ); - this.sharedLRUCache.deleteChargingStationTemplate(this.stationInfo?.templateHash); + this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash); // Initialize this.initialize(); // Restart the ATG @@ -690,7 +700,7 @@ export class ChargingStation { } this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash); this.templateFileWatcher?.close(); - this.sharedLRUCache.deleteChargingStationTemplate(this.stationInfo?.templateHash); + this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash); delete this.bootNotificationResponse; this.started = false; parentPort?.postMessage(MessageChannelUtils.buildStoppedMessage(this)); @@ -903,8 +913,8 @@ export class ChargingStation { private getTemplateFromFile(): ChargingStationTemplate | undefined { let template: ChargingStationTemplate; try { - if (this.sharedLRUCache.hasChargingStationTemplate(this.stationInfo?.templateHash)) { - template = this.sharedLRUCache.getChargingStationTemplate(this.stationInfo.templateHash); + if (this.sharedLRUCache.hasChargingStationTemplate(this.templateFileHash)) { + template = this.sharedLRUCache.getChargingStationTemplate(this.templateFileHash); } else { const measureId = `${FileType.ChargingStationTemplate} read`; const beginId = PerformanceStatistics.beginMeasure(measureId); @@ -917,6 +927,7 @@ export class ChargingStation { .update(JSON.stringify(template)) .digest('hex'); this.sharedLRUCache.setChargingStationTemplate(template); + this.templateFileHash = template.templateHash; } } catch (error) { FileUtils.handleFileException( @@ -993,9 +1004,14 @@ export class ChargingStation { stationInfo.resetTime = !Utils.isNullOrUndefined(stationTemplate?.resetTime) ? stationTemplate.resetTime * 1000 : Constants.CHARGING_STATION_DEFAULT_RESET_TIME; - // Initialize evses or connectors if needed (FIXME: should be factored out but connectors and evses configuration are only available in templates) - this.initializeConnectorsOrEvses(stationInfo); stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo); + if (stationInfo?.Connectors) { + ChargingStationUtils.checkConnectorsConfiguration( + stationInfo, + this.templateFile, + this.logPrefix() + ); + } delete stationInfo?.Connectors; delete stationInfo?.Evses; return stationInfo; @@ -1051,10 +1067,17 @@ export class ChargingStation { } private initialize(): void { + const stationTemplate = this.getTemplateFromFile(); + if (Utils.isNullOrUndefined(stationTemplate)) { + const errorMsg = `Failed to read charging station template file ${this.templateFile}`; + logger.error(`${this.logPrefix()} ${errorMsg}`); + throw new BaseError(errorMsg); + } this.configurationFile = path.join( path.dirname(this.templateFile.replace('station-templates', 'configurations')), - `${ChargingStationUtils.getHashId(this.index, this.getTemplateFromFile())}.json` + `${ChargingStationUtils.getHashId(this.index, stationTemplate)}.json` ); + this.initializeConnectorsOrEvses(stationTemplate); this.stationInfo = this.getStationInfo(); if ( this.stationInfo.firmwareStatus === FirmwareStatus.Installing && @@ -1290,12 +1313,12 @@ export class ChargingStation { this.saveOcppConfiguration(); } - private initializeConnectorsOrEvses(stationInfo: ChargingStationInfo) { - if (stationInfo?.Connectors && !stationInfo?.Evses) { - this.initializeConnectors(stationInfo); - } else if (stationInfo?.Evses && !stationInfo?.Connectors) { - this.initializeEvses(stationInfo); - } else if (stationInfo?.Evses && stationInfo?.Connectors) { + private initializeConnectorsOrEvses(stationTemplate: ChargingStationTemplate) { + if (stationTemplate?.Connectors && !stationTemplate?.Evses) { + this.initializeConnectors(stationTemplate); + } else if (stationTemplate?.Evses && !stationTemplate?.Connectors) { + this.initializeEvses(stationTemplate); + } else if (stationTemplate?.Evses && stationTemplate?.Connectors) { const errorMsg = `Connectors and evses defined at the same time in template file ${this.templateFile}`; logger.error(`${this.logPrefix()} ${errorMsg}`); throw new BaseError(errorMsg); @@ -1306,72 +1329,51 @@ export class ChargingStation { } } - private initializeConnectors(stationInfo: ChargingStationInfo): void { - if (!stationInfo?.Connectors && this.connectors.size === 0) { + private initializeConnectors(stationTemplate: ChargingStationTemplate): void { + if (!stationTemplate?.Connectors && this.connectors.size === 0) { const errorMsg = `No already defined connectors and charging station information from template ${this.templateFile} with no connectors configuration defined`; logger.error(`${this.logPrefix()} ${errorMsg}`); throw new BaseError(errorMsg); } - if (!stationInfo?.Connectors[0]) { + if (!stationTemplate?.Connectors[0]) { logger.warn( `${this.logPrefix()} Charging station information from template ${ this.templateFile } with no connector id 0 configuration` ); } - if (stationInfo?.Connectors) { - const configuredMaxConnectors = - ChargingStationUtils.getConfiguredNumberOfConnectors(stationInfo); - ChargingStationUtils.checkConfiguredMaxConnectors( - configuredMaxConnectors, - this.templateFile, - this.logPrefix() - ); + if (stationTemplate?.Connectors) { + const { configuredMaxConnectors, templateMaxConnectors, templateMaxAvailableConnectors } = + ChargingStationUtils.checkConnectorsConfiguration( + stationTemplate, + this.templateFile, + this.logPrefix() + ); const connectorsConfigHash = crypto .createHash(Constants.DEFAULT_HASH_ALGORITHM) - .update(`${JSON.stringify(stationInfo?.Connectors)}${configuredMaxConnectors.toString()}`) + .update( + `${JSON.stringify(stationTemplate?.Connectors)}${configuredMaxConnectors.toString()}` + ) .digest('hex'); const connectorsConfigChanged = this.connectors?.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash; if (this.connectors?.size === 0 || connectorsConfigChanged) { connectorsConfigChanged && this.connectors.clear(); this.connectorsConfigurationHash = connectorsConfigHash; - const templateMaxConnectors = ChargingStationUtils.getMaxNumberOfConnectors( - stationInfo.Connectors - ); - ChargingStationUtils.checkTemplateMaxConnectors( - templateMaxConnectors, - this.templateFile, - this.logPrefix() - ); - const templateMaxAvailableConnectors = stationInfo?.Connectors[0] - ? templateMaxConnectors - 1 - : templateMaxConnectors; - if ( - configuredMaxConnectors > templateMaxAvailableConnectors && - !stationInfo?.randomConnectors - ) { - logger.warn( - `${this.logPrefix()} Number of connectors exceeds the number of connector configurations in template ${ - this.templateFile - }, forcing random connector configurations affectation` - ); - stationInfo.randomConnectors = true; - } if (templateMaxConnectors > 0) { for (let connectorId = 0; connectorId <= configuredMaxConnectors; connectorId++) { if ( connectorId === 0 && - (!stationInfo?.Connectors[connectorId] || - this.getUseConnectorId0(stationInfo) === false) + (!stationTemplate?.Connectors[connectorId] || + this.getUseConnectorId0(stationTemplate) === false) ) { continue; } const templateConnectorId = - connectorId > 0 && stationInfo?.randomConnectors + connectorId > 0 && stationTemplate?.randomConnectors ? Utils.getRandomInteger(templateMaxAvailableConnectors, 1) : connectorId; - const connectorStatus = stationInfo?.Connectors[templateConnectorId]; + const connectorStatus = stationTemplate?.Connectors[templateConnectorId]; ChargingStationUtils.checkStationInfoConnectorStatus( templateConnectorId, connectorStatus, @@ -1399,43 +1401,43 @@ export class ChargingStation { } } - private initializeEvses(stationInfo: ChargingStationInfo): void { - if (!stationInfo?.Evses && this.evses.size === 0) { + private initializeEvses(stationTemplate: ChargingStationTemplate): void { + if (!stationTemplate?.Evses && this.evses.size === 0) { const errorMsg = `No already defined evses and charging station information from template ${this.templateFile} with no evses configuration defined`; logger.error(`${this.logPrefix()} ${errorMsg}`); throw new BaseError(errorMsg); } - if (!stationInfo?.Evses[0]) { + if (!stationTemplate?.Evses[0]) { logger.warn( `${this.logPrefix()} Charging station information from template ${ this.templateFile } with no evse id 0 configuration` ); } - if (!stationInfo?.Evses[0]?.Connectors[0]) { + if (!stationTemplate?.Evses[0]?.Connectors[0]) { logger.warn( `${this.logPrefix()} Charging station information from template ${ this.templateFile } with evse id 0 with no connector id 0 configuration` ); } - if (stationInfo?.Evses) { + if (stationTemplate?.Evses) { const evsesConfigHash = crypto .createHash(Constants.DEFAULT_HASH_ALGORITHM) - .update(`${JSON.stringify(stationInfo?.Evses)}`) + .update(`${JSON.stringify(stationTemplate?.Evses)}`) .digest('hex'); const evsesConfigChanged = this.evses?.size !== 0 && this.evsesConfigurationHash !== evsesConfigHash; if (this.evses?.size === 0 || evsesConfigChanged) { evsesConfigChanged && this.evses.clear(); this.evsesConfigurationHash = evsesConfigHash; - const templateMaxEvses = ChargingStationUtils.getMaxNumberOfEvses(stationInfo?.Evses); + const templateMaxEvses = ChargingStationUtils.getMaxNumberOfEvses(stationTemplate?.Evses); if (templateMaxEvses > 0) { - for (const evse in stationInfo.Evses) { + for (const evse in stationTemplate.Evses) { const evseId = Utils.convertToInt(evse); this.evses.set(evseId, { connectors: ChargingStationUtils.buildConnectorsMap( - stationInfo?.Evses[evse]?.Connectors, + stationTemplate?.Evses[evse]?.Connectors, this.logPrefix(), this.templateFile ), @@ -1479,8 +1481,8 @@ export class ChargingStation { fs.readFileSync(this.configurationFile, 'utf8') ) as ChargingStationConfiguration; PerformanceStatistics.endMeasure(measureId, beginId); - this.configurationFileHash = configuration.configurationHash; this.sharedLRUCache.setChargingStationConfiguration(configuration); + this.configurationFileHash = configuration.configurationHash; } } catch (error) { FileUtils.handleFileException( @@ -1553,8 +1555,8 @@ export class ChargingStation { fs.closeSync(fileDescriptor); PerformanceStatistics.endMeasure(measureId, beginId); this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash); - this.configurationFileHash = configurationHash; this.sharedLRUCache.setChargingStationConfiguration(configurationData); + this.configurationFileHash = configurationHash; } else { logger.debug( `${this.logPrefix()} Not saving unchanged charging station configuration file ${ @@ -1861,8 +1863,8 @@ export class ChargingStation { ); } - private getUseConnectorId0(stationInfo?: ChargingStationInfo): boolean { - return (stationInfo ?? this.stationInfo)?.useConnectorId0 ?? true; + private getUseConnectorId0(stationTemplate?: ChargingStationTemplate): boolean { + return stationTemplate?.useConnectorId0 ?? true; } private async stopRunningTransactions(reason = StopTransactionReason.NONE): Promise { diff --git a/src/charging-station/ChargingStationUtils.ts b/src/charging-station/ChargingStationUtils.ts index cac243ff..517a3c9a 100644 --- a/src/charging-station/ChargingStationUtils.ts +++ b/src/charging-station/ChargingStationUtils.ts @@ -128,22 +128,6 @@ export class ChargingStationUtils { return Object.keys(connectors).length; } - public static checkTemplateMaxConnectors( - templateMaxConnectors: number, - templateFile: string, - logPrefix: string - ): void { - if (templateMaxConnectors === 0) { - logger.warn( - `${logPrefix} Charging station information from template ${templateFile} with empty connectors configuration` - ); - } else if (templateMaxConnectors < 0) { - logger.error( - `${logPrefix} Charging station information from template ${templateFile} with no connectors configuration defined` - ); - } - } - public static getBootConnectorStatus( chargingStation: ChargingStation, connectorId: number, @@ -169,42 +153,39 @@ export class ChargingStationUtils { return connectorBootStatus; } - public static getConfiguredNumberOfConnectors(stationInfo: ChargingStationInfo): number { - let configuredMaxConnectors: number; - if (Utils.isNotEmptyArray(stationInfo.numberOfConnectors) === true) { - const numberOfConnectors = stationInfo.numberOfConnectors as number[]; - configuredMaxConnectors = - numberOfConnectors[Math.floor(Utils.secureRandom() * numberOfConnectors.length)]; - } else if (Utils.isUndefined(stationInfo.numberOfConnectors) === false) { - configuredMaxConnectors = stationInfo.numberOfConnectors as number; - } else if (stationInfo.Connectors && !stationInfo.Evses) { - configuredMaxConnectors = stationInfo?.Connectors[0] - ? ChargingStationUtils.getMaxNumberOfConnectors(stationInfo.Connectors) - 1 - : ChargingStationUtils.getMaxNumberOfConnectors(stationInfo.Connectors); - } else if (stationInfo.Evses && !stationInfo.Connectors) { - configuredMaxConnectors = 0; - for (const evse in stationInfo.Evses) { - if (evse === '0') { - continue; - } - configuredMaxConnectors += ChargingStationUtils.getMaxNumberOfConnectors( - stationInfo.Evses[evse].Connectors - ); - } - } - return configuredMaxConnectors; - } - - public static checkConfiguredMaxConnectors( - configuredMaxConnectors: number, + public static checkConnectorsConfiguration( + stationTemplate: ChargingStationTemplate | ChargingStationInfo, templateFile: string, logPrefix: string - ): void { - if (configuredMaxConnectors <= 0) { + ): { + configuredMaxConnectors: number; + templateMaxConnectors: number; + templateMaxAvailableConnectors: number; + } { + const configuredMaxConnectors = + ChargingStationUtils.getConfiguredNumberOfConnectors(stationTemplate); + ChargingStationUtils.checkConfiguredMaxConnectors( + configuredMaxConnectors, + templateFile, + logPrefix + ); + const templateMaxConnectors = ChargingStationUtils.getMaxNumberOfConnectors( + stationTemplate.Connectors + ); + ChargingStationUtils.checkTemplateMaxConnectors(templateMaxConnectors, templateFile, logPrefix); + const templateMaxAvailableConnectors = stationTemplate?.Connectors[0] + ? templateMaxConnectors - 1 + : templateMaxConnectors; + if ( + configuredMaxConnectors > templateMaxAvailableConnectors && + !stationTemplate?.randomConnectors + ) { logger.warn( - `${logPrefix} Charging station information from template ${templateFile} with ${configuredMaxConnectors} connectors` + `${logPrefix} Number of connectors exceeds the number of connector configurations in template ${templateFile}, forcing random connector configurations affectation` ); + stationTemplate.randomConnectors = true; } + return { configuredMaxConnectors, templateMaxConnectors, templateMaxAvailableConnectors }; } public static checkStationInfoConnectorStatus( @@ -542,6 +523,62 @@ export class ChargingStationUtils { ); } + private static getConfiguredNumberOfConnectors( + stationTemplate: ChargingStationTemplate | ChargingStationInfo + ): number { + let configuredMaxConnectors: number; + if (Utils.isNotEmptyArray(stationTemplate.numberOfConnectors) === true) { + const numberOfConnectors = stationTemplate.numberOfConnectors as number[]; + configuredMaxConnectors = + numberOfConnectors[Math.floor(Utils.secureRandom() * numberOfConnectors.length)]; + } else if (Utils.isUndefined(stationTemplate.numberOfConnectors) === false) { + configuredMaxConnectors = stationTemplate.numberOfConnectors as number; + } else if (stationTemplate.Connectors && !stationTemplate.Evses) { + configuredMaxConnectors = stationTemplate?.Connectors[0] + ? ChargingStationUtils.getMaxNumberOfConnectors(stationTemplate.Connectors) - 1 + : ChargingStationUtils.getMaxNumberOfConnectors(stationTemplate.Connectors); + } else if (stationTemplate.Evses && !stationTemplate.Connectors) { + configuredMaxConnectors = 0; + for (const evse in stationTemplate.Evses) { + if (evse === '0') { + continue; + } + configuredMaxConnectors += ChargingStationUtils.getMaxNumberOfConnectors( + stationTemplate.Evses[evse].Connectors + ); + } + } + return configuredMaxConnectors; + } + + private static checkConfiguredMaxConnectors( + configuredMaxConnectors: number, + templateFile: string, + logPrefix: string + ): void { + if (configuredMaxConnectors <= 0) { + logger.warn( + `${logPrefix} Charging station information from template ${templateFile} with ${configuredMaxConnectors} connectors` + ); + } + } + + private static checkTemplateMaxConnectors( + templateMaxConnectors: number, + templateFile: string, + logPrefix: string + ): void { + if (templateMaxConnectors === 0) { + logger.warn( + `${logPrefix} Charging station information from template ${templateFile} with empty connectors configuration` + ); + } else if (templateMaxConnectors < 0) { + logger.error( + `${logPrefix} Charging station information from template ${templateFile} with no connectors configuration defined` + ); + } + } + private static initializeConnectorStatus(connectorStatus: ConnectorStatus): void { connectorStatus.availability = AvailabilityType.Operative; connectorStatus.idTagLocalAuthorized = false; -- 2.34.1