Skip to content

port(m7): CP simulator 2.0.1 — auto-trip a variable monitor when an inbound SetVariables write crosses its threshold #573

Description

@duyhuynh-vn

Context

Follow-up to #545 (PR #570), which added the injection seam for OCPP 2.0.1 variable monitoring: ChargePoint::trip_variable_monitor(component, variable, actual_value) looks up every installed monitor watching a (component, variable) identity and originates a schema-valid NotifyEvent.req. That is a test/application-driven hook — the monitor never trips on its own.

This issue closes the remaining half: make a monitor trip autonomously when a CSMS-originated SetVariables write moves a monitored variable across a threshold the CSMS previously installed via SetVariableMonitoring. This is the real, inbound-CALL-triggered path that the PR #570 body explicitly deferred:

Auto-wiring a trip to a SetVariables write that crosses a threshold (a real inbound-CALL-triggered trip — which would use the RemoteCommand queue) is out of scope; this is the injection seam only.

Because the trip is now raised from inside an inbound-CALL handler (SetVariables), it must be drained through the existing RemoteCommand queue rather than emitted inline (the re-entrancy reason NotifyReport / NotifyMonitoringReport already queue), unlike the CP-initiated hooks that emit inline.

Scope (small slice)

  • Threshold-kind monitors only: Upper/LowerThreshold (→ Alerting) and Delta (→ Delta). Periodic/PeriodicClockAligned are time-driven, not write-driven — out of scope here.
  • Only the write path that actually changes a monitored variable's value: an accepted SetVariables write whose new value crosses (enters/leaves the alerting band for thresholds; moves by ≥ the configured delta for Delta) fires exactly one NotifyEvent per matched monitor, reusing the trip_variable_monitor emitter and its trigger-derivation / correlation / monotonic seqNo+eventId logic already landed in port(m7): CP simulator 2.0.1 — emit NotifyEvent when a CSMS-installed variable monitor trips #545.
  • Rejected SetVariables writes (unknown variable, read-only, out of range) never trip.
  • Hysteresis: a write that stays on the same side of a threshold as the previous value does not re-fire (no duplicate NotifyEvent storm on repeated same-band writes).

Failure modes / trust boundary

  • The trip is enqueued as a new RemoteCommand variant (e.g. V201MonitorTrip { .. }) drained by the command-consumer task, so the SetVariables handler returns its SetVariablesResponse without re-entering the receive loop.
  • Caller/CSMS-supplied values remain opaque and are threaded to the wire verbatim (same guarantee as port(m7): CP simulator 2.0.1 — emit NotifyEvent when a CSMS-installed variable monitor trips #545); an oversized actualValue surfaces as an outbound-schema Err, never a panic or silent truncation.
  • No monitor match → no RemoteCommand enqueued, no seqNo burned (silent no-op).

Test matrix

Acceptance criteria

  • An accepted SetVariables write that crosses an installed threshold/delta monitor auto-originates one schema-valid NotifyEvent per matched monitor, via the RemoteCommand queue (not inline).
  • Reuses the port(m7): CP simulator 2.0.1 — emit NotifyEvent when a CSMS-installed variable monitor trips #545 emitter (trigger derivation, EventDataType correlation, monotonic seqNo+eventId, CustomMonitor notification type) — no duplicated trip logic.
  • Periodic/PeriodicClockAligned monitors are not affected; rejected and non-monitored writes never trip; hysteresis prevents same-band re-fire.
  • Trust boundary preserved: opaque values threaded verbatim; oversized value → outbound-schema Err, no panic.
  • cargo fmt --check, cargo clippy --all-targets -- -D warnings, cargo test --workspace all green.

Reference

The mobilityhouse/ocpp Python library is protocol-only (message dataclasses + @on routing), so the behavior being ported here is the Charging Station side, not a Python module. The wire semantics are pinned by the ported types:

Depends on

#545 / #570 (the trip_variable_monitor emitter + monitors_for_variable store read this builds on). Best implemented after #570 merges.


🌙 Filed by the nightly dev during issue grooming (2026-09-09) — the milestone's earliest-incomplete queue had fewer than 3 unclaimed well-scoped port issues, and this is a genuine, code-verified follow-up gap explicitly deferred by #570 (not queue padding). Direction for the broader M7→M8 track remains tracked in #256.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions