Files
secs-gem/include/secsgem/gem/host_handler.hpp
T
raphael 63bb0cf933 B1: HostHandler base class
GEM host-side counterpart to the existing equipment server: wraps an
HSMS Connection (Active mode), installs an inbound dispatch table that
auto-acks the messages a host is expected to passively accept, and
exposes the GEM workflow primitives.

Inbound dispatch:
  S5F1  Alarm Report          observe (alarm handler) + S5F2 Accept
  S6F11 Event Report          observe (event handler) + S6F12 Accept
  S6F25 Spool Data Ready      S6F26 Accept (host policy: pull on demand)
  S10F1 Terminal Display      observe + S10F2 Accepted
  S9F*  Equipment errors      observe (s9 handler); no ack (one-way)

Workflow shortcuts:
  establish_communication()   S1F13 -> S1F14
  go_remote()                 S1F17 -> S1F18
  go_offline()                S1F15 -> S1F16

Plus a low-level send_request() escape hatch so the senders coming in
B2/B3 don't have to friend the connection internals.

Drive-by: event_reports.hpp was missing `<optional>` (worked transitively
through the equipment-side include chain but not when included from the
host-side standalone).

secsgem-py has `gem/hosthandler.py`; this mirrors its surface for the
inbound-ack and lifecycle parts.  Outbound senders land in B2/B3.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 22:49:52 +02:00

95 lines
3.8 KiB
C++

#pragma once
#include <array>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <system_error>
#include <vector>
#include "secsgem/gem/store/event_reports.hpp"
#include "secsgem/hsms/connection.hpp"
#include "secsgem/secs2/message.hpp"
// GEM host-side message handler. Wraps an HSMS Connection (Active mode)
// and provides:
//
// - Inbound dispatch with sensible automatic acks for the messages a
// host is expected to accept passively (S5F1 alarms, S6F11 event
// reports, S9F* errors, S10F1 terminal text). Each gets a
// user-settable observer callback.
//
// - High-level workflow shortcuts: establish_communication() (S1F13)
// and go_remote() (S1F17) are the standard GEM host startup pair.
//
// - Convenience senders for the GEM/E40/E94 message surface
// (see host_handler_senders.hpp for the full menu in B2/B3).
//
// Mirrors secsgem-py's `GemHostHandler` in `gem/hosthandler.py`. Equipment-
// side behaviour lives in the existing server app; pulling it into a
// matching `EquipmentHandler` class is a separate refactor.
namespace secsgem::gem {
class HostHandler : public std::enable_shared_from_this<HostHandler> {
public:
using ReplyHandler = std::function<void(std::error_code, const secs2::Message&)>;
using EventHandler = std::function<void(uint32_t dataid, uint32_t ceid,
const std::vector<ReportData>& reports)>;
using AlarmHandler = std::function<void(uint32_t alid, uint8_t alcd,
const std::string& altx)>;
using TerminalHandler = std::function<void(uint32_t tid, const std::string& text)>;
using S9Handler = std::function<void(uint8_t function,
const std::array<uint8_t, 10>& mhead)>;
using LogHandler = std::function<void(const std::string&)>;
explicit HostHandler(std::shared_ptr<hsms::Connection> conn);
// Inbound observers. Each is optional; if unset, the message is acked
// (where required) and otherwise ignored.
void set_event_handler(EventHandler h) { on_event_ = std::move(h); }
void set_alarm_handler(AlarmHandler h) { on_alarm_ = std::move(h); }
void set_terminal_handler(TerminalHandler h) { on_terminal_ = std::move(h); }
void set_s9_handler(S9Handler h) { on_s9_ = std::move(h); }
void set_log_handler(LogHandler h) { on_log_ = std::move(h); }
// Wire the inbound message handler into the underlying Connection.
// Call once before Connection::start() so the dispatch table is live
// when the first inbound primary arrives.
void install();
// ---- Workflow shortcuts ------------------------------------------------
// S1F13/F14 Establish Communication. Resolves `cb` with the equipment's
// S1F14 reply (or a timeout / error).
void establish_communication(ReplyHandler cb);
// S1F17/F18 Request Online. Equipment may reply Accepted / NotAllowed /
// AlreadyOnline; the raw S1F18 message is forwarded to `cb`.
void go_remote(ReplyHandler cb);
// S1F15/F16 Request Offline. Counterpart to go_remote().
void go_offline(ReplyHandler cb);
// ---- Low-level: forward a primary verbatim ----------------------------
void send_request(secs2::Message msg, ReplyHandler cb);
// Access to the underlying connection (sample apps and tests need it).
hsms::Connection& connection() { return *conn_; }
protected:
// Single inbound dispatch entry point — installed on Connection.
std::optional<secs2::Message> handle_inbound(const secs2::Message& msg);
void log(const std::string& msg);
std::shared_ptr<hsms::Connection> conn_;
EventHandler on_event_;
AlarmHandler on_alarm_;
TerminalHandler on_terminal_;
S9Handler on_s9_;
LogHandler on_log_;
};
} // namespace secsgem::gem