Skip to content

fix(ocpp): three detection rules disagree with the OCPP 1.6 spec (toolkit 0.4.5 parity) #92

Description

@sepehr-safari

Summary

Three detection rules in src/ocpp/detection.zig read the OCPP 1.6 spec wrongly.
They are the same three mistakes the toolkit had, fixed there in
ocpp-debugkit/toolkit#154, #155 and #156 and published as toolkit 0.4.5. Studio
still has all three, so the two engines now disagree.

The contract-v1 harness cannot catch this. It compares only the 15 pinned
fixtures, and none of them exercises any of the three gaps, so both
implementations pass the contract while disagreeing on everything outside it.

1. FIRMWARE_UPDATE_FAILURE matches statuses that are not 1.6 values

firmware_failure_statuses is DownloadFailed, DownloadPaused, InstallFailed,
InstallRebootingFailed. The 1.6 FirmwareStatus enumeration (edition 2, section
7.25) is Downloaded, DownloadFailed, Downloading, Idle,
InstallationFailed, Installing, Installed, and its two failure values are
DownloadFailed and InstallationFailed.

So one of the four matched strings is right. DownloadPaused is an OCPP 2.0.1
value, and an intermediate state there rather than a failure; InstallFailed and
InstallRebootingFailed are in neither enumeration (2.0.1 spells the analogous
values InstallationFailed and InstallRebooting). And the spec's own
InstallationFailed is missing, so a conformant station reporting a failed
install produces no failure. That is the direction that costs the user: a failed
download leaves a station on its old firmware, a failed install can leave it
degraded.

The unit test at detection.zig asserts on InstallFailed, so it passes today
only because the rule and the test agree on the same non-spec string.

2. isValidTransition does not match the section 4.9 table

The matrix disagrees with the table in both directions: it flags 22 transitions
the table permits and permits 2 it does not list. The table has 53 permitted
transitions across the nine ChargePointStatus values.

The false positives sit mostly in the recovery rows. The I1-I8 row lets a
connector return from Faulted to any pre-fault state and the H row lets one
come back from Unavailable into any operative state, but the matrix allows
recovery only to Available. So a station that faults mid-session and resumes
charging, or that takes a scheduled availability change during a session (C8,
D8, E8, F8), gets a warning saying its firmware is wrong when it is right.

In the other direction, Preparing -> Unavailable and Finishing -> Reserved
have no cell in the table and are not reported.

The matrix is also a single 700-column expression, which is why it drifted without
anyone noticing.

3. FAILED_AUTHORIZATION only fires on Invalid

The 1.6 AuthorizationStatus enumeration (edition 2, section 7.2) has five values
and only Accepted permits charging. Blocked, Expired and ConcurrentTx are
refusals: same shape on the wire, same outcome for the driver. The rule reports
one of the four, and silence from a detector reads as "this is not the problem".

Proposed fix

Port the three toolkit fixes:

  • Narrow firmware_failure_statuses to DownloadFailed and InstallationFailed.
  • Replace isValidTransition with the section 4.9 table transcribed cell by cell,
    in a form where a row can be read against the spec, with the table's own cell
    labels alongside it.
  • Cover all four refusing AuthorizationStatus values, naming the status in the
    description.

Tests: fix the InstallFailed assertion, and add coverage for the newly detected
statuses plus a test that transcribes the section 4.9 table independently so the
matrix has to agree with it.

contract-v1 should come out untouched. The toolkit side was checked with all
three fixes applied and the 15 goldens still match, 0 drift, so no fixture or
golden needs editing here either.

Out of scope

Two adjacent questions, each worth its own issue, deliberately not changed so this
stays a straight port:

  1. A repeated identical status counts as a violation, since the table has no
    diagonal. A charge point answering a TriggerMessage for StatusNotification
    legitimately repeats its current status.
  2. Section 4.9 limits connectorId 0 to Available, Unavailable and Faulted.
    Every transition among those three is in the table, so applying one matrix to
    both produces no false positives, but an out-of-set status on connectorId 0 is
    not flagged either.

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