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:
- 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.
- 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.
Summary
Three detection rules in
src/ocpp/detection.zigread 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. Studiostill has all three, so the two engines now disagree.
The
contract-v1harness cannot catch this. It compares only the 15 pinnedfixtures, 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_FAILUREmatches statuses that are not 1.6 valuesfirmware_failure_statusesisDownloadFailed,DownloadPaused,InstallFailed,InstallRebootingFailed. The 1.6FirmwareStatusenumeration (edition 2, section7.25) is
Downloaded,DownloadFailed,Downloading,Idle,InstallationFailed,Installing,Installed, and its two failure values areDownloadFailedandInstallationFailed.So one of the four matched strings is right.
DownloadPausedis an OCPP 2.0.1value, and an intermediate state there rather than a failure;
InstallFailedandInstallRebootingFailedare in neither enumeration (2.0.1 spells the analogousvalues
InstallationFailedandInstallRebooting). And the spec's ownInstallationFailedis missing, so a conformant station reporting a failedinstall 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.zigasserts onInstallFailed, so it passes todayonly because the rule and the test agree on the same non-spec string.
2.
isValidTransitiondoes not match the section 4.9 tableThe 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
ChargePointStatusvalues.The false positives sit mostly in the recovery rows. The
I1-I8row lets aconnector return from
Faultedto any pre-fault state and theHrow lets onecome back from
Unavailableinto any operative state, but the matrix allowsrecovery only to
Available. So a station that faults mid-session and resumescharging, 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 -> UnavailableandFinishing -> Reservedhave 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_AUTHORIZATIONonly fires onInvalidThe 1.6
AuthorizationStatusenumeration (edition 2, section 7.2) has five valuesand only
Acceptedpermits charging.Blocked,ExpiredandConcurrentTxarerefusals: 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:
firmware_failure_statusestoDownloadFailedandInstallationFailed.isValidTransitionwith 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.
AuthorizationStatusvalues, naming the status in thedescription.
Tests: fix the
InstallFailedassertion, and add coverage for the newly detectedstatuses plus a test that transcribes the section 4.9 table independently so the
matrix has to agree with it.
contract-v1should come out untouched. The toolkit side was checked with allthree 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:
diagonal. A charge point answering a
TriggerMessageforStatusNotificationlegitimately repeats its current status.
Available,UnavailableandFaulted.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.