From 6833a3b40f4e07b7b022cf0e50efe325143e5e47 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sun, 22 Mar 2026 12:40:27 +0100 Subject: [PATCH] fix(ocpp2): build meter values payload in broadcast channel for OCPP 2.0.x The OCPP 2.0 branch in handleMeterValues was passing the raw broadcast channel payload through to requestHandler without constructing the required evseId and meterValue fields. Build the payload using buildMeterValue and resolve evseId from connectorId, matching the pattern used by the OCPP 1.6 branch. --- .../ChargingStationWorkerBroadcastChannel.ts | 23 ++++++++++++++++--- ...rgingStationWorkerBroadcastChannel.test.ts | 23 ++++++++++++++++++- .../helpers/StationHelpers.ts | 3 +++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts b/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts index 57a87856..f90a1c4e 100644 --- a/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts +++ b/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts @@ -68,7 +68,7 @@ import { logger, } from '../../utils/index.js' import { getConfigurationKey } from '../ConfigurationKeyUtils.js' -import { buildMeterValue } from '../ocpp/index.js' +import { buildMeterValue, OCPP20ServiceUtils } from '../ocpp/index.js' import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js' const moduleName = 'ChargingStationWorkerBroadcastChannel' @@ -470,17 +470,35 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne private async handleMeterValues ( requestPayload?: BroadcastChannelRequestPayload ): Promise { + const connectorId = requestPayload?.connectorId if ( this.chargingStation.stationInfo?.ocppVersion === OCPPVersion.VERSION_20 || this.chargingStation.stationInfo?.ocppVersion === OCPPVersion.VERSION_201 ) { + const txUpdatedInterval = OCPP20ServiceUtils.getTxUpdatedInterval(this.chargingStation) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const evseId = this.chargingStation.getEvseIdByConnectorId(connectorId!) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const transactionId = this.chargingStation.getConnectorStatus(connectorId!)?.transactionId return await this.chargingStation.ocppRequestService.requestHandler< MeterValuesRequest, MeterValuesResponse >( this.chargingStation, RequestCommand.METER_VALUES, - requestPayload as MeterValuesRequest, + { + evseId, + meterValue: [ + buildMeterValue( + this.chargingStation, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + connectorId!, + transactionId, + txUpdatedInterval + ), + ], + ...requestPayload, + } as MeterValuesRequest, this.requestParams ) } @@ -488,7 +506,6 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne this.chargingStation, StandardParametersKey.MeterValueSampleInterval ) - const connectorId = requestPayload?.connectorId // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const transactionId = this.chargingStation.getConnectorStatus(connectorId!)?.transactionId return await this.chargingStation.ocppRequestService.requestHandler< diff --git a/tests/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.test.ts b/tests/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.test.ts index c392a4ea..437673ea 100644 --- a/tests/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.test.ts +++ b/tests/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.test.ts @@ -17,6 +17,7 @@ import { GenericStatus, GetCertificateStatusEnumType, Iso15118EVCertificateStatusEnumType, + MeterValueMeasurand, OCPP20AuthorizationStatusEnumType, OCPPVersion, ProcedureName, @@ -739,6 +740,18 @@ await describe('ChargingStationWorkerBroadcastChannel', async () => { await it('should dispatch METER_VALUES for OCPP 2.0.1 via requestHandler', async () => { const { sentRequests, station } = createMockStationWithRequestTracking() + // Add MeterValues template to connector 1 so buildMeterValue can construct a valid payload + const connectorStatus = station.getConnectorStatus(1) + if (connectorStatus != null) { + connectorStatus.MeterValues = [ + { + measurand: MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER, + unit: 'Wh', + value: 0, + }, + ] + } + instance = new ChargingStationWorkerBroadcastChannel(station) const testable = createTestableWorkerBroadcastChannel(instance) @@ -746,7 +759,7 @@ await describe('ChargingStationWorkerBroadcastChannel', async () => { data: [ randomUUID(), BroadcastChannelProcedureName.METER_VALUES, - { hashIds: [station.stationInfo?.hashId] }, + { connectorId: 1, hashIds: [station.stationInfo?.hashId] }, ], }) @@ -754,6 +767,14 @@ await describe('ChargingStationWorkerBroadcastChannel', async () => { assert.strictEqual(sentRequests.length, 1) assert.strictEqual(sentRequests[0].command, RequestCommand.METER_VALUES) + assert.ok( + sentRequests[0].payload.evseId != null, + 'OCPP 2.0.1 meter values payload should contain evseId' + ) + assert.ok( + Array.isArray(sentRequests[0].payload.meterValue), + 'OCPP 2.0.1 meter values payload should contain meterValue array' + ) }) }) diff --git a/tests/charging-station/helpers/StationHelpers.ts b/tests/charging-station/helpers/StationHelpers.ts index 663be584..4b25e6fc 100644 --- a/tests/charging-station/helpers/StationHelpers.ts +++ b/tests/charging-station/helpers/StationHelpers.ts @@ -486,6 +486,9 @@ export function createMockChargingStation ( } return undefined }, + getConnectorMaximumAvailablePower (_connectorId: number): number { + return stationInfoOverrides?.maximumPower ?? 22000 + }, // Methods getConnectorStatus (connectorId: number): ConnectorStatus | undefined { if (useEvses) { -- 2.43.0