A1: S5F13-F18 exception recovery messages (E5 §9.5-9.7)
Catalog gains the recover-request / recover-complete / recover-abort loop that closes E5's exception lifecycle. Wire shapes: S5F13/F14 Exception Recover Request / Acknowledge S5F15/F16 Exception Recover Complete Notify / Acknowledge S5F17/F18 Exception Recover Abort Request / Acknowledge Acks use the same AlarmAck byte already in use by S5F10/F12. EXRESULT on F15 is modelled as ASCII (the common case; vendor-specific richer shapes are a YAML edit). Round-trip tests cover all three pairs. Server dispatch is left for a later commit alongside the per-alarm AlarmStateMachine (Tranche C) — this commit is wire-coverage only. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -172,6 +172,9 @@ YAML/handler extensions, not surgery):
|
||||
| S5F7 / S5F8 | H→E | ✅ | catalog | ✅ round-trip |
|
||||
| S5F9 / S5F10 | E→H | ✅ | catalog | ✅ round-trip (exception post) |
|
||||
| S5F11 / S5F12 | E→H | ✅ | catalog | ✅ round-trip (exception clear) |
|
||||
| S5F13 / S5F14 | H→E | ✅ | catalog | ✅ round-trip (exception recover request) |
|
||||
| S5F15 / S5F16 | E→H | ✅ | catalog | ✅ round-trip (exception recover complete) |
|
||||
| S5F17 / S5F18 | H→E | ✅ | catalog | ✅ round-trip (exception recover abort) |
|
||||
| S6F1 / S6F2 | E→H | ✅ | catalog | ✅ round-trip |
|
||||
| S6F11 / S6F12 | E→H | ✅ | catalog | ✅ round-trip + demo |
|
||||
| S6F23 / S6F24 | H→E | ✅ | catalog | ✅ round-trip + demo |
|
||||
|
||||
@@ -725,6 +725,70 @@ messages:
|
||||
builder: s5f12_exception_clear_confirm
|
||||
body: {kind: scalar, item_type: BINARY_BYTE, enum: AlarmAck, param: ack}
|
||||
|
||||
# S5F13 / S5F14 — Exception Recover Request / Acknowledge (E5 §9.5).
|
||||
# Host asks the equipment to execute a specific recovery action from
|
||||
# the EXRECVRA list that the prior S5F9 advertised. EXRECVRA on F13
|
||||
# is a single action string, distinct from the list of allowed actions
|
||||
# on F9. F14 echoes ACKC5 as our AlarmAck byte.
|
||||
- id: S5F13
|
||||
stream: 5
|
||||
function: 13
|
||||
w: true
|
||||
builder: s5f13_exception_recover_request
|
||||
parser: parse_s5f13
|
||||
body:
|
||||
kind: list
|
||||
struct_name: ExceptionRecoverRequest
|
||||
fields:
|
||||
- {name: exid, shape: {kind: scalar, item_type: U4}}
|
||||
- {name: exrecvra, shape: {kind: scalar, item_type: ASCII}}
|
||||
|
||||
- id: S5F14
|
||||
stream: 5
|
||||
function: 14
|
||||
builder: s5f14_exception_recover_ack
|
||||
body: {kind: scalar, item_type: BINARY_BYTE, enum: AlarmAck, param: ack}
|
||||
|
||||
# S5F15 / S5F16 — Exception Recover Complete Notify / Acknowledge
|
||||
# (E5 §9.6). Equipment signals that the recovery action requested by
|
||||
# the matching S5F13 has finished; EXRESULT carries any structured
|
||||
# outcome the equipment wishes to report (we model it as ASCII, which
|
||||
# is the common case — vendor-specific richer shapes are a YAML edit).
|
||||
- id: S5F15
|
||||
stream: 5
|
||||
function: 15
|
||||
w: true
|
||||
builder: s5f15_exception_recover_complete_notify
|
||||
parser: parse_s5f15
|
||||
body:
|
||||
kind: list
|
||||
struct_name: ExceptionRecoverComplete
|
||||
fields:
|
||||
- {name: exid, shape: {kind: scalar, item_type: U4}}
|
||||
- {name: exresult, shape: {kind: scalar, item_type: ASCII}}
|
||||
|
||||
- id: S5F16
|
||||
stream: 5
|
||||
function: 16
|
||||
builder: s5f16_exception_recover_complete_ack
|
||||
body: {kind: scalar, item_type: BINARY_BYTE, enum: AlarmAck, param: ack}
|
||||
|
||||
# S5F17 / S5F18 — Exception Recover Abort Request / Acknowledge
|
||||
# (E5 §9.7). Host aborts a recovery action currently in flight.
|
||||
- id: S5F17
|
||||
stream: 5
|
||||
function: 17
|
||||
w: true
|
||||
builder: s5f17_exception_recover_abort_request
|
||||
parser: parse_s5f17
|
||||
body: {kind: scalar, item_type: U4, param: exid}
|
||||
|
||||
- id: S5F18
|
||||
stream: 5
|
||||
function: 18
|
||||
builder: s5f18_exception_recover_abort_ack
|
||||
body: {kind: scalar, item_type: BINARY_BYTE, enum: AlarmAck, param: ack}
|
||||
|
||||
# =====================================================================
|
||||
# S6 — Data collection
|
||||
# =====================================================================
|
||||
|
||||
@@ -498,6 +498,48 @@ TEST_CASE("S5F11 / S5F12 exception clear round-trip") {
|
||||
CHECK(*ack_byte(s5f12_exception_clear_confirm(AlarmAck::Accept)) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("S5F13 / S5F14 exception recover request round-trip") {
|
||||
auto m = s5f13_exception_recover_request(42, "RETRY");
|
||||
CHECK(m.stream == 5);
|
||||
CHECK(m.function == 13);
|
||||
CHECK(m.reply_expected);
|
||||
|
||||
auto parsed = parse_s5f13(m);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(parsed->exid == 42);
|
||||
CHECK(parsed->exrecvra == "RETRY");
|
||||
|
||||
CHECK(*ack_byte(s5f14_exception_recover_ack(AlarmAck::Accept)) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("S5F15 / S5F16 exception recover complete round-trip") {
|
||||
auto m = s5f15_exception_recover_complete_notify(42, "OK");
|
||||
CHECK(m.stream == 5);
|
||||
CHECK(m.function == 15);
|
||||
CHECK(m.reply_expected);
|
||||
|
||||
auto parsed = parse_s5f15(m);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(parsed->exid == 42);
|
||||
CHECK(parsed->exresult == "OK");
|
||||
|
||||
CHECK(*ack_byte(s5f16_exception_recover_complete_ack(AlarmAck::Accept)) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("S5F17 / S5F18 exception recover abort round-trip") {
|
||||
auto m = s5f17_exception_recover_abort_request(42);
|
||||
CHECK(m.stream == 5);
|
||||
CHECK(m.function == 17);
|
||||
CHECK(m.reply_expected);
|
||||
|
||||
// Scalar U4 parser returns the value directly.
|
||||
auto exid = parse_s5f17(m);
|
||||
REQUIRE(exid.has_value());
|
||||
CHECK(*exid == 42);
|
||||
|
||||
CHECK(*ack_byte(s5f18_exception_recover_abort_ack(AlarmAck::Accept)) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("S7F19 / S7F20 EPPD list") {
|
||||
auto req = s7f19_current_eppd_request();
|
||||
CHECK(req.stream == 7);
|
||||
|
||||
Reference in New Issue
Block a user