feat(ocpp-cp): emit v201 TransactionEvent(CablePluggedIn) for the EVConnected plug-in phase (M7) - #581
Merged
Conversation
…onnected plug-in phase (M7) Port the OCPP 2.0.1 cable-plugged, pre-authorization transaction phase into the CP simulator: a new V201-only driver hook drives a connector into the EVConnected state and originates one TransactionEvent(triggerReason = CablePluggedIn, chargingState = EVConnected). This is the companion #577 (landed as #580) deferred, and extends #577's state-transition plumbing rather than duplicating it: - v201_transaction::transaction_event_state_transition: a new pub(crate) core that both the ChargingStateChanged builder (#577) and the new CablePluggedIn builder delegate to (same interim-Updated shape: new chargingState, no meterValue, no idToken). - transaction_event_cable_plugged_in: the new pure builder. - ChargePoint::plug_in_cable(evse_id): the new driver hook, sharing the resolve -> compare-and-set -> emit core (emit_transaction_state_change) with transition_charging_state. Redundant / no-transaction cases are inert; a 1.6J station refuses with OcppError::NotSupported. Reconciling the full TxStartPoint variants (having the plug-in genuinely open the transaction before authorization, Started vs Updated per start point) is a deliberate follow-up, out of this slice's scope. Closes #579. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RR4rxKX9fJungJiibgJZ5S
…spans The `Documentation` CI check (`cargo doc --document-private-items` with `RUSTDOCFLAGS=--deny warnings`) failed on `rustdoc::private_intra_doc_links`: the public docs for `transaction_event_cable_plugged_in` and `ChargePoint::plug_in_cable` linked the private `transaction_event_state_transition` and `emit_transaction_state_change`. Such links resolve only under `--document-private-items` and break in the public docs, so rustdoc denies them. Reference both as plain code spans instead — no public link to a private item. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RR4rxKX9fJungJiibgJZ5S
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports the OCPP 2.0.1 cable-plugged, pre-authorization transaction phase into the CP simulator (M7): a new V201-only driver hook drives a connector into the
EVConnectedstate and originates oneTransactionEvent(triggerReason = CablePluggedIn, chargingState = EVConnected). This is the exact companion #577 (landed as #580) deferred.Closes #579Real use case
A driver pulls up and plugs the cable in before authorizing (delayed authorization / plug-and-charge, where the station's
TxStartPointincludesEVConnected). A real charging station opens the transaction and reports aTransactionEventwithtriggerReason = CablePluggedIn/chargingState = EVConnected— the "cable in, not yet authorized/charging" phase — so the back office sees the car is physically connected and waiting, not idle. Today the simulator jumps straight toCharging, so a downstream CSMS never observes this phase that plug-and-charge and delayed-authorization conformance exercises. This adds the hook that emits it.What changed
crates/ocpp-cp, extending #577's plumbing rather than duplicating it:v201_transaction::transaction_event_state_transition— a newpub(crate)core builder that both theChargingStateChangedbuilder (port(m7): CP simulator 2.0.1 — emit TransactionEvent(Updated, ChargingStateChanged) on SuspendedEV / SuspendedEVSE / Charging transitions #577) and the newCablePluggedInbuilder delegate to. Same interim-Updatedshape: the newtransactionInfo.chargingState, nometerValue(the reading rides theMeterValuePeriodicpath), noidToken(not an authorization event).transaction_event_cable_plugged_in— the new pure builder (CablePluggedIn/EVConnected).ChargePoint::plug_in_cable(evse_id)— the new driver hook. It shares the resolve → compare-and-set → emit core (emit_transaction_state_change) withtransition_charging_state, so behavior is identical bar the trigger reason / target state.Failure modes / trust boundary: driver-originated (not inbound-CALL-triggered), so it emits inline via
call(). Redundant transition (alreadyEVConnected) →Redundant, no event, noseqNoburned (hysteresis). No active transaction (incl. out-of-rangeevse_id) →NoTransaction, inert, no panic. A transport/timeout/CALLERROR leaves the modeled state unchanged. The builder is pure and its output is schema-validated bycall().What was ported
mobilityhouse/ocpp is protocol-only, so the ported artefact is the Charging Station behavior; the wire tokens are pinned by the already-ported types:
ocpp/v201/enums.py—ChargingStateEnumType.ev_connected = "EVConnected",TriggerReasonEnumType.cable_plugged_in = "CablePluggedIn".ocpp/v201/call.pyclass TransactionEvent;ocpp/v201/datatypes.pyTransactionType.charging_state.Test plan
cargo fmt --all --check,cargo clippy --all-targets -- -D warnings,cargo test --workspaceall green locally (816 passing inocpp-cp, +6).New tests:
v201_transaction.rs):cable_plugged_in_event_carries_ev_connected_shape,cable_plugged_in_round_trips_the_exact_wire_tokens, and the schema-validity sweep extended to the plug-in event.lib.rs):plug_in_cable_emits_one_ev_connected_event,plug_in_cable_redundant_transition_is_a_noop(no event, noseqNoburned),plug_in_cable_with_no_active_transaction_is_inert,plug_in_cable_is_v201_only.Acceptance criteria
EVConnectedphase and originates one schema-validTransactionEvent(triggerReason = CablePluggedIn, chargingState = EVConnected).TransactionEventstate-change plumbing (sharedpub(crate)core — no duplicated builder).seqNoburned, no panic);Endedstill closes withIdle.seqNocontinues the transaction's monotonic stream.OcppError::NotSupported.cargo fmt --check,cargo clippy --all-targets -- -D warnings,cargo test --workspaceall green.Known gaps / notes
EVConnected/CablePluggedInphase against an already-open transaction context (reusing port(m7): CP simulator 2.0.1 — emit TransactionEvent(Updated, ChargingStateChanged) on SuspendedEV / SuspendedEVSE / Charging transitions #577's plumbing), and the emitted event is an interimUpdated. Reconciling the fullTxStartPointvariants (Authorized / PowerPathClosed / EnergyTransfer) — having the plug-in genuinely open the transaction before authorization, and choosingStartedvsUpdatedper the configured start point — is a deliberate follow-up, out of this slice's scope (as port(m7): CP simulator 2.0.1 — emit the pre-authorization EVConnected state + TransactionEvent(triggerReason=CablePluggedIn) when the cable is plugged before Started #579 notes). A follow-up issue tracks it.🤖 Generated with Claude Code
https://claude.ai/code/session_01RR4rxKX9fJungJiibgJZ5S
Generated by Claude Code