diff --git a/.fusa-reqs-pending.json b/.fusa-reqs-pending.json index 10498cd..51aa546 100644 --- a/.fusa-reqs-pending.json +++ b/.fusa-reqs-pending.json @@ -118,6 +118,76 @@ "asil": "ASIL-B", "tc18": "§12.7.10 Table 28 (own-access-control rule, \"Request_stream_index... allowed to access this sequencer\"), TC18.txt L3460-3493; §12.9.6 Table 30 (SEQUENCER_NOT_KNOWN=2, UNAUTHORIZED_ACCESS=3), TC18.txt L3812-3844", "gap": "Confirmed absent (2026-08-22): rcp::acf::WireErrorCode::SequencerNotKnown (acf.hpp:695) is defined but grepped as having ZERO consumers anywhere in include/ or tests/ -- no caller ever produces it. This is consistent with rcp/request.hpp's own explicit \"TODO(phase1-followup)\" (lines 219-225) acknowledging that c-RCP's REQ-SEQ-013/014-style per-sequencer-owner/access-control gate is not yet ported: SequencerTable has no owner-per-sequencer concept to distinguish \"unknown index\" from \"real sequencer, wrong owner\" in the first place, so there is nothing yet to map to these two distinct wire codes. Real, TC18-cited (§12.7.10 Table 28) gap; the WireErrorCode enumerator itself lives in rcp/acf.hpp (in this batch's scope) but the classifier logic's natural home is rcp/request.hpp (out of this batch's scope, and already tracked there via that file's own TODO)." + }, + { + "id": "REQ-CMP-008", + "title": "encode_compound_request() shall reject an unrecognized request_type", + "text": "rcp::request::encode_compound_request() shall report a failure (e.g. std::optional> == std::nullopt) when type is neither is_compound() nor is_compound_wait(), rather than silently encoding whatever RequestTypeOpcode value it is given.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "tc18": "§11.2.2.2 Table 7 / Figure 8 (GBB message for compound requests: request_type, cmp_start_state, cmp_next_state, cmp_sequencer, cmp_exec_delay, cmp_repetitions), TC18.txt L1379-1458; §11.2.2.3 Table 8 / Figure 9 (GBB message for compound wait requests: request_type, cmpw_start_state, cmpw_next_state, cmpw_sequencer, cmpw_exec_delay, cmpw_repetitions), TC18.txt L1476-1567", + "gap": "Confirmed absent (2026-08-22): encode_compound_request() (request.hpp:540-551) takes `type` as a plain RequestTypeOpcode parameter and never checks is_compound(type)||is_compound_wait(type) before calling encode_request_type()/acf::encode_acf_gbb() — it will happily encode e.g. RequestTypeOpcode::Chained into a syntactically well-formed 'compound' frame. Its return type is a plain std::vector (not std::optional, unlike encode_timed_request() at request.hpp:1048, which does reject via std::nullopt), so there is structurally no way for it to signal rejection even if the check were added without a signature change. No test in test_request.cpp exercises this path (grepped for a REQ-CMP-008 inline tag or an 'encode_compound_request' + invalid-type test — none found; the only tag near this area, [REQ-CMP-011] at test_request.cpp:316, tests a round-trip, not rejection). Root cause is shared with acf.hpp's own documented 'always returns bytes, never an error code' contract for encode_acf_abb()/encode_acf_gbb() (acf.hpp:445-451) that every conditional-request encoder in this file is built on — same architecture, not unique to this function." + }, + { + "id": "REQ-CMP-009", + "title": "encode_compound_request() shall reject an oversized payload", + "text": "rcp::request::encode_compound_request() shall report a failure when payload.size() would produce an acf_msg_length exceeding the ACF_GBB payload bound, rather than silently masking/wrapping it onto the wire.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "tc18": "§11.2.2.2 Table 7 / Figure 8 (GBB message for compound requests: request_type, cmp_start_state, cmp_next_state, cmp_sequencer, cmp_exec_delay, cmp_repetitions), TC18.txt L1379-1458; §11.2.2.3 Table 8 / Figure 9 (GBB message for compound wait requests: request_type, cmpw_start_state, cmpw_next_state, cmpw_sequencer, cmpw_exec_delay, cmpw_repetitions), TC18.txt L1476-1567", + "gap": "Confirmed absent (2026-08-22): encode_compound_request() forwards payload directly to acf::encode_acf_gbb() (request.hpp:550), which by its own documented design (acf.hpp:445-451, 'encode_acf_abb()/encode_acf_gbb() below mask acf_msg_length to 9 bits on encode... rather than rejecting an oversized payload outright... a caller that must not silently wrap can check a payload's size against these bounds itself before encoding') never rejects an oversized payload — it masks acf_msg_length to 9 bits and proceeds. encode_compound_request()'s own plain-vector return type has no channel to report such a rejection even if added. No test exercises an oversized-payload call. Same shared architectural root cause as REQ-CMP-008; both would need encode_compound_request() to gain either a caller-side kAcfGbbMaxPayload check (returning std::optional, mirroring encode_timed_request()'s own convention) or an equivalent guard, which does not exist today." + }, + { + "id": "REQ-TRIG-003", + "title": "encode_triggered_request() shall reject an unrecognized request_type", + "text": "rcp::request::encode_triggered_request() shall report a failure when type is not is_triggered(type), rather than silently encoding whatever RequestTypeOpcode value it is given.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "tc18": "§11.2.2.5 Figure 11 / Table 10 (GBB message for triggered requests: request_type=0x0E/0x8E, trigger_source_ep, trigger_signal_nr, trigger_threshold, trigger_exec_delay, trigger_repetitions), TC18.txt L1685-1755", + "gap": "Confirmed absent (2026-08-22): encode_triggered_request() (request.hpp:941-949) takes `type` as a plain RequestTypeOpcode parameter and never checks is_triggered(type) before encoding — mirrors REQ-CMP-008's exact gap shape (same missing-validation pattern, same plain-vector return type with no rejection channel, same acf::encode_acf_gbb() root cause). No test in test_request.cpp calls encode_triggered_request() with a non-triggered opcode and checks for a rejection; the existing [REQ-TRIG-005] tests are round-trips with valid opcodes only." + }, + { + "id": "REQ-CHAIN-003", + "title": "encode_chained_member() shall reject an oversized payload", + "text": "rcp::request::encode_chained_member() shall report a failure when payload.size() would produce an acf_msg_length exceeding the ACF_GBB payload bound, rather than silently masking/wrapping it onto the wire.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "tc18": "§11.2.2.6 Figure 12 / field table (GBB for chained requests: request_type=0x01, reserved octets, chain_exec_delay, cs), TC18.txt L1773-1847; general ACF_GBB frame-validation basis", + "gap": "Confirmed absent (2026-08-22): encode_chained_member() (request.hpp:716-731) forwards payload directly to acf::encode_acf_gbb() with no size check and a plain std::vector return type — identical shape to REQ-CMP-009's gap (same acf.hpp:445-451 'never rejects, caller's responsibility' root cause). No test exercises an oversized-payload call to encode_chained_member()." + }, + { + "id": "REQ-CHAIN-008", + "title": "A chained request submitted with no predecessor shall be rejected, and its whole chain ignored", + "text": "rcp::request::RequestLedger shall reject (or otherwise refuse to execute) a chained request record submitted with no chained_predecessor at all, reporting an error equivalent to c-RCP's CHAIN_ERROR, and shall not execute any request that would have chained from it — per TC18 §11.2.2.6, 'If the first request in an AVTPDU is a chain request, then there is no predecessor to chain to, thus the entire chain will be ignored.'", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "tc18": "§11.2.2.6 (\"If the first request in an AVTPDU is a chain request, then there is no predecessor to chain to, thus the entire chain will be ignored. An error response with the error code 'CHAIN_ERROR' to each request will be generated\"), TC18.txt L1796-1798", + "gap": "Confirmed absent (2026-08-22): RequestRecord::chained_predecessor is std::optional (request.hpp:1315), and RequestLedger::submit() (request.hpp:1354-1362) never inspects it — a Chained-opcode record with chained_predecessor == std::nullopt is accepted and tracked exactly like any other Pending record, with no special rejection path. RequestErrc (request.hpp:270-285) has no CHAIN_ERROR-equivalent enumerator at all — the closest neighbors (request_not_found, request_canceled, invalid_lifecycle_transition) are semantically unrelated. This is distinct from REQ-CHAIN-009's 'already-aborted chain' case (which cascade_cancel()/propagate_chain_completion() genuinely do handle, unconditionally cancelling every downstream chained_successor once one member aborts — see request.hpp's own file-header delta #4/judgment-call note at lines 207-217): REQ-CHAIN-008 is about a chained member that never had a predecessor relationship recorded in the first place, which nothing in RequestLedger detects or rejects today. A caller assembling chained_predecessor/chained_successors links at submission time (this codebase's own design, not c-RCP's positional has_predecessor bool) could in principle already choose not to submit a first-frame Chained request with a false chained_predecessor link, but RequestLedger itself provides no enforcement or detection of the case, and no test in test_request.cpp exercises it." + }, + { + "id": "REQ-TIMED-012", + "title": "A request carried under a TSCF header is postponed until the header's presentation time", + "text": "A caller-facing admission/dispatch entry point shall, given an AVTPDU decoded under a TSCF header (carrying its own avtp_timestamp), defer executing any request in it until that timestamp is reached, using the already-implemented timed_admit()/timed_due() primitives (REQ-TIMED-007/008/011) as the gate.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "tc18": "§11.2, TC18.txt L1159; §11.2.1, TC18.txt L1204", + "gap": "Confirmed absent (2026-08-22): rcp/request.hpp implements every TSCF-admission PRIMITIVE this requirement depends on (timed_admit()/timed_due()/timed_too_far(), request.hpp:1094-1125) but, by this file's own explicit design note (request.hpp:246-250, 'this header... does not implement a running scheduler thread; wiring select_next_due()'s output into an actual dispatch loop is left to the embedding application'), provides no dispatch/admission LOOP that actually calls them against a real TSCF-decoded avtp_timestamp. Grepped the whole tree (include/, src/) for a dispatch-tscf/admit-gate equivalent to c-RCP's rcp_mock_server_dispatch_tscf()/rcp_server_endpoint_admit(): none exists — rcp/l2.hpp/udp.hpp encode/decode TSCF *framing* (use_tscf bit, avtp_timestamp field) but never route a decoded timestamp into timed_admit()/timed_due(). This is a genuine, confirmed absence of end-to-end behavior (not merely an unwired primitive): nothing in cpp-RCP today defers ANY request's execution until a TSCF header's presentation time arrives. Per this file's own design note this may be intentionally out of request.hpp's charter (a library of primitives, not a server), but the requirement's own text (postponement actually happening) has no home anywhere else in the tree either." + }, + { + "id": "REQ-TIMED-013", + "title": "Standard requests, and timed requests under a TSCF header, are sent as ACF_ABB", + "text": "A caller building a Standard or Timed request for transmission under a TSCF header shall encode it as ACF_ABB (not the ACF_GBB message_timestamp-repurposing path REQ-TIMED-002/003 model), with the TSCF header's own avtp_timestamp carrying the presentation time, and a receiver's admission/dispatch path shall honor that presentation time per REQ-TIMED-012.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "tc18": "§11.2, TC18.txt L1182; §11.2.1, TC18.txt L1204", + "gap": "Confirmed absent (2026-08-22): shares REQ-TIMED-012's exact root cause and evidence — rcp/acf.hpp's encode_acf_abb() and rcp/l2.hpp's/udp.hpp's TSCF framing already provide the wire-encoding half of this pair (a caller can build a TSCF-headed ACF_ABB frame today), but the decode/admission half (a dispatch entry point that decodes a real TSCF-wrapped ACF_ABB frame and threads its avtp_timestamp into timed_admit()/timed_due() the way REQ-TIMED-012 describes) does not exist anywhere in cpp-RCP. See REQ-TIMED-012's gap text for the full detail; both ids share one missing piece (a TSCF-aware dispatch/admission entry point) and would likely be closed together." } ] -} +} \ No newline at end of file diff --git a/.fusa-reqs.json b/.fusa-reqs.json index 4c942c9..e4b0e5a 100644 --- a/.fusa-reqs.json +++ b/.fusa-reqs.json @@ -4373,6 +4373,832 @@ "scope": "tc18", "status": "implemented", "tc18": "§12.9.4/§12.9.5 (queue-full eviction and overflow-bit rule), see REQ-RMAP-059/061 for the byte-budget and per-message-ceiling siblings this rule is independent of" + }, + { + "id": "REQ-CMP-001", + "title": "is_safety_variant() reports the MSB (0x80) of a RequestTypeOpcode", + "text": "rcp::request::is_safety_variant() shall return true iff the opcode's 0x80 bit is set, i.e. iff type is exactly one of RequestTypeOpcode::CompoundSafety/CompoundWaitSafety/TriggeredSafety.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 (\"If the MSB of the identifier (0x8x) is set the request is treated as a safety request and will not be deleted when the EP needs to go to safe state.\"), TC18.txt L1258-1259", + "tc18_master_id": "TC18-11.2.2-001" + }, + { + "id": "REQ-CMP-002", + "title": "is_compound() recognizes RequestTypeOpcode::Compound and its safety variant", + "text": "rcp::request::is_compound() shall return true iff type == RequestTypeOpcode::Compound or type == RequestTypeOpcode::CompoundSafety.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 Table 5 (request_type 0x0F/0x8F = Compound), TC18.txt L1262", + "tc18_master_id": "TC18-11.2.2-002" + }, + { + "id": "REQ-CMP-003", + "title": "is_compound_wait() recognizes RequestTypeOpcode::CompoundWait and its safety variant", + "text": "rcp::request::is_compound_wait() shall return true iff type == RequestTypeOpcode::CompoundWait or type == RequestTypeOpcode::CompoundWaitSafety.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 Table 5 (request_type 0x0B/0x8B = Compound wait), TC18.txt L1263", + "tc18_master_id": "TC18-11.2.2-002" + }, + { + "id": "REQ-CMP-010", + "title": "encode_compound_request() packs the step at the specification's own sub-field offsets", + "text": "rcp::request::encode_compound_request() shall produce an ACF_GBB-framed frame with mtv forced to false and shall write the repurposed message_timestamp region as: request_type at octet offset 0, step.start_state at offset 1, step.next_state at offset 2, step.sequencer_index at offset 3, step.exec_delay as two big-endian octets at offsets 4-5, and step.repeat_count as two big-endian octets at offsets 6-7, per encode_compound_step_params().", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.3 Figure 9 (compound-wait GBB sub-field offsets), TC18.txt L1494-1506", + "tc18_master_id": "TC18-11.2.2.3-005" + }, + { + "id": "REQ-CMP-011", + "title": "decode_compound_request() rejects a frame shorter than the ACF_GBB header", + "text": "rcp::request::decode_compound_request() shall return AvtpErrc::short_buffer (via acf::decode_acf_gbb() inside detail::decode_repurposed()) when the input is shorter than the ACF_GBB fixed header or its declared payload length.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 (\"conditional requests are sent using ACF_GBB having mtv = 0 (time stamp not valid) and using the bytes of the message_timestamp field to convey the execution condition\"), TC18.txt L1255-1258; general ACF_GBB frame-validation basis", + "tc18_master_id": "TC18-11.2-005" + }, + { + "id": "REQ-CMP-012", + "title": "decode_compound_request() rejects a non-ACF_GBB message", + "text": "rcp::request::decode_compound_request() shall return AcfErrc::bad_acf_msg_type (via acf::decode_acf_gbb() inside detail::decode_repurposed()) when the leading octet does not identify an ACF_GBB message.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 (\"conditional requests are sent using ACF_GBB having mtv = 0 (time stamp not valid) and using the bytes of the message_timestamp field to convey the execution condition\"), TC18.txt L1255-1258; general ACF_GBB frame-validation basis", + "tc18_master_id": "TC18-11.2-005" + }, + { + "id": "REQ-CMP-013", + "title": "decode_compound_request() rejects a message whose mtv is not untimed", + "text": "rcp::request::decode_compound_request() shall return RequestErrc::timestamp_not_repurposed (via decode_request_type() inside detail::decode_repurposed()) when the decoded AcfMessageInfo::mtv is true.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 (\"conditional requests are sent using ACF_GBB having mtv = 0 (time stamp not valid) and using the bytes of the message_timestamp field to convey the execution condition\"), TC18.txt L1255-1258", + "tc18_master_id": "TC18-11.2-005" + }, + { + "id": "REQ-CMP-014", + "title": "decode_compound_request() rejects a request_type it does not recognize", + "text": "rcp::request::decode_compound_request() shall return RequestErrc::unknown_request_type when the decoded opcode is neither is_compound() nor is_compound_wait().", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 Table 5 (\"first byte in message_timestamp field\" -> \"type of condition\": 0x0F/0x8F Compound, 0x0B/0x8B Compound wait), TC18.txt L1254-1267", + "tc18_master_id": "TC18-11.2.2-002" + }, + { + "id": "REQ-CMP-015", + "title": "decode_compound_request() recovers every sub-field from the specification's own offsets", + "text": "rcp::request::decode_compound_request() shall, given a frame whose repurposed message_timestamp region carries the layout REQ-CMP-010 defines, return no error and recover type, byte_bus_id, transaction_num, evt_op, and every CompoundStep sub-field (start_state, next_state, sequencer_index, exec_delay, repeat_count) from those same offsets and widths via decode_compound_step_params().", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.3 Figure 9 (compound-wait GBB sub-field offsets), TC18.txt L1494-1506", + "tc18_master_id": "TC18-11.2.2.3-005" + }, + { + "id": "REQ-CMP-016", + "title": "encode_clear_non_safestate() encodes a fixed-length, opcode-0x06, mtv-untimed frame", + "text": "rcp::request::encode_clear_non_safestate() shall produce an ACF_GBB frame addressed to byte_bus_id, echoing transaction_num, with mtv forced to false and the message_timestamp region's leading byte equal to RequestTypeOpcode::ClearNonSafestate and its remaining 7 bytes zero.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.2 Table 14 (clear-non-safestate, request_type 0x06), TC18.txt L2093-2124", + "tc18_master_id": "TC18-11.2.3.2-004" + }, + { + "id": "REQ-CMP-017", + "title": "decode_clear_non_safestate() round-trips a frame produced by its own encoder", + "text": "rcp::request::decode_clear_non_safestate() shall, given a frame produced by encode_clear_non_safestate(), return no error and recover the original byte_bus_id and transaction_num.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.2 Figure 15 (clear-non-safestate GBB layout), TC18.txt L2073-2082", + "tc18_master_id": "TC18-11.2.3.2-002" + }, + { + "id": "REQ-CMP-018", + "title": "decode_clear_non_safestate() rejects a request_type other than ClearNonSafestate", + "text": "rcp::request::decode_clear_non_safestate() shall return RequestErrc::unknown_request_type when the decoded opcode is not RequestTypeOpcode::ClearNonSafestate, given the same short-frame/bad-msg-type/not-repurposed failure modes as decode_compound_request().", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.2 Table 14 (clear-non-safestate, request_type 0x06), TC18.txt L2093-2124", + "tc18_master_id": "TC18-11.2.3.2-004" + }, + { + "id": "REQ-CMP-019", + "title": "SequencerTable::advance_guard() is true iff the target sequencer is still in start_state", + "text": "rcp::request::SequencerTable::advance_guard() shall return true iff step.sequencer_index is a valid index into the table and the table's current state at that index is nonzero (enabled) and equal to step.start_state, and shall return false for an invalid index. It shall not treat a start_state of zero specially — that sentinel governs whether a request may start (REQ-CMP-025), not whether its sequencer is advanced afterwards.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.2 (\"the state (cmp_next_state) the sequencer shall enter after the compound request has been executed (if the sequencer is still in cmp_start_state)\"), TC18.txt L1385-1387", + "tc18_master_id": "TC18-11.2.2.2-007" + }, + { + "id": "REQ-CMP-020", + "title": "SequencerTable::exec_delay_elapsed() compares elapsed against exec_delay", + "text": "rcp::request::SequencerTable::exec_delay_elapsed() shall return true iff elapsed >= step.exec_delay, both expressed in multiples of the addressed endpoint's configured ep_delay_time, not in milliseconds.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.2 Table 7 (\"cmp_exec_delay: delay time between the sequencer reaching the state defined in start_state and the start of the execution of the request... execution shall happen even in case the state changes before the delay expired\"), TC18.txt L1439-1442", + "tc18_master_id": "TC18-11.2.2.2-007" + }, + { + "id": "REQ-CMP-021", + "title": "SequencerTable::tick() advances the sequencer only once both the delay has elapsed and the guard holds", + "text": "rcp::request::SequencerTable::tick() shall set the target sequencer to step.next_state (via apply_next_state()) and return true iff both exec_delay_elapsed(step, elapsed) and advance_guard(step) hold; otherwise it shall leave the table unchanged and return false. When step.next_state is zero it shall leave the sequencer in its current state and still return true, per the \"sequencer remains in the current state\" sentinel apply_next_state() implements.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.2 Table 7 / Figure 8 (GBB message for compound requests: request_type, cmp_start_state, cmp_next_state, cmp_sequencer, cmp_exec_delay, cmp_repetitions), TC18.txt L1379-1458; \"If cmp_next_state is = 0 then the sequencer remains in the current state, when the execution is finalized.\", TC18.txt L1387-1388", + "tc18_master_id": "TC18-11.2.2.2-007" + }, + { + "id": "REQ-CMP-022", + "title": "SequencerTable::tick() never advances a sequencer that has already left start_state, even after its delay elapses", + "text": "rcp::request::SequencerTable::tick() shall return false and leave the table unchanged when exec_delay_elapsed(step, elapsed) holds but the target sequencer's current state does not equal step.start_state.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.2 Table 7 (\"cmp_exec_delay: delay time between the sequencer reaching the state defined in start_state and the start of the execution of the request... execution shall happen even in case the state changes before the delay expired\"), TC18.txt L1439-1442; §11.2.2.2 (\"the state (cmp_next_state) the sequencer shall enter after the compound request has been executed (if the sequencer is still in cmp_start_state)\"), TC18.txt L1385-1387", + "tc18_master_id": "TC18-11.2.2.2-007" + }, + { + "id": "REQ-CMP-023", + "title": "SequencerTable::wait_tick() advances the sequencer only once both condition_met and the guard hold", + "text": "rcp::request::SequencerTable::wait_tick() shall set the target sequencer to step.next_state (via apply_next_state()) and return true iff both condition_met and advance_guard(step) are true; otherwise it shall leave the table unchanged and return false. When step.next_state is zero it shall leave the sequencer in its current state and still return true, per the same sentinel REQ-CMP-021 names.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.3 (\"When the check finds that the condition is met the cmpw_exec_delay needs to expire before a response is generated, else the request execution is finalized without further action.\"), TC18.txt L1480-1482; §11.2.2.2 (\"the state (cmp_next_state) the sequencer shall enter after the compound request has been executed (if the sequencer is still in cmp_start_state)\"), TC18.txt L1385-1387", + "tc18_master_id": "TC18-11.2.2.3-002" + }, + { + "id": "REQ-CMP-024", + "title": "SequencerTable::wait_tick() never advances a sequencer that has already left start_state, even on a condition match", + "text": "rcp::request::SequencerTable::wait_tick() shall return false and leave the table unchanged when condition_met is true but the target sequencer's current state does not equal step.start_state.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.3 (\"When the check finds that the condition is met the cmpw_exec_delay needs to expire before a response is generated, else the request execution is finalized without further action.\"), TC18.txt L1480-1482", + "tc18_master_id": "TC18-11.2.2.3-002" + }, + { + "id": "REQ-CMP-025", + "title": "SequencerTable::start_condition_met() implements the \"any state\" start_state sentinel", + "text": "rcp::request::SequencerTable::start_condition_met() shall return true iff step.sequencer_index is a valid index into the table, the table's current state at that index is nonzero (enabled), and either step.start_state is zero (the request starts in whatever enabled state the sequencer currently holds) or the current state equals step.start_state. It shall return false for an index the table does not hold and for a disabled (state==0) sequencer, including when start_state is zero.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.2 (\"If cmp_start_state = 0 then the request shall be started in any state unless the sequencer is disabled which will prohibit the execution of the compound request.\"), TC18.txt L1389-1391; same rule for compound-wait, §11.2.2.3, TC18.txt L1489-1491", + "tc18_master_id": "TC18-11.2.2.2-002" + }, + { + "id": "REQ-CMP-026", + "title": "encode_compound_request() packs evt_op into the shared ACF header, not one of step's repurposed sub-fields", + "text": "rcp::request::encode_compound_request() shall write its evt_op parameter into the encoded frame's AcfMessageInfo::evt_op field via acf::encode_acf_gbb(), independently of every CompoundStep sub-field packed into the repurposed message_timestamp region. For a compound-wait request this is the TC18 §13.5.1 comparison-mode selector; a plain compound request has no comparison of its own.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.5.1, TC18.txt L4134-4136", + "tc18_master_id": "TC18-13.5.1-002" + }, + { + "id": "REQ-CMP-027", + "title": "decode_compound_request() surfaces the decoded evt_op field", + "text": "rcp::request::decode_compound_request() shall, on success, populate out.evt_op with the decoded AcfMessageInfo::evt_op field, independently of every recovered CompoundStep sub-field.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§13.5.1, TC18.txt L4134-4136", + "tc18_master_id": "TC18-13.5.1-002" + }, + { + "id": "REQ-CMP-028", + "title": "decode_clear_non_safestate() rejects a nonzero reserved message_timestamp octet", + "text": "rcp::request::decode_clear_non_safestate() shall return RequestErrc::reserved_field_nonzero when any of message_timestamp's 7 trailing octets (every octet after the leading request_type byte) carries a set bit, since clear-non-safestate carries no sub-field of its own.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.2 Table 14 (\"reserved: All bits shall be written as 0, else the request shall be rejected\"), TC18.txt L2093-2097", + "tc18_master_id": "TC18-11.2.3.2-004" + }, + { + "id": "REQ-CMP-029", + "title": "decode_clear_non_safestate() rejects nonzero evt[2:0], hs, or cs", + "text": "rcp::request::decode_clear_non_safestate() shall return RequestErrc::evt_hs_cs_nonzero (via detail::check_evt_hs_cs_zero()) when the decoded AcfMessageInfo's evt_op, hs, or cs is nonzero.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.2 Table 14 (\"evt[2:0], hs, cs: All bits shall be written as 0, else the request shall be rejected with error code = UNSUPPORTED_CMD\"), TC18.txt L2118-2124", + "tc18_master_id": "TC18-11.2.3.2-004" + }, + { + "id": "REQ-TRIG-001", + "title": "is_triggered() recognizes RequestTypeOpcode::Triggered and its safety variant", + "text": "rcp::request::is_triggered() shall return true iff type == RequestTypeOpcode::Triggered or type == RequestTypeOpcode::TriggeredSafety.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 Table 5 (request_type 0x0E/0x8E = Triggered), TC18.txt L1264; §11.2.2.5 (\"Each endpoint may be configured to start a request upon receiving a selected trigger signal\"), TC18.txt L1691", + "tc18_master_id": "TC18-11.2.2-002" + }, + { + "id": "REQ-TRIG-004", + "title": "encode_triggered_request() packs the trigger-selection sub-fields at the specification's own offsets", + "text": "rcp::request::encode_triggered_request() shall produce an ACF_GBB frame with mtv forced to false and shall write the repurposed message_timestamp region as: request_type at octet offset 0, step.trigger_source_ep at offset 1, step.trigger_signal_nr at offset 2, step.trigger_threshold at offset 3, step.exec_delay as two big-endian octets at offsets 4-5, and step.repeat_count as two big-endian octets at offsets 6-7, per encode_triggered_step_params().", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.5 Table 10 (trigger_source_ep/trigger_signal sub-field offsets), TC18.txt L1720-1721", + "tc18_master_id": "TC18-11.2.2.5-006" + }, + { + "id": "REQ-TRIG-005", + "title": "decode_triggered_request() maps ACF decode failures to their RequestErrc equivalents", + "text": "rcp::request::decode_triggered_request() shall return AvtpErrc::short_buffer or AcfErrc::bad_acf_msg_type (via detail::decode_repurposed()) when acf::decode_acf_gbb() reports the corresponding failure.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 (\"conditional requests are sent using ACF_GBB having mtv = 0 (time stamp not valid) and using the bytes of the message_timestamp field to convey the execution condition\"), TC18.txt L1255-1258; general ACF_GBB frame-validation basis", + "tc18_master_id": "TC18-11.2-005" + }, + { + "id": "REQ-TRIG-006", + "title": "decode_triggered_request() rejects a non-repurposed message_timestamp", + "text": "rcp::request::decode_triggered_request() shall return RequestErrc::timestamp_not_repurposed when the decoded mtv is true.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 (\"conditional requests are sent using ACF_GBB having mtv = 0 (time stamp not valid) and using the bytes of the message_timestamp field to convey the execution condition\"), TC18.txt L1255-1258", + "tc18_master_id": "TC18-11.2-005" + }, + { + "id": "REQ-TRIG-007", + "title": "decode_triggered_request() rejects a non-triggered opcode and otherwise recovers every sub-field", + "text": "rcp::request::decode_triggered_request() shall return RequestErrc::unknown_request_type when the decoded opcode does not satisfy is_triggered(), and shall otherwise populate out.type, out.byte_bus_id, out.transaction_num, a borrowed out.payload view, and every TriggeredStep sub-field from the offsets and widths REQ-TRIG-004 defines.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 Table 5 (\"0x0E, 0x8E\" -> \"Triggered\"), TC18.txt L1254-1267", + "tc18_master_id": "TC18-11.2.2-002" + }, + { + "id": "REQ-TRIG-008", + "title": "triggered_enter_started() resets the trigger-occurrence counter", + "text": "rcp::request::triggered_enter_started() shall set rt.occurrence_count to 0 and rt.started to true.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.9.3 Table 29 (Triggered request row: \"Upon entering RS state the counted number of received trigger signals is reset to zero and newly arriving trigger signals are counted from zero. The execution delay timer is started.\"), TC18.txt L3690-3695", + "tc18_master_id": "TC18-12.9.3-002" + }, + { + "id": "REQ-TRIG-009", + "title": "triggered_record_occurrence() counts only the trigger this request selected", + "text": "rcp::request::triggered_record_occurrence() shall increment rt.occurrence_count and return true iff rt.started is true and the observed occurrence matches this request's own selection, i.e. source_ep == step.trigger_source_ep and signal_nr == step.trigger_signal_nr. It shall leave rt entirely unchanged and return false for an occurrence from any other endpoint or any other trigger signal number, and for any occurrence while rt.started is false.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.9.3 Table 29 (Triggered request row: \"Upon entering RS state the counted number of received trigger signals is reset to zero and newly arriving trigger signals are counted from zero. The execution delay timer is started.\"), TC18.txt L3690-3695; §11.2.2.5 Table 10 (\"trigger_source_ep defines the endpoint which issues the trigger... trigger_signal_nr defines the trigger number of the endpoint addressed by trigger_source_ep\"), TC18.txt L1720-1721", + "tc18_master_id": "TC18-11.2.2.5-006" + }, + { + "id": "REQ-TRIG-010", + "title": "triggered_threshold_reached() counts occurrences that must precede execution", + "text": "rcp::request::triggered_threshold_reached() shall return true iff rt.occurrence_count > step.trigger_threshold, so that a trigger_threshold of zero is satisfied by one occurrence and a trigger_threshold of N by N+1 occurrences.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.5 Table 10 (trigger_threshold), TC18.txt L1722-1724", + "tc18_master_id": "TC18-11.2.2.5-006" + }, + { + "id": "REQ-TRIG-011", + "title": "triggered_exec_delay_elapsed() compares elapsed against exec_delay", + "text": "rcp::request::triggered_exec_delay_elapsed() shall return true iff elapsed >= step.exec_delay, both expressed in multiples of the addressed endpoint's configured ep_delay_time, not in milliseconds.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.5 Table 10 (trigger_exec_delay), TC18.txt L1725-1727", + "tc18_master_id": "TC18-11.2.2.5-006" + }, + { + "id": "REQ-TRIG-012", + "title": "triggered_tick() only fires when started, the threshold is reached, the delay has elapsed, and the endpoint is idle", + "text": "rcp::request::triggered_tick() shall return false and leave rt unchanged unless rt.started is true, triggered_threshold_reached(step, rt) holds, triggered_exec_delay_elapsed(step, elapsed) holds, and endpoint_idle is true.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.9.3 Table 29 (Triggered request row: \"Upon entering RS state the counted number of received trigger signals is reset to zero and newly arriving trigger signals are counted from zero. The execution delay timer is started.\"), TC18.txt L3690-3695; \"When the trigger_exec_delay timer has expired the state advances to RE as soon as the EP is idle and no request with higher priority is pending.\", TC18.txt L3692-3693", + "tc18_master_id": "TC18-12.9.3-002" + }, + { + "id": "REQ-TRIG-013", + "title": "triggered_tick() resets the runtime on a successful fire and advances no sequencer", + "text": "When every condition REQ-TRIG-012 names holds, rcp::request::triggered_tick() shall reset rt.occurrence_count to 0 and rt.started to false and return true. It shall not read or write any SequencerTable state: a triggered request carries no sequencer, start_state, or next_state sub-field.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.9.3 Table 29 (Triggered request row: \"Upon entering RS state the counted number of received trigger signals is reset to zero and newly arriving trigger signals are counted from zero. The execution delay timer is started.\"), TC18.txt L3690-3695; TC18.txt L3692-3693 (trigger_exec_delay expiry + EP-idle gating)", + "tc18_master_id": "TC18-12.9.3-002" + }, + { + "id": "REQ-CHAIN-002", + "title": "encode_chained_member() leaves every reserved sub-field octet zero", + "text": "rcp::request::encode_chained_member() shall write octets 1-3 and 6-7 of the repurposed message_timestamp region as all-zero. It shall not encode any chain_length or chain_position sub-field: the specification defines neither, and a member's position in a chain is its position among the enclosing AVTPDU's requests.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.6 Table 11 (chained request reserved fields), TC18.txt L1827-1830", + "tc18_master_id": "TC18-11.2.2.6-010" + }, + { + "id": "REQ-CHAIN-004", + "title": "encode_chained_member() packs chain_exec_delay and cs at the specification's own positions", + "text": "rcp::request::encode_chained_member() shall produce an ACF_GBB frame with mtv forced to false, the opcode byte RequestTypeOpcode::Chained at octet offset 0 of the repurposed message_timestamp region, chain_exec_delay as two big-endian octets at offsets 4-5, and cs encoded into AcfMessageInfo's own cs bit.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.6 Table 11 (chain_exec_delay, cs semantics), TC18.txt L1828-1839", + "tc18_master_id": "TC18-11.2.2.6-010" + }, + { + "id": "REQ-CHAIN-005", + "title": "decode_chained_member() maps ACF decode failures to their RequestErrc equivalents", + "text": "rcp::request::decode_chained_member() shall return AvtpErrc::short_buffer or AcfErrc::bad_acf_msg_type (via detail::decode_repurposed()) when acf::decode_acf_gbb() reports the corresponding failure.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 (\"conditional requests are sent using ACF_GBB having mtv = 0 (time stamp not valid) and using the bytes of the message_timestamp field to convey the execution condition\"), TC18.txt L1255-1258; general ACF_GBB frame-validation basis", + "tc18_master_id": "TC18-11.2-005" + }, + { + "id": "REQ-CHAIN-006", + "title": "decode_chained_member() rejects a non-repurposed message_timestamp", + "text": "rcp::request::decode_chained_member() shall return RequestErrc::timestamp_not_repurposed when the decoded mtv is true.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 (\"conditional requests are sent using ACF_GBB having mtv = 0 (time stamp not valid) and using the bytes of the message_timestamp field to convey the execution condition\"), TC18.txt L1255-1258", + "tc18_master_id": "TC18-11.2-005" + }, + { + "id": "REQ-CHAIN-007", + "title": "decode_chained_member() rejects a non-chained opcode and otherwise recovers chain_exec_delay and cs", + "text": "rcp::request::decode_chained_member() shall return RequestErrc::unknown_request_type when the decoded opcode is not RequestTypeOpcode::Chained, and shall otherwise populate out.byte_bus_id, out.chain_exec_delay (from offsets 4-5), out.cs, out.transaction_num, and a borrowed out.payload view.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 Table 5 (\"0x01\" -> \"Chained\"), TC18.txt L1254-1267", + "tc18_master_id": "TC18-11.2.2-002" + }, + { + "id": "REQ-CHAIN-010", + "title": "RequestLedger's chained-successor propagation reads cs off the member about to run, about its predecessor's outcome", + "text": "rcp::request::should_execute_chained(cs, predecessor_errored) shall return true whenever cs is false (RCP_CHAINED_CS_CONTINUE_ON_ERROR, execute regardless), and shall return true iff predecessor_errored is false when cs is true (RCP_CHAINED_CS_ABORT_ON_ERROR). RequestLedger::finalize()'s propagate_chain_completion() shall consult should_execute_chained() with each direct successor's own cs, not the finalizing predecessor's, and cascade_cancel() that successor (and, transitively, every one of its own chained_successors regardless of their cs) whenever should_execute_chained() returns false. A predecessor that finalizes with errored=false never aborts any successor, independent of cs.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.6 Table 11 (cs conditional-start semantics), TC18.txt L1833-1839", + "tc18_master_id": "TC18-11.2.2.6-011" + }, + { + "id": "REQ-CHAIN-011", + "title": "chained_exec_delay_elapsed() compares elapsed against chain_exec_delay", + "text": "rcp::request::chained_exec_delay_elapsed() shall return true iff elapsed >= chain_exec_delay, both expressed in multiples of the addressed endpoint's configured ep_delay_time and measured from the moment the member's predecessor finalized.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.6 (\"chain_exec_delay: Execution delay in multiples of ep_delay_time... for the request execution after the previous request has been finalized.\"), TC18.txt L1828-1829", + "tc18_master_id": "TC18-11.2.2.6-010" + }, + { + "id": "REQ-CHAIN-012", + "title": "decode_chained_member() rejects a non-zero reserved sub-field octet", + "text": "rcp::request::decode_chained_member() shall return RequestErrc::reserved_field_nonzero when any of octets 1-3 or 6-7 of the repurposed message_timestamp region carries a set bit.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.6 (\"reserved: All bits shall be written as 0, else the request will be rejected\" / \"...shall be rejected\"), TC18.txt L1827, L1830", + "tc18_master_id": "TC18-11.2.2.6-010" + }, + { + "id": "REQ-TIMED-002", + "title": "timed_feature_enabled() requires the conditional-requests option bit set", + "text": "rcp::request::timed_feature_enabled() shall return true iff regmap::kOptConditionalRequests is set in options, and false otherwise. cpp-RCP's svr_implemented_options bitmask is coarser than c-RCP's four independent per-feature bits (compound-wait/trigger/chained/time-sync); kOptConditionalRequests is the one bit this codebase already uses to mean \"some conditional-request kind, including Timed, is implemented\".", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.7 (\"requests can be sent under TSCF header, where an avtp_timestamp is included... A second option is to convey the presentation time in the message_timestamp field of an ACF_GBB.\"), TC18.txt L1867-1868; conformance bullets \"Implementation of gPTP (IEEE802.1as)\" / \"Support of timed request\", TC18.txt L2399-2400" + }, + { + "id": "REQ-TIMED-003", + "title": "encode_timed_request() packs a 48-bit presentation_time and holds the reserved octet at zero", + "text": "rcp::request::encode_timed_request() shall produce an ACF_GBB frame with mtv forced to false, the opcode byte RequestTypeOpcode::Timed at octet offset 0 of the repurposed message_timestamp region, an all-zero reserved octet at offset 1, and presentation_time as six big-endian octets spanning offsets 2-7. It shall return std::nullopt, never a truncated frame, when presentation_time exceeds kTimedPresentationTimeMax.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.7 Figure 13 (48-bit presentation_time offset), TC18.txt L1871-1882" + }, + { + "id": "REQ-TIMED-004", + "title": "decode_timed_request() maps ACF decode failures and rejects a non-repurposed message_timestamp", + "text": "rcp::request::decode_timed_request() shall return AvtpErrc::short_buffer or AcfErrc::bad_acf_msg_type (via detail::decode_repurposed()) when acf::decode_acf_gbb() reports the corresponding failure, and RequestErrc::timestamp_not_repurposed when the decoded mtv is true.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 (\"conditional requests are sent using ACF_GBB having mtv = 0 (time stamp not valid) and using the bytes of the message_timestamp field to convey the execution condition\"), TC18.txt L1255-1258; general ACF_GBB frame-validation basis" + }, + { + "id": "REQ-TIMED-005", + "title": "decode_timed_request() rejects a non-timed opcode and otherwise recovers the 48-bit presentation_time", + "text": "rcp::request::decode_timed_request() shall return RequestErrc::unknown_request_type when the decoded opcode is not RequestTypeOpcode::Timed, and shall otherwise populate out.byte_bus_id, out.transaction_num, a borrowed out.payload view, and out.presentation_time from the six octets at offsets 2-7 of the repurposed region, in [0, kTimedPresentationTimeMax].", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2 Table 5 (\"0x0A\" -> \"Timed\"), TC18.txt L1254-1267" + }, + { + "id": "REQ-TIMED-006", + "title": "timed_too_far() is wraparound-safe in the 48-bit presentation-time domain", + "text": "rcp::request::timed_too_far() shall compute presentation_time - now modulo kTimedPresentationTimeModulus (via detail::timed_forward_delta()), shall return false whenever that difference exceeds half the modulus (i.e. presentation_time is at or before now, via detail::timed_in_the_past()), and shall otherwise return true iff the difference exceeds max_horizon.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.7 (\"The RC Server may reject the request, when the presentation_time is too far in the future, which is implementation dependent (error code = PRESENTATION_TIME_TOO_FAR).\"), TC18.txt L1953-1954" + }, + { + "id": "REQ-TIMED-007", + "title": "timed_admit() reports TimedAdmission::RejectGptpFail whenever gptp_locked is false", + "text": "rcp::request::timed_admit() shall return TimedAdmission::RejectGptpFail whenever gptp_locked is false, regardless of presentation_time, now, or max_horizon, taking priority over the presentation-time-too-far check.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.7 (\"In case the time synchronization hasn't been established, timed requests (either in the format shown here or under TSCF header) shall be rejected and an error response shall be sent (error code = GPTP_FAIL).\"), TC18.txt L1955-1956" + }, + { + "id": "REQ-TIMED-008", + "title": "timed_admit() reports RejectPresentationTimeTooFar or Accept when gPTP is locked", + "text": "rcp::request::timed_admit() shall return TimedAdmission::RejectPresentationTimeTooFar when gptp_locked is true and timed_too_far(presentation_time, now, max_horizon) holds, and TimedAdmission::Accept otherwise.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.7 (\"The RC Server may reject the request, when the presentation_time is too far in the future\"), TC18.txt L1953" + }, + { + "id": "REQ-TIMED-009", + "title": "decode_timed_request() rejects a non-zero reserved octet", + "text": "rcp::request::decode_timed_request() shall return RequestErrc::reserved_field_nonzero when the reserved octet at offset 1 of the repurposed message_timestamp region (params[0]) carries any set bit.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.7 Table 12 (reserved bits must be 0), TC18.txt L1906" + }, + { + "id": "REQ-TIMED-010", + "title": "decode_timed_request() rejects a timed request with hs or cs set", + "text": "rcp::request::decode_timed_request() shall return RequestErrc::unsupported_cmd when the decoded AcfMessageInfo's hs or cs bit is set.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.7 Table 12 (hs and cs shall both be 0b), TC18.txt L1911-1912" + }, + { + "id": "REQ-TIMED-011", + "title": "timed_due() reports whether a presentation_time has arrived", + "text": "rcp::request::timed_due() shall return true iff presentation_time is at or before now in the same wrapping 48-bit domain timed_too_far() uses, i.e. iff the request's execution condition is satisfied.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.2.7 (\"In any case the presentation_time is the earliest point in time, when the request shall be executed.\"), TC18.txt L1952" + }, + { + "id": "REQ-CANCEL-002", + "title": "encode_clear_all() encodes a zero-payload clear-all request", + "text": "rcp::request::encode_clear_all() shall produce an ACF_GBB frame with mtv forced to false, the opcode byte set to RequestTypeOpcode::ClearAll, and the remaining repurposed sub-field bytes zeroed.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.1 Figure 14 + Table 13 (clear-all, request_type 0x05), TC18.txt L1983-2050", + "tc18_master_id": "TC18-11.2.3.1-004" + }, + { + "id": "REQ-CANCEL-003", + "title": "decode_clear_all() maps ACF decode failures and rejects a non-repurposed message_timestamp", + "text": "rcp::request::decode_clear_all() shall return AvtpErrc::short_buffer or AcfErrc::bad_acf_msg_type (via detail::decode_repurposed()) when acf::decode_acf_gbb() reports the corresponding failure, and RequestErrc::timestamp_not_repurposed when the decoded mtv is true.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.1 Figure 14 / Table 13 (Usage of GBB message to cancel all requests, request_type=0x05), TC18.txt L1983-2050; general ACF_GBB frame-validation basis and §11.2.2 (\"conditional requests are sent using ACF_GBB having mtv = 0 (time stamp not valid) and using the bytes of the message_timestamp field to convey the execution condition\"), TC18.txt L1255-1258", + "tc18_master_id": "TC18-11.2.3.1-004" + }, + { + "id": "REQ-CANCEL-004", + "title": "decode_clear_all() rejects an opcode byte that is not ClearAll", + "text": "rcp::request::decode_clear_all() shall return RequestErrc::unknown_request_type when the decoded opcode is not RequestTypeOpcode::ClearAll, and shall otherwise populate out.byte_bus_id and out.transaction_num.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.1 Table 13 (clear-all, request_type 0x05), TC18.txt L2024-2025", + "tc18_master_id": "TC18-11.2.3.1-004" + }, + { + "id": "REQ-CANCEL-005", + "title": "encode_clear_single() packs clear_transaction_num at the specification's own offset", + "text": "rcp::request::encode_clear_single() shall produce an ACF_GBB frame with mtv forced to false, the opcode byte RequestTypeOpcode::ClearSingle at octet offset 0 of the repurposed message_timestamp region, clear_transaction_num at octet offset 3, and octets 1-2 and 4-7 all-zero.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.3 Figure 16 (clear-single, clear_transaction_num offset), TC18.txt L2140-2158", + "tc18_master_id": "TC18-11.2.3.3-002" + }, + { + "id": "REQ-CANCEL-006", + "title": "decode_clear_single() maps ACF decode failures and rejects a non-repurposed message_timestamp", + "text": "rcp::request::decode_clear_single() shall return AvtpErrc::short_buffer or AcfErrc::bad_acf_msg_type (via detail::decode_repurposed()) when acf::decode_acf_gbb() reports the corresponding failure, and RequestErrc::timestamp_not_repurposed when the decoded mtv is true.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.3 Figure 16 / Table 15 (Usage of GBB message to cancel a single, specific request, request_type=0x07, clear_transaction_num), TC18.txt L2140-2210; general ACF_GBB frame-validation basis and §11.2.2 (\"conditional requests are sent using ACF_GBB having mtv = 0 (time stamp not valid) and using the bytes of the message_timestamp field to convey the execution condition\"), TC18.txt L1255-1258", + "tc18_master_id": "TC18-11.2.3.3-004" + }, + { + "id": "REQ-CANCEL-007", + "title": "decode_clear_single() recovers clear_transaction_num from the specification's own offset", + "text": "rcp::request::decode_clear_single() shall return RequestErrc::unknown_request_type when the decoded opcode is not RequestTypeOpcode::ClearSingle, RequestErrc::reserved_field_nonzero when any of octets 1-2 or 4-7 of the repurposed region carries a set bit, and shall otherwise populate out.byte_bus_id, out.transaction_num, and out.clear_transaction_num from octet offset 3.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.3 Table 15 (clear-single, clear_transaction_num), TC18.txt L2185", + "tc18_master_id": "TC18-11.2.3.3-004" + }, + { + "id": "REQ-CANCEL-008", + "title": "RequestLedger treats exactly Pending/Started records as cancellable", + "text": "rcp::request::RequestLedger::cancel_single()/cascade_cancel() shall treat a record as cancellable iff its state is RequestState::Pending or RequestState::Started, and shall report RequestErrc::request_not_cancellable (never cancelling it) for a record whose state is UnderExecution, Finalized, or already Canceled.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3 (\"A request can be cancelled while it is pending and after it has been started until it is under execution. Requests under execution will not be aborted.\"), TC18.txt L1977-1978", + "tc18_master_id": "TC18-11.2.3-001" + }, + { + "id": "REQ-CANCEL-009", + "title": "RequestLedger::cancel_single() reports request_not_found when the target was never tracked", + "text": "rcp::request::RequestLedger::cancel_single() shall return RequestErrc::request_not_found whenever the given transaction_num is not tracked by this ledger at all, regardless of what state it would otherwise have been in.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.3 (\"The request initiating the cancellation will create an error response with the error code = REQUEST_NOT_FOUND, when the clear_transaction_num was not found.\"), TC18.txt L2212-2213", + "tc18_master_id": "TC18-11.2.3.3-005" + }, + { + "id": "REQ-CANCEL-010", + "title": "RequestLedger::cancel_single() reports request_not_cancellable for a found request outside the cancellable window", + "text": "rcp::request::RequestLedger::cancel_single() shall return RequestErrc::request_not_cancellable whenever the target transaction_num is tracked but its state is UnderExecution, Finalized, or already Canceled — distinct from request_not_found, which is reserved for a transaction_num never tracked at all.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3 (\"A request can be cancelled while it is pending and after it has been started until it is under execution. Requests under execution will not be aborted.\"), TC18.txt L1977-1978", + "tc18_master_id": "TC18-11.2.3-001" + }, + { + "id": "REQ-CANCEL-011", + "title": "RequestLedger::cancel_single() cancels a found, cancellable request", + "text": "rcp::request::RequestLedger::cancel_single() shall transition the target record to RequestState::Canceled and set its outcome to RequestErrc::request_canceled, and return no error, whenever the target transaction_num is tracked and its state is Pending or Started.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3 (\"A request can be cancelled while it is pending and after it has been started until it is under execution. Requests under execution will not be aborted.\"), TC18.txt L1977-1978; §11.2.3.3 (\"The request initiating the cancellation will create an error response with the error code = REQUEST_NOT_FOUND, when the clear_transaction_num was not found.\"), TC18.txt L2212-2213", + "tc18_master_id": "TC18-11.2.3-001" + }, + { + "id": "REQ-CANCEL-012", + "title": "Cancelling a chained request cascades to every transitive chained successor", + "text": "rcp::request::RequestLedger::cancel_single() shall, via cascade_cancel(), transition to RequestState::Canceled not only the target record but every record transitively reachable through its own chained_successors list, so that cancelling a chain's first member also cancels every not-yet-executing successor in that chain.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3 (\"If a request is cancelled to which a request is chained, then the chained successors shall be cancelled by the RC Server as well.\"), TC18.txt L1978-1979", + "tc18_master_id": "TC18-11.2.3-002" + }, + { + "id": "REQ-CANCEL-013", + "title": "decode_clear_all() rejects a nonzero reserved message_timestamp octet", + "text": "rcp::request::decode_clear_all() shall return RequestErrc::reserved_field_nonzero when any of message_timestamp's 7 trailing octets (every octet after the leading request_type byte) carries a set bit, since clear-all carries no sub-field of its own.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.1 Table 13 (\"reserved: All bits shall be written as 0, else the request shall be rejected\"), TC18.txt L2025", + "tc18_master_id": "TC18-11.2.3.1-004" + }, + { + "id": "REQ-CANCEL-014", + "title": "decode_clear_all() rejects nonzero evt[2:0], hs, or cs", + "text": "rcp::request::decode_clear_all() shall return RequestErrc::evt_hs_cs_nonzero (via detail::check_evt_hs_cs_zero()) when the decoded AcfMessageInfo's evt_op, hs, or cs is nonzero.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.1 Table 13 (evt/hs/cs field descriptions), TC18.txt L2026-2030", + "tc18_master_id": "TC18-11.2.3.1-004" + }, + { + "id": "REQ-CANCEL-015", + "title": "decode_clear_single() rejects nonzero evt[2:0], hs, or cs", + "text": "rcp::request::decode_clear_single() shall return RequestErrc::evt_hs_cs_nonzero (via detail::check_evt_hs_cs_zero()) when the decoded AcfMessageInfo's evt_op, hs, or cs is nonzero. Distinct from REQ-CANCEL-007's own RequestErrc::reserved_field_nonzero check, which validates message_timestamp's own reserved octets, a different wire region from the ACF header's evt/hs/cs bits.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§11.2.3.3 (\"Evt, hs and cs shall be zero\"), Table 15, TC18.txt L2140-2185", + "tc18_master_id": "TC18-11.2.3.3-004" + }, + { + "id": "REQ-SCHED-002", + "title": "RequestCategory and priority_rank() encode the cancellation > triggered > timed > compound > compound-wait > chained > standard ordering", + "text": "rcp::request::priority_rank() shall return a strictly lower numeric value (via the RequestCategory enum's own declaration order: Cancellation=0 .. Standard=6) for RequestCategory::Cancellation than ::Triggered, for ::Triggered than ::Timed, for ::Timed than ::Compound, for ::Compound than ::CompoundWait, for ::CompoundWait than ::Chained, and for ::Chained than ::Standard.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.9.2 (\"In case two or more requests are due for execution then the priorities are as follows: 1. cancellation 2. triggered 3. timed 4. compound 5. compound wait 6. chained 7. standard\"), TC18.txt L3652-3663" + }, + { + "id": "REQ-SCHED-003", + "title": "select_next_due() orders by priority rank first and FIFO arrival second", + "text": "rcp::request::select_next_due() shall return the index of the DueCandidate with the lowest priority_rank(category) among `due`, and among candidates of equal rank shall pick the one with the lowest arrival_seq (FIFO tie-break); it shall return std::nullopt for an empty input.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.9.2 (\"In case two or more requests are due for execution then the priorities are as follows: 1. cancellation 2. triggered 3. timed 4. compound 5. compound wait 6. chained 7. standard\"), TC18.txt L3652-3663; §12.9.2 (\"When two or more requests with the same priority are due then starting the execution shall happen in the order with which the requests arrived in the EP request storage.\"), TC18.txt L3663-3664" + }, + { + "id": "REQ-SCHED-007", + "title": "frame_timing_consistent() is trivially true for NTSCF frames and empty member lists", + "text": "rcp::request::frame_timing_consistent() shall return true whenever is_tscf is false, and shall return true whenever member_is_timed is empty, regardless of member_is_timed's contents.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.9.1.1 (\"As the AVTPDU has either a TSCF or NTSCF header a mixture of requests with and without presentation time in a single frame is not possible. A presentation time from a TSCF header will be applied to all ACFtypes within the frame.\"), TC18.txt L3622-3624" + }, + { + "id": "REQ-SCHED-008", + "title": "frame_timing_consistent() rejects a TSCF frame mixing timed and untimed members", + "text": "For is_tscf == true and a non-empty member_is_timed, rcp::request::frame_timing_consistent() shall return true iff every entry in member_is_timed equals member_is_timed[0], and false otherwise.", + "standard": "iso26262", + "level": "ASIL-B", + "asil": "ASIL-B", + "scope": "tc18", + "status": "implemented", + "tc18": "§12.9.1.1 (\"As the AVTPDU has either a TSCF or NTSCF header a mixture of requests with and without presentation time in a single frame is not possible. A presentation time from a TSCF header will be applied to all ACFtypes within the frame.\"), TC18.txt L3622-3624" } ] -} +} \ No newline at end of file diff --git a/include/rcp/request.hpp b/include/rcp/request.hpp index 776af22..ff7866e 100644 --- a/include/rcp/request.hpp +++ b/include/rcp/request.hpp @@ -13,8 +13,6 @@ // fusa:req REQ-CMP-001 // fusa:req REQ-CMP-002 // fusa:req REQ-CMP-003 -// fusa:req REQ-CMP-008 -// fusa:req REQ-CMP-009 // fusa:req REQ-CMP-010 // fusa:req REQ-CMP-011 // fusa:req REQ-CMP-012 @@ -36,7 +34,6 @@ // fusa:req REQ-CMP-028 // fusa:req REQ-CMP-029 // fusa:req REQ-TRIG-001 -// fusa:req REQ-TRIG-003 // fusa:req REQ-TRIG-004 // fusa:req REQ-TRIG-005 // fusa:req REQ-TRIG-006 @@ -48,7 +45,6 @@ // fusa:req REQ-TRIG-012 // fusa:req REQ-TRIG-013 // fusa:req REQ-CHAIN-002 -// fusa:req REQ-CHAIN-003 // fusa:req REQ-CHAIN-004 // fusa:req REQ-CHAIN-005 // fusa:req REQ-CHAIN-006 @@ -76,6 +72,7 @@ // fusa:req REQ-CANCEL-009 // fusa:req REQ-CANCEL-010 // fusa:req REQ-CANCEL-011 +// fusa:req REQ-CANCEL-012 // fusa:req REQ-CANCEL-013 // fusa:req REQ-CANCEL-014 // fusa:req REQ-CANCEL-015 @@ -236,6 +233,38 @@ // for the second; neither is core protocol behavior, just a named // convenience c-RCP happens to also offer. // +// TODO(phase6-followup): Phase 6 (requirement-catalog re-derivation, cpp-RCP +// issue #129 batch 3/13) confirmed the following real, currently-unfixed +// gaps against c-RCP while re-deriving .fusa-reqs.json entries for this +// file's own already-orphan-cited ids — filed to .fusa-reqs-pending.json +// (REQ-CMP-008/009, REQ-TRIG-003, REQ-CHAIN-003, REQ-CHAIN-008, REQ-TIMED- +// 012/013), not fixed here (out of this cataloging pass's scope): +// - encode_compound_request()/encode_triggered_request()/encode_chained_ +// member() never validate their own `type`/payload-size inputs before +// encoding (REQ-CMP-008/009, REQ-TRIG-003, REQ-CHAIN-003): each forwards +// straight to acf::encode_acf_gbb(), which by its own documented design +// (acf.hpp's "always returns bytes, never an error code" contract) never +// rejects an oversized payload or unrecognized opcode, and each of these +// three functions returns a plain std::vector (not +// std::optional, unlike encode_timed_request()'s own std::nullopt-on- +// invalid-input convention just below), so there is structurally no +// channel to signal rejection even if the check were added without a +// signature change. +// - RequestLedger::submit() never rejects a Chained-opcode record whose +// chained_predecessor is unset (REQ-CHAIN-008): TC18 §11.2.2.6 requires +// a chain request with no predecessor (e.g. the first request in an +// AVTPDU) to be rejected and its whole chain ignored; this file has no +// CHAIN_ERROR-equivalent RequestErrc value and no detection for this +// case at all. Distinct from the "already-aborted chain" case +// (REQ-CHAIN-009), which cascade_cancel()/propagate_chain_completion() +// genuinely do handle — see delta #4 above. +// - No dispatch/admission entry point anywhere in cpp-RCP threads a real +// TSCF header's avtp_timestamp into timed_admit()/timed_due() +// (REQ-TIMED-012/013): this file provides every primitive the gate +// needs, but per this file's own design note two paragraphs below, it +// does not itself run a dispatch loop, and no other module in this tree +// supplies one either. +// // Field names and behavior below implement TC18's *behavior* as described in // an internal structured extraction of the specification; no text from that // document is reproduced here. The concrete opcode-to-byte mapping, the diff --git a/tests/test_request.cpp b/tests/test_request.cpp index 5bd22a1..9d50206 100644 --- a/tests/test_request.cpp +++ b/tests/test_request.cpp @@ -13,8 +13,6 @@ // fusa:test REQ-CMP-001 // fusa:test REQ-CMP-002 // fusa:test REQ-CMP-003 -// fusa:test REQ-CMP-008 -// fusa:test REQ-CMP-009 // fusa:test REQ-CMP-010 // fusa:test REQ-CMP-011 // fusa:test REQ-CMP-012 @@ -36,7 +34,6 @@ // fusa:test REQ-CMP-028 // fusa:test REQ-CMP-029 // fusa:test REQ-TRIG-001 -// fusa:test REQ-TRIG-003 // fusa:test REQ-TRIG-004 // fusa:test REQ-TRIG-005 // fusa:test REQ-TRIG-006 @@ -48,7 +45,6 @@ // fusa:test REQ-TRIG-012 // fusa:test REQ-TRIG-013 // fusa:test REQ-CHAIN-002 -// fusa:test REQ-CHAIN-003 // fusa:test REQ-CHAIN-004 // fusa:test REQ-CHAIN-005 // fusa:test REQ-CHAIN-006 @@ -76,6 +72,7 @@ // fusa:test REQ-CANCEL-009 // fusa:test REQ-CANCEL-010 // fusa:test REQ-CANCEL-011 +// fusa:test REQ-CANCEL-012 // fusa:test REQ-CANCEL-013 // fusa:test REQ-CANCEL-014 // fusa:test REQ-CANCEL-015 @@ -313,7 +310,7 @@ TEST_CASE("kCompoundRepeatInfinite is the two-octet all-ones sentinel", "[reques } TEST_CASE("compound request round-trips through encode_compound_request/decode_compound_request", - "[request][REQ-CMP-011]") { + "[request][REQ-CMP-011][REQ-CMP-015]") { CompoundStep step; step.start_state = 1; step.next_state = 2; @@ -339,7 +336,7 @@ TEST_CASE("compound request round-trips through encode_compound_request/decode_c } TEST_CASE("compound-wait safety request round-trips, evt_op independent of step sub-fields", - "[request][REQ-CMP-026]") { + "[request][REQ-CMP-026][REQ-CMP-027]") { CompoundStep step; step.start_state = 4; step.next_state = 0; // "leave it where it is" sentinel @@ -395,7 +392,7 @@ TEST_CASE("decode_compound_request rejects a non-ACF_GBB message", "[request][RE // ── clear-non-safestate (0x06) ──────────────────────────────────────────────── -TEST_CASE("clear-non-safestate round-trips through encode/decode", "[request][REQ-CMP-016]") { +TEST_CASE("clear-non-safestate round-trips through encode/decode", "[request][REQ-CMP-016][REQ-CMP-017]") { auto encoded = encode_clear_non_safestate(/*byte_bus_id=*/3, /*transaction_num=*/7); ClearNonSafestateRequest out; REQUIRE_FALSE(decode_clear_non_safestate(encoded.data(), encoded.size(), out)); @@ -965,6 +962,20 @@ TEST_CASE("chained member wire sub-field offsets: chain_exec_delay at octets 4.. REQUIRE(encoded[15] == 0); } +TEST_CASE("decode_chained_member maps ACF decode failures to their RequestErrc equivalents", + "[request][REQ-CHAIN-005]") { + auto full = encode_chained_member(1, 0, false, 1); + std::vector too_short(full.begin(), full.begin() + 4); + ChainedMember out; + auto ec_short = decode_chained_member(too_short.data(), too_short.size(), out); + REQUIRE(ec_short == rcp::avtp::make_error_code(rcp::avtp::AvtpErrc::short_buffer)); + + rcp::acf::AcfMessageInfo abb_info; + auto abb_encoded = rcp::acf::encode_acf_abb(abb_info, {}); + auto ec_bad_type = decode_chained_member(abb_encoded.data(), abb_encoded.size(), out); + REQUIRE(ec_bad_type == rcp::acf::make_error_code(rcp::acf::AcfErrc::bad_acf_msg_type)); +} + TEST_CASE("decode_chained_member rejects a non-chained opcode", "[request][REQ-CHAIN-007]") { auto encoded = encode_clear_all(1, 1); ChainedMember out; @@ -1061,6 +1072,27 @@ TEST_CASE("decode_timed_request rejects hs or cs set", "[request][REQ-TIMED-010] make_error_code(RequestErrc::unsupported_cmd)); } +TEST_CASE("decode_timed_request maps ACF decode failures and rejects a non-repurposed message_timestamp", + "[request][REQ-TIMED-004]") { + auto full = *encode_timed_request(1, 0, 1); + std::vector too_short(full.begin(), full.begin() + 4); + TimedRequest out; + auto ec_short = decode_timed_request(too_short.data(), too_short.size(), out); + REQUIRE(ec_short == rcp::avtp::make_error_code(rcp::avtp::AvtpErrc::short_buffer)); + + rcp::acf::AcfMessageInfo abb_info; + auto abb_encoded = rcp::acf::encode_acf_abb(abb_info, {}); + auto ec_bad_type = decode_timed_request(abb_encoded.data(), abb_encoded.size(), out); + REQUIRE(ec_bad_type == rcp::acf::make_error_code(rcp::acf::AcfErrc::bad_acf_msg_type)); + + rcp::acf::AcfMessageInfo mtv_info; + mtv_info.byte_bus_id = 1; + mtv_info.mtv = true; + auto mtv_encoded = rcp::acf::encode_acf_gbb(mtv_info, uint64_t{0x0A} << 56, {}); + auto ec_not_repurposed = decode_timed_request(mtv_encoded.data(), mtv_encoded.size(), out); + REQUIRE(ec_not_repurposed == make_error_code(RequestErrc::timestamp_not_repurposed)); +} + TEST_CASE("decode_timed_request rejects a non-timed opcode", "[request][REQ-TIMED-005]") { auto encoded = encode_clear_all(1, 1); TimedRequest out; @@ -1388,7 +1420,7 @@ TEST_CASE("RequestLedger::cancel_single reports request_not_found for a transact TEST_CASE("RequestLedger::cancel_single reports request_not_cancellable — not request_not_found — " "for a request that is tracked but already past cancellation (c-RCP delta #3)", - "[request][REQ-CANCEL-010]") { + "[request][REQ-CANCEL-010][REQ-CANCEL-008]") { RequestLedger ledger; RequestRecord rec; rec.transaction_num = 1;