]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
fix: use Component.Variable[.Instance] key format for OCPP 2.0 variable persistence
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 27 Mar 2026 11:31:06 +0000 (12:31 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 27 Mar 2026 11:31:06 +0000 (12:31 +0100)
The previous format used bare variable names (e.g., 'Enabled'), causing
collisions across components (9 components define Enabled, 8+ define
Available, etc.). Now uses the spec's (Component, Variable, Instance)
triplet as the persisted configuration key.

- Rewrite computeConfigurationKeyName to return Component.Variable format
- Remove shouldFlattenInstance (no longer needed)
- Extract buildConfigKey helper in ConfigurationKeyUtils, export via barrel
- Add MaxCertificateChainSize to OCPP20OptionalVariableName enum
- Update all direct config key lookups with component prefix
- Update keba-ocpp2 template keys to new format
- Update OCPP2_PARAMETER_KEY_MAP resolved values
- Replace all enum string literals in log messages with enum references
- Update all tests to use new key format

16 files changed:
src/assets/station-templates/keba-ocpp2.station-template.json
src/charging-station/ConfigurationKeyUtils.ts
src/charging-station/index.ts
src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts
src/charging-station/ocpp/2.0/OCPP20ResponseService.ts
src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts
src/charging-station/ocpp/2.0/OCPP20VariableManager.ts
src/charging-station/ocpp/2.0/OCPP20VariableRegistry.ts
src/types/ocpp/2.0/Variables.ts
tests/charging-station/ConfigurationKeyUtils.test.ts
tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-CertificateSigned.test.ts
tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-GetBaseReport.test.ts
tests/charging-station/ocpp/2.0/OCPP20IncomingRequestService-SetVariables.test.ts
tests/charging-station/ocpp/2.0/OCPP20ResponseService-BootNotification.test.ts
tests/charging-station/ocpp/2.0/OCPP20TestUtils.ts
tests/charging-station/ocpp/2.0/OCPP20VariableManager.test.ts

index dabea003ad75ff3ed6f51297f21b56ffa1bda2a9..351d611725a159f1a71335710666f28bddd99321 100644 (file)
   "Configuration": {
     "configurationKey": [
       {
-        "key": "TxUpdatedMeasurands",
+        "key": "SampledDataCtrlr.TxUpdatedMeasurands",
         "readonly": false,
         "value": "Energy.Active.Import.Register,Power.Active.Import,Current.Import,Voltage"
       },
       {
-        "key": "TxUpdatedInterval",
+        "key": "SampledDataCtrlr.TxUpdatedInterval",
         "readonly": false,
         "value": "30"
       },
       {
-        "key": "AuthorizeRemoteStart",
+        "key": "AuthCtrlr.AuthorizeRemoteStart",
         "readonly": false,
         "value": "false"
       },
       {
-        "key": "LocalPreAuthorization",
+        "key": "AuthCtrlr.LocalPreAuthorization",
         "readonly": false,
         "value": "false"
       },
       {
-        "key": "OfflineTxForUnknownIdEnabled",
+        "key": "AuthCtrlr.OfflineTxForUnknownIdEnabled",
         "readonly": false,
         "value": "false"
       },
       {
-        "key": "MessageAttempts.TransactionEvent",
+        "key": "OCPPCommCtrlr.MessageAttempts.TransactionEvent",
         "readonly": false,
         "value": "3"
       },
       {
-        "key": "MessageAttemptInterval.TransactionEvent",
+        "key": "OCPPCommCtrlr.MessageAttemptInterval.TransactionEvent",
         "readonly": false,
         "value": "20"
       },
       {
-        "key": "WebSocketPingInterval",
+        "key": "ChargingStation.WebSocketPingInterval",
         "readonly": false,
         "value": "60"
       }
index 8b08d03d66170a4025f46e07a852f1a872aa3006..891255a2aa872cb546409ebaacdf6c89f633d937 100644 (file)
@@ -3,11 +3,17 @@ import type { ChargingStation } from './ChargingStation.js'
 import {
   type ConfigurationKey,
   type ConfigurationKeyType,
+  OCPP20ComponentName,
   OCPPVersion,
   StandardParametersKey,
 } from '../types/index.js'
 import { logger, once } from '../utils/index.js'
 
+export const buildConfigKey = (component: string, variable: string, instance?: string): string => {
+  const base = `${component}.${variable}`
+  return instance != null ? `${base}.${instance}` : base
+}
+
 const OCPP2_PARAMETER_KEY_MAP = new Map<
   ConfigurationKeyType,
   {
@@ -17,15 +23,39 @@ const OCPP2_PARAMETER_KEY_MAP = new Map<
       >(
       (
         [
-          [StandardParametersKey.AuthorizeRemoteTxRequests, StandardParametersKey.AuthorizeRemoteStart],
-          [StandardParametersKey.ConnectionTimeOut, StandardParametersKey.EVConnectionTimeOut],
+          [
+            StandardParametersKey.AuthorizeRemoteTxRequests,
+            buildConfigKey(OCPP20ComponentName.AuthCtrlr, StandardParametersKey.AuthorizeRemoteStart),
+          ],
+          [
+            StandardParametersKey.ConnectionTimeOut,
+            buildConfigKey(OCPP20ComponentName.TxCtrlr, StandardParametersKey.EVConnectionTimeOut),
+          ],
           [
             StandardParametersKey.LocalAuthorizeOffline,
-            StandardParametersKey.LocalAuthorizationOffline,
+            buildConfigKey(
+              OCPP20ComponentName.AuthCtrlr,
+              StandardParametersKey.LocalAuthorizationOffline
+            ),
+          ],
+          [
+            StandardParametersKey.LocalPreAuthorize,
+            buildConfigKey(OCPP20ComponentName.AuthCtrlr, StandardParametersKey.LocalPreAuthorization),
+          ],
+          [
+            StandardParametersKey.MeterValueSampleInterval,
+            buildConfigKey(
+              OCPP20ComponentName.SampledDataCtrlr,
+              StandardParametersKey.TxUpdatedInterval
+            ),
+          ],
+          [
+            StandardParametersKey.MeterValuesSampledData,
+            buildConfigKey(
+              OCPP20ComponentName.SampledDataCtrlr,
+              StandardParametersKey.TxUpdatedMeasurands
+            ),
           ],
-          [StandardParametersKey.LocalPreAuthorize, StandardParametersKey.LocalPreAuthorization],
-          [StandardParametersKey.MeterValueSampleInterval, StandardParametersKey.TxUpdatedInterval],
-          [StandardParametersKey.MeterValuesSampledData, StandardParametersKey.TxUpdatedMeasurands],
         ] as [ConfigurationKeyType, ConfigurationKeyType][]
       ).map(([from, to]) => [
         from,
index 7a48f729f0636c22c17ac59b8df0b09a49f37f2d..0d25e222e7c8213ff46b976e4364b5c4c68f4045 100644 (file)
@@ -2,6 +2,7 @@ export { Bootstrap } from './Bootstrap.js'
 export type { ChargingStation } from './ChargingStation.js'
 export {
   addConfigurationKey,
+  buildConfigKey,
   getConfigurationKey,
   setConfigurationKeyValue,
 } from './ConfigurationKeyUtils.js'
index bc7e9328e729e6d2b512aa852d9ef35ef72b7b25..7871b9f282dbcd24b7b05c4a3357ec98e3c1b5bc 100644 (file)
@@ -80,6 +80,7 @@ import {
   type OCPP20NotifyReportRequest,
   type OCPP20NotifyReportResponse,
   OCPP20OperationalStatusEnumType,
+  OCPP20OptionalVariableName,
   OCPP20ReasonEnumType,
   OCPP20RequestCommand,
   type OCPP20RequestStartTransactionRequest,
@@ -130,7 +131,7 @@ import {
   truncateId,
   validateUUID,
 } from '../../../utils/index.js'
-import { getConfigurationKey } from '../../ConfigurationKeyUtils.js'
+import { buildConfigKey, getConfigurationKey } from '../../ConfigurationKeyUtils.js'
 import {
   getIdTagsFile,
   hasPendingReservation,
@@ -1251,19 +1252,25 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
     }
 
     // A02.FR.16: Enforce MaxCertificateChainSize — reject if chain exceeds configured limit
-    const maxChainSizeKey = getConfigurationKey(chargingStation, 'MaxCertificateChainSize')
+    const maxChainSizeKey = getConfigurationKey(
+      chargingStation,
+      buildConfigKey(
+        OCPP20ComponentName.SecurityCtrlr,
+        OCPP20OptionalVariableName.MaxCertificateChainSize
+      )
+    )
     if (maxChainSizeKey?.value != null) {
       const maxChainSize = parseInt(maxChainSizeKey.value, 10)
       if (!isNaN(maxChainSize) && maxChainSize > 0) {
         const chainByteSize = Buffer.byteLength(certificateChain, 'utf8')
         if (chainByteSize > maxChainSize) {
           logger.warn(
-            `${chargingStation.logPrefix()} ${moduleName}.handleRequestCertificateSigned: Certificate chain size ${chainByteSize.toString()} bytes exceeds MaxCertificateChainSize ${maxChainSize.toString()} bytes`
+            `${chargingStation.logPrefix()} ${moduleName}.handleRequestCertificateSigned: Certificate chain size ${chainByteSize.toString()} bytes exceeds ${OCPP20OptionalVariableName.MaxCertificateChainSize as string} ${maxChainSize.toString()} bytes`
           )
           return {
             status: GenericStatus.Rejected,
             statusInfo: {
-              additionalInfo: `Certificate chain size (${chainByteSize.toString()} bytes) exceeds MaxCertificateChainSize (${maxChainSize.toString()} bytes)`,
+              additionalInfo: `Certificate chain size (${chainByteSize.toString()} bytes) exceeds ${OCPP20OptionalVariableName.MaxCertificateChainSize as string} (${maxChainSize.toString()} bytes)`,
               reasonCode: ReasonCodeEnumType.InvalidCertificate,
             },
           }
index c96d516db18efd29f88743426368a31b65e2e1f4..fcc8923a15f50eea0559024cbe3044cd0fcc7583 100644 (file)
@@ -1,6 +1,10 @@
 import type { ValidateFunction } from 'ajv'
 
-import { addConfigurationKey, type ChargingStation } from '../../../charging-station/index.js'
+import {
+  addConfigurationKey,
+  buildConfigKey,
+  type ChargingStation,
+} from '../../../charging-station/index.js'
 import {
   ChargingStationEvents,
   ConnectorStatusEnum,
@@ -8,6 +12,7 @@ import {
   OCPP20AuthorizationStatusEnumType,
   type OCPP20AuthorizeResponse,
   type OCPP20BootNotificationResponse,
+  OCPP20ComponentName,
   type OCPP20DataTransferResponse,
   type OCPP20FirmwareStatusNotificationResponse,
   type OCPP20Get15118EVCertificateResponse,
@@ -37,7 +42,6 @@ import { mapOCPP20TokenType, OCPPAuthServiceFactory } from '../auth/index.js'
 import { OCPPResponseService } from '../OCPPResponseService.js'
 import { sendAndSetConnectorStatus } from '../OCPPServiceUtils.js'
 import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
-
 const moduleName = 'OCPP20ResponseService'
 
 /**
@@ -199,7 +203,10 @@ export class OCPP20ResponseService extends OCPPResponseService {
         )
         addConfigurationKey(
           chargingStation,
-          OCPP20OptionalVariableName.HeartbeatInterval,
+          buildConfigKey(
+            OCPP20ComponentName.OCPPCommCtrlr,
+            OCPP20OptionalVariableName.HeartbeatInterval
+          ),
           payload.interval.toString(),
           {},
           { overwrite: true, save: true }
index 6936629e2aebd1d073333273595d849006c30bbb..7cd6adc7138cf2df27bcfaaaba245df384dac744 100644 (file)
@@ -37,7 +37,7 @@ import {
   logger,
   validateIdentifierString,
 } from '../../../utils/index.js'
-import { getConfigurationKey } from '../../ConfigurationKeyUtils.js'
+import { buildConfigKey, getConfigurationKey } from '../../ConfigurationKeyUtils.js'
 import {
   buildMeterValue,
   OCPPServiceUtils,
@@ -292,11 +292,17 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils {
     try {
       const itemsCfg = getConfigurationKey(
         chargingStation,
-        OCPP20RequiredVariableName.ItemsPerMessage
+        buildConfigKey(
+          OCPP20ComponentName.DeviceDataCtrlr,
+          OCPP20RequiredVariableName.ItemsPerMessage
+        )
       )?.value
       const bytesCfg = getConfigurationKey(
         chargingStation,
-        OCPP20RequiredVariableName.BytesPerMessage
+        buildConfigKey(
+          OCPP20ComponentName.DeviceDataCtrlr,
+          OCPP20RequiredVariableName.BytesPerMessage
+        )
       )?.value
       if (itemsCfg && /^\d+$/.test(itemsCfg)) {
         itemsLimit = convertToIntOrNaN(itemsCfg)
index 0b43690b13d0d2bbfcf3cc31c14ae37a94a2b7bf..b42fa61ff99226a2fb52a7e1e1cd937a69b54617 100644 (file)
@@ -23,6 +23,7 @@ import { Constants, convertToIntOrNaN, logger } from '../../../utils/index.js'
 import { type ChargingStation } from '../../ChargingStation.js'
 import {
   addConfigurationKey,
+  buildConfigKey,
   getConfigurationKey,
   setConfigurationKeyValue,
 } from '../../ConfigurationKeyUtils.js'
@@ -44,15 +45,9 @@ const isOCPP20RequiredVariableName = (name: string): name is OCPP20RequiredVaria
   return Object.values(OCPP20RequiredVariableName).includes(name as OCPP20RequiredVariableName)
 }
 
-const shouldFlattenInstance = (variableMetadata: VariableMetadata): boolean => {
-  // TODO: Generalize instance flattening via registry metadata
-  return variableMetadata.variable === (OCPP20RequiredVariableName.MessageAttemptInterval as string)
-}
-const computeConfigurationKeyName = (variableMetadata: VariableMetadata): string => {
-  return variableMetadata.instance != null && !shouldFlattenInstance(variableMetadata)
-    ? `${variableMetadata.variable}.${variableMetadata.instance}`
-    : variableMetadata.variable
-}
+const computeConfigurationKeyName = (variableMetadata: VariableMetadata): string =>
+  buildConfigKey(variableMetadata.component, variableMetadata.variable, variableMetadata.instance)
+
 export class OCPP20VariableManager {
   private static instance: null | OCPP20VariableManager = null
 
@@ -522,12 +517,18 @@ export class OCPP20VariableManager {
     let valueSize: string | undefined
     let reportingValueSize: string | undefined
     if (!invalidVariables.has(valueSizeKey)) {
-      valueSize = getConfigurationKey(chargingStation, OCPP20RequiredVariableName.ValueSize)?.value
+      valueSize = getConfigurationKey(
+        chargingStation,
+        buildConfigKey(OCPP20ComponentName.DeviceDataCtrlr, OCPP20RequiredVariableName.ValueSize)
+      )?.value
     }
     if (!invalidVariables.has(reportingValueSizeKey)) {
       reportingValueSize = getConfigurationKey(
         chargingStation,
-        OCPP20RequiredVariableName.ReportingValueSize
+        buildConfigKey(
+          OCPP20ComponentName.DeviceDataCtrlr,
+          OCPP20RequiredVariableName.ReportingValueSize
+        )
       )?.value
     }
     // Apply ValueSize first then ReportingValueSize
@@ -890,13 +891,16 @@ export class OCPP20VariableManager {
       if (!invalidVariables.has(configurationValueSizeKey)) {
         configurationValueSizeRaw = getConfigurationKey(
           chargingStation,
-          OCPP20RequiredVariableName.ConfigurationValueSize
+          buildConfigKey(
+            OCPP20ComponentName.DeviceDataCtrlr,
+            OCPP20RequiredVariableName.ConfigurationValueSize
+          )
         )?.value
       }
       if (!invalidVariables.has(valueSizeKey)) {
         valueSizeRaw = getConfigurationKey(
           chargingStation,
-          OCPP20RequiredVariableName.ValueSize
+          buildConfigKey(OCPP20ComponentName.DeviceDataCtrlr, OCPP20RequiredVariableName.ValueSize)
         )?.value
       }
       const cfgLimit = convertToIntOrNaN(configurationValueSizeRaw ?? '')
index 4ff7a3666bc060b243dd053a668157655c61557b..9a19930d051ec53ab8393c78168f6f2ca9089ac3 100644 (file)
@@ -1967,7 +1967,10 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     supportedAttributes: [AttributeEnumType.Actual],
     variable: 'Identity',
   },
-  [buildRegistryKey(OCPP20ComponentName.SecurityCtrlr as string, 'MaxCertificateChainSize')]: {
+  [buildRegistryKey(
+    OCPP20ComponentName.SecurityCtrlr as string,
+    OCPP20OptionalVariableName.MaxCertificateChainSize
+  )]: {
     component: OCPP20ComponentName.SecurityCtrlr as string,
     dataType: DataEnumType.integer,
     description:
@@ -1976,7 +1979,7 @@ export const VARIABLE_REGISTRY: Record<string, VariableMetadata> = {
     mutability: MutabilityEnumType.ReadWrite,
     persistence: PersistenceEnumType.Persistent,
     supportedAttributes: [AttributeEnumType.Actual],
-    variable: 'MaxCertificateChainSize',
+    variable: OCPP20OptionalVariableName.MaxCertificateChainSize as string,
   },
   [buildRegistryKey(
     OCPP20ComponentName.SecurityCtrlr as string,
index 22e0b31313cd4e01b27d182d67bbb225deceea0d..ee419459a32fecd6ac061364ae1aa99bdeaf2857 100644 (file)
@@ -34,6 +34,7 @@ export enum OCPP20DeviceInfoVariableName {
 
 export enum OCPP20OptionalVariableName {
   HeartbeatInterval = 'HeartbeatInterval',
+  MaxCertificateChainSize = 'MaxCertificateChainSize',
   WebSocketPingInterval = 'WebSocketPingInterval',
 }
 
index ebf989e60bc7d99f3262b544af52c1107a912e69..e9aca27eca5ca77d5e891bd1f7c919a1071669b7 100644 (file)
@@ -9,11 +9,12 @@ import type { ChargingStationOcppConfiguration } from '../../src/types/index.js'
 
 import {
   addConfigurationKey,
+  buildConfigKey,
   deleteConfigurationKey,
   getConfigurationKey,
   setConfigurationKeyValue,
 } from '../../src/charging-station/ConfigurationKeyUtils.js'
-import { OCPPVersion, StandardParametersKey } from '../../src/types/index.js'
+import { OCPP20ComponentName, OCPPVersion, StandardParametersKey } from '../../src/types/index.js'
 import { logger } from '../../src/utils/index.js'
 import { standardCleanup } from '../helpers/TestLifecycleHelpers.js'
 import { createMockChargingStation } from './ChargingStationTestUtils.js'
@@ -95,7 +96,13 @@ await describe('ConfigurationKeyUtils', async () => {
       if (k == null) {
         assert.fail('Expected configuration key to be found')
       }
-      assert.strictEqual(k.key, StandardParametersKey.TxUpdatedMeasurands)
+      assert.strictEqual(
+        k.key,
+        buildConfigKey(
+          OCPP20ComponentName.SampledDataCtrlr,
+          StandardParametersKey.TxUpdatedMeasurands
+        )
+      )
       assert.strictEqual(k.value, VALUE_A)
     })
 
@@ -113,7 +120,10 @@ await describe('ConfigurationKeyUtils', async () => {
       if (k == null) {
         assert.fail('Expected configuration key to be found')
       }
-      assert.strictEqual(k.key, StandardParametersKey.EVConnectionTimeOut)
+      assert.strictEqual(
+        k.key,
+        buildConfigKey(OCPP20ComponentName.TxCtrlr, StandardParametersKey.EVConnectionTimeOut)
+      )
       assert.strictEqual(k.value, '30')
     })
 
@@ -384,7 +394,10 @@ await describe('ConfigurationKeyUtils', async () => {
       if (k == null) {
         assert.fail('Expected configuration key to be found')
       }
-      assert.strictEqual(k.key, StandardParametersKey.AuthorizeRemoteStart)
+      assert.strictEqual(
+        k.key,
+        buildConfigKey(OCPP20ComponentName.AuthCtrlr, StandardParametersKey.AuthorizeRemoteStart)
+      )
       assert.strictEqual(k.value, 'false')
     })
 
@@ -402,7 +415,13 @@ await describe('ConfigurationKeyUtils', async () => {
       if (k == null) {
         assert.fail('Expected configuration key to be found')
       }
-      assert.strictEqual(k.key, StandardParametersKey.TxUpdatedInterval)
+      assert.strictEqual(
+        k.key,
+        buildConfigKey(
+          OCPP20ComponentName.SampledDataCtrlr,
+          StandardParametersKey.TxUpdatedInterval
+        )
+      )
       assert.strictEqual(k.value, '60')
     })
   })
index 9ce65d5343d48a4e303cc753faa0684ca67039ef..dd29f8cd5ca4bf09f2272c2e05a9f7fe32a25781 100644 (file)
@@ -9,7 +9,7 @@ import { afterEach, beforeEach, describe, it, mock } from 'node:test'
 import type { ChargingStation } from '../../../../src/charging-station/index.js'
 import type { ChargingStationWithCertificateManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20CertificateManager.js'
 
-import { addConfigurationKey } from '../../../../src/charging-station/ConfigurationKeyUtils.js'
+import { addConfigurationKey, buildConfigKey } 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 {
@@ -17,6 +17,8 @@ import {
   GenericStatus,
   type OCPP20CertificateSignedRequest,
   type OCPP20CertificateSignedResponse,
+  OCPP20ComponentName,
+  OCPP20OptionalVariableName,
   OCPP20RequestCommand,
   OCPPVersion,
   ReasonCodeEnumType,
@@ -306,7 +308,14 @@ await describe('I04 - CertificateSigned', async () => {
         storeCertificateResult: true,
       })
 
-      addConfigurationKey(station, 'MaxCertificateChainSize', '10')
+      addConfigurationKey(
+        station,
+        buildConfigKey(
+          OCPP20ComponentName.SecurityCtrlr,
+          OCPP20OptionalVariableName.MaxCertificateChainSize
+        ),
+        '10'
+      )
 
       const request: OCPP20CertificateSignedRequest = {
         certificateChain: VALID_PEM_CERTIFICATE,
@@ -321,7 +330,11 @@ await describe('I04 - CertificateSigned', async () => {
       assert.strictEqual(response.status, GenericStatus.Rejected)
       assert.notStrictEqual(response.statusInfo, undefined)
       assert.strictEqual(response.statusInfo?.reasonCode, 'InvalidCertificate')
-      assert.ok(response.statusInfo.additionalInfo?.includes('MaxCertificateChainSize'))
+      assert.ok(
+        response.statusInfo.additionalInfo?.includes(
+          OCPP20OptionalVariableName.MaxCertificateChainSize as string
+        )
+      )
     })
 
     await it('should accept certificate chain within MaxCertificateChainSize', async () => {
@@ -330,7 +343,14 @@ await describe('I04 - CertificateSigned', async () => {
         storeCertificateResult: true,
       })
 
-      addConfigurationKey(station, 'MaxCertificateChainSize', '100000')
+      addConfigurationKey(
+        station,
+        buildConfigKey(
+          OCPP20ComponentName.SecurityCtrlr,
+          OCPP20OptionalVariableName.MaxCertificateChainSize
+        ),
+        '100000'
+      )
 
       const request: OCPP20CertificateSignedRequest = {
         certificateChain: VALID_PEM_CERTIFICATE,
index 690c68b99a4ff77f7619dc5ba8db7a36c0995541..aeb892f100eb8c7ccac92fc034a7fd42b1da4287 100644 (file)
@@ -10,11 +10,14 @@ import type { ChargingStation } from '../../../../src/charging-station/index.js'
 
 import {
   addConfigurationKey,
+  buildConfigKey,
   setConfigurationKeyValue,
 } from '../../../../src/charging-station/ConfigurationKeyUtils.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 { OCPP20VariableManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableManager.js'
+import {
+  OCPP20VariableManager,
+} from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableManager.js'
 import {
   AttributeEnumType,
   GenericDeviceModelStatusEnumType,
@@ -273,7 +276,10 @@ await describe('B07 - Get Base Report', async () => {
   // ReportingValueSize truncation test
   await it('should truncate long SequenceList/MemberList values per ReportingValueSize', () => {
     // Ensure ReportingValueSize is at a small value (default is Constants.OCPP_VALUE_ABSOLUTE_MAX_LENGTH). We will override configuration key if absent.
-    const reportingSizeKey = StandardParametersKey.ReportingValueSize
+    const reportingSizeKey = buildConfigKey(
+      OCPP20ComponentName.DeviceDataCtrlr,
+      StandardParametersKey.ReportingValueSize
+    )
     // Add or lower configuration key to 10 to force truncation
     addConfigurationKey(station, reportingSizeKey, '10', undefined, {
       overwrite: true,
index bf0b5f518853843e3839fa8b94953b602dfd480d..00acc59b3ab661c759c229f85d099adc5692ead4 100644 (file)
@@ -7,8 +7,7 @@ import { millisecondsToSeconds } from 'date-fns'
 import assert from 'node:assert/strict'
 import { afterEach, beforeEach, describe, it } from 'node:test'
 
-import type { ChargingStation } from '../../../../src/charging-station/index.js'
-
+import { buildConfigKey, 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 { OCPP20VariableManager } from '../../../../src/charging-station/ocpp/2.0/OCPP20VariableManager.js'
@@ -510,7 +509,10 @@ await describe('B05 - Set Variables', async () => {
     const postCalcLimit = preEstimate + 10
     upsertConfigurationKey(
       mockStation,
-      OCPP20RequiredVariableName.BytesPerMessage,
+      buildConfigKey(
+        OCPP20ComponentName.DeviceDataCtrlr,
+        OCPP20RequiredVariableName.BytesPerMessage
+      ),
       postCalcLimit.toString(),
       false
     )
@@ -541,7 +543,11 @@ await describe('B05 - Set Variables', async () => {
   await it('should enforce ConfigurationValueSize when ValueSize unset (service propagation)', () => {
     resetValueSizeLimits(mockStation)
     setConfigurationValueSize(mockStation, 100)
-    upsertConfigurationKey(mockStation, OCPP20RequiredVariableName.ValueSize, '')
+    upsertConfigurationKey(
+      mockStation,
+      buildConfigKey(OCPP20ComponentName.DeviceDataCtrlr, OCPP20RequiredVariableName.ValueSize),
+      ''
+    )
     const prefix = 'wss://example.com/'
     const withinLimit = prefix + 'a'.repeat(100 - prefix.length)
     const overLimit = prefix + 'a'.repeat(100 - prefix.length + 1)
@@ -575,7 +581,14 @@ await describe('B05 - Set Variables', async () => {
 
   await it('should enforce ValueSize when ConfigurationValueSize unset (service propagation)', () => {
     resetValueSizeLimits(mockStation)
-    upsertConfigurationKey(mockStation, OCPP20RequiredVariableName.ConfigurationValueSize, '')
+    upsertConfigurationKey(
+      mockStation,
+      buildConfigKey(
+        OCPP20ComponentName.DeviceDataCtrlr,
+        OCPP20RequiredVariableName.ConfigurationValueSize
+      ),
+      ''
+    )
     setValueSize(mockStation, 120)
     const prefix = 'wss://example.com/'
     const withinLimit = prefix + 'b'.repeat(120 - prefix.length)
index 88c40ca56d0b804e14040f9a88d55ae882c21aaa..16ba3781052dea12f1c91ba0632550a49158b3fd 100644 (file)
@@ -17,10 +17,12 @@ import { afterEach, beforeEach, describe, it, mock } from 'node:test'
 
 import type { MockChargingStation } from '../../ChargingStationTestUtils.js'
 
+import { buildConfigKey } from '../../../../src/charging-station/index.js'
 import { OCPP20ResponseService } from '../../../../src/charging-station/ocpp/2.0/OCPP20ResponseService.js'
 import {
   ChargingStationEvents,
   type OCPP20BootNotificationResponse,
+  OCPP20ComponentName,
   OCPP20OptionalVariableName,
   OCPP20RequestCommand,
   OCPPVersion,
@@ -132,7 +134,13 @@ await describe('B01 - BootNotificationResponse handler', async () => {
       assert.fail('Expected configKey to be defined')
     }
     assert.strictEqual(configKey.length, 1)
-    assert.strictEqual(configKey[0].key, OCPP20OptionalVariableName.HeartbeatInterval)
+    assert.strictEqual(
+      configKey[0].key,
+      buildConfigKey(
+        OCPP20ComponentName.OCPPCommCtrlr,
+        OCPP20OptionalVariableName.HeartbeatInterval
+      )
+    )
     assert.strictEqual(configKey[0].value, '300')
   })
 
index fd1c7ca4b6d6146cb0e704d7ef8c0193a5cca310..64971750c1c4abc5ec404de33813f7dbfed2f14d 100644 (file)
@@ -18,12 +18,14 @@ import type {
 } from '../../../../src/types/index.js'
 import type { OCPP20RequestCommand } from '../../../../src/types/index.js'
 
+import { buildConfigKey } from '../../../../src/charging-station/index.js'
 import { OCPP20RequestService } from '../../../../src/charging-station/ocpp/2.0/OCPP20RequestService.js'
 import { OCPP20ResponseService } from '../../../../src/charging-station/ocpp/2.0/OCPP20ResponseService.js'
 import {
   ConnectorStatusEnum,
   DeleteCertificateStatusEnumType,
   HashAlgorithmEnumType,
+  OCPP20ComponentName,
   OCPP20IdTokenEnumType,
   OCPP20RequiredVariableName,
   OCPPVersion,
@@ -257,8 +259,16 @@ export function resetConnectorTransactionState (chargingStation: ChargingStation
  * @param chargingStation Charging station test instance whose configuration limits are reset.
  */
 export function resetLimits (chargingStation: ChargingStation) {
-  upsertConfigurationKey(chargingStation, OCPP20RequiredVariableName.ItemsPerMessage, '100')
-  upsertConfigurationKey(chargingStation, OCPP20RequiredVariableName.BytesPerMessage, '10000')
+  upsertConfigurationKey(
+    chargingStation,
+    buildConfigKey(OCPP20ComponentName.DeviceDataCtrlr, OCPP20RequiredVariableName.ItemsPerMessage),
+    '100'
+  )
+  upsertConfigurationKey(
+    chargingStation,
+    buildConfigKey(OCPP20ComponentName.DeviceDataCtrlr, OCPP20RequiredVariableName.BytesPerMessage),
+    '10000'
+  )
 }
 
 /**
@@ -268,7 +278,10 @@ export function resetLimits (chargingStation: ChargingStation) {
 export function resetReportingValueSize (chargingStation: ChargingStation) {
   upsertConfigurationKey(
     chargingStation,
-    OCPP20RequiredVariableName.ReportingValueSize,
+    buildConfigKey(
+      OCPP20ComponentName.DeviceDataCtrlr,
+      OCPP20RequiredVariableName.ReportingValueSize
+    ),
     Constants.OCPP_VALUE_ABSOLUTE_MAX_LENGTH.toString()
   )
 }
@@ -281,12 +294,15 @@ export function resetReportingValueSize (chargingStation: ChargingStation) {
 export function resetValueSizeLimits (chargingStation: ChargingStation) {
   upsertConfigurationKey(
     chargingStation,
-    OCPP20RequiredVariableName.ConfigurationValueSize,
+    buildConfigKey(
+      OCPP20ComponentName.DeviceDataCtrlr,
+      OCPP20RequiredVariableName.ConfigurationValueSize
+    ),
     Constants.OCPP_VALUE_ABSOLUTE_MAX_LENGTH.toString()
   )
   upsertConfigurationKey(
     chargingStation,
-    OCPP20RequiredVariableName.ValueSize,
+    buildConfigKey(OCPP20ComponentName.DeviceDataCtrlr, OCPP20RequiredVariableName.ValueSize),
     Constants.OCPP_VALUE_ABSOLUTE_MAX_LENGTH.toString()
   )
 }
@@ -299,7 +315,10 @@ export function resetValueSizeLimits (chargingStation: ChargingStation) {
 export function setConfigurationValueSize (chargingStation: ChargingStation, size: number) {
   upsertConfigurationKey(
     chargingStation,
-    OCPP20RequiredVariableName.ConfigurationValueSize,
+    buildConfigKey(
+      OCPP20ComponentName.DeviceDataCtrlr,
+      OCPP20RequiredVariableName.ConfigurationValueSize
+    ),
     size.toString()
   )
 }
@@ -312,7 +331,10 @@ export function setConfigurationValueSize (chargingStation: ChargingStation, siz
 export function setReportingValueSize (chargingStation: ChargingStation, size: number) {
   upsertConfigurationKey(
     chargingStation,
-    OCPP20RequiredVariableName.ReportingValueSize,
+    buildConfigKey(
+      OCPP20ComponentName.DeviceDataCtrlr,
+      OCPP20RequiredVariableName.ReportingValueSize
+    ),
     size.toString()
   )
 }
@@ -330,12 +352,12 @@ export function setStrictLimits (
 ) {
   upsertConfigurationKey(
     chargingStation,
-    OCPP20RequiredVariableName.ItemsPerMessage,
+    buildConfigKey(OCPP20ComponentName.DeviceDataCtrlr, OCPP20RequiredVariableName.ItemsPerMessage),
     itemsLimit.toString()
   )
   upsertConfigurationKey(
     chargingStation,
-    OCPP20RequiredVariableName.BytesPerMessage,
+    buildConfigKey(OCPP20ComponentName.DeviceDataCtrlr, OCPP20RequiredVariableName.BytesPerMessage),
     bytesLimit.toString()
   )
 }
@@ -346,7 +368,11 @@ export function setStrictLimits (
  * @param size Desired stored value size limit.
  */
 export function setValueSize (chargingStation: ChargingStation, size: number) {
-  upsertConfigurationKey(chargingStation, OCPP20RequiredVariableName.ValueSize, size.toString())
+  upsertConfigurationKey(
+    chargingStation,
+    buildConfigKey(OCPP20ComponentName.DeviceDataCtrlr, OCPP20RequiredVariableName.ValueSize),
+    size.toString()
+  )
 }
 
 /**
index 1bd20781bd87f411f26cfd72b4bf5b1855137896..eb043680f54608ed0179dcf59342a896a2dc4384 100644 (file)
@@ -10,6 +10,7 @@ import { afterEach, beforeEach, describe, it } from 'node:test'
 import type { ChargingStation } from '../../../../src/charging-station/index.js'
 
 import {
+  buildConfigKey,
   deleteConfigurationKey,
   getConfigurationKey,
 } from '../../../../src/charging-station/ConfigurationKeyUtils.js'
@@ -79,12 +80,18 @@ await describe('B05 - OCPP20VariableManager', async () => {
       ocppConfiguration: {
         configurationKey: [
           {
-            key: StandardParametersKey.HeartbeatInterval,
+            key: buildConfigKey(
+              OCPP20ComponentName.OCPPCommCtrlr,
+              StandardParametersKey.HeartbeatInterval
+            ),
             readonly: false,
             value: millisecondsToSeconds(Constants.DEFAULT_HEARTBEAT_INTERVAL).toString(),
           },
           {
-            key: StandardParametersKey.WebSocketPingInterval,
+            key: buildConfigKey(
+              OCPP20ComponentName.ChargingStation,
+              StandardParametersKey.WebSocketPingInterval
+            ),
             readonly: false,
             value: Constants.DEFAULT_WEBSOCKET_PING_INTERVAL.toString(),
           },
@@ -1067,7 +1074,10 @@ await describe('B05 - OCPP20VariableManager', async () => {
     await it('should avoid duplicate persistence operations when value unchanged', () => {
       const keyBefore = getConfigurationKey(
         station,
-        OCPP20OptionalVariableName.HeartbeatInterval as unknown as VariableType['name']
+        buildConfigKey(
+          OCPP20ComponentName.OCPPCommCtrlr,
+          OCPP20OptionalVariableName.HeartbeatInterval
+        )
       )
       assert.notStrictEqual(keyBefore, undefined)
       const originalValue = keyBefore?.value
@@ -1089,7 +1099,10 @@ await describe('B05 - OCPP20VariableManager', async () => {
       assert.strictEqual(changed.attributeStatus, SetVariableStatusEnumType.Accepted)
       const keyAfterChange = getConfigurationKey(
         station,
-        OCPP20OptionalVariableName.HeartbeatInterval as unknown as VariableType['name']
+        buildConfigKey(
+          OCPP20ComponentName.OCPPCommCtrlr,
+          OCPP20OptionalVariableName.HeartbeatInterval
+        )
       )
       assert.notStrictEqual(keyAfterChange?.value, originalValue)
       const reverted = manager.setVariables(station, [
@@ -1102,7 +1115,10 @@ await describe('B05 - OCPP20VariableManager', async () => {
       assert.strictEqual(reverted.attributeStatus, SetVariableStatusEnumType.Accepted)
       const keyAfterRevert = getConfigurationKey(
         station,
-        OCPP20OptionalVariableName.HeartbeatInterval as unknown as VariableType['name']
+        buildConfigKey(
+          OCPP20ComponentName.OCPPCommCtrlr,
+          OCPP20OptionalVariableName.HeartbeatInterval
+        )
       )
       assert.strictEqual(keyAfterRevert?.value, originalValue)
     })
@@ -1110,12 +1126,12 @@ await describe('B05 - OCPP20VariableManager', async () => {
     await it('should add missing configuration key with default during self-check', () => {
       deleteConfigurationKey(
         station,
-        OCPP20RequiredVariableName.EVConnectionTimeOut as unknown as VariableType['name'],
+        buildConfigKey(OCPP20ComponentName.TxCtrlr, OCPP20RequiredVariableName.EVConnectionTimeOut),
         { save: false }
       )
       const before = getConfigurationKey(
         station,
-        OCPP20RequiredVariableName.EVConnectionTimeOut as unknown as VariableType['name']
+        buildConfigKey(OCPP20ComponentName.TxCtrlr, OCPP20RequiredVariableName.EVConnectionTimeOut)
       )
       assert.strictEqual(before, undefined)
       const res = manager.getVariables(station, [
@@ -1129,7 +1145,7 @@ await describe('B05 - OCPP20VariableManager', async () => {
       assert.strictEqual(res.attributeValue, Constants.DEFAULT_EV_CONNECTION_TIMEOUT.toString())
       const after = getConfigurationKey(
         station,
-        OCPP20RequiredVariableName.EVConnectionTimeOut as unknown as VariableType['name']
+        buildConfigKey(OCPP20ComponentName.TxCtrlr, OCPP20RequiredVariableName.EVConnectionTimeOut)
       )
       assert.notStrictEqual(after, undefined)
     })
@@ -1354,7 +1370,7 @@ await describe('B05 - OCPP20VariableManager', async () => {
       // remove ValueSize to simulate unset
       deleteConfigurationKey(
         station,
-        OCPP20RequiredVariableName.ValueSize as unknown as VariableType['name'],
+        buildConfigKey(OCPP20ComponentName.DeviceDataCtrlr, OCPP20RequiredVariableName.ValueSize),
         { save: false }
       )
       const okRes = manager.setVariables(station, [
@@ -1384,7 +1400,10 @@ await describe('B05 - OCPP20VariableManager', async () => {
       setValueSize(station, 40)
       deleteConfigurationKey(
         station,
-        OCPP20RequiredVariableName.ConfigurationValueSize as unknown as VariableType['name'],
+        buildConfigKey(
+          OCPP20ComponentName.DeviceDataCtrlr,
+          OCPP20RequiredVariableName.ConfigurationValueSize
+        ),
         { save: false }
       )
       const okRes = manager.setVariables(station, [
@@ -1552,7 +1571,10 @@ await describe('B05 - OCPP20VariableManager', async () => {
 
       const beforeCfg = getConfigurationKey(
         station,
-        OCPP20RequiredVariableName.FileTransferProtocols as unknown as VariableType['name']
+        buildConfigKey(
+          OCPP20ComponentName.OCPPCommCtrlr,
+          OCPP20RequiredVariableName.FileTransferProtocols
+        )
       )
       assert.strictEqual(beforeCfg?.value, 'HTTPS,FTPS,SFTP')
       const rejected = manager.setVariables(station, [
@@ -1574,7 +1596,10 @@ await describe('B05 - OCPP20VariableManager', async () => {
       assert.strictEqual(afterGet.attributeValue, 'HTTPS,FTPS,SFTP')
       const afterCfg = getConfigurationKey(
         station,
-        OCPP20RequiredVariableName.FileTransferProtocols as unknown as VariableType['name']
+        buildConfigKey(
+          OCPP20ComponentName.OCPPCommCtrlr,
+          OCPP20RequiredVariableName.FileTransferProtocols
+        )
       )
       assert.strictEqual(afterCfg?.value, beforeCfg.value)
     })
@@ -1737,7 +1762,10 @@ await describe('B05 - OCPP20VariableManager', async () => {
       // Ensure ReportingValueSize unset
       deleteConfigurationKey(
         station,
-        OCPP20RequiredVariableName.ReportingValueSize as unknown as VariableType['name'],
+        buildConfigKey(
+          OCPP20ComponentName.DeviceDataCtrlr,
+          OCPP20RequiredVariableName.ReportingValueSize
+        ),
         { save: false }
       )
       // Temporarily set large ValueSize to allow storing long value
@@ -1803,7 +1831,7 @@ await describe('B05 - OCPP20VariableManager', async () => {
       const overLongValue = buildWsExampleUrl(3000, 'c')
       upsertConfigurationKey(
         station,
-        OCPP20VendorVariableName.ConnectionUrl as unknown as VariableType['name'],
+        buildConfigKey(OCPP20ComponentName.ChargingStation, OCPP20VendorVariableName.ConnectionUrl),
         overLongValue
       )
       // Set generous ValueSize (1500) and ReportingValueSize (1400) so only absolute cap applies (since both < Constants.OCPP_VALUE_ABSOLUTE_MAX_LENGTH)
@@ -1862,12 +1890,18 @@ await describe('B05 - OCPP20VariableManager', async () => {
     await it('should auto-create persistent OrganizationName configuration key during self-check', () => {
       deleteConfigurationKey(
         station,
-        OCPP20RequiredVariableName.OrganizationName as unknown as VariableType['name'],
+        buildConfigKey(
+          OCPP20ComponentName.SecurityCtrlr,
+          OCPP20RequiredVariableName.OrganizationName
+        ),
         { save: false }
       )
       const before = getConfigurationKey(
         station,
-        OCPP20RequiredVariableName.OrganizationName as unknown as VariableType['name']
+        buildConfigKey(
+          OCPP20ComponentName.SecurityCtrlr,
+          OCPP20RequiredVariableName.OrganizationName
+        )
       )
       assert.strictEqual(before, undefined)
       const res = manager.getVariables(station, [
@@ -1880,7 +1914,10 @@ await describe('B05 - OCPP20VariableManager', async () => {
       assert.strictEqual(res.attributeValue, 'ChangeMeOrg')
       const after = getConfigurationKey(
         station,
-        OCPP20RequiredVariableName.OrganizationName as unknown as VariableType['name']
+        buildConfigKey(
+          OCPP20ComponentName.SecurityCtrlr,
+          OCPP20RequiredVariableName.OrganizationName
+        )
       )
       assert.notStrictEqual(after, undefined)
       assert.strictEqual(after?.value, 'ChangeMeOrg')
@@ -1931,7 +1968,11 @@ await describe('B05 - OCPP20VariableManager', async () => {
       // Ensure no configuration key exists before operations
       const cfgBefore = getConfigurationKey(
         station,
-        OCPP20RequiredVariableName.MessageAttemptInterval as unknown as VariableType['name']
+        buildConfigKey(
+          OCPP20ComponentName.OCPPCommCtrlr,
+          OCPP20RequiredVariableName.MessageAttemptInterval,
+          'TransactionEvent'
+        )
       )
       assert.strictEqual(cfgBefore, undefined)
       const initialGet = manager.getVariables(station, [
@@ -2043,7 +2084,11 @@ await describe('B05 - OCPP20VariableManager', async () => {
 
       const cfgAfter = getConfigurationKey(
         station,
-        OCPP20RequiredVariableName.MessageAttemptInterval as unknown as VariableType['name']
+        buildConfigKey(
+          OCPP20ComponentName.OCPPCommCtrlr,
+          OCPP20RequiredVariableName.MessageAttemptInterval,
+          'TransactionEvent'
+        )
       )
       assert.notStrictEqual(cfgAfter, undefined)
       assert.strictEqual(cfgAfter?.value, '7')
@@ -2170,7 +2215,7 @@ await describe('B05 - OCPP20VariableManager', async () => {
     try {
       deleteConfigurationKey(
         stationA,
-        OCPP20RequiredVariableName.EVConnectionTimeOut as unknown as VariableType['name'],
+        buildConfigKey(OCPP20ComponentName.TxCtrlr, OCPP20RequiredVariableName.EVConnectionTimeOut),
         { save: false }
       )
       VARIABLE_REGISTRY[registryKey].defaultValue = undefined