X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStation.ts;h=7534f66e72f38abd778d11397b42708a03ab0af3;hb=43be4c0834fe806a68f99036805779f29058d74e;hp=a57b5bd46545057ddbbe28e3d29e0b2a781a9c9a;hpb=2466918c081644f9a1dc4722efbd002d7baf2925;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index a57b5bd4..7534f66e 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -1,4 +1,4 @@ -// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. +// Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved. import { createHash } from 'node:crypto' import { EventEmitter } from 'node:events' @@ -132,8 +132,9 @@ import { buildStartedMessage, buildStoppedMessage, buildUpdatedMessage, - cloneObject, + clone, convertToBoolean, + convertToDate, convertToInt, exponentialDelay, formatDurationMilliSeconds, @@ -169,8 +170,8 @@ export class ChargingStation extends EventEmitter { public performanceStatistics!: PerformanceStatistics | undefined public heartbeatSetInterval?: NodeJS.Timeout public ocppRequestService!: OCPPRequestService - public bootNotificationRequest!: BootNotificationRequest - public bootNotificationResponse!: BootNotificationResponse | undefined + public bootNotificationRequest?: BootNotificationRequest + public bootNotificationResponse?: BootNotificationResponse public powerDivider?: number private stopping: boolean private configurationFile!: string @@ -228,18 +229,20 @@ export class ChargingStation extends EventEmitter { `${ this.stationInfo?.supervisionUrlOcppConfiguration === true && isNotEmptyString(this.stationInfo.supervisionUrlOcppKey) && - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - isNotEmptyString(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)?.value) - ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)!.value + isNotEmptyString(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey)?.value) + ? getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey)?.value : this.configuredSupervisionUrl.href }/${this.stationInfo?.chargingStationId}` ) } public logPrefix = (): string => { - if (isNotEmptyString(this.stationInfo?.chargingStationId)) { - return logPrefix(` ${this.stationInfo?.chargingStationId} |`) + if ( + this instanceof ChargingStation && + this.stationInfo != null && + isNotEmptyString(this.stationInfo.chargingStationId) + ) { + return logPrefix(` ${this.stationInfo.chargingStationId} |`) } let stationTemplate: ChargingStationTemplate | undefined try { @@ -259,7 +262,7 @@ export class ChargingStation extends EventEmitter { public getNumberOfPhases (stationInfo?: ChargingStationInfo): number { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo! + const localStationInfo = stationInfo ?? this.stationInfo! switch (this.getCurrentOutType(stationInfo)) { case CurrentType.AC: return localStationInfo.numberOfPhases ?? 3 @@ -423,8 +426,10 @@ export class ChargingStation extends EventEmitter { return numberOfRunningTransactions } - public getConnectorIdByTransactionId (transactionId: number): number | undefined { - if (this.hasEvses) { + public getConnectorIdByTransactionId (transactionId: number | undefined): number | undefined { + if (transactionId == null) { + return undefined + } else if (this.hasEvses) { for (const evseStatus of this.evses.values()) { for (const [connectorId, connectorStatus] of evseStatus.connectors) { if (connectorStatus.transactionId === transactionId) { @@ -442,19 +447,18 @@ export class ChargingStation extends EventEmitter { } public getEnergyActiveImportRegisterByTransactionId ( - transactionId: number, + transactionId: number | undefined, rounded = false ): number { return this.getEnergyActiveImportRegister( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.getConnectorStatus(this.getConnectorIdByTransactionId(transactionId)!)!, + this.getConnectorStatus(this.getConnectorIdByTransactionId(transactionId)!), rounded ) } public getEnergyActiveImportRegisterByConnectorId (connectorId: number, rounded = false): number { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return this.getEnergyActiveImportRegister(this.getConnectorStatus(connectorId)!, rounded) + return this.getEnergyActiveImportRegister(this.getConnectorStatus(connectorId), rounded) } public getAuthorizeRemoteTxRequests (): boolean { @@ -498,8 +502,7 @@ export class ChargingStation extends EventEmitter { this.stationInfo?.supervisionUrlOcppConfiguration === true && isNotEmptyString(this.stationInfo.supervisionUrlOcppKey) ) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - setConfigurationKeyValue(this, this.stationInfo.supervisionUrlOcppKey!, url) + setConfigurationKeyValue(this, this.stationInfo.supervisionUrlOcppKey, url) } else { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.stationInfo!.supervisionUrls = url @@ -513,7 +516,7 @@ export class ChargingStation extends EventEmitter { this.heartbeatSetInterval = setInterval(() => { this.ocppRequestService .requestHandler(this, RequestCommand.HEARTBEAT) - .catch((error) => { + .catch(error => { logger.error( `${this.logPrefix()} Error while sending '${RequestCommand.HEARTBEAT}':`, error @@ -557,21 +560,22 @@ export class ChargingStation extends EventEmitter { logger.error(`${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId}`) return } - if (this.getConnectorStatus(connectorId) == null) { + const connectorStatus = this.getConnectorStatus(connectorId) + if (connectorStatus == null) { logger.error( `${this.logPrefix()} Trying to start MeterValues on non existing connector id ${connectorId}` ) return } - if (this.getConnectorStatus(connectorId)?.transactionStarted === false) { + if (connectorStatus.transactionStarted === false) { logger.error( `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction started` ) return } else if ( - this.getConnectorStatus(connectorId)?.transactionStarted === true && - this.getConnectorStatus(connectorId)?.transactionId == null + connectorStatus.transactionStarted === true && + connectorStatus.transactionId == null ) { logger.error( `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction id` @@ -579,13 +583,12 @@ export class ChargingStation extends EventEmitter { return } if (interval > 0) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.getConnectorStatus(connectorId)!.transactionSetInterval = setInterval(() => { + connectorStatus.transactionSetInterval = setInterval(() => { const meterValue = buildMeterValue( this, connectorId, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.getConnectorStatus(connectorId)!.transactionId!, + connectorStatus.transactionId!, interval ) this.ocppRequestService @@ -594,11 +597,11 @@ export class ChargingStation extends EventEmitter { RequestCommand.METER_VALUES, { connectorId, - transactionId: this.getConnectorStatus(connectorId)?.transactionId, + transactionId: connectorStatus.transactionId, meterValue: [meterValue] } ) - .catch((error) => { + .catch(error => { logger.error( `${this.logPrefix()} Error while sending '${RequestCommand.METER_VALUES}':`, error @@ -615,8 +618,9 @@ export class ChargingStation extends EventEmitter { } public stopMeterValues (connectorId: number): void { - if (this.getConnectorStatus(connectorId)?.transactionSetInterval != null) { - clearInterval(this.getConnectorStatus(connectorId)?.transactionSetInterval) + const connectorStatus = this.getConnectorStatus(connectorId) + if (connectorStatus?.transactionSetInterval != null) { + clearInterval(connectorStatus.transactionSetInterval) } } @@ -827,8 +831,7 @@ export class ChargingStation extends EventEmitter { public startAutomaticTransactionGenerator (connectorIds?: number[]): void { this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(this) if (isNotEmptyArray(connectorIds)) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - for (const connectorId of connectorIds!) { + for (const connectorId of connectorIds) { this.automaticTransactionGenerator?.startConnector(connectorId) } } else { @@ -840,8 +843,7 @@ export class ChargingStation extends EventEmitter { public stopAutomaticTransactionGenerator (connectorIds?: number[]): void { if (isNotEmptyArray(connectorIds)) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - for (const connectorId of connectorIds!) { + for (const connectorId of connectorIds) { this.automaticTransactionGenerator?.stopConnector(connectorId) } } else { @@ -855,8 +857,7 @@ export class ChargingStation extends EventEmitter { connectorId: number, reason?: StopTransactionReason ): Promise { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const transactionId = this.getConnectorStatus(connectorId)!.transactionId! + const transactionId = this.getConnectorStatus(connectorId)?.transactionId if ( this.stationInfo?.beginEndMeterValues === true && this.stationInfo.ocppStrictCompliance === true && @@ -1088,31 +1089,30 @@ export class ChargingStation extends EventEmitter { private getStationInfoFromTemplate (): ChargingStationInfo { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const stationTemplate: ChargingStationTemplate = this.getTemplateFromFile()! + const stationTemplate = this.getTemplateFromFile()! checkTemplate(stationTemplate, this.logPrefix(), this.templateFile) const warnTemplateKeysDeprecationOnce = once(warnTemplateKeysDeprecation, this) warnTemplateKeysDeprecationOnce(stationTemplate, this.logPrefix(), this.templateFile) if (stationTemplate.Connectors != null) { checkConnectorsConfiguration(stationTemplate, this.logPrefix(), this.templateFile) } - const stationInfo: ChargingStationInfo = stationTemplateToStationInfo(stationTemplate) + const stationInfo = stationTemplateToStationInfo(stationTemplate) stationInfo.hashId = getHashId(this.index, stationTemplate) stationInfo.chargingStationId = getChargingStationId(this.index, stationTemplate) stationInfo.ocppVersion = stationTemplate.ocppVersion ?? OCPPVersion.VERSION_16 createSerialNumber(stationTemplate, stationInfo) stationInfo.voltageOut = this.getVoltageOut(stationInfo) if (isNotEmptyArray(stationTemplate.power)) { - stationTemplate.power = stationTemplate.power as number[] const powerArrayRandomIndex = Math.floor(secureRandom() * stationTemplate.power.length) stationInfo.maximumPower = stationTemplate.powerUnit === PowerUnits.KILO_WATT ? stationTemplate.power[powerArrayRandomIndex] * 1000 : stationTemplate.power[powerArrayRandomIndex] } else { - stationTemplate.power = stationTemplate.power as number stationInfo.maximumPower = stationTemplate.powerUnit === PowerUnits.KILO_WATT - ? stationTemplate.power * 1000 + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + stationTemplate.power! * 1000 : stationTemplate.power } stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo) @@ -1120,8 +1120,7 @@ export class ChargingStation extends EventEmitter { stationTemplate.firmwareVersionPattern ?? Constants.SEMVER_PATTERN if ( isNotEmptyString(stationInfo.firmwareVersion) && - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - !new RegExp(stationInfo.firmwareVersionPattern).test(stationInfo.firmwareVersion!) + !new RegExp(stationInfo.firmwareVersionPattern).test(stationInfo.firmwareVersion) ) { logger.warn( `${this.logPrefix()} Firmware version '${stationInfo.firmwareVersion}' in template file ${ @@ -1160,8 +1159,8 @@ export class ChargingStation extends EventEmitter { private getStationInfo (): ChargingStationInfo { const defaultStationInfo = Constants.DEFAULT_STATION_INFO - const stationInfoFromTemplate: ChargingStationInfo = this.getStationInfoFromTemplate() - const stationInfoFromFile: ChargingStationInfo | undefined = this.getStationInfoFromFile( + const stationInfoFromTemplate = this.getStationInfoFromTemplate() + const stationInfoFromFile = this.getStationInfoFromFile( stationInfoFromTemplate.stationInfoPersistentConfiguration ) // Priority: @@ -1220,13 +1219,10 @@ export class ChargingStation extends EventEmitter { ) { const patternGroup = this.stationInfo.firmwareUpgrade?.versionUpgrade?.patternGroup ?? - this.stationInfo.firmwareVersion?.split('.').length - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const match = new RegExp(this.stationInfo.firmwareVersionPattern!) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .exec(this.stationInfo.firmwareVersion!) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ?.slice(1, patternGroup! + 1) + this.stationInfo.firmwareVersion.split('.').length + const match = new RegExp(this.stationInfo.firmwareVersionPattern) + .exec(this.stationInfo.firmwareVersion) + ?.slice(1, patternGroup + 1) if (match != null) { const patchLevelIndex = match.length - 1 match[patchLevelIndex] = ( @@ -1259,7 +1255,7 @@ export class ChargingStation extends EventEmitter { this.initializeOcppConfiguration() this.initializeOcppServices() this.once(ChargingStationEvents.accepted, () => { - this.startMessageSequence().catch((error) => { + this.startMessageSequence().catch(error => { logger.error(`${this.logPrefix()} Error while starting the message sequence:`, error) }) }) @@ -1306,37 +1302,31 @@ export class ChargingStation extends EventEmitter { if ( this.stationInfo?.supervisionUrlOcppConfiguration === true && isNotEmptyString(this.stationInfo.supervisionUrlOcppKey) && - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!) == null + getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey) == null ) { addConfigurationKey( this, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.stationInfo.supervisionUrlOcppKey!, + this.stationInfo.supervisionUrlOcppKey, this.configuredSupervisionUrl.href, { reboot: true } ) } else if ( this.stationInfo?.supervisionUrlOcppConfiguration === false && isNotEmptyString(this.stationInfo.supervisionUrlOcppKey) && - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!) != null + getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey) != null ) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - deleteConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!, { save: false }) + deleteConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey, { save: false }) } if ( isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) && - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!) == null + getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey) == null ) { addConfigurationKey( this, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.stationInfo!.amperageLimitationOcppKey!, + this.stationInfo.amperageLimitationOcppKey, // prettier-ignore // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - (this.stationInfo!.maximumAmperage! * getAmperageLimitationUnitDivider(this.stationInfo)).toString() + (this.stationInfo.maximumAmperage! * getAmperageLimitationUnitDivider(this.stationInfo)).toString() ) } if (getConfigurationKey(this, StandardParametersKey.SupportedFeatureProfiles) == null) { @@ -1407,11 +1397,11 @@ export class ChargingStation extends EventEmitter { private initializeConnectorsOrEvsesFromFile (configuration: ChargingStationConfiguration): void { if (configuration.connectorsStatus != null && configuration.evsesStatus == null) { for (const [connectorId, connectorStatus] of configuration.connectorsStatus.entries()) { - this.connectors.set(connectorId, cloneObject(connectorStatus)) + this.connectors.set(connectorId, clone(connectorStatus)) } } else if (configuration.evsesStatus != null && configuration.connectorsStatus == null) { for (const [evseId, evseStatusConfiguration] of configuration.evsesStatus.entries()) { - const evseStatus = cloneObject(evseStatusConfiguration) + const evseStatus = clone(evseStatusConfiguration) delete evseStatus.connectorsStatus this.evses.set(evseId, { ...(evseStatus as EvseStatus), @@ -1498,7 +1488,7 @@ export class ChargingStation extends EventEmitter { this.logPrefix(), this.templateFile ) - this.connectors.set(connectorId, cloneObject(connectorStatus)) + this.connectors.set(connectorId, clone(connectorStatus)) } initializeConnectorsMapStatus(this.connectors, this.logPrefix()) this.saveConnectorsStatus() @@ -1642,7 +1632,7 @@ export class ChargingStation extends EventEmitter { const configurationFromFile = this.getConfigurationFromFile() let configurationData: ChargingStationConfiguration = configurationFromFile != null - ? cloneObject(configurationFromFile) + ? clone(configurationFromFile) : {} if (this.stationInfo?.stationInfoPersistentConfiguration === true) { configurationData.stationInfo = this.stationInfo @@ -1705,7 +1695,7 @@ export class ChargingStation extends EventEmitter { this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash) this.sharedLRUCache.setChargingStationConfiguration(configurationData) this.configurationFileHash = configurationHash - }).catch((error) => { + }).catch(error => { handleFileException( this.configurationFile, FileType.ChargingStationConfiguration, @@ -1771,11 +1761,16 @@ export class ChargingStation extends EventEmitter { >(this, RequestCommand.BOOT_NOTIFICATION, this.bootNotificationRequest, { skipBufferingOnError: true }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.bootNotificationResponse.currentTime = convertToDate( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + this.bootNotificationResponse?.currentTime + )! if (!this.isRegistered()) { this.stationInfo?.registrationMaxRetries !== -1 && ++registrationRetryCount await sleep( // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - this.bootNotificationResponse.interval != null + this.bootNotificationResponse?.interval != null ? secondsToMilliseconds(this.bootNotificationResponse.interval) : Constants.DEFAULT_BOOT_NOTIFICATION_INTERVAL ) @@ -1794,8 +1789,9 @@ export class ChargingStation extends EventEmitter { } } else { logger.error( - `${this.logPrefix()} Registration failure: maximum retries reached (${registrationRetryCount}) or retry disabled (${this - .stationInfo?.registrationMaxRetries})` + `${this.logPrefix()} Registration failure: maximum retries reached (${registrationRetryCount}) or retry disabled (${ + this.stationInfo?.registrationMaxRetries + })` ) } this.autoReconnectRetryCount = 0 @@ -2014,20 +2010,25 @@ export class ChargingStation extends EventEmitter { logger.error(`${this.logPrefix()} WebSocket error:`, error) } - private getEnergyActiveImportRegister (connectorStatus: ConnectorStatus, rounded = false): number { + private getEnergyActiveImportRegister ( + connectorStatus: ConnectorStatus | undefined, + rounded = false + ): number { if (this.stationInfo?.meteringPerTransaction === true) { return ( (rounded - ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - Math.round(connectorStatus.transactionEnergyActiveImportRegisterValue!) - : connectorStatus.transactionEnergyActiveImportRegisterValue) ?? 0 + ? connectorStatus?.transactionEnergyActiveImportRegisterValue != null + ? Math.round(connectorStatus.transactionEnergyActiveImportRegisterValue) + : undefined + : connectorStatus?.transactionEnergyActiveImportRegisterValue) ?? 0 ) } return ( (rounded - ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - Math.round(connectorStatus.energyActiveImportRegisterValue!) - : connectorStatus.energyActiveImportRegisterValue) ?? 0 + ? connectorStatus?.energyActiveImportRegisterValue != null + ? Math.round(connectorStatus.energyActiveImportRegisterValue) + : undefined + : connectorStatus?.energyActiveImportRegisterValue) ?? 0 ) } @@ -2106,15 +2107,11 @@ export class ChargingStation extends EventEmitter { private getAmperageLimitation (): number | undefined { if ( isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) && - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - getConfigurationKey(this, this.stationInfo!.amperageLimitationOcppKey!) != null + getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey) != null ) { return ( - convertToInt( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - getConfigurationKey(this, this.stationInfo!.amperageLimitationOcppKey!)!.value - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ) / getAmperageLimitationUnitDivider(this.stationInfo!) + convertToInt(getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey)?.value) / + getAmperageLimitationUnitDivider(this.stationInfo) ) } } @@ -2225,7 +2222,7 @@ export class ChargingStation extends EventEmitter { } private startWebSocketPing (): void { - const webSocketPingInterval: number = + const webSocketPingInterval = getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval) != null ? convertToInt( getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval)?.value @@ -2269,9 +2266,7 @@ export class ChargingStation extends EventEmitter { let configuredSupervisionUrlIndex: number switch (Configuration.getSupervisionUrlDistribution()) { case SupervisionUrlDistribution.RANDOM: - configuredSupervisionUrlIndex = Math.floor( - secureRandom() * (supervisionUrls as string[]).length - ) + configuredSupervisionUrlIndex = Math.floor(secureRandom() * supervisionUrls.length) break case SupervisionUrlDistribution.ROUND_ROBIN: case SupervisionUrlDistribution.CHARGING_STATION_AFFINITY: @@ -2286,12 +2281,13 @@ export class ChargingStation extends EventEmitter { SupervisionUrlDistribution.CHARGING_STATION_AFFINITY }` ) - configuredSupervisionUrlIndex = (this.index - 1) % (supervisionUrls as string[]).length + configuredSupervisionUrlIndex = (this.index - 1) % supervisionUrls.length break } - configuredSupervisionUrl = (supervisionUrls as string[])[configuredSupervisionUrlIndex] + configuredSupervisionUrl = supervisionUrls[configuredSupervisionUrlIndex] } else { - configuredSupervisionUrl = supervisionUrls as string + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + configuredSupervisionUrl = supervisionUrls! } if (isNotEmptyString(configuredSupervisionUrl)) { return new URL(configuredSupervisionUrl)