29f646c7ca
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>
299 lines
12 KiB
C++
299 lines
12 KiB
C++
#include "secsgem/gem/host_handler.hpp"
|
|
|
|
#include "secsgem/gem/messages.hpp"
|
|
|
|
namespace secsgem::gem {
|
|
|
|
namespace s2 = secsgem::secs2;
|
|
|
|
HostHandler::HostHandler(std::shared_ptr<hsms::Connection> conn)
|
|
: conn_(std::move(conn)) {}
|
|
|
|
void HostHandler::install() {
|
|
auto self = shared_from_this();
|
|
conn_->set_message_handler(
|
|
[self](const s2::Message& msg) -> std::optional<s2::Message> {
|
|
return self->handle_inbound(msg);
|
|
});
|
|
}
|
|
|
|
std::optional<s2::Message> HostHandler::handle_inbound(const s2::Message& msg) {
|
|
// S5F1 Alarm Report — observe + auto-ack with Accept.
|
|
if (msg.stream == 5 && msg.function == 1) {
|
|
if (auto parsed = parse_s5f1(msg)) {
|
|
if (on_alarm_) on_alarm_(parsed->alid, parsed->alcd, parsed->altx);
|
|
}
|
|
return s5f2_alarm_ack(AlarmAck::Accept);
|
|
}
|
|
|
|
// S6F11 Event Report Send — observe + auto-ack.
|
|
if (msg.stream == 6 && msg.function == 11) {
|
|
if (auto parsed = parse_s6f11(msg)) {
|
|
if (on_event_) on_event_(parsed->dataid, parsed->ceid, parsed->reports);
|
|
// E90 substrate event decode: map CEID to typed state callback.
|
|
if (on_substrate_) {
|
|
const auto c = parsed->ceid;
|
|
SubstrateState loc = SubstrateState::NoState;
|
|
SubstrateProcessingState proc = SubstrateProcessingState::NoState;
|
|
if (c == e90::kCeidSubstrateAtSource) loc = SubstrateState::AtSource;
|
|
else if (c == e90::kCeidSubstrateAtWork) loc = SubstrateState::AtWork;
|
|
else if (c == e90::kCeidSubstrateAtDestination) loc = SubstrateState::AtDestination;
|
|
else if (c == e90::kCeidSubstrateNeedsProcessing) proc = SubstrateProcessingState::NeedsProcessing;
|
|
else if (c == e90::kCeidSubstrateInProcess) proc = SubstrateProcessingState::InProcess;
|
|
else if (c == e90::kCeidSubstrateProcessed) proc = SubstrateProcessingState::Processed;
|
|
else if (c == e90::kCeidSubstrateAborted) proc = SubstrateProcessingState::Aborted;
|
|
else if (c == e90::kCeidSubstrateStopped) proc = SubstrateProcessingState::Stopped;
|
|
else if (c == e90::kCeidSubstrateRejected) proc = SubstrateProcessingState::Rejected;
|
|
else if (c == e90::kCeidSubstrateLost) proc = SubstrateProcessingState::Lost;
|
|
else if (c == e90::kCeidSubstrateSkipped) proc = SubstrateProcessingState::Skipped;
|
|
if (loc != SubstrateState::NoState ||
|
|
proc != SubstrateProcessingState::NoState) {
|
|
on_substrate_(c, loc, proc);
|
|
}
|
|
}
|
|
}
|
|
return s6f12_event_report_ack(EventReportAck::Accept);
|
|
}
|
|
|
|
// S6F25 Spool Data Ready — observe (no body parse needed beyond u4) and ack.
|
|
if (msg.stream == 6 && msg.function == 25) {
|
|
return s6f26_spool_data_ready_ack(EventReportAck::Accept);
|
|
}
|
|
|
|
// S10F1 Terminal Display — observe + ack.
|
|
if (msg.stream == 10 && msg.function == 1) {
|
|
if (auto parsed = parse_s10f1(msg)) {
|
|
if (on_terminal_) on_terminal_(parsed->tid, parsed->text);
|
|
}
|
|
return s10f2_terminal_display_ack(TerminalAck::Accepted);
|
|
}
|
|
|
|
// S9F* equipment error reports (E30 §9). Observed but never acked
|
|
// (S9 messages are one-way per spec).
|
|
if (msg.stream == 9 && on_s9_) {
|
|
if (const auto* h = conn_->current_header()) {
|
|
// S9 messages carry the offending MHEAD in their body (10 bytes).
|
|
// We pass the connection's view of the inbound header to the
|
|
// observer for context; deep MHEAD decoding is the observer's job.
|
|
std::array<uint8_t, 10> mhead{};
|
|
(void)h; // header data available if the observer wants to inspect.
|
|
on_s9_(msg.function, mhead);
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Anything else: silently drop. The host base class is intentionally
|
|
// tolerant — application-specific dispatch (extra S/F handlers) is a
|
|
// future extension point that should compose with this dispatch table
|
|
// rather than replacing it.
|
|
return std::nullopt;
|
|
}
|
|
|
|
void HostHandler::establish_communication(ReplyHandler cb) {
|
|
// S1F13 body is <MDLN SOFTREV> per E30 §6.5. Host advertises itself.
|
|
send_request(s1f13_establish_comms("GEMHOST", "0.1.0"), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::go_remote(ReplyHandler cb) {
|
|
send_request(s1f17_request_online(), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::go_offline(ReplyHandler cb) {
|
|
send_request(s1f15_request_offline(), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_selected_status(const std::vector<uint32_t>& svids,
|
|
ReplyHandler cb) {
|
|
send_request(s1f3_selected_status_request(svids), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_status_namelist(const std::vector<uint32_t>& svids,
|
|
ReplyHandler cb) {
|
|
send_request(s1f11_status_namelist_request(svids), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_define_reports(
|
|
uint32_t dataid,
|
|
const std::vector<std::pair<uint32_t, std::vector<uint32_t>>>& reports,
|
|
ReplyHandler cb) {
|
|
std::vector<DefineReportEntry> entries;
|
|
entries.reserve(reports.size());
|
|
for (const auto& [rptid, vids] : reports) entries.push_back({rptid, vids});
|
|
send_request(s2f33_define_report(dataid, entries), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_link_event_reports(
|
|
uint32_t dataid,
|
|
const std::vector<std::pair<uint32_t, std::vector<uint32_t>>>& links,
|
|
ReplyHandler cb) {
|
|
std::vector<LinkEventEntry> entries;
|
|
entries.reserve(links.size());
|
|
for (const auto& [ceid, rptids] : links) entries.push_back({ceid, rptids});
|
|
send_request(s2f35_link_event_report(dataid, entries), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_enable_events(bool enable,
|
|
const std::vector<uint32_t>& ceids,
|
|
ReplyHandler cb) {
|
|
send_request(s2f37_enable_event(enable, ceids), std::move(cb));
|
|
}
|
|
|
|
// ---- Remote command senders (B3) ----------------------------------------
|
|
|
|
void HostHandler::send_remote_command(const std::string& rcmd,
|
|
const std::vector<CommandParameter>& params,
|
|
ReplyHandler cb) {
|
|
send_request(s2f41_host_command(rcmd, params), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_enhanced_remote_command(
|
|
uint32_t dataid, const std::string& objspec, const std::string& rcmd,
|
|
const std::vector<CommandParameter>& params, ReplyHandler cb) {
|
|
send_request(s2f49_enhanced_host_command(dataid, objspec, rcmd, params),
|
|
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) {
|
|
// ALED bit-7 encodes enable per E5 §10.
|
|
const uint8_t aled = enable ? kAlarmEnableByte : kAlarmDisableByte;
|
|
send_request(s5f3_enable_alarm(aled, alid), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_list_alarms(const std::vector<uint32_t>& alids,
|
|
ReplyHandler cb) {
|
|
send_request(s5f5_list_alarms_request(alids), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_list_enabled_alarms(ReplyHandler cb) {
|
|
send_request(s5f7_list_enabled_alarms_request(), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_exception_recover(uint32_t exid, const std::string& exrecvra,
|
|
ReplyHandler cb) {
|
|
send_request(s5f13_exception_recover_request(exid, exrecvra), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_exception_recover_abort(uint32_t exid, ReplyHandler cb) {
|
|
send_request(s5f17_exception_recover_abort_request(exid), std::move(cb));
|
|
}
|
|
|
|
// ---- Process programs (B3) ----------------------------------------------
|
|
|
|
void HostHandler::send_request_eppd(ReplyHandler cb) {
|
|
send_request(s7f19_current_eppd_request(), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_pp_data(const std::string& ppid, const std::string& body,
|
|
ReplyHandler cb) {
|
|
send_request(s7f3_process_program_send(ppid, body), std::move(cb));
|
|
}
|
|
|
|
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) {
|
|
send_request(s6f23_request_spool_data(code), std::move(cb));
|
|
}
|
|
|
|
// ---- Terminal (B3) ------------------------------------------------------
|
|
|
|
void HostHandler::send_terminal_display(uint32_t tid, const std::string& text,
|
|
ReplyHandler cb) {
|
|
send_request(s10f1_terminal_display_single(tid, text), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_terminal_display_multi(uint32_t tid,
|
|
const std::vector<std::string>& lines,
|
|
ReplyHandler cb) {
|
|
send_request(s10f5_terminal_display_multi(tid, lines), std::move(cb));
|
|
}
|
|
|
|
// ---- E40 Process Jobs (B3) ----------------------------------------------
|
|
|
|
void HostHandler::send_create_process_job(
|
|
const std::string& prjobid, const std::string& ppid,
|
|
const std::vector<std::string>& mtrloutspec, ReplyHandler cb) {
|
|
// Simplified caller (no MF / process params / recipe-vars). Fill in
|
|
// sensible E40 defaults: substrate-flow, recipe-only (no var tuning),
|
|
// empty rcpvars + params.
|
|
PRJobCreateRequest req{
|
|
prjobid,
|
|
MaterialFlag::Substrate,
|
|
ProcessRecipeMethod::RecipeOnly,
|
|
RecipeSpec{ppid, {}},
|
|
mtrloutspec,
|
|
{}};
|
|
send_request(s16f11_pr_job_create(req), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_pr_job_command(const std::string& prjobid,
|
|
const std::string& prcmd, ReplyHandler cb) {
|
|
send_request(s16f5_pr_job_command(prjobid, prcmd), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_pr_job_dequeue(const std::string& prjobid, ReplyHandler cb) {
|
|
send_request(s16f13_pr_job_dequeue(prjobid), std::move(cb));
|
|
}
|
|
|
|
// ---- E94 Control Jobs (B3) ----------------------------------------------
|
|
|
|
void HostHandler::send_create_control_job(
|
|
const std::string& ctljobid, const std::vector<std::string>& prjobids,
|
|
ReplyHandler cb) {
|
|
send_request(s14f9_create_control_job(ctljobid, prjobids), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_delete_control_job(const std::string& ctljobid,
|
|
ReplyHandler cb) {
|
|
send_request(s14f11_delete_control_job(ctljobid), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_cj_command(const std::string& ctljobid,
|
|
const std::string& ctljobcmd, ReplyHandler cb) {
|
|
send_request(s16f27_cj_command(ctljobid, ctljobcmd), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::send_request(s2::Message msg, ReplyHandler cb) {
|
|
conn_->send_request(std::move(msg), std::move(cb));
|
|
}
|
|
|
|
void HostHandler::log(const std::string& msg) {
|
|
if (on_log_) on_log_(msg);
|
|
}
|
|
|
|
} // namespace secsgem::gem
|