D2: E87 wire messages (S3F17/F18 + F23/F24 + F25/F26 + F27/F28)

Adds the Carrier Management wire surface that drives the FSMs from D1:

  S3F17 / S3F18  CarrierAction (host issues ProceedWithCarrier /
                 CancelCarrier / BindCarrierID / etc.; CommandParameter
                 list is reused from S2F41).
  S3F23 / S3F24  PortGroupChangeReport (equipment notifies host of port
                 group composition changes).
  S3F25 / S3F26  CarrierTransfer (host instructs source -> target port
                 transfer).
  S3F27 / S3F28  CancelCarrier (host cancels an outstanding carrier op).

Two new ack enums in messages_helpers.hpp:
  CarrierActionAck — CAACK byte; covers the common error responses
                     (CarrierIDUnknown, Inaccessible, ActionInProgress).
  PortGroupAck     — PGACK byte (Accept / Error).

Round-trip tests for all four pairs.  Server-side dispatch + the
CarrierStore + LoadPortStore that the FSMs key into land in D3/D4.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 00:11:35 +02:00
parent 94c26c0771
commit 5f1444c756
3 changed files with 174 additions and 0 deletions
+52
View File
@@ -737,3 +737,55 @@ TEST_CASE("S12F19 Map Error Send round-trip") {
CHECK(parsed->errtext == "unrecognized MID");
CHECK_FALSE(m.reply_expected);
}
// ---- S3 E87 Carrier Management -----------------------------------------
TEST_CASE("S3F17 / S3F18 CarrierAction round-trip") {
std::vector<CommandParameter> params{
{"PORTID", s2::Item::u1(uint8_t{1})},
};
auto m = s3f17_carrier_action(/*dataid=*/3, "ProceedWithCarrier",
"CAR-001", params);
CHECK(m.stream == 3);
CHECK(m.function == 17);
CHECK(m.reply_expected);
auto parsed = parse_s3f17(m);
REQUIRE(parsed.has_value());
CHECK(parsed->dataid == 3);
CHECK(parsed->carrieraction == "ProceedWithCarrier");
CHECK(parsed->carrierid == "CAR-001");
REQUIRE(parsed->params.size() == 1);
CHECK(parsed->params[0].name == "PORTID");
CHECK(*ack_byte(s3f18_carrier_action_ack(CarrierActionAck::Accept)) == 0);
CHECK(*ack_byte(s3f18_carrier_action_ack(CarrierActionAck::CarrierIDUnknown)) == 3);
}
TEST_CASE("S3F23 / S3F24 PortGroupChangeReport round-trip") {
auto m = s3f23_port_group_change_report("GROUP-A", {1, 2, 3});
auto parsed = parse_s3f23(m);
REQUIRE(parsed.has_value());
CHECK(parsed->ctngrpid == "GROUP-A");
REQUIRE(parsed->portids.size() == 3);
CHECK(parsed->portids[1] == 2);
CHECK(*ack_byte(s3f24_port_group_change_ack(PortGroupAck::Accept)) == 0);
}
TEST_CASE("S3F25 / S3F26 CarrierTransfer round-trip") {
auto m = s3f25_carrier_transfer("CAR-001", /*source=*/1, /*target=*/2);
auto parsed = parse_s3f25(m);
REQUIRE(parsed.has_value());
CHECK(parsed->carrierid == "CAR-001");
CHECK(parsed->source_portid == 1);
CHECK(parsed->target_portid == 2);
CHECK(*ack_byte(s3f26_carrier_transfer_ack(CarrierActionAck::Accept)) == 0);
}
TEST_CASE("S3F27 / S3F28 CancelCarrier round-trip") {
auto m = s3f27_cancel_carrier("CAR-001");
auto parsed = parse_s3f27(m);
REQUIRE(parsed.has_value());
CHECK(*parsed == "CAR-001");
CHECK(*ack_byte(s3f28_cancel_carrier_ack(CarrierActionAck::Accept)) == 0);
}