HostHandler: senders for the AA tranche messages
tests / build-and-test (push) Failing after 34s

A host couldn't drive the new messages through the HostHandler class —
only the server side knew how to dispatch them.  Adds six new senders
plus a unit test that walks each through a real loopback connection:

  * send_legacy_remote_command  -> S2F21
  * send_event_report_request   -> S6F15
  * send_individual_report_request -> S6F19
  * send_annotated_report_request  -> S6F21
  * send_pp_load_inquire        -> S7F1
  * send_delete_pp              -> S7F17

Suite: 296 cases / 1571 assertions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 01:20:23 +02:00
parent 7c28e2589c
commit 29f646c7ca
3 changed files with 106 additions and 0 deletions
+22
View File
@@ -124,6 +124,20 @@ class HostHandler : public std::enable_shared_from_this<HostHandler> {
const std::vector<CommandParameter>& params,
ReplyHandler cb);
// S2F21/F22 legacy Remote Command (no parameter list, single ack byte).
void send_legacy_remote_command(const std::string& rcmd, ReplyHandler cb);
// ---- Host-initiated event/report queries (B3) -------------------------
// S6F15/F16: pull the current event-report payload for a given CEID.
void send_event_report_request(uint32_t ceid, ReplyHandler cb);
// S6F19/F20: pull a single RPTID's current values (no annotation).
void send_individual_report_request(uint32_t rptid, ReplyHandler cb);
// S6F21/F22: pull a single RPTID's values with (VID, V) annotation.
void send_annotated_report_request(uint32_t rptid, ReplyHandler cb);
// ---- Alarm management (B3) --------------------------------------------
// S5F3/F4 Enable/Disable Alarm. Spec uses ALED bit-7 to encode enable.
@@ -144,6 +158,14 @@ class HostHandler : public std::enable_shared_from_this<HostHandler> {
ReplyHandler cb); // S7F3/F4
void send_request_pp(const std::string& ppid, ReplyHandler cb); // S7F5/F6
// S7F1/F2 Process Program Load Inquire: announce a forthcoming S7F3
// of `length` bytes; equipment grants or denies.
void send_pp_load_inquire(const std::string& ppid, uint32_t length,
ReplyHandler cb);
// S7F17/F18 Delete Process Program: empty list = delete all.
void send_delete_pp(const std::vector<std::string>& ppids, ReplyHandler cb);
// ---- Spool (B3) -------------------------------------------------------
void send_request_spool(SpoolRequestCode code, ReplyHandler cb); // S6F23/F24
+29
View File
@@ -153,6 +153,25 @@ void HostHandler::send_enhanced_remote_command(
std::move(cb));
}
void HostHandler::send_legacy_remote_command(const std::string& rcmd,
ReplyHandler cb) {
send_request(s2f21_remote_command(rcmd), std::move(cb));
}
// ---- Host-initiated event/report queries --------------------------------
void HostHandler::send_event_report_request(uint32_t ceid, ReplyHandler cb) {
send_request(s6f15_event_report_request(ceid), std::move(cb));
}
void HostHandler::send_individual_report_request(uint32_t rptid, ReplyHandler cb) {
send_request(s6f19_individual_report_request(rptid), std::move(cb));
}
void HostHandler::send_annotated_report_request(uint32_t rptid, ReplyHandler cb) {
send_request(s6f21_annotated_report_request(rptid), std::move(cb));
}
// ---- Alarm management (B3) ----------------------------------------------
void HostHandler::send_enable_alarm(uint32_t alid, bool enable, ReplyHandler cb) {
@@ -194,6 +213,16 @@ void HostHandler::send_request_pp(const std::string& ppid, ReplyHandler cb) {
send_request(s7f5_process_program_request(ppid), std::move(cb));
}
void HostHandler::send_pp_load_inquire(const std::string& ppid, uint32_t length,
ReplyHandler cb) {
send_request(s7f1_pp_load_inquire(ppid, length), std::move(cb));
}
void HostHandler::send_delete_pp(const std::vector<std::string>& ppids,
ReplyHandler cb) {
send_request(s7f17_delete_pp_send(ppids), std::move(cb));
}
// ---- Spool (B3) ---------------------------------------------------------
void HostHandler::send_request_spool(SpoolRequestCode code, ReplyHandler cb) {
+55
View File
@@ -100,6 +100,20 @@ struct Recorder {
return gem::s16f6_pr_job_command_ack(gem::HostCmdAck::Accept);
if (msg.stream == 14 && msg.function == 9)
return gem::s14f10_create_control_job_ack("CJ-1", gem::ObjectAck::Success);
// New senders (Task 18): provide canned replies so the host's
// send_*_request transactions complete instead of timing out.
if (msg.stream == 2 && msg.function == 21)
return gem::s2f22_remote_command_ack(gem::HostCmdAck::Accept);
if (msg.stream == 6 && msg.function == 15)
return gem::s6f16_event_report_data({0, 300, {}});
if (msg.stream == 6 && msg.function == 19)
return gem::s6f20_individual_report_data({});
if (msg.stream == 6 && msg.function == 21)
return gem::s6f22_annotated_report_data({});
if (msg.stream == 7 && msg.function == 1)
return gem::s7f2_pp_load_grant(gem::ProcessProgramAck::Accept);
if (msg.stream == 7 && msg.function == 17)
return gem::s7f18_delete_pp_ack(gem::ProcessProgramAck::Accept);
// Unhandled — leave the host to time out on T3.
return std::nullopt;
};
@@ -254,6 +268,47 @@ TEST_CASE("HostHandler: E40/E94 job creation senders produce correct wire") {
pump_until(w.io, [&] { return cmd_ack; });
}
TEST_CASE("HostHandler: new senders produce correct wire shapes") {
// Wires up the host + equipment, installs the handler, fires each
// new sender, and verifies the (stream, function) of the request
// the equipment received. The equipment auto-acks each via Recorder.
Wired w;
Recorder rec;
w.equipment->set_message_handler(rec.handler());
w.handler->install();
w.host->start();
w.equipment->start();
bool selected = false;
w.host->set_selected_handler([&] { selected = true; });
pump_until(w.io, [&] { return selected; });
auto run_one = [&](auto fire, uint8_t want_stream, uint8_t want_fn) {
bool done = false;
fire([&](std::error_code ec, const s2::Message&) {
REQUIRE_FALSE(ec);
done = true;
});
pump_until(w.io, [&] { return done; });
REQUIRE_FALSE(rec.last.empty());
CHECK(rec.last.back().stream == want_stream);
CHECK(rec.last.back().function == want_fn);
};
run_one([&](auto cb) { w.handler->send_legacy_remote_command("PAUSE", cb); },
2, 21);
run_one([&](auto cb) { w.handler->send_event_report_request(300, cb); }, 6, 15);
run_one([&](auto cb) { w.handler->send_individual_report_request(1, cb); },
6, 19);
run_one([&](auto cb) { w.handler->send_annotated_report_request(1, cb); },
6, 21);
run_one([&](auto cb) {
w.handler->send_pp_load_inquire("RECIPE-X", 4096, cb);
}, 7, 1);
run_one([&](auto cb) {
w.handler->send_delete_pp({"RECIPE-X", "RECIPE-Y"}, cb);
}, 7, 17);
}
TEST_CASE("HostHandler: inbound S5F1 alarm auto-acks with S5F2") {
Wired w;
// Equipment side stays silent on inbound; we only need the host to