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:
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user