Repository navigation
fix: v2.20.0 — TC18 conformance fix pass II - #98
Merged
Merged
Conversation
Second gap-audit pass against the specification's own worked numeric examples and per-endpoint request-handling text. Fixes, in priority order: - acf_msg_length is now actually computed (cpp-RCP-01, critical): AcfMessageInfo::acf_msg_length defaulted to 0 and nothing filled it in, so every frame this library emitted carried acf_msg_length=0 despite the header *bit layout* already being correct. New acf::compute_acf_msg_length() computes the real quadlet count; encode_acf_abb/encode_acf_gbb call it automatically whenever a caller leaves acf_msg_length at its 0 default, while still respecting a caller-computed nonzero value (e.g. e2e.hpp's apply_acf_length_adjustment baking in a CRC trailer's +1 quadlet). Verified end to end against the spec's own two worked examples (ACF_ABB -> 0x05 for a 20-byte frame, ACF_GBB -> 0x07 for a 28-byte frame) in tests/test_e2e.cpp. - Multiple ACF requests packed into one AVTPDU are now handled (cpp-RCP-04-fresh, critical): §12.9.1.1 mandates it. New acf::decode_acf_messages/AcfEntry walk a buffer that may hold more than one ACF_ABB/ACF_GBB message; udp.hpp gains a parallel MultiFrame/encode_multi_frame/decode_multi_frame envelope, and udp::Server::serve() now dispatches and answers every request in an inbound datagram individually instead of only the first one. - Error responses now carry a Table 27 error-code byte_msg_payload (cpp-RCP-02, high): acf::WireErrorCode fixes Table 27's 17 numeric values (deliberately independent of regmap.hpp's own RegMapErrc ordinals); mock.hpp gains wire_error_code_for() and routes every ErrorResponse through one set_error_response() helper. - GPIO's payload decoder now rejects a mis-sized buffer (cpp-RCP-05-fresh, medium), matching PWM's exact-four-bytes fix (cpp-RCP-03, applied from a pre-verified diff in this same pass). - ISELED gains a real ACF payload codec (cpp-RCP-A4-iseled, medium): encode/decode for both the Instruction/Address/Data request shape (Figure 40) and the Address/Data[11:0] response shape (Figure 41) — previously only validated in-memory structs existed, with no way to actually build or parse an ISELED request/response for the wire. - PWM_IN's trigger signals now match Table 44 (cpp-RCP-A4-pwmin, low): RisingEdge/FallingEdge replace the single invented MidPulse signal. - control_data_length is now validated against the buffer it actually arrived in (cpp-RCP-A3, low), via the already-defined-but-unused avtp::AvtpErrc::length_mismatch. Deferred: cpp-RCP-A1 (CI's cpfusa analyze step is non-gating) and cpp-RCP-A2 (GitHub Actions pinned to mutable tags, not SHAs) — both process/CI items with no TC18 wire-conformance content, deferred because this environment has no cpfusa binary/build to verify an analyze-gating change against and no way to test a SHA-pin bump's effect on hosted Actions runners from here. Wire-format-adjacent, non-breaking-at-the-API-level: every emitted ACF frame's acf_msg_length bytes and every error response's payload bytes now differ from what v2.19.0 emitted (0 -> correct value; empty -> one octet). No C++ function signature changed. kVersion/CMakeLists.txt bump 2.19.0 -> 2.20.0 accordingly. Full local build (C++17, all targets) and ctest (52/52 suites) pass. Signed-off-by: Matt <47545907+SoundMatt@users.noreply.github.com>
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
Second gap-audit pass against the OPEN Alliance TC18 Remote Control Protocol Specification v0.5.1_RC, addressing every
mandatory:trueitem in the worklist plus two additional low-priority items. Two process/CI items were deferred (see below).Worklist completion
acf_msg_lengthnever computed)control_data_lengthnever validated)cpfusa analyzenon-gating)Self-verification: acf_msg_length worked examples
Per instructions, hand-verified the new
acf::compute_acf_msg_length()+encode_acf_abb/encode_acf_gbbagainst the specification's own two fully worked numeric examples, end to end (header layout was already correct from the prior v2.19.0 pass and is untouched here — only length computation was missing). This exact scenario is now pinned astests/test_e2e.cpp's"acf_msg_length end-to-end matches the specification's own worked examples":ACF_ABB (6-byte payload + 2 pad bytes, caller-padded to 8 bytes total, + trailing 4-byte CRC32):
compute_acf_msg_length(kAcfMsgTypeAbb, 8)= 4 (header 2 quadlets + payload 2 quadlets)apply_acf_length_adjustment()→ 5 (+1 quadlet for the CRC trailer)frame[0] & 0x01 == 0,frame[1] == 5→ acf_msg_length = 0x05, matching the spec exactlyappend_crc: 8 (header) + 8 (payload) + 4 (CRC) = 20 bytes = 5 quadlets ✓ACF_GBB (7-byte payload + 1 pad byte, caller-padded to 8 bytes total, + trailing 4-byte CRC32):
compute_acf_msg_length(kAcfMsgTypeGbb, 8)= 6 (header 2 + timestamp 2 + payload 2 quadlets)apply_acf_length_adjustment()→ 7 (+1 quadlet for the CRC trailer)frame[0] & 0x01 == 0,frame[1] == 7→ acf_msg_length = 0x07, matching the spec exactlyappend_crc: 8 (header) + 8 (timestamp) + 8 (payload) + 4 (CRC) = 28 bytes = 7 quadlets ✓Ran locally:
Design notes
acf_msg_lengthauto-fill is sentinel-based, not unconditional:encode_acf_abb/encode_acf_gbbonly compute a value when the caller leftAcfMessageInfo::acf_msg_lengthat its 0 default (true for every real production call site today — 0 is never a legitimate real wire value, since the header alone is 2 quadlets). A caller that has already set a nonzero value itself (the CRC-adjustment flow) is always respected unchanged. This preserves 100% of the existing hand-constructed-header test suite (which explicitly setsacf_msg_lengthin most cases) while fixing the "always literally 0" defect for every real caller — confirmed by a full local test run with zero test changes needed for the base fix itself.acf_msg_lengthto find message boundaries precisely, which requires each non-final message's payload to already be quadlet-aligned (true for GPIO/PWM's fixed 4-byte payloads). A non-final message with a non-aligned payload falls back to "consume the rest of the buffer" (the same convention the single-message decoder always used) rather than corrupting the parse — documented indecode_acf_messages's own comment.regmap::RegMapErrc, not a renumbering of it:RegMapErrc's ordinals (1–4) are this codebase's own internalstd::error_codevalues used elsewhere and were left untouched;mock.hpp's newwire_error_code_for()explicitly translates by name toacf::WireErrorCode's correct Table 27 numeric values (3, 4, 11, 15 for the fourRegMapErrccases) rather than reusing the internal ordinal.Build / test
Full local build (C++17, all targets, fresh out-of-tree build dir) and
ctest: 52/52 suites pass, including all new/updated tests (test_acf, test_e2e, test_udp, test_mock, test_gpio, test_pwm, test_iseled).cpfusa(the cpp-FuSa safety-tooling CLI CI also gates on) could not be run locally — nocpp-FuSacheckout or builtcpfusabinary is available in this environment, and building it from source was out of scope for this pass's time budget. This is the same tooling cpp-RCP-A1 concerns; deferred alongside it.Deferred (honest reasons)
cpfusa analyzestep runs non-gating via|| true): changing what CI gates on is a policy call this pass can't safely make blind — with no localcpfusabuild available to see what findings would newly gate the build, flipping this could surprise-fail CI in ways I have no way to verify here.Both are low-severity, explicitly non-wire-conformance items per the worklist itself, and are reasonable to pick up in a dedicated follow-up pass with the right tooling/verification available.