X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStation.ts;h=b09134f88e9e1f915519bf325b14c32407b50d44;hb=de7b9e0583275225509c099f75a2801371da54d0;hp=94b6d8bd1145529506f0c198c6fbf033124ce607;hpb=499c5f482f7bae009522cc4579ed7baf3fb717b0;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 94b6d8bd..b09134f8 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -22,6 +22,7 @@ import { import { buildConnectorsMap, checkChargingStation, + checkConfiguration, checkConnectorsConfiguration, checkStationInfoConnectorStatus, checkTemplate, @@ -37,11 +38,9 @@ import { getMaxNumberOfEvses, getNumberOfReservableConnectors, getPhaseRotationValue, - hasFeatureProfile, hasReservationExpired, initializeConnectorsMapStatus, propagateSerialNumber, - removeExpiredReservations, stationTemplateToStationInfo, warnTemplateKeysDeprecation, } from './Helpers'; @@ -50,13 +49,16 @@ import { OCPP16IncomingRequestService, OCPP16RequestService, OCPP16ResponseService, - OCPP16ServiceUtils, OCPP20IncomingRequestService, OCPP20RequestService, OCPP20ResponseService, type OCPPIncomingRequestService, type OCPPRequestService, - OCPPServiceUtils, + buildMeterValue, + buildStatusNotificationRequest, + buildTransactionEndMeterValue, + getMessageTypeString, + sendAndSetConnectorStatus, } from './ocpp'; import { SharedLRUCache } from './SharedLRUCache'; import { BaseError, OCPPError } from '../exception'; @@ -91,7 +93,6 @@ import { type IncomingRequestCommand, type JsonType, MessageType, - type MeterValue, MeterValueMeasurand, type MeterValuesRequest, type MeterValuesResponse, @@ -114,6 +115,7 @@ import { SupervisionUrlDistribution, SupportedFeatureProfiles, VendorParametersKey, + Voltage, type WSError, WebSocketCloseEventStatusCode, type WsOptions, @@ -157,6 +159,7 @@ import { export class ChargingStation extends EventEmitter { public readonly index: number; public readonly templateFile: string; + public stationInfo!: ChargingStationInfo; public started: boolean; public starting: boolean; public idTagsCache: IdTagsCache; @@ -172,7 +175,6 @@ export class ChargingStation extends EventEmitter { public bootNotificationRequest!: BootNotificationRequest; public bootNotificationResponse!: BootNotificationResponse | undefined; public powerDivider!: number; - private internalStationInfo!: ChargingStationInfo; private stopping: boolean; private configurationFile!: string; private configurationFileHash!: string; @@ -182,14 +184,13 @@ export class ChargingStation extends EventEmitter { private ocppIncomingRequestService!: OCPPIncomingRequestService; private readonly messageBuffer: Set; private configuredSupervisionUrl!: URL; - private wsConnectionRestarted: boolean; private autoReconnectRetryCount: number; private templateFileWatcher!: FSWatcher | undefined; private templateFileHash!: string; private readonly sharedLRUCache: SharedLRUCache; private webSocketPingSetInterval?: NodeJS.Timeout; private readonly chargingStationWorkerBroadcastChannel: ChargingStationWorkerBroadcastChannel; - private reservationExpirationSetInterval?: NodeJS.Timeout; + private flushMessageBufferSetInterval?: NodeJS.Timeout; constructor(index: number, templateFile: string) { super(); @@ -197,7 +198,6 @@ export class ChargingStation extends EventEmitter { this.starting = false; this.stopping = false; this.wsConnection = null; - this.wsConnectionRestarted = false; this.autoReconnectRetryCount = 0; this.index = index; this.templateFile = templateFile; @@ -226,39 +226,10 @@ export class ChargingStation extends EventEmitter { return this.connectors.size === 0 && this.evses.size > 0; } - public get stationInfo(): ChargingStationInfo { - return { - ...{ - enableStatistics: false, - remoteAuthorization: true, - currentOutType: CurrentType.AC, - ocppStrictCompliance: true, - outOfOrderEndMeterValues: false, - beginEndMeterValues: false, - meteringPerTransaction: true, - transactionDataMeterValues: false, - mainVoltageMeterValues: true, - phaseLineToLineVoltageMeterValues: false, - customValueLimitationMeterValues: true, - supervisionUrlOcppConfiguration: false, - supervisionUrlOcppKey: VendorParametersKey.ConnectionUrl, - ocppVersion: OCPPVersion.VERSION_16, - ocppPersistentConfiguration: true, - stationInfoPersistentConfiguration: true, - automaticTransactionGeneratorPersistentConfiguration: true, - autoReconnectMaxRetries: -1, - registrationMaxRetries: -1, - reconnectExponentialDelay: false, - stopTransactionsOnStopped: true, - }, - ...this.internalStationInfo, - }; - } - private get wsConnectionUrl(): URL { return new URL( `${ - this.stationInfo?.supervisionUrlOcppConfiguration && + this.stationInfo?.supervisionUrlOcppConfiguration === true && isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) && isNotEmptyString(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)?.value) ? getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)!.value @@ -268,14 +239,18 @@ export class ChargingStation extends EventEmitter { } public logPrefix = (): string => { - return logPrefix( - ` ${ - (isNotEmptyString(this?.stationInfo?.chargingStationId) - ? this?.stationInfo?.chargingStationId - : getChargingStationId(this.index, this.getTemplateFromFile()!)) ?? - 'Error at building log prefix' - } |`, - ); + if (isNotEmptyString(this?.stationInfo?.chargingStationId)) { + return logPrefix(` ${this?.stationInfo?.chargingStationId} |`); + } + let stationTemplate: ChargingStationTemplate | undefined; + try { + stationTemplate = JSON.parse( + readFileSync(this.templateFile, 'utf8'), + ) as ChargingStationTemplate; + } catch { + stationTemplate = undefined; + } + return logPrefix(` ${getChargingStationId(this.index, stationTemplate)} |`); }; public hasIdTags(): boolean { @@ -286,7 +261,7 @@ export class ChargingStation extends EventEmitter { const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo; switch (this.getCurrentOutType(stationInfo)) { case CurrentType.AC: - return !isUndefined(localStationInfo.numberOfPhases) ? localStationInfo.numberOfPhases! : 3; + return localStationInfo.numberOfPhases ?? 3; case CurrentType.DC: return 0; } @@ -296,10 +271,6 @@ export class ChargingStation extends EventEmitter { return this?.wsConnection?.readyState === WebSocket.OPEN; } - public getRegistrationStatus(): RegistrationStatusEnumType | undefined { - return this?.bootNotificationResponse?.status; - } - public inUnknownState(): boolean { return isNullOrUndefined(this?.bootNotificationResponse?.status); } @@ -482,7 +453,9 @@ export class ChargingStation extends EventEmitter { this, StandardParametersKey.AuthorizeRemoteTxRequests, ); - return authorizeRemoteTxRequests ? convertToBoolean(authorizeRemoteTxRequests.value) : false; + return authorizeRemoteTxRequests !== undefined + ? convertToBoolean(authorizeRemoteTxRequests.value) + : false; } public getLocalAuthListEnabled(): boolean { @@ -490,16 +463,18 @@ export class ChargingStation extends EventEmitter { this, StandardParametersKey.LocalAuthListEnabled, ); - return localAuthListEnabled ? convertToBoolean(localAuthListEnabled.value) : false; + return localAuthListEnabled !== undefined + ? convertToBoolean(localAuthListEnabled.value) + : false; } public getHeartbeatInterval(): number { const HeartbeatInterval = getConfigurationKey(this, StandardParametersKey.HeartbeatInterval); - if (HeartbeatInterval) { + if (HeartbeatInterval !== undefined) { return secondsToMilliseconds(convertToInt(HeartbeatInterval.value)); } const HeartBeatInterval = getConfigurationKey(this, StandardParametersKey.HeartBeatInterval); - if (HeartBeatInterval) { + if (HeartBeatInterval !== undefined) { return secondsToMilliseconds(convertToInt(HeartBeatInterval.value)); } this.stationInfo?.autoRegister === false && @@ -525,7 +500,7 @@ export class ChargingStation extends EventEmitter { } public startHeartbeat(): void { - if (this.getHeartbeatInterval() > 0 && !this.heartbeatSetInterval) { + if (this.getHeartbeatInterval() > 0 && this.heartbeatSetInterval === undefined) { this.heartbeatSetInterval = setInterval(() => { this.ocppRequestService .requestHandler(this, RequestCommand.HEARTBEAT) @@ -541,7 +516,7 @@ export class ChargingStation extends EventEmitter { this.getHeartbeatInterval(), )}`, ); - } else if (this.heartbeatSetInterval) { + } else if (this.heartbeatSetInterval !== undefined) { logger.info( `${this.logPrefix()} Heartbeat already started every ${formatDurationMilliSeconds( this.getHeartbeatInterval(), @@ -598,8 +573,7 @@ export class ChargingStation extends EventEmitter { } if (interval > 0) { this.getConnectorStatus(connectorId)!.transactionSetInterval = setInterval(() => { - // FIXME: Implement OCPP version agnostic helpers - const meterValue: MeterValue = OCPP16ServiceUtils.buildMeterValue( + const meterValue = buildMeterValue( this, connectorId, this.getConnectorStatus(connectorId)!.transactionId!, @@ -632,7 +606,7 @@ export class ChargingStation extends EventEmitter { } public stopMeterValues(connectorId: number) { - if (this.getConnectorStatus(connectorId)?.transactionSetInterval) { + if (this.getConnectorStatus(connectorId)?.transactionSetInterval !== undefined) { clearInterval(this.getConnectorStatus(connectorId)?.transactionSetInterval); } } @@ -644,9 +618,6 @@ export class ChargingStation extends EventEmitter { if (this.stationInfo?.enableStatistics === true) { this.performanceStatistics?.start(); } - if (hasFeatureProfile(this, SupportedFeatureProfiles.Reservation)) { - this.startReservationExpirationSetInterval(); - } this.openWSConnection(); // Monitor charging station template file this.templateFileWatcher = watchJsonFile( @@ -707,9 +678,6 @@ export class ChargingStation extends EventEmitter { if (this.stationInfo?.enableStatistics === true) { this.performanceStatistics?.stop(); } - if (hasFeatureProfile(this, SupportedFeatureProfiles.Reservation)) { - this.stopReservationExpirationSetInterval(); - } this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash); this.templateFileWatcher?.close(); this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash); @@ -741,6 +709,7 @@ export class ChargingStation extends EventEmitter { public bufferMessage(message: string): void { this.messageBuffer.add(message); + this.setIntervalFlushMessageBuffer(); } public openWSConnection( @@ -880,8 +849,7 @@ export class ChargingStation extends EventEmitter { this.stationInfo?.ocppStrictCompliance === true && this.stationInfo?.outOfOrderEndMeterValues === false ) { - // FIXME: Implement OCPP version agnostic helpers - const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue( + const transactionEndMeterValue = buildTransactionEndMeterValue( this, connectorId, this.getEnergyActiveImportRegisterByTransactionId(transactionId!), @@ -915,14 +883,11 @@ export class ChargingStation extends EventEmitter { public async addReservation(reservation: Reservation): Promise { const reservationFound = this.getReservationBy('reservationId', reservation.reservationId); - if (!isUndefined(reservationFound)) { - await this.removeReservation( - reservationFound!, - ReservationTerminationReason.REPLACE_EXISTING, - ); + if (reservationFound !== undefined) { + await this.removeReservation(reservationFound, ReservationTerminationReason.REPLACE_EXISTING); } this.getConnectorStatus(reservation.connectorId)!.reservation = reservation; - await OCPPServiceUtils.sendAndSetConnectorStatus( + await sendAndSetConnectorStatus( this, reservation.connectorId, ConnectorStatusEnum.Reserved, @@ -944,7 +909,7 @@ export class ChargingStation extends EventEmitter { case ReservationTerminationReason.RESERVATION_CANCELED: case ReservationTerminationReason.REPLACE_EXISTING: case ReservationTerminationReason.EXPIRED: - await OCPPServiceUtils.sendAndSetConnectorStatus( + await sendAndSetConnectorStatus( this, reservation.connectorId, ConnectorStatusEnum.Available, @@ -1004,31 +969,26 @@ export class ChargingStation extends EventEmitter { return false; } - private startReservationExpirationSetInterval(customInterval?: number): void { - const interval = customInterval ?? Constants.DEFAULT_RESERVATION_EXPIRATION_INTERVAL; - if (interval > 0) { - logger.info( - `${this.logPrefix()} Reservation expiration date checks started every ${formatDurationMilliSeconds( - interval, - )}`, - ); - this.reservationExpirationSetInterval = setInterval((): void => { - removeExpiredReservations(this).catch(Constants.EMPTY_FUNCTION); - }, interval); + private setIntervalFlushMessageBuffer(): void { + if (this.flushMessageBufferSetInterval === undefined) { + this.flushMessageBufferSetInterval = setInterval(() => { + if (this.isWebSocketConnectionOpened() === true && this.inAcceptedState() === true) { + this.flushMessageBuffer(); + } + if (this.messageBuffer.size === 0) { + this.clearIntervalFlushMessageBuffer(); + } + }, Constants.DEFAULT_MESSAGE_BUFFER_FLUSH_INTERVAL); } } - private stopReservationExpirationSetInterval(): void { - if (!isNullOrUndefined(this.reservationExpirationSetInterval)) { - clearInterval(this.reservationExpirationSetInterval); + private clearIntervalFlushMessageBuffer() { + if (this.flushMessageBufferSetInterval !== undefined) { + clearInterval(this.flushMessageBufferSetInterval); + delete this.flushMessageBufferSetInterval; } } - // private restartReservationExpiryDateSetInterval(): void { - // this.stopReservationExpirationSetInterval(); - // this.startReservationExpirationSetInterval(); - // } - private getNumberOfReservableConnectors(): number { let numberOfReservableConnectors = 0; if (this.hasEvses) { @@ -1063,14 +1023,24 @@ export class ChargingStation extends EventEmitter { [, , commandName] = JSON.parse(message) as OutgoingRequest; beginId = PerformanceStatistics.beginMeasure(commandName); } - this.wsConnection?.send(message); - isRequest && PerformanceStatistics.endMeasure(commandName!, beginId!); - logger.debug( - `${this.logPrefix()} >> Buffered ${OCPPServiceUtils.getMessageTypeString( - messageType, - )} payload sent: ${message}`, - ); - this.messageBuffer.delete(message); + this.wsConnection?.send(message, (error?: Error) => { + isRequest && PerformanceStatistics.endMeasure(commandName!, beginId!); + if (isNullOrUndefined(error)) { + logger.debug( + `${this.logPrefix()} >> Buffered ${getMessageTypeString( + messageType, + )} OCPP message sent '${JSON.stringify(message)}'`, + ); + this.messageBuffer.delete(message); + } else { + logger.debug( + `${this.logPrefix()} >> Buffered ${getMessageTypeString( + messageType, + )} OCPP message '${JSON.stringify(message)}' send failed:`, + error, + ); + } + }); } } } @@ -1158,9 +1128,11 @@ export class ChargingStation extends EventEmitter { return stationInfo; } - private getStationInfoFromFile(): ChargingStationInfo | undefined { + private getStationInfoFromFile( + stationInfoPersistentConfiguration = true, + ): ChargingStationInfo | undefined { let stationInfo: ChargingStationInfo | undefined; - if (this.stationInfo?.stationInfoPersistentConfiguration === true) { + if (stationInfoPersistentConfiguration === true) { stationInfo = this.getConfigurationFromFile()?.stationInfo; if (stationInfo) { delete stationInfo?.infoHash; @@ -1170,13 +1142,38 @@ export class ChargingStation extends EventEmitter { } private getStationInfo(): ChargingStationInfo { + const defaultStationInfo: Partial = { + enableStatistics: false, + remoteAuthorization: true, + currentOutType: CurrentType.AC, + mainVoltageMeterValues: true, + phaseLineToLineVoltageMeterValues: false, + customValueLimitationMeterValues: true, + ocppStrictCompliance: true, + outOfOrderEndMeterValues: false, + beginEndMeterValues: false, + meteringPerTransaction: true, + transactionDataMeterValues: false, + supervisionUrlOcppConfiguration: false, + supervisionUrlOcppKey: VendorParametersKey.ConnectionUrl, + ocppVersion: OCPPVersion.VERSION_16, + ocppPersistentConfiguration: true, + stationInfoPersistentConfiguration: true, + automaticTransactionGeneratorPersistentConfiguration: true, + autoReconnectMaxRetries: -1, + registrationMaxRetries: -1, + reconnectExponentialDelay: false, + stopTransactionsOnStopped: true, + }; const stationInfoFromTemplate: ChargingStationInfo = this.getStationInfoFromTemplate(); - const stationInfoFromFile: ChargingStationInfo | undefined = this.getStationInfoFromFile(); + const stationInfoFromFile: ChargingStationInfo | undefined = this.getStationInfoFromFile( + stationInfoFromTemplate?.stationInfoPersistentConfiguration, + ); // Priority: // 1. charging station info from template // 2. charging station info from configuration file if (stationInfoFromFile?.templateHash === stationInfoFromTemplate.templateHash) { - return stationInfoFromFile!; + return { ...defaultStationInfo, ...stationInfoFromFile! }; } stationInfoFromFile && propagateSerialNumber( @@ -1184,7 +1181,7 @@ export class ChargingStation extends EventEmitter { stationInfoFromFile, stationInfoFromTemplate, ); - return stationInfoFromTemplate; + return { ...defaultStationInfo, ...stationInfoFromTemplate }; } private saveStationInfo(): void { @@ -1212,11 +1209,12 @@ export class ChargingStation extends EventEmitter { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing (stationConfiguration?.connectorsStatus || stationConfiguration?.evsesStatus) ) { + checkConfiguration(stationConfiguration, this.logPrefix(), this.configurationFile); this.initializeConnectorsOrEvsesFromFile(stationConfiguration); } else { this.initializeConnectorsOrEvsesFromTemplate(stationTemplate); } - this.internalStationInfo = this.getStationInfo(); + this.stationInfo = this.getStationInfo(); if ( this.stationInfo.firmwareStatus === FirmwareStatus.Installing && isNotEmptyString(this.stationInfo.firmwareVersion) && @@ -1289,16 +1287,16 @@ export class ChargingStation extends EventEmitter { } private initializeOcppConfiguration(): void { - if (!getConfigurationKey(this, StandardParametersKey.HeartbeatInterval)) { + if (isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.HeartbeatInterval))) { addConfigurationKey(this, StandardParametersKey.HeartbeatInterval, '0'); } - if (!getConfigurationKey(this, StandardParametersKey.HeartBeatInterval)) { + if (isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.HeartBeatInterval))) { addConfigurationKey(this, StandardParametersKey.HeartBeatInterval, '0', { visible: false }); } if ( - this.stationInfo?.supervisionUrlOcppConfiguration && + this.stationInfo?.supervisionUrlOcppConfiguration === true && isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) && - !getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!) + isNullOrUndefined(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)) ) { addConfigurationKey( this, @@ -1307,15 +1305,15 @@ export class ChargingStation extends EventEmitter { { reboot: true }, ); } else if ( - !this.stationInfo?.supervisionUrlOcppConfiguration && + this.stationInfo?.supervisionUrlOcppConfiguration === false && isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) && - getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!) + !isNullOrUndefined(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)) ) { deleteConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!, { save: false }); } if ( isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) && - !getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!) + isNullOrUndefined(getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!)) ) { addConfigurationKey( this, @@ -1325,7 +1323,9 @@ export class ChargingStation extends EventEmitter { ).toString(), ); } - if (!getConfigurationKey(this, StandardParametersKey.SupportedFeatureProfiles)) { + if ( + isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.SupportedFeatureProfiles)) + ) { addConfigurationKey( this, StandardParametersKey.SupportedFeatureProfiles, @@ -1339,14 +1339,18 @@ export class ChargingStation extends EventEmitter { { readonly: true }, { overwrite: true }, ); - if (!getConfigurationKey(this, StandardParametersKey.MeterValuesSampledData)) { + if ( + isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.MeterValuesSampledData)) + ) { addConfigurationKey( this, StandardParametersKey.MeterValuesSampledData, MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER, ); } - if (!getConfigurationKey(this, StandardParametersKey.ConnectorPhaseRotation)) { + if ( + isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.ConnectorPhaseRotation)) + ) { const connectorsPhaseRotation: string[] = []; if (this.hasEvses) { for (const evseStatus of this.evses.values()) { @@ -1369,18 +1373,20 @@ export class ChargingStation extends EventEmitter { connectorsPhaseRotation.toString(), ); } - if (!getConfigurationKey(this, StandardParametersKey.AuthorizeRemoteTxRequests)) { + if ( + isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.AuthorizeRemoteTxRequests)) + ) { addConfigurationKey(this, StandardParametersKey.AuthorizeRemoteTxRequests, 'true'); } if ( - !getConfigurationKey(this, StandardParametersKey.LocalAuthListEnabled) && + isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.LocalAuthListEnabled)) && getConfigurationKey(this, StandardParametersKey.SupportedFeatureProfiles)?.value?.includes( SupportedFeatureProfiles.LocalAuthListManagement, ) ) { addConfigurationKey(this, StandardParametersKey.LocalAuthListEnabled, 'false'); } - if (!getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)) { + if (isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut))) { addConfigurationKey( this, StandardParametersKey.ConnectionTimeOut, @@ -1631,9 +1637,9 @@ export class ChargingStation extends EventEmitter { } if ( this.stationInfo?.ocppPersistentConfiguration === true && - this.ocppConfiguration?.configurationKey + Array.isArray(this.ocppConfiguration?.configurationKey) ) { - configurationData.configurationKey = this.ocppConfiguration.configurationKey; + configurationData.configurationKey = this.ocppConfiguration?.configurationKey; } else { delete configurationData.configurationKey; } @@ -1721,7 +1727,7 @@ export class ChargingStation extends EventEmitter { private getOcppConfigurationFromFile(): ChargingStationOcppConfiguration | undefined { const configurationKey = this.getConfigurationFromFile()?.configurationKey; - if (this.stationInfo?.ocppPersistentConfiguration === true && configurationKey) { + if (this.stationInfo?.ocppPersistentConfiguration === true && Array.isArray(configurationKey)) { return { configurationKey }; } return undefined; @@ -1776,7 +1782,6 @@ export class ChargingStation extends EventEmitter { .stationInfo?.registrationMaxRetries})`, ); } - this.wsConnectionRestarted = false; this.autoReconnectRetryCount = 0; this.emit(ChargingStationEvents.updated); } else { @@ -1786,7 +1791,7 @@ export class ChargingStation extends EventEmitter { } } - private async onClose(code: number, reason: Buffer): Promise { + private async onClose(code: WebSocketCloseEventStatusCode, reason: Buffer): Promise { switch (code) { // Normal close case WebSocketCloseEventStatusCode.CLOSE_NORMAL: @@ -1818,7 +1823,7 @@ export class ChargingStation extends EventEmitter { } throw new OCPPError( ErrorType.PROTOCOL_ERROR, - `Cached request for message id ${messageId} ${OCPPServiceUtils.getMessageTypeString( + `Cached request for message id ${messageId} ${getMessageTypeString( messageType, )} is not an array`, undefined, @@ -1843,6 +1848,7 @@ export class ChargingStation extends EventEmitter { commandName, commandPayload, ); + this.emit(ChargingStationEvents.updated); } private handleResponseMessage(response: Response): void { @@ -1891,7 +1897,7 @@ export class ChargingStation extends EventEmitter { private async onMessage(data: RawData): Promise { let request: IncomingRequest | Response | ErrorResponse | undefined; - let messageType: number | undefined; + let messageType: MessageType | undefined; let errorMsg: string; try { // eslint-disable-next-line @typescript-eslint/no-base-to-string @@ -1919,7 +1925,6 @@ export class ChargingStation extends EventEmitter { logger.error(`${this.logPrefix()} ${errorMsg}`); throw new OCPPError(ErrorType.PROTOCOL_ERROR, errorMsg); } - this.emit(ChargingStationEvents.updated); } else { throw new OCPPError( ErrorType.PROTOCOL_ERROR, @@ -2031,10 +2036,10 @@ export class ChargingStation extends EventEmitter { // 0 for disabling private getConnectionTimeout(): number { - if (getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)) { - return ( - parseInt(getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)!.value!) ?? - Constants.DEFAULT_CONNECTION_TIMEOUT + if (getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut) !== undefined) { + return convertToInt( + getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)!.value! ?? + Constants.DEFAULT_CONNECTION_TIMEOUT, ); } return Constants.DEFAULT_CONNECTION_TIMEOUT; @@ -2048,8 +2053,8 @@ export class ChargingStation extends EventEmitter { return powerDivider; } - private getMaximumAmperage(stationInfo: ChargingStationInfo): number | undefined { - const maximumPower = this.getMaximumPower(stationInfo); + private getMaximumAmperage(stationInfo?: ChargingStationInfo): number | undefined { + const maximumPower = (stationInfo ?? this.stationInfo).maximumPower!; switch (this.getCurrentOutType(stationInfo)) { case CurrentType.AC: return ACElectricUtils.amperagePerPhaseFromPower( @@ -2062,27 +2067,21 @@ export class ChargingStation extends EventEmitter { } } - private getMaximumPower(stationInfo?: ChargingStationInfo): number { - return (stationInfo ?? this.stationInfo).maximumPower!; - } - private getCurrentOutType(stationInfo?: ChargingStationInfo): CurrentType { return (stationInfo ?? this.stationInfo).currentOutType ?? CurrentType.AC; } - private getVoltageOut(stationInfo?: ChargingStationInfo): number { - const defaultVoltageOut = getDefaultVoltageOut( - this.getCurrentOutType(stationInfo), - this.logPrefix(), - this.templateFile, + private getVoltageOut(stationInfo?: ChargingStationInfo): Voltage { + return ( + (stationInfo ?? this.stationInfo).voltageOut ?? + getDefaultVoltageOut(this.getCurrentOutType(stationInfo), this.logPrefix(), this.templateFile) ); - return (stationInfo ?? this.stationInfo).voltageOut ?? defaultVoltageOut; } private getAmperageLimitation(): number | undefined { if ( isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) && - getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!) + getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!) !== undefined ) { return ( convertToInt( @@ -2111,12 +2110,7 @@ export class ChargingStation extends EventEmitter { if (evseId > 0) { for (const [connectorId, connectorStatus] of evseStatus.connectors) { const connectorBootStatus = getBootConnectorStatus(this, connectorId, connectorStatus); - await OCPPServiceUtils.sendAndSetConnectorStatus( - this, - connectorId, - connectorBootStatus, - evseId, - ); + await sendAndSetConnectorStatus(this, connectorId, connectorBootStatus, evseId); } } } @@ -2128,11 +2122,11 @@ export class ChargingStation extends EventEmitter { connectorId, this.getConnectorStatus(connectorId)!, ); - await OCPPServiceUtils.sendAndSetConnectorStatus(this, connectorId, connectorBootStatus); + await sendAndSetConnectorStatus(this, connectorId, connectorBootStatus); } } } - if (this.stationInfo?.firmwareStatus === FirmwareStatus.Installing) { + if (this.stationInfo.firmwareStatus === FirmwareStatus.Installing) { await this.ocppRequestService.requestHandler< FirmwareStatusNotificationRequest, FirmwareStatusNotificationResponse @@ -2146,7 +2140,7 @@ export class ChargingStation extends EventEmitter { if (this.getAutomaticTransactionGeneratorConfiguration().enable === true) { this.startAutomaticTransactionGenerator(); } - this.wsConnectionRestarted === true && this.flushMessageBuffer(); + this.flushMessageBuffer(); } private async stopMessageSequence( @@ -2173,7 +2167,7 @@ export class ChargingStation extends EventEmitter { >( this, RequestCommand.STATUS_NOTIFICATION, - OCPPServiceUtils.buildStatusNotificationRequest( + buildStatusNotificationRequest( this, connectorId, ConnectorStatusEnum.Unavailable, @@ -2193,11 +2187,7 @@ export class ChargingStation extends EventEmitter { >( this, RequestCommand.STATUS_NOTIFICATION, - OCPPServiceUtils.buildStatusNotificationRequest( - this, - connectorId, - ConnectorStatusEnum.Unavailable, - ), + buildStatusNotificationRequest(this, connectorId, ConnectorStatusEnum.Unavailable), ); delete this.getConnectorStatus(connectorId)?.status; } @@ -2206,13 +2196,13 @@ export class ChargingStation extends EventEmitter { } private startWebSocketPing(): void { - const webSocketPingInterval: number = getConfigurationKey( - this, - StandardParametersKey.WebSocketPingInterval, - ) - ? convertToInt(getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval)?.value) - : 0; - if (webSocketPingInterval > 0 && !this.webSocketPingSetInterval) { + const webSocketPingInterval: number = + getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval) !== undefined + ? convertToInt( + getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval)?.value, + ) + : 0; + if (webSocketPingInterval > 0 && this.webSocketPingSetInterval === undefined) { this.webSocketPingSetInterval = setInterval(() => { if (this.isWebSocketConnectionOpened() === true) { this.wsConnection?.ping(); @@ -2223,7 +2213,7 @@ export class ChargingStation extends EventEmitter { webSocketPingInterval, )}`, ); - } else if (this.webSocketPingSetInterval) { + } else if (this.webSocketPingSetInterval !== undefined) { logger.info( `${this.logPrefix()} WebSocket ping already started every ${formatDurationSeconds( webSocketPingInterval, @@ -2237,7 +2227,7 @@ export class ChargingStation extends EventEmitter { } private stopWebSocketPing(): void { - if (this.webSocketPingSetInterval) { + if (this.webSocketPingSetInterval !== undefined) { clearInterval(this.webSocketPingSetInterval); delete this.webSocketPingSetInterval; } @@ -2282,7 +2272,7 @@ export class ChargingStation extends EventEmitter { } private stopHeartbeat(): void { - if (this.heartbeatSetInterval) { + if (this.heartbeatSetInterval !== undefined) { clearInterval(this.heartbeatSetInterval); delete this.heartbeatSetInterval; } @@ -2334,7 +2324,6 @@ export class ChargingStation extends EventEmitter { }, { closeOpened: true }, ); - this.wsConnectionRestarted = true; } else if (this.stationInfo?.autoReconnectMaxRetries !== -1) { logger.error( `${this.logPrefix()} WebSocket connection retries failure: maximum retries reached (${