]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
feat(ocpp): add EncodingMethodEnumType enum for signed meter value encoding methods
authorJérôme Benoit <jerome.benoit@sap.com>
Tue, 7 Apr 2026 18:39:26 +0000 (20:39 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Tue, 7 Apr 2026 18:39:26 +0000 (20:39 +0200)
- Add EncodingMethodEnumType enum with OCMF and EDL values based on
  OCA Application Note and OCPP 2.0.1/2.1 specs
- Use enum in SignedMeterData interface and wire types (with string
  union for extensibility)
- Replace hardcoded 'OCMF' string constant with enum value
- Update all tests to use EncodingMethodEnumType consistently

src/charging-station/ocpp/OCPPSignedMeterDataGenerator.ts
src/types/index.ts
src/types/ocpp/1.6/MeterValues.ts
src/types/ocpp/2.0/MeterValues.ts
src/types/ocpp/Configuration.ts
tests/charging-station/ocpp/1.6/OCPP16SignedMeterValues.test.ts
tests/charging-station/ocpp/2.0/OCPP20SignedMeterValues.test.ts
tests/charging-station/ocpp/OCPPSignedMeterDataGenerator.test.ts
tests/types/ocpp/1.6/MeterValues.test.ts

index 6474a25fe375637e90fd1ac420dec716b34e9f08..bb09b3dfea95d4d87ef520e6843446e644f814b9 100644 (file)
@@ -1,6 +1,7 @@
 import { createHash } from 'node:crypto'
 
 import {
+  EncodingMethodEnumType,
   type JsonObject,
   MeterValueContext,
   MeterValueUnit,
@@ -9,7 +10,7 @@ import {
 import { roundTo } from '../../utils/index.js'
 
 export interface SignedMeterData extends JsonObject {
-  encodingMethod: string
+  encodingMethod: EncodingMethodEnumType
   publicKey: string
   signedMeterData: string
   signingMethod: SigningMethodEnumType
@@ -25,7 +26,7 @@ export interface SignedMeterDataParams {
 }
 
 const DEFAULT_SIGNING_METHOD = SigningMethodEnumType.ECDSA_secp256r1_SHA256
-const ENCODING_METHOD = 'OCMF'
+const DEFAULT_ENCODING_METHOD = EncodingMethodEnumType.OCMF
 
 const contextToTxCode = (context: MeterValueContext): string => {
   switch (context) {
@@ -78,7 +79,7 @@ export const generateSignedMeterData = (
   const ocmfString = `OCMF|${JSON.stringify(ocmfPayload)}|{"SA":"${resolvedSigningMethod}","SD":"${simulatedSignature}"}`
 
   return {
-    encodingMethod: ENCODING_METHOD,
+    encodingMethod: DEFAULT_ENCODING_METHOD,
     publicKey: publicKeyHex != null ? buildPublicKeyValue(publicKeyHex) : '',
     signedMeterData: Buffer.from(ocmfString).toString('base64'),
     signingMethod: resolvedSigningMethod,
index f39ba6341b4a7dd134ed92eb922d2deaadc3c77a..4a7a17ecada7cf5d346008bb8baec16421227f12 100644 (file)
@@ -339,6 +339,7 @@ export { type GenericResponse, GenericStatus, RegistrationStatusEnumType } from
 export {
   type ConfigurationKeyType,
   ConnectorPhaseRotation,
+  EncodingMethodEnumType,
   type OCPPConfigurationKey,
   PublicKeyWithSignedMeterValueEnumType,
   SigningMethodEnumType,
index 021f97649ee30f09e15769759bf01586d5bbbcd5..6bcd4b7fe14e6c69fb395d31e67e3e6af8d6ca85 100644 (file)
@@ -1,7 +1,7 @@
 import type { EmptyObject } from '../../EmptyObject.js'
 import type { JsonObject } from '../../JsonType.js'
 
-import { type SigningMethodEnumType } from '../Configuration.js'
+import { type EncodingMethodEnumType, type SigningMethodEnumType } from '../Configuration.js'
 
 export enum OCPP16MeterValueContext {
   INTERRUPTION_BEGIN = 'Interruption.Begin',
@@ -108,7 +108,7 @@ export interface OCPP16SampledValue extends JsonObject {
 }
 
 export interface OCPP16SignedMeterValue extends JsonObject {
-  encodingMethod: string
+  encodingMethod: EncodingMethodEnumType | string
   publicKey: string
   signedMeterData: string
   signingMethod: '' | SigningMethodEnumType
index fc34a54656d04068da6adca4765e2885cf3fd0e5..33788545a458a88f68ae4f277dedba1e42a504b2 100644 (file)
@@ -2,7 +2,7 @@ import type { EmptyObject } from '../../EmptyObject.js'
 import type { JsonObject } from '../../JsonType.js'
 import type { CustomDataType, OCPP20UnitEnumType } from './Common.js'
 
-import { type SigningMethodEnumType } from '../Configuration.js'
+import { type EncodingMethodEnumType, type SigningMethodEnumType } from '../Configuration.js'
 
 export enum OCPP20LocationEnumType {
   Body = 'Body',
@@ -91,7 +91,7 @@ export interface OCPP20SampledValue extends JsonObject {
 
 export interface OCPP20SignedMeterValue extends JsonObject {
   customData?: CustomDataType
-  encodingMethod: string // maxLength: 50
+  encodingMethod: EncodingMethodEnumType | string // maxLength: 50
   publicKey: string // Base64 encoded, maxLength: 2500
   signedMeterData: string // Base64 encoded, maxLength: 2500
   signingMethod: '' | SigningMethodEnumType // maxLength: 50
index 991e1fb02149864589bb176f8ad674887640d0b5..202c9a72072f0705f0e798b5fcec54833feb2e21 100644 (file)
@@ -22,6 +22,11 @@ export enum ConnectorPhaseRotation {
   Unknown = 'Unknown',
 }
 
+export enum EncodingMethodEnumType {
+  EDL = 'EDL',
+  OCMF = 'OCMF',
+}
+
 export enum PublicKeyWithSignedMeterValueEnumType {
   EveryMeterValue = 'EveryMeterValue',
   Never = 'Never',
index d58bf8aa1179530a55f1e3b54a2204382dc605d1..88a114383bfc2ed4e1824a923a7dedad294b2af4 100644 (file)
@@ -13,6 +13,7 @@ import type { ChargingStation } from '../../../../src/charging-station/index.js'
 import { buildSignedOCPP16SampledValue } from '../../../../src/charging-station/ocpp/1.6/OCPP16RequestBuilders.js'
 import { OCPP16ServiceUtils } from '../../../../src/charging-station/ocpp/1.6/OCPP16ServiceUtils.js'
 import {
+  EncodingMethodEnumType,
   type OCPP16MeterValue,
   OCPP16MeterValueContext,
   OCPP16MeterValueFormat,
@@ -39,7 +40,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => {
   await describe('buildSignedOCPP16SampledValue', async () => {
     await it('should return SampledValue with format=SignedData', () => {
       const signedData: OCPP16SignedMeterValue = {
-        encodingMethod: 'OCMF',
+        encodingMethod: EncodingMethodEnumType.OCMF,
         publicKey: '',
         signedMeterData: 'dGVzdA==',
         signingMethod: '',
@@ -55,7 +56,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => {
 
     await it('should set measurand to Energy.Active.Import.Register', () => {
       const signedData: OCPP16SignedMeterValue = {
-        encodingMethod: 'OCMF',
+        encodingMethod: EncodingMethodEnumType.OCMF,
         publicKey: '',
         signedMeterData: 'dGVzdA==',
         signingMethod: '',
@@ -71,7 +72,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => {
 
     await it('should set location to Outlet', () => {
       const signedData: OCPP16SignedMeterValue = {
-        encodingMethod: 'OCMF',
+        encodingMethod: EncodingMethodEnumType.OCMF,
         publicKey: '',
         signedMeterData: 'dGVzdA==',
         signingMethod: '',
@@ -87,7 +88,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => {
 
     await it('should set value to JSON-serialized SignedMeterValue', () => {
       const signedData: OCPP16SignedMeterValue = {
-        encodingMethod: 'OCMF',
+        encodingMethod: EncodingMethodEnumType.OCMF,
         publicKey: 'abc123',
         signedMeterData: 'dGVzdA==',
         signingMethod: '',
@@ -99,7 +100,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => {
       )
 
       const parsed = JSON.parse(result.value) as OCPP16SignedMeterValue
-      assert.strictEqual(parsed.encodingMethod, 'OCMF')
+      assert.strictEqual(parsed.encodingMethod, EncodingMethodEnumType.OCMF)
       assert.strictEqual(parsed.signingMethod, '')
       assert.strictEqual(parsed.signedMeterData, 'dGVzdA==')
       assert.strictEqual(parsed.publicKey, 'abc123')
@@ -107,7 +108,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => {
 
     await it('should use the provided context', () => {
       const signedData: OCPP16SignedMeterValue = {
-        encodingMethod: 'OCMF',
+        encodingMethod: EncodingMethodEnumType.OCMF,
         publicKey: '',
         signedMeterData: 'dGVzdA==',
         signingMethod: '',
@@ -296,7 +297,7 @@ await describe('OCPP 1.6 — Signed MeterValues', async () => {
       assert.strictEqual(typeof parsed.signingMethod, 'string')
       assert.strictEqual(typeof parsed.signedMeterData, 'string')
       assert.strictEqual(typeof parsed.publicKey, 'string')
-      assert.strictEqual(parsed.encodingMethod, 'OCMF')
+      assert.strictEqual(parsed.encodingMethod, EncodingMethodEnumType.OCMF)
       assert.strictEqual(parsed.signingMethod, SigningMethodEnumType.ECDSA_secp256r1_SHA256)
     })
   })
index 7be581b73e52f6943161161a38643a4d86aea0cb..0013ed6bf06b85598e5d029139f35bb8b3b9b428 100644 (file)
@@ -14,6 +14,7 @@ import { buildOCPP20SampledValue } from '../../../../src/charging-station/ocpp/2
 import { buildMeterValue } from '../../../../src/charging-station/ocpp/OCPPServiceUtils.js'
 import { type SampledValueSigningConfig } from '../../../../src/charging-station/ocpp/OCPPSignedMeterValueUtils.js'
 import {
+  EncodingMethodEnumType,
   MeterValueMeasurand,
   OCPP20ComponentName,
   OCPP20ReadingContextEnumType,
@@ -68,7 +69,7 @@ await describe('OCPP 2.0 Signed Meter Values', async () => {
         sampledValue.signedMeterValue.signingMethod,
         SigningMethodEnumType.ECDSA_secp256r1_SHA256
       )
-      assert.strictEqual(sampledValue.signedMeterValue.encodingMethod, 'OCMF')
+      assert.strictEqual(sampledValue.signedMeterValue.encodingMethod, EncodingMethodEnumType.OCMF)
     })
 
     await it('should not add signedMeterValue when signing is disabled', () => {
@@ -246,7 +247,10 @@ await describe('OCPP 2.0 Signed Meter Values', async () => {
         energySampledValue.signedMeterValue.signingMethod,
         SigningMethodEnumType.ECDSA_secp256r1_SHA256
       )
-      assert.strictEqual(energySampledValue.signedMeterValue.encodingMethod, 'OCMF')
+      assert.strictEqual(
+        energySampledValue.signedMeterValue.encodingMethod,
+        EncodingMethodEnumType.OCMF
+      )
     })
 
     await it('should not add signedMeterValue when SignReadings is not configured', () => {
index aae72c44aa99be92858515c1fb86d2020f025020..d7c7852ad67229e19ce29af9845aba921bf243eb 100644 (file)
@@ -20,6 +20,7 @@ import {
   type SignedMeterDataParams,
 } from '../../../src/charging-station/ocpp/OCPPSignedMeterDataGenerator.js'
 import {
+  EncodingMethodEnumType,
   MeterValueContext,
   MeterValueUnit,
   SigningMethodEnumType,
@@ -68,7 +69,7 @@ await describe('SignedMeterDataGenerator', async () => {
   await it('should set encodingMethod to OCMF', () => {
     const result = generateSignedMeterData(DEFAULT_PARAMS)
 
-    assert.strictEqual(result.encodingMethod, 'OCMF')
+    assert.strictEqual(result.encodingMethod, EncodingMethodEnumType.OCMF)
   })
 
   await it('should return empty publicKey when no publicKeyHex provided', () => {
index 34e3c70eccb541af31505041f844f6f75c5f6d1f..774ae39b18eddd13c938d0936938e6dbeb35e210 100644 (file)
@@ -7,6 +7,7 @@ import assert from 'node:assert/strict'
 import { describe, it } from 'node:test'
 
 import {
+  EncodingMethodEnumType,
   OCPP16MeterValueFormat,
   type OCPP16SignedMeterValue,
   SigningMethodEnumType,
@@ -26,12 +27,12 @@ await describe('OCPP 1.6 meter value types', async () => {
   await describe('OCPP16SignedMeterValue', async () => {
     await it('should compile as an interface with correct field names', () => {
       const signedMeterValue: OCPP16SignedMeterValue = {
-        encodingMethod: 'OCMF',
+        encodingMethod: EncodingMethodEnumType.OCMF,
         publicKey: 'b2NhOmJhc2UxNjphc24xOmZha2VrZXk=', // cspell:disable-line
         signedMeterData: 'T0NNRnx7fXxmYWtlc2lnbmF0dXJl', // cspell:disable-line
         signingMethod: SigningMethodEnumType.ECDSA_secp256r1_SHA256,
       }
-      assert.strictEqual(signedMeterValue.encodingMethod, 'OCMF')
+      assert.strictEqual(signedMeterValue.encodingMethod, EncodingMethodEnumType.OCMF)
       assert.strictEqual(
         signedMeterValue.signingMethod,
         SigningMethodEnumType.ECDSA_secp256r1_SHA256