X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStation.ts;h=de005ddf86dfb058e614e08899361e744a0a7563;hb=1ca4a038d1b263b3b66a3c1872742bd98a696e75;hp=0ae3be5f14ef9cb32e0f06ec5cfe0f3e662c7450;hpb=f7c2994d6d3bd83aac94fec10def8730562d3c8f;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 0ae3be5f..de005ddf 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -9,31 +9,24 @@ import { parentPort } from 'node:worker_threads'; import merge from 'just-merge'; import WebSocket, { type RawData } from 'ws'; +import { AutomaticTransactionGenerator } from './AutomaticTransactionGenerator'; +import { ChargingStationWorkerBroadcastChannel } from './broadcast-channel/ChargingStationWorkerBroadcastChannel'; +import { ChargingStationConfigurationUtils } from './ChargingStationConfigurationUtils'; +import { ChargingStationUtils } from './ChargingStationUtils'; +import { IdTagsCache } from './IdTagsCache'; import { - AutomaticTransactionGenerator, - ChargingStationConfigurationUtils, - ChargingStationUtils, - ChargingStationWorkerBroadcastChannel, - IdTagsCache, - MessageChannelUtils, - SharedLRUCache, -} from './internal'; -import { - // OCPP16IncomingRequestService, + OCPP16IncomingRequestService, OCPP16RequestService, - // OCPP16ResponseService, + OCPP16ResponseService, OCPP16ServiceUtils, OCPP20IncomingRequestService, OCPP20RequestService, - // OCPP20ResponseService, + OCPP20ResponseService, type OCPPIncomingRequestService, type OCPPRequestService, - // OCPPServiceUtils, + OCPPServiceUtils, } from './ocpp'; -import { OCPP16IncomingRequestService } from './ocpp/1.6/OCPP16IncomingRequestService'; -import { OCPP16ResponseService } from './ocpp/1.6/OCPP16ResponseService'; -import { OCPP20ResponseService } from './ocpp/2.0/OCPP20ResponseService'; -import { OCPPServiceUtils } from './ocpp/OCPPServiceUtils'; +import { SharedLRUCache } from './SharedLRUCache'; import { BaseError, OCPPError } from '../exception'; import { PerformanceStatistics } from '../performance'; import { @@ -42,7 +35,6 @@ import { type BootNotificationRequest, type BootNotificationResponse, type CachedRequest, - type ChargingStationAutomaticTransactionGeneratorConfiguration, type ChargingStationConfiguration, type ChargingStationInfo, type ChargingStationOcppConfiguration, @@ -77,6 +69,7 @@ import { RequestCommand, type Response, StandardParametersKey, + type Status, type StatusNotificationRequest, type StatusNotificationResponse, StopTransactionReason, @@ -96,8 +89,13 @@ import { Configuration, Constants, DCElectricUtils, + ErrorUtils, FileUtils, + MessageChannelUtils, Utils, + buildChargingStationAutomaticTransactionGeneratorConfiguration, + buildConnectorsStatus, + buildEvsesStatus, logger, } from '../utils'; @@ -163,11 +161,17 @@ export class ChargingStation { return new URL( `${ this.getSupervisionUrlOcppConfiguration() && - Utils.isNotEmptyString(this.getSupervisionUrlOcppKey()) + Utils.isNotEmptyString(this.getSupervisionUrlOcppKey()) && + Utils.isNotEmptyString( + ChargingStationConfigurationUtils.getConfigurationKey( + this, + this.getSupervisionUrlOcppKey() + )?.value + ) ? ChargingStationConfigurationUtils.getConfigurationKey( this, this.getSupervisionUrlOcppKey() - )?.value + ).value : this.configuredSupervisionUrl.href }/${this.stationInfo.chargingStationId}` ); @@ -815,12 +819,29 @@ export class ChargingStation { public getAutomaticTransactionGeneratorConfiguration(): | AutomaticTransactionGeneratorConfiguration | undefined { + let automaticTransactionGeneratorConfiguration: + | AutomaticTransactionGeneratorConfiguration + | undefined; const automaticTransactionGeneratorConfigurationFromFile = this.getConfigurationFromFile()?.automaticTransactionGenerator; - if (automaticTransactionGeneratorConfigurationFromFile) { - return automaticTransactionGeneratorConfigurationFromFile; + if ( + this.getAutomaticTransactionGeneratorPersistentConfiguration() && + automaticTransactionGeneratorConfigurationFromFile + ) { + automaticTransactionGeneratorConfiguration = + automaticTransactionGeneratorConfigurationFromFile; + } else { + automaticTransactionGeneratorConfiguration = + this.getTemplateFromFile()?.AutomaticTransactionGenerator; } - return this.getTemplateFromFile()?.AutomaticTransactionGenerator; + return { + ...Constants.DEFAULT_ATG_CONFIGURATION, + ...automaticTransactionGeneratorConfiguration, + }; + } + + public getAutomaticTransactionGeneratorStatuses(): Status[] | undefined { + return this.getConfigurationFromFile()?.automaticTransactionGeneratorStatuses; } public startAutomaticTransactionGenerator(connectorIds?: number[]): void { @@ -936,7 +957,7 @@ export class ChargingStation { this.templateFileHash = template.templateHash; } } catch (error) { - FileUtils.handleFileException( + ErrorUtils.handleFileException( this.templateFile, FileType.ChargingStationTemplate, error as NodeJS.ErrnoException, @@ -948,7 +969,7 @@ export class ChargingStation { private getStationInfoFromTemplate(): ChargingStationInfo { const stationTemplate: ChargingStationTemplate | undefined = this.getTemplateFromFile(); - ChargingStationUtils.checkTemplateFile(stationTemplate, this.logPrefix(), this.templateFile); + ChargingStationUtils.checkTemplate(stationTemplate, this.logPrefix(), this.templateFile); ChargingStationUtils.warnTemplateKeysDeprecation( stationTemplate, this.logPrefix(), @@ -1055,6 +1076,10 @@ export class ChargingStation { return this.stationInfo?.stationInfoPersistentConfiguration ?? true; } + private getAutomaticTransactionGeneratorPersistentConfiguration(): boolean { + return this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration ?? true; + } + private handleUnsupportedVersion(version: OCPPVersion) { const errorMsg = `Unsupported protocol version '${version}' configured in template file ${this.templateFile}`; logger.error(`${this.logPrefix()} ${errorMsg}`); @@ -1063,15 +1088,14 @@ export class ChargingStation { private initialize(): void { const stationTemplate = this.getTemplateFromFile(); - ChargingStationUtils.checkTemplateFile(stationTemplate, this.logPrefix(), this.templateFile); + ChargingStationUtils.checkTemplate(stationTemplate, this.logPrefix(), this.templateFile); this.configurationFile = path.join( path.dirname(this.templateFile.replace('station-templates', 'configurations')), `${ChargingStationUtils.getHashId(this.index, stationTemplate)}.json` ); const chargingStationConfiguration = this.getConfigurationFromFile(); - const featureFlag = false; if ( - featureFlag && + chargingStationConfiguration?.stationInfo?.templateHash === stationTemplate?.templateHash && (chargingStationConfiguration?.connectorsStatus || chargingStationConfiguration?.evsesStatus) ) { this.initializeConnectorsOrEvsesFromFile(chargingStationConfiguration); @@ -1454,7 +1478,7 @@ export class ChargingStation { if (stationTemplate?.Evses) { const evsesConfigHash = crypto .createHash(Constants.DEFAULT_HASH_ALGORITHM) - .update(`${JSON.stringify(stationTemplate?.Evses)}`) + .update(JSON.stringify(stationTemplate?.Evses)) .digest('hex'); const evsesConfigChanged = this.evses?.size !== 0 && this.evsesConfigurationHash !== evsesConfigHash; @@ -1498,7 +1522,7 @@ export class ChargingStation { private getConfigurationFromFile(): ChargingStationConfiguration | undefined { let configuration: ChargingStationConfiguration | undefined; - if (this.configurationFile && fs.existsSync(this.configurationFile)) { + if (Utils.isNotEmptyString(this.configurationFile) && fs.existsSync(this.configurationFile)) { try { if (this.sharedLRUCache.hasChargingStationConfiguration(this.configurationFileHash)) { configuration = this.sharedLRUCache.getChargingStationConfiguration( @@ -1515,7 +1539,7 @@ export class ChargingStation { this.configurationFileHash = configuration.configurationHash; } } catch (error) { - FileUtils.handleFileException( + ErrorUtils.handleFileException( this.configurationFile, FileType.ChargingStationConfiguration, error as NodeJS.ErrnoException, @@ -1526,18 +1550,10 @@ export class ChargingStation { return configuration; } - private saveChargingStationAutomaticTransactionGeneratorConfiguration( - stationTemplate?: ChargingStationTemplate - ): void { - this.saveConfiguration({ - automaticTransactionGenerator: (stationTemplate ?? this.getTemplateFromFile()) - .AutomaticTransactionGenerator, - ...(!Utils.isNullOrUndefined(this.automaticTransactionGenerator?.connectorsStatus) && { - automaticTransactionGeneratorStatuses: [ - ...this.automaticTransactionGenerator.connectorsStatus.values(), - ], - }), - }); + private saveChargingStationAutomaticTransactionGeneratorConfiguration(): void { + if (this.getAutomaticTransactionGeneratorPersistentConfiguration()) { + this.saveConfiguration(); + } } private saveConnectorsStatus() { @@ -1548,10 +1564,8 @@ export class ChargingStation { this.saveConfiguration(); } - private saveConfiguration( - chargingStationAutomaticTransactionGeneratorConfiguration?: ChargingStationAutomaticTransactionGeneratorConfiguration - ): void { - if (this.configurationFile) { + private saveConfiguration(): void { + if (Utils.isNotEmptyString(this.configurationFile)) { try { if (!fs.existsSync(path.dirname(this.configurationFile))) { fs.mkdirSync(path.dirname(this.configurationFile), { recursive: true }); @@ -1560,39 +1574,44 @@ export class ChargingStation { Utils.cloneObject(this.getConfigurationFromFile()) ?? {}; if (this.getStationInfoPersistentConfiguration() && this.stationInfo) { configurationData.stationInfo = this.stationInfo; + } else { + delete configurationData.stationInfo; } if (this.getOcppPersistentConfiguration() && this.ocppConfiguration?.configurationKey) { configurationData.configurationKey = this.ocppConfiguration.configurationKey; + } else { + delete configurationData.configurationKey; } - if (chargingStationAutomaticTransactionGeneratorConfiguration) { - configurationData = merge( - configurationData, - chargingStationAutomaticTransactionGeneratorConfiguration - ); + configurationData = merge( + configurationData, + buildChargingStationAutomaticTransactionGeneratorConfiguration(this) + ); + if ( + !this.getAutomaticTransactionGeneratorPersistentConfiguration() || + !this.getAutomaticTransactionGeneratorConfiguration() + ) { + delete configurationData.automaticTransactionGenerator; } if (this.connectors.size > 0) { - configurationData.connectorsStatus = [...this.connectors.values()].map( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest - ); + configurationData.connectorsStatus = buildConnectorsStatus(this); + } else { + delete configurationData.connectorsStatus; } if (this.evses.size > 0) { - configurationData.evsesStatus = [...this.evses.values()].map((evseStatus) => { - const status = { - ...evseStatus, - connectorsStatus: [...evseStatus.connectors.values()].map( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest - ), - }; - delete status.connectors; - return status as EvseStatusConfiguration; - }); + configurationData.evsesStatus = buildEvsesStatus(this); + } else { + delete configurationData.evsesStatus; } delete configurationData.configurationHash; const configurationHash = crypto .createHash(Constants.DEFAULT_HASH_ALGORITHM) - .update(JSON.stringify(configurationData)) + .update( + JSON.stringify({ + stationInfo: configurationData.stationInfo, + configurationKey: configurationData.configurationKey, + automaticTransactionGenerator: configurationData.automaticTransactionGenerator, + } as ChargingStationConfiguration) + ) .digest('hex'); if (this.configurationFileHash !== configurationHash) { AsyncLock.acquire(AsyncLockType.configuration) @@ -1609,7 +1628,7 @@ export class ChargingStation { this.configurationFileHash = configurationHash; }) .catch((error) => { - FileUtils.handleFileException( + ErrorUtils.handleFileException( this.configurationFile, FileType.ChargingStationConfiguration, error as NodeJS.ErrnoException, @@ -1627,7 +1646,7 @@ export class ChargingStation { ); } } catch (error) { - FileUtils.handleFileException( + ErrorUtils.handleFileException( this.configurationFile, FileType.ChargingStationConfiguration, error as NodeJS.ErrnoException, @@ -1646,9 +1665,11 @@ export class ChargingStation { } private getOcppConfigurationFromFile(): ChargingStationOcppConfiguration | undefined { - if (this.getOcppPersistentConfiguration() === true) { - return { configurationKey: this.getConfigurationFromFile()?.configurationKey }; + const configurationKey = this.getConfigurationFromFile()?.configurationKey; + if (this.getOcppPersistentConfiguration() === true && configurationKey) { + return { configurationKey }; } + return undefined; } private getOcppConfiguration(): ChargingStationOcppConfiguration | undefined { @@ -1676,11 +1697,11 @@ export class ChargingStation { skipBufferingOnError: true, }); if (this.isRegistered() === false) { - this.getRegistrationMaxRetries() !== -1 && registrationRetryCount++; + this.getRegistrationMaxRetries() !== -1 && ++registrationRetryCount; await Utils.sleep( this?.bootNotificationResponse?.interval ? this.bootNotificationResponse.interval * 1000 - : Constants.OCPP_DEFAULT_BOOT_NOTIFICATION_INTERVAL + : Constants.DEFAULT_BOOT_NOTIFICATION_INTERVAL ); } } while ( @@ -2173,6 +2194,7 @@ export class ChargingStation { } private getConfiguredSupervisionUrl(): URL { + let configuredSupervisionUrl: string; const supervisionUrls = this.stationInfo?.supervisionUrls ?? Configuration.getSupervisionUrls(); if (Utils.isNotEmptyArray(supervisionUrls)) { let configuredSupervisionUrlIndex: number; @@ -2194,9 +2216,16 @@ export class ChargingStation { configuredSupervisionUrlIndex = (this.index - 1) % supervisionUrls.length; break; } - return new URL(supervisionUrls[configuredSupervisionUrlIndex]); + configuredSupervisionUrl = supervisionUrls[configuredSupervisionUrlIndex]; + } else { + configuredSupervisionUrl = supervisionUrls as string; } - return new URL(supervisionUrls as string); + if (Utils.isNotEmptyString(configuredSupervisionUrl)) { + return new URL(configuredSupervisionUrl); + } + const errorMsg = 'No supervision url(s) configured'; + logger.error(`${this.logPrefix()} ${errorMsg}`); + throw new BaseError(`${errorMsg}`); } private stopHeartbeat(): void { @@ -2230,7 +2259,7 @@ export class ChargingStation { this.autoReconnectRetryCount < this.getAutoReconnectMaxRetries() || this.getAutoReconnectMaxRetries() === -1 ) { - this.autoReconnectRetryCount++; + ++this.autoReconnectRetryCount; const reconnectDelay = this.getReconnectExponentialDelay() ? Utils.exponentialDelay(this.autoReconnectRetryCount) : this.getConnectionTimeout() * 1000;