feat(ocpp16): wire signed meter values into trigger + broadcast paths (issue #1936 d) (#1945)
* feat(ocpp16): wire signed meter values into trigger + broadcast paths (issue #1936 d)
OCPP 1.6 Signed Meter Values whitepaper v1.0 requires that when
SampledDataSignReadings + SampledDataSignUpdatedReadings are enabled
and a signing key is configured, a paired SignedData SampledValue
accompanies the Raw SampledValue in every Sample.Periodic-context
MeterValue. The periodic loop (OCPP16ServiceUtils.startUpdatedMeterValues)
already applies POST-HOC signing after buildMeterValue returns, so
periodic MeterValues are signed today.
Two callsites were missing the signing wrapper:
1. OCPP 1.6 TriggerMessage(MeterValues) handler in
OCPP16IncomingRequestService.ts (both the specific-connectorId
branch and the broadcast-to-all-connectors branch).
2. Worker broadcast channel in
ChargingStationWorkerBroadcastChannel.handleMeterValues
(cross-version handler; signing is 1.6-only per the whitepaper).
Both paths silently emitted unsigned Raw values instead of the
whitepaper-mandated paired SignedData SampledValue when signing was
enabled.
Fix: consolidate the signing block from startUpdatedMeterValues into
a new public static helper OCPP16ServiceUtils.appendSignedUpdatedReadings
that mutates the MeterValue in place. The helper is a no-op when
signing is disabled or the connector's signing config is missing.
Apply the helper at the three missing callsites and refactor
startUpdatedMeterValues to call it too.
Spec citation: OCA Application Note 'Signed Meter Values for OCPP 1.6'
v1.0 §3.2.1 (paired SignedData SampledValue).
Closes issue #1936 item (d).
* docs(ocpp16): apply round-1 review fixes on signed MV wire-up (issue #1936 d)
Address cross-validated R1 findings:
- Naming coherence in TriggerMessage broadcast-to-all branch: rename
loop-local variables from abbreviated 'id'/'cs'/'txId'/'mv' to
full 'connectorId'/'connectorStatus'/'transactionId'/'meterValue'
to match the specific-connector sibling branch style. Per AGENTS.md
naming coherence: two adjacent branches doing the same thing with
different name styles is the 'synonym creates ambiguity' pattern.
- Rewrite the broadcast channel guard comment to correctly attribute
the '!isOcpp2' skip. Previous wording claimed 'the whitepaper is OCPP
1.6 specific', but the whitepaper §4 explicitly covers OCPP 2.x. The
actual reason for the skip is that OCPP 2.0.x signing is applied
inline inside buildMeterValue via the versioned dispatcher's signing
hook, so post-hoc wrapping is the 1.6 pattern only.
- Tighten the appendSignedUpdatedReadings @description to drop
refactor-history narrative ('Consolidates the signing block used by
every trigger/broadcast path...'). Replace with operational spec
citing whitepaper §3.3.6 SampledDataSignUpdatedReadings and the
mutation contract on connectorStatus.publicKeySentInTransaction
(per PublicKeyWithSignedMeterValue = OncePerTransaction).
* fix(ocpp16): harden signed MV helper and thread reading context (issue #1936 d)
Enforce the transactionStarted invariant inside `appendSignedUpdatedReadings` so callsites
with looser guards cannot leak signed emissions past `resetConnectorTransactionStatus`.
Accept an optional reading `context` parameter (default `Sample.Periodic`);
`TriggerMessage(MeterValues)` callsites pass `Trigger` per OCPP 1.6 Core Table 30 so the
signed payload's context field matches its emission source.
Downgrade the `readSigningConfigForConnector` disabled-state log from `warn` to `debug`;
the helper is a hot-path probe whose `undefined` return already conveys the disabled state
to callers.