From: Jérôme Benoit Date: Mon, 16 Mar 2026 12:00:33 +0000 (+0100) Subject: refactor(ocpp20): prefix FirmwareStatusEnumType, extend FirmwareStatus union X-Git-Tag: ocpp-server@v3.1.1~28 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=fd511796449af6b1903dd431476da8d4c9c7f613;p=e-mobility-charging-stations-simulator.git refactor(ocpp20): prefix FirmwareStatusEnumType, extend FirmwareStatus union - Rename FirmwareStatusEnumType → OCPP20FirmwareStatusEnumType per existing OCPP16FirmwareStatus naming convention - Extend FirmwareStatus union to include all OCPP 2.0.1 firmware states - Extend FirmwareStatusNotificationRequest to union both versions - Write firmwareStatus to stationInfo in sendFirmwareStatusNotification fixing hasFirmwareUpdateInProgress which was always returning false - Stack-specific code uses OCPP20FirmwareStatusEnumType, common code uses generic FirmwareStatus union --- diff --git a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts index c4c25456..5362441c 100644 --- a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts @@ -24,8 +24,6 @@ import { DeleteCertificateStatusEnumType, ErrorType, type EvseStatus, - FirmwareStatus, - FirmwareStatusEnumType, type FirmwareType, GenericDeviceModelStatusEnumType, GenericStatus, @@ -55,6 +53,7 @@ import { type OCPP20DeleteCertificateRequest, type OCPP20DeleteCertificateResponse, OCPP20DeviceInfoVariableName, + OCPP20FirmwareStatusEnumType, type OCPP20FirmwareStatusNotificationRequest, type OCPP20FirmwareStatusNotificationResponse, type OCPP20GetBaseReportRequest, @@ -153,7 +152,7 @@ const moduleName = 'OCPP20IncomingRequestService' interface OCPP20PerStationState { activeFirmwareUpdateAbortController?: AbortController activeFirmwareUpdateRequestId?: number - lastFirmwareStatus?: FirmwareStatusEnumType + lastFirmwareStatus?: OCPP20FirmwareStatusEnumType preInoperativeConnectorStatuses: Map reportDataCache: Map } @@ -367,8 +366,8 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { const ss = this.getStationState(chargingStation) const firmwareStatus = ss.lastFirmwareStatus == null || - ss.lastFirmwareStatus === FirmwareStatusEnumType.Installed - ? FirmwareStatusEnumType.Idle + ss.lastFirmwareStatus === OCPP20FirmwareStatusEnumType.Installed + ? OCPP20FirmwareStatusEnumType.Idle : ss.lastFirmwareStatus chargingStation.ocppRequestService .requestHandler< @@ -2807,9 +2806,14 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { private hasFirmwareUpdateInProgress (chargingStation: ChargingStation): boolean { const firmwareStatus = chargingStation.stationInfo?.firmwareStatus return ( - firmwareStatus === FirmwareStatus.Downloading || - firmwareStatus === FirmwareStatus.Downloaded || - firmwareStatus === FirmwareStatus.Installing + firmwareStatus === OCPP20FirmwareStatusEnumType.Downloading || + firmwareStatus === OCPP20FirmwareStatusEnumType.Downloaded || + firmwareStatus === OCPP20FirmwareStatusEnumType.Installing || + firmwareStatus === OCPP20FirmwareStatusEnumType.DownloadScheduled || + firmwareStatus === OCPP20FirmwareStatusEnumType.DownloadPaused || + firmwareStatus === OCPP20FirmwareStatusEnumType.InstallScheduled || + firmwareStatus === OCPP20FirmwareStatusEnumType.InstallRebooting || + firmwareStatus === OCPP20FirmwareStatusEnumType.SignatureVerified ) } @@ -3137,10 +3141,13 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { private sendFirmwareStatusNotification ( chargingStation: ChargingStation, - status: FirmwareStatusEnumType, + status: OCPP20FirmwareStatusEnumType, requestId: number ): Promise { this.getStationState(chargingStation).lastFirmwareStatus = status + if (chargingStation.stationInfo != null) { + chargingStation.stationInfo.firmwareStatus = status + } return chargingStation.ocppRequestService.requestHandler< OCPP20FirmwareStatusNotificationRequest, OCPP20FirmwareStatusNotificationResponse @@ -3338,7 +3345,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { if (retrieveTime > now) { await this.sendFirmwareStatusNotification( chargingStation, - FirmwareStatusEnumType.DownloadScheduled, + OCPP20FirmwareStatusEnumType.DownloadScheduled, requestId ) await sleep(retrieveTime - now) @@ -3347,7 +3354,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { await this.sendFirmwareStatusNotification( chargingStation, - FirmwareStatusEnumType.Downloading, + OCPP20FirmwareStatusEnumType.Downloading, requestId ) @@ -3358,7 +3365,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { if (location.trim() === '' || !this.isValidFirmwareLocation(location)) { await this.sendFirmwareStatusNotification( chargingStation, - FirmwareStatusEnumType.DownloadFailed, + OCPP20FirmwareStatusEnumType.DownloadFailed, requestId ) logger.warn( @@ -3370,7 +3377,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { await this.sendFirmwareStatusNotification( chargingStation, - FirmwareStatusEnumType.Downloaded, + OCPP20FirmwareStatusEnumType.Downloaded, requestId ) @@ -3379,7 +3386,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { if (checkAborted()) return await this.sendFirmwareStatusNotification( chargingStation, - FirmwareStatusEnumType.SignatureVerified, + OCPP20FirmwareStatusEnumType.SignatureVerified, requestId ) } @@ -3391,7 +3398,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { if (installTime > currentTime) { await this.sendFirmwareStatusNotification( chargingStation, - FirmwareStatusEnumType.InstallScheduled, + OCPP20FirmwareStatusEnumType.InstallScheduled, requestId ) await sleep(installTime - currentTime) @@ -3415,7 +3422,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { await this.sendFirmwareStatusNotification( chargingStation, - FirmwareStatusEnumType.Installing, + OCPP20FirmwareStatusEnumType.Installing, requestId ) @@ -3423,7 +3430,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { if (checkAborted()) return await this.sendFirmwareStatusNotification( chargingStation, - FirmwareStatusEnumType.Installed, + OCPP20FirmwareStatusEnumType.Installed, requestId ) diff --git a/src/charging-station/ocpp/2.0/OCPP20RequestService.ts b/src/charging-station/ocpp/2.0/OCPP20RequestService.ts index aeb14a0e..2bf1d99e 100644 --- a/src/charging-station/ocpp/2.0/OCPP20RequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20RequestService.ts @@ -8,9 +8,9 @@ import { type CertificateActionEnumType, type CertificateSigningUseEnumType, ErrorType, - type FirmwareStatusEnumType, type JsonObject, type JsonType, + type OCPP20FirmwareStatusEnumType, type OCPP20FirmwareStatusNotificationRequest, type OCPP20FirmwareStatusNotificationResponse, type OCPP20Get15118EVCertificateRequest, @@ -99,7 +99,7 @@ export class OCPP20RequestService extends OCPPRequestService { */ public async requestFirmwareStatusNotification ( chargingStation: ChargingStation, - status: FirmwareStatusEnumType, + status: OCPP20FirmwareStatusEnumType, requestId?: number ): Promise { logger.debug( diff --git a/src/charging-station/ocpp/2.0/__testable__/OCPP20RequestServiceTestable.ts b/src/charging-station/ocpp/2.0/__testable__/OCPP20RequestServiceTestable.ts index c1bcd313..a21ac268 100644 --- a/src/charging-station/ocpp/2.0/__testable__/OCPP20RequestServiceTestable.ts +++ b/src/charging-station/ocpp/2.0/__testable__/OCPP20RequestServiceTestable.ts @@ -24,8 +24,8 @@ import { mock } from 'node:test' import type { CertificateActionEnumType, CertificateSigningUseEnumType, - FirmwareStatusEnumType, JsonType, + OCPP20FirmwareStatusEnumType, OCPP20FirmwareStatusNotificationResponse, OCPP20Get15118EVCertificateResponse, OCPP20GetCertificateStatusResponse, @@ -88,7 +88,7 @@ export interface TestableOCPP20RequestService { */ requestFirmwareStatusNotification: ( chargingStation: ChargingStation, - status: FirmwareStatusEnumType, + status: OCPP20FirmwareStatusEnumType, requestId?: number ) => Promise diff --git a/src/types/index.ts b/src/types/index.ts index 24f41f49..8cf719fc 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -153,7 +153,6 @@ export { DataEnumType, DataTransferStatusEnumType, DeleteCertificateStatusEnumType, - FirmwareStatusEnumType, type FirmwareType, GenericDeviceModelStatusEnumType, GetCertificateIdUseEnumType, @@ -169,6 +168,7 @@ export { MessageTriggerEnumType, type NetworkConnectionProfileType, OCPP20ComponentName, + OCPP20FirmwareStatusEnumType, OCPP20UnitEnumType, type OCSPRequestDataType, OperationalStatusEnumType, diff --git a/src/types/ocpp/2.0/Common.ts b/src/types/ocpp/2.0/Common.ts index 9e755a22..cb2b72b8 100644 --- a/src/types/ocpp/2.0/Common.ts +++ b/src/types/ocpp/2.0/Common.ts @@ -66,23 +66,6 @@ export enum DeleteCertificateStatusEnumType { NotFound = 'NotFound', } -export enum FirmwareStatusEnumType { - Downloaded = 'Downloaded', - DownloadFailed = 'DownloadFailed', - Downloading = 'Downloading', - DownloadPaused = 'DownloadPaused', - DownloadScheduled = 'DownloadScheduled', - Idle = 'Idle', - InstallationFailed = 'InstallationFailed', - Installed = 'Installed', - Installing = 'Installing', - InstallRebooting = 'InstallRebooting', - InstallScheduled = 'InstallScheduled', - InstallVerificationFailed = 'InstallVerificationFailed', - InvalidSignature = 'InvalidSignature', - SignatureVerified = 'SignatureVerified', -} - export enum GenericDeviceModelStatusEnumType { Accepted = 'Accepted', EmptyResultSet = 'EmptyResultSet', @@ -235,6 +218,23 @@ export enum OCPP20ComponentName { VehicleIdSensor = 'VehicleIdSensor', } +export enum OCPP20FirmwareStatusEnumType { + Downloaded = 'Downloaded', + DownloadFailed = 'DownloadFailed', + Downloading = 'Downloading', + DownloadPaused = 'DownloadPaused', + DownloadScheduled = 'DownloadScheduled', + Idle = 'Idle', + InstallationFailed = 'InstallationFailed', + Installed = 'Installed', + Installing = 'Installing', + InstallRebooting = 'InstallRebooting', + InstallScheduled = 'InstallScheduled', + InstallVerificationFailed = 'InstallVerificationFailed', + InvalidSignature = 'InvalidSignature', + SignatureVerified = 'SignatureVerified', +} + export enum OCPP20UnitEnumType { AMP = 'A', ARBITRARY_STRENGTH_UNIT = 'ASU', diff --git a/src/types/ocpp/2.0/Requests.ts b/src/types/ocpp/2.0/Requests.ts index 0b370692..b22a17b7 100644 --- a/src/types/ocpp/2.0/Requests.ts +++ b/src/types/ocpp/2.0/Requests.ts @@ -8,7 +8,6 @@ import type { CertificateSigningUseEnumType, ChargingStationType, CustomDataType, - FirmwareStatusEnumType, FirmwareType, GetCertificateIdUseEnumType, InstallCertificateUseEnumType, @@ -16,6 +15,7 @@ import type { LogParametersType, MessageTriggerEnumType, NetworkConnectionProfileType, + OCPP20FirmwareStatusEnumType, OCSPRequestDataType, OperationalStatusEnumType, ReportBaseEnumType, @@ -118,7 +118,7 @@ export interface OCPP20DeleteCertificateRequest extends JsonObject { export interface OCPP20FirmwareStatusNotificationRequest extends JsonObject { customData?: CustomDataType requestId?: number - status: FirmwareStatusEnumType + status: OCPP20FirmwareStatusEnumType } export interface OCPP20Get15118EVCertificateRequest extends JsonObject { diff --git a/src/types/ocpp/Requests.ts b/src/types/ocpp/Requests.ts index f3a93972..f7df86a5 100644 --- a/src/types/ocpp/Requests.ts +++ b/src/types/ocpp/Requests.ts @@ -21,9 +21,10 @@ import { type OCPP16ReserveNowRequest, type OCPP16StatusNotificationRequest, } from './1.6/Requests.js' -import { OperationalStatusEnumType } from './2.0/Common.js' +import { OCPP20FirmwareStatusEnumType, OperationalStatusEnumType } from './2.0/Common.js' import { type OCPP20BootNotificationRequest, + type OCPP20FirmwareStatusNotificationRequest, OCPP20IncomingRequestCommand, OCPP20RequestCommand, type OCPP20StatusNotificationRequest, @@ -44,7 +45,9 @@ export type DiagnosticsStatusNotificationRequest = OCPP16DiagnosticsStatusNotifi export type ErrorCallback = (ocppError: OCPPError, requestStatistic?: boolean) => void -export type FirmwareStatusNotificationRequest = OCPP16FirmwareStatusNotificationRequest +export type FirmwareStatusNotificationRequest = + | OCPP16FirmwareStatusNotificationRequest + | OCPP20FirmwareStatusNotificationRequest export type HeartbeatRequest = OCPP16HeartbeatRequest @@ -108,9 +111,10 @@ export type DiagnosticsStatus = OCPP16DiagnosticsStatus export const FirmwareStatus = { ...OCPP16FirmwareStatus, + ...OCPP20FirmwareStatusEnumType, } as const // eslint-disable-next-line @typescript-eslint/no-redeclare -export type FirmwareStatus = OCPP16FirmwareStatus +export type FirmwareStatus = OCPP16FirmwareStatus | OCPP20FirmwareStatusEnumType export type ReserveNowRequest = OCPP16ReserveNowRequest diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-UpdateFirmware.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-UpdateFirmware.test.ts index 29965dc8..f4b60c80 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-UpdateFirmware.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-UpdateFirmware.test.ts @@ -11,8 +11,8 @@ import type { ChargingStation } from '../../../../src/charging-station/index.js' import { createTestableIncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js' import { OCPP20IncomingRequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.js' import { - FirmwareStatusEnumType, type FirmwareType, + OCPP20FirmwareStatusEnumType, OCPP20IncomingRequestCommand, OCPP20RequestCommand, type OCPP20UpdateFirmwareRequest, @@ -349,7 +349,7 @@ await describe('L01/L02 - UpdateFirmware', async () => { await flushMicrotasks() assert.strictEqual(sentRequests.length, 1) - assert.strictEqual(sentRequests[0].payload.status, FirmwareStatusEnumType.Downloading) + assert.strictEqual(sentRequests[0].payload.status, OCPP20FirmwareStatusEnumType.Downloading) const secondRequest: OCPP20UpdateFirmwareRequest = { firmware: { @@ -397,18 +397,18 @@ await describe('L01/L02 - UpdateFirmware', async () => { sentRequests[0].command, OCPP20RequestCommand.FIRMWARE_STATUS_NOTIFICATION ) - assert.strictEqual(sentRequests[0].payload.status, FirmwareStatusEnumType.Downloading) + assert.strictEqual(sentRequests[0].payload.status, OCPP20FirmwareStatusEnumType.Downloading) assert.strictEqual(sentRequests[0].payload.requestId, 1) t.mock.timers.tick(2000) await flushMicrotasks() assert.strictEqual(sentRequests.length, 3) - assert.strictEqual(sentRequests[1].payload.status, FirmwareStatusEnumType.Downloaded) - assert.strictEqual(sentRequests[2].payload.status, FirmwareStatusEnumType.Installing) + assert.strictEqual(sentRequests[1].payload.status, OCPP20FirmwareStatusEnumType.Downloaded) + assert.strictEqual(sentRequests[2].payload.status, OCPP20FirmwareStatusEnumType.Installing) t.mock.timers.tick(1000) await flushMicrotasks() - assert.strictEqual(sentRequests[3].payload.status, FirmwareStatusEnumType.Installed) + assert.strictEqual(sentRequests[3].payload.status, OCPP20FirmwareStatusEnumType.Installed) // H11: SecurityEventNotification for FirmwareUpdated assert.strictEqual(sentRequests.length, 5) @@ -445,12 +445,15 @@ await describe('L01/L02 - UpdateFirmware', async () => { await flushMicrotasks() assert.strictEqual(sentRequests.length, 1) - assert.strictEqual(sentRequests[0].payload.status, FirmwareStatusEnumType.Downloading) + assert.strictEqual(sentRequests[0].payload.status, OCPP20FirmwareStatusEnumType.Downloading) t.mock.timers.tick(2000) await flushMicrotasks() assert.strictEqual(sentRequests.length, 2) - assert.strictEqual(sentRequests[1].payload.status, FirmwareStatusEnumType.DownloadFailed) + assert.strictEqual( + sentRequests[1].payload.status, + OCPP20FirmwareStatusEnumType.DownloadFailed + ) assert.strictEqual(sentRequests[1].payload.requestId, 7) }) }) @@ -483,7 +486,10 @@ await describe('L01/L02 - UpdateFirmware', async () => { await flushMicrotasks() assert.strictEqual(sentRequests.length, 2) - assert.strictEqual(sentRequests[1].payload.status, FirmwareStatusEnumType.DownloadFailed) + assert.strictEqual( + sentRequests[1].payload.status, + OCPP20FirmwareStatusEnumType.DownloadFailed + ) assert.strictEqual(sentRequests[1].payload.requestId, 8) }) }) @@ -560,16 +566,19 @@ await describe('L01/L02 - UpdateFirmware', async () => { t.mock.timers.tick(2000) await flushMicrotasks() assert.strictEqual(sentRequests.length, 2) - assert.strictEqual(sentRequests[1].payload.status, FirmwareStatusEnumType.Downloaded) + assert.strictEqual(sentRequests[1].payload.status, OCPP20FirmwareStatusEnumType.Downloaded) t.mock.timers.tick(500) await flushMicrotasks() - assert.strictEqual(sentRequests[2].payload.status, FirmwareStatusEnumType.SignatureVerified) - assert.strictEqual(sentRequests[3].payload.status, FirmwareStatusEnumType.Installing) + assert.strictEqual( + sentRequests[2].payload.status, + OCPP20FirmwareStatusEnumType.SignatureVerified + ) + assert.strictEqual(sentRequests[3].payload.status, OCPP20FirmwareStatusEnumType.Installing) t.mock.timers.tick(1000) await flushMicrotasks() - assert.strictEqual(sentRequests[4].payload.status, FirmwareStatusEnumType.Installed) + assert.strictEqual(sentRequests[4].payload.status, OCPP20FirmwareStatusEnumType.Installed) // H11: SecurityEventNotification after Installed assert.strictEqual(sentRequests.length, 6) diff --git a/tests/charging-station/ocpp/2.0/OCPP20RequestService-FirmwareStatusNotification.test.ts b/tests/charging-station/ocpp/2.0/OCPP20RequestService-FirmwareStatusNotification.test.ts index e279940a..f4eec9a7 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20RequestService-FirmwareStatusNotification.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20RequestService-FirmwareStatusNotification.test.ts @@ -14,7 +14,7 @@ import { type TestableOCPP20RequestService, } from '../../../../src/charging-station/ocpp/2.0/__testable__/index.js' import { - FirmwareStatusEnumType, + OCPP20FirmwareStatusEnumType, type OCPP20FirmwareStatusNotificationRequest, type OCPP20FirmwareStatusNotificationResponse, OCPP20RequestCommand, @@ -56,13 +56,17 @@ await describe('J01 - FirmwareStatusNotification', async () => { }) await it('should send FirmwareStatusNotification with Downloading status', async () => { - await service.requestFirmwareStatusNotification(station, FirmwareStatusEnumType.Downloading, 42) + await service.requestFirmwareStatusNotification( + station, + OCPP20FirmwareStatusEnumType.Downloading, + 42 + ) assert.strictEqual(sendMessageMock.mock.calls.length, 1) const sentPayload = sendMessageMock.mock.calls[0] .arguments[2] as OCPP20FirmwareStatusNotificationRequest - assert.strictEqual(sentPayload.status, FirmwareStatusEnumType.Downloading) + assert.strictEqual(sentPayload.status, OCPP20FirmwareStatusEnumType.Downloading) assert.strictEqual(sentPayload.requestId, 42) const commandName = sendMessageMock.mock.calls[0].arguments[3] @@ -74,7 +78,7 @@ await describe('J01 - FirmwareStatusNotification', async () => { await service.requestFirmwareStatusNotification( station, - FirmwareStatusEnumType.Installed, + OCPP20FirmwareStatusEnumType.Installed, testRequestId ) @@ -82,14 +86,14 @@ await describe('J01 - FirmwareStatusNotification', async () => { const sentPayload = sendMessageMock.mock.calls[0] .arguments[2] as OCPP20FirmwareStatusNotificationRequest - assert.strictEqual(sentPayload.status, FirmwareStatusEnumType.Installed) + assert.strictEqual(sentPayload.status, OCPP20FirmwareStatusEnumType.Installed) assert.strictEqual(sentPayload.requestId, testRequestId) }) await it('should return empty response from CSMS', async () => { const response = await service.requestFirmwareStatusNotification( station, - FirmwareStatusEnumType.Downloaded, + OCPP20FirmwareStatusEnumType.Downloaded, 1 )