Skip to content

port(m7): CP simulator 2.0.1 — CP-initiated DataTransfer (originate a vendor-specific extension request) #571

Description

@duyhuynh-vn

Context

DataTransfer is the 2.0.1 vendor-specific escape hatch: a vendorId plus optional messageId and free-form data, answered with a DataTransferStatusEnumType and its own optional data. It is bidirectional — either side may originate it.

The message type is already ported (#154 / PR #156: DataTransferRequest / DataTransferResponse, DataTransferStatusEnumType, bundled schema in SchemaValidator::v201()). And the CP already handles the inbound direction on 1.6J — crates/ocpp-cp/src/data_transfer.rs routes inbound DataTransfer CALLs through a vendor registry (@on("DataTransfer")-style), defaulting to UnknownVendorId.

What is still missing is the CP-initiated (outbound) origination path on the 2.0.1 simulator: a driver hook that lets the charging station originate a DataTransfer.req to the CSMS and surface the typed .conf (status + optional data). This is the same outbound-CALL shape used by request_security_event_notification (#563), request_sign_certificate (#547), and the just-landed request_notify_charging_limit / request_cleared_charging_limit (#564 / PR #566) — not a reply to an inbound CALL.

Real use case: a vendor extension (e.g. a proprietary diagnostics ping, a display-asset push, or an OEM battery-health report) that has no standard OCPP message. The station originates it as DataTransfer and acts on the CSMS's Accepted / Rejected / UnknownMessageId / UnknownVendorId verdict.

Python reference

  • ocpp/v201/call.py — class DataTransfer (vendor_id: str, optional message_id: str, optional data: Any).
  • ocpp/v201/call_result.py — class DataTransfer (status: DataTransferStatusEnumType, optional status_info, optional data: Any).
  • ocpp/v201/enums.py — DataTransferStatusEnumType (Accepted / Rejected / UnknownMessageId / UnknownVendorId).

Proposed design

  • A pure v201_data_transfer_request(vendor_id, message_id, data) builder in v201_command, threading its inputs verbatim (data is free-form serde_json::Value, Optional[Any] in the reference), with a schema-validity test against the bundled 2.0.1 FINAL JSON Schema.
  • A V201-only driver hook ChargePoint::request_data_transfer(vendor_id, message_id, data) -> Result<DataTransferResponse, OcppError> that emits the CALL inline via call() (which schema-validates the outgoing request and the incoming .conf against the CP's 2.0.1 validator) and returns the typed response (status + optional data), so the caller can branch on the verdict. A 1.6J station is refused with OcppError::NotSupported (mirrors request_security_event_notification).
  • Unlike the ack-only notifications, DataTransfer.conf is not empty — return the parsed DataTransferResponse rather than Ok(()), so status / data / statusInfo are visible to the caller.

Acceptance criteria

  • Originate DataTransfer.req (vendorId required; optional messageId, data), V201-only; 1.6J refused with NotSupported.
  • Pure builder in v201_command with schema-validity tests: with and without optional messageId / data; data round-trips arbitrary JSON (object / array / string / number) without loss.
  • Optional messageId / data omitted are absent on the wire (not null); required vendorId always present.
  • The .conf is parsed and the typed DataTransferResponse (status + optional data / statusInfo) is surfaced without panic across all four DataTransferStatusEnumType values; transport / timeout / CALLERROR propagate as OcppError.
  • Trust boundary: caller-supplied data is threaded verbatim, never parsed/executed; an oversized/hostile payload surfaces as an Err from outbound schema validation, never a panic (test).
  • cargo fmt --check, cargo clippy --all-targets -- -D warnings, cargo test --workspace all green.

Milestone / labels

M7 — OCPP 2.0.1. Labels: port, m7. Small (one builder + one hook + tests; well under the ~500-LOC target).

Known gaps / notes


Filed by the nightly dev during the 2026-09-08 run while landing the #566 merge and closing #564 — replenishing the M7 CP-initiated backlog. The v201 message type already exists (#154 / #156); this is the missing outbound driver hook.

Activity

  1. duyhuynh-vn commented on Sep 8, 2026

    @duyhuynh-vn
    CollaboratorAuthor

    🌙 Nightly dev picking this up — 2026-09-08


    Generated by Claude Code

  2. duyhuynh-vn commented on Sep 8, 2026

    @duyhuynh-vn
    CollaboratorAuthor

    Opened PR #572 with the CP-initiated DataTransfer origination hook (request_data_transfer + v201_data_transfer_request builder + tests). All acceptance criteria addressed; cargo fmt --check, clippy -D warnings, and cargo test --workspace green locally. Awaiting review — I won't self-merge.


    Generated by Claude Code

  3. duyhuynh-vn commented on Sep 11, 2026

    @duyhuynh-vn
    CollaboratorAuthor

    Closing as completed: PR #572 (the CP-initiated DataTransfer origination hook — request_data_transfer + v201_data_transfer_request builder + tests) has merged to main. All acceptance criteria here are satisfied on main:

    • ChargePoint::request_data_transfer(vendor_id, message_id, data) -> Result<DataTransferResponse, OcppError> (V201-only; 1.6J → NotSupported), surfacing the typed .conf (status + optional data/statusInfo).
    • Pure v201_data_transfer_request builder with schema-validity + arbitrary-JSON round-trip tests, optional messageId/data omitted (not null).
    • Trust boundary: caller-supplied data threaded verbatim; oversized payload → outbound-schema Err, no panic (tested across all four DataTransferStatusEnumType values).

    The PR didn't carry a Closes #571 keyword, so this issue stayed open — closing it now during grooming. Inbound v201 DataTransfer routing remains a separate seam (already handled via the shared registry; out of scope here).


    Generated by Claude Code

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