From 4d1de41db4e2740a8a7e7de32670400f2d72dfd5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Fri, 3 Apr 2026 17:09:52 +0200 Subject: [PATCH] refactor: replace string literals with OCPP enum constants Add AllowReset and MasterPassGroupId to OCPP20OptionalVariableName enum. Replace all hardcoded OCPP variable name strings with enum references in source and test files. Move EVSE Component section comment to correct position. --- .../ocpp/2.0/OCPP20IncomingRequestService.ts | 4 +- .../ocpp/2.0/OCPP20VariableRegistry.ts | 42 +++++++++---------- src/types/ocpp/2.0/Variables.ts | 2 + tests/charging-station/SharedLRUCache.test.ts | 5 ++- ...0IncomingRequestService-MasterPass.test.ts | 7 ++-- .../ocpp/2.0/OCPP20Integration.test.ts | 13 +++--- ...0ServiceUtils-enforceMessageLimits.test.ts | 10 ++--- 7 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts index a175e806..eeb89f2b 100644 --- a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts @@ -1971,7 +1971,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { !OCPP20ServiceUtils.readVariableAsBoolean( chargingStation, OCPP20ComponentName.EVSE, - 'AllowReset', + OCPP20OptionalVariableName.AllowReset, true ) ) { @@ -2371,7 +2371,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { const masterPassGroupId = OCPP20ServiceUtils.readVariableValue( chargingStation, OCPP20ComponentName.AuthCtrlr, - 'MasterPassGroupId' + OCPP20OptionalVariableName.MasterPassGroupId ) if ( masterPassGroupId != null && diff --git a/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts b/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts index f8819f8c..1de52d7e 100644 --- a/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts +++ b/src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts @@ -365,17 +365,6 @@ export const VARIABLE_REGISTRY: Record = { variable: 'DisableRemoteAuthorization', }, - [buildRegistryKey(OCPP20ComponentName.AuthCtrlr, 'MasterPassGroupId')]: { - component: OCPP20ComponentName.AuthCtrlr, - dataType: DataEnumType.string, - description: - 'IdTokens that have this id as groupId belong to the Master Pass Group. They can stop any ongoing transaction but cannot start transactions.', - maxLength: 36, - mutability: MutabilityEnumType.ReadWrite, - persistence: PersistenceEnumType.Persistent, - supportedAttributes: [AttributeEnumType.Actual], - variable: 'MasterPassGroupId', - }, [buildRegistryKey(OCPP20ComponentName.AuthCtrlr, 'OfflineTxForUnknownIdEnabled')]: { component: OCPP20ComponentName.AuthCtrlr, dataType: DataEnumType.boolean, @@ -386,6 +375,17 @@ export const VARIABLE_REGISTRY: Record = { supportedAttributes: [AttributeEnumType.Actual], variable: 'OfflineTxForUnknownIdEnabled', }, + [buildRegistryKey(OCPP20ComponentName.AuthCtrlr, OCPP20OptionalVariableName.MasterPassGroupId)]: { + component: OCPP20ComponentName.AuthCtrlr, + dataType: DataEnumType.string, + description: + 'IdTokens that have this id as groupId belong to the Master Pass Group. They can stop any ongoing transaction but cannot start transactions.', + maxLength: 36, + mutability: MutabilityEnumType.ReadWrite, + persistence: PersistenceEnumType.Persistent, + supportedAttributes: [AttributeEnumType.Actual], + variable: OCPP20OptionalVariableName.MasterPassGroupId, + }, [buildRegistryKey( OCPP20ComponentName.AuthCtrlr, OCPP20RequiredVariableName.AuthorizeRemoteStart @@ -944,16 +944,6 @@ export const VARIABLE_REGISTRY: Record = { }, // EVSE Component - [buildRegistryKey(OCPP20ComponentName.EVSE, 'AllowReset')]: { - component: OCPP20ComponentName.EVSE, - dataType: DataEnumType.boolean, - defaultValue: 'true', - description: 'Can be used to announce that an EVSE can be reset individually', - mutability: MutabilityEnumType.ReadOnly, - persistence: PersistenceEnumType.Persistent, - supportedAttributes: [AttributeEnumType.Actual], - variable: 'AllowReset', - }, [buildRegistryKey(OCPP20ComponentName.EVSE, 'Available')]: { component: OCPP20ComponentName.EVSE, dataType: DataEnumType.boolean, @@ -1031,6 +1021,16 @@ export const VARIABLE_REGISTRY: Record = { supportedAttributes: [AttributeEnumType.Actual], variable: OCPP20DeviceInfoVariableName.AvailabilityState, }, + [buildRegistryKey(OCPP20ComponentName.EVSE, OCPP20OptionalVariableName.AllowReset)]: { + component: OCPP20ComponentName.EVSE, + dataType: DataEnumType.boolean, + defaultValue: 'true', + description: 'Can be used to announce that an EVSE can be reset individually', + mutability: MutabilityEnumType.ReadOnly, + persistence: PersistenceEnumType.Persistent, + supportedAttributes: [AttributeEnumType.Actual], + variable: OCPP20OptionalVariableName.AllowReset, + }, // FirmwareCtrlr Component [buildRegistryKey( diff --git a/src/types/ocpp/2.0/Variables.ts b/src/types/ocpp/2.0/Variables.ts index 9c59e0f6..d2a01c89 100644 --- a/src/types/ocpp/2.0/Variables.ts +++ b/src/types/ocpp/2.0/Variables.ts @@ -33,10 +33,12 @@ export enum OCPP20DeviceInfoVariableName { } export enum OCPP20OptionalVariableName { + AllowReset = 'AllowReset', CertSigningRepeatTimes = 'CertSigningRepeatTimes', CertSigningWaitMinimum = 'CertSigningWaitMinimum', ConfigurationValueSize = 'ConfigurationValueSize', HeartbeatInterval = 'HeartbeatInterval', + MasterPassGroupId = 'MasterPassGroupId', MaxCertificateChainSize = 'MaxCertificateChainSize', MaxEnergyOnInvalidId = 'MaxEnergyOnInvalidId', NonEvseSpecific = 'NonEvseSpecific', diff --git a/tests/charging-station/SharedLRUCache.test.ts b/tests/charging-station/SharedLRUCache.test.ts index e803d810..e81c36ee 100644 --- a/tests/charging-station/SharedLRUCache.test.ts +++ b/tests/charging-station/SharedLRUCache.test.ts @@ -19,6 +19,7 @@ import type { import { Bootstrap } from '../../src/charging-station/Bootstrap.js' import { SharedLRUCache } from '../../src/charging-station/SharedLRUCache.js' +import { OCPP16StandardParametersKey } from '../../src/types/index.js' import { standardCleanup } from '../helpers/TestLifecycleHelpers.js' interface BootstrapStatic { @@ -34,7 +35,9 @@ function createCacheableConfiguration (hash: string): ChargingStationConfigurati return { automaticTransactionGenerator: { enable: false, maxDuration: 120, minDuration: 60 }, configurationHash: hash, - configurationKey: [{ key: 'HeartbeatInterval', readonly: false, value: '60' }], + configurationKey: [ + { key: OCPP16StandardParametersKey.HeartbeatInterval, readonly: false, value: '60' }, + ], stationInfo: { chargingStationId: 'test-station' }, } as unknown as ChargingStationConfiguration } diff --git a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-MasterPass.test.ts b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-MasterPass.test.ts index 170873d5..bd30481b 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-MasterPass.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-MasterPass.test.ts @@ -18,6 +18,7 @@ import { OCPPAuthServiceFactory } from '../../../../src/charging-station/ocpp/au import { GetVariableStatusEnumType, OCPP20IdTokenEnumType, + OCPP20OptionalVariableName, OCPPVersion, RequestStartStopStatusEnumType, } from '../../../../src/types/index.js' @@ -81,7 +82,7 @@ await describe('C12.FR.09 - MasterPassGroupId Check', async () => { const results = originalGetVariables(station, requests) for (let i = 0; i < (requests as { variable?: { name?: string } }[]).length; i++) { const req = (requests as { variable?: { name?: string } }[])[i] - if (req.variable?.name === 'MasterPassGroupId') { + if (req.variable?.name === OCPP20OptionalVariableName.MasterPassGroupId) { results[i] = { ...results[i], attributeStatus: GetVariableStatusEnumType.Accepted, @@ -142,7 +143,7 @@ await describe('C12.FR.09 - MasterPassGroupId Check', async () => { const results = originalGetVariables(station, requests) for (let i = 0; i < (requests as { variable?: { name?: string } }[]).length; i++) { const req = (requests as { variable?: { name?: string } }[])[i] - if (req.variable?.name === 'MasterPassGroupId') { + if (req.variable?.name === OCPP20OptionalVariableName.MasterPassGroupId) { results[i] = { ...results[i], attributeStatus: GetVariableStatusEnumType.Accepted, @@ -185,7 +186,7 @@ await describe('C12.FR.09 - MasterPassGroupId Check', async () => { const results = originalGetVariables(station, requests) for (let i = 0; i < (requests as { variable?: { name?: string } }[]).length; i++) { const req = (requests as { variable?: { name?: string } }[])[i] - if (req.variable?.name === 'MasterPassGroupId') { + if (req.variable?.name === OCPP20OptionalVariableName.MasterPassGroupId) { results[i] = { ...results[i], attributeStatus: GetVariableStatusEnumType.Accepted, diff --git a/tests/charging-station/ocpp/2.0/OCPP20Integration.test.ts b/tests/charging-station/ocpp/2.0/OCPP20Integration.test.ts index 5c71ab6c..69d3b4e8 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20Integration.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20Integration.test.ts @@ -21,6 +21,7 @@ import { AttributeEnumType, GetVariableStatusEnumType, OCPP20ComponentName, + OCPP20OptionalVariableName, OCPPVersion, SetVariableStatusEnumType, } from '../../../../src/types/index.js' @@ -72,7 +73,7 @@ await describe('OCPP 2.0 Integration — SetVariables → GetVariables consisten { attributeValue: '60', component: { name: OCPP20ComponentName.OCPPCommCtrlr }, - variable: { name: 'HeartbeatInterval' }, + variable: { name: OCPP20OptionalVariableName.HeartbeatInterval }, }, ], } @@ -81,7 +82,7 @@ await describe('OCPP 2.0 Integration — SetVariables → GetVariables consisten { attributeType: AttributeEnumType.Actual, component: { name: OCPP20ComponentName.OCPPCommCtrlr }, - variable: { name: 'HeartbeatInterval' }, + variable: { name: OCPP20OptionalVariableName.HeartbeatInterval }, }, ], } @@ -133,12 +134,12 @@ await describe('OCPP 2.0 Integration — SetVariables → GetVariables consisten { attributeValue: '30', component: { name: OCPP20ComponentName.OCPPCommCtrlr }, - variable: { name: 'HeartbeatInterval' }, + variable: { name: OCPP20OptionalVariableName.HeartbeatInterval }, }, { attributeValue: '20', component: { name: OCPP20ComponentName.OCPPCommCtrlr }, - variable: { name: 'WebSocketPingInterval' }, + variable: { name: OCPP20OptionalVariableName.WebSocketPingInterval }, }, ], } @@ -150,11 +151,11 @@ await describe('OCPP 2.0 Integration — SetVariables → GetVariables consisten getVariableData: [ { component: { name: OCPP20ComponentName.OCPPCommCtrlr }, - variable: { name: 'HeartbeatInterval' }, + variable: { name: OCPP20OptionalVariableName.HeartbeatInterval }, }, { component: { name: OCPP20ComponentName.OCPPCommCtrlr }, - variable: { name: 'WebSocketPingInterval' }, + variable: { name: OCPP20OptionalVariableName.WebSocketPingInterval }, }, ], } diff --git a/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-enforceMessageLimits.test.ts b/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-enforceMessageLimits.test.ts index de5a4484..3d4cd911 100644 --- a/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-enforceMessageLimits.test.ts +++ b/tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-enforceMessageLimits.test.ts @@ -10,7 +10,7 @@ import { OCPP20ServiceUtils, type RejectionReason, } from '../../../../src/charging-station/ocpp/2.0/OCPP20ServiceUtils.js' -import { ReasonCodeEnumType } from '../../../../src/types/index.js' +import { OCPP20OptionalVariableName, ReasonCodeEnumType } from '../../../../src/types/index.js' import { standardCleanup } from '../../../helpers/TestLifecycleHelpers.js' interface MockLogger { @@ -77,7 +77,7 @@ await describe('OCPP20ServiceUtils.enforceMessageLimits', async () => { await it('should return rejected:false and empty results when both limits are 0', () => { const station = makeMockStation() const logger = makeMockLogger() - const items = [makeItem('HeartbeatInterval', '30')] + const items = [makeItem(OCPP20OptionalVariableName.HeartbeatInterval, '30')] const result = OCPP20ServiceUtils.enforceMessageLimits( station, @@ -227,7 +227,7 @@ await describe('OCPP20ServiceUtils.enforceMessageLimits', async () => { await it('should return rejected:false when data size is under the bytes limit', () => { const station = makeMockStation() const logger = makeMockLogger() - const items = [makeItem('HeartbeatInterval', '30')] + const items = [makeItem(OCPP20OptionalVariableName.HeartbeatInterval, '30')] const result = OCPP20ServiceUtils.enforceMessageLimits( station, @@ -339,7 +339,7 @@ await describe('OCPP20ServiceUtils.enforceMessageLimits', async () => { await it('should pass original item to buildRejected callback', () => { const station = makeMockStation() const logger = makeMockLogger() - const item = makeItem('HeartbeatInterval', 'abc') + const item = makeItem(OCPP20OptionalVariableName.HeartbeatInterval, 'abc') const capturedItems: TestItem[] = [] OCPP20ServiceUtils.enforceMessageLimits( @@ -363,7 +363,7 @@ await describe('OCPP20ServiceUtils.enforceMessageLimits', async () => { await it('should pass reason with info and reasonCode to buildRejected callback', () => { const station = makeMockStation() const logger = makeMockLogger() - const item = makeItem('WebSocketPingInterval', 'xyz') + const item = makeItem(OCPP20OptionalVariableName.WebSocketPingInterval, 'xyz') const capturedReasons: RejectionReason[] = [] OCPP20ServiceUtils.enforceMessageLimits( -- 2.43.0