E3: Host-side E90 substrate event observer

Adds a typed substrate-event callback to HostHandler that decodes the
canonical E90 CEIDs from incoming S6F11 messages into the matching
SubstrateState / SubstrateProcessingState enum values.  Host
applications now get strongly-typed substrate observability without
having to maintain their own CEID-to-state lookup.

  using SubstrateEventHandler =
      std::function<void(uint32_t ceid, SubstrateState location,
                         SubstrateProcessingState processing)>;

  void set_substrate_event_handler(SubstrateEventHandler);

Axes not addressed by a given CEID stay at NoState — the handler
distinguishes "this CEID updates the location axis" from "this CEID
updates the processing axis" so the host can keep its own per-
substrate FSM in sync.

Closes Tranche E — E90 Substrate Tracking end-to-end (FSM + Store +
CEIDs + server emission + host observer).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 01:23:11 +02:00
parent 511950aa4a
commit 777fa5e9f9
2 changed files with 38 additions and 5 deletions
+17 -5
View File
@@ -8,9 +8,11 @@
#include <system_error>
#include <vector>
#include "secsgem/gem/e90_constants.hpp"
#include "secsgem/gem/store/event_reports.hpp"
#include "secsgem/gem/store/host_commands.hpp" // CommandParameter
#include "secsgem/gem/store/spool.hpp" // SpoolRequestCode
#include "secsgem/gem/substrate_state.hpp" // SubstrateState, SubstrateProcessingState
#include "secsgem/hsms/connection.hpp"
#include "secsgem/secs2/item.hpp"
#include "secsgem/secs2/message.hpp"
@@ -45,6 +47,12 @@ class HostHandler : public std::enable_shared_from_this<HostHandler> {
using S9Handler = std::function<void(uint8_t function,
const std::array<uint8_t, 10>& mhead)>;
using LogHandler = std::function<void(const std::string&)>;
// E90 substrate event observer. Triggered when an inbound S6F11
// carries a CEID in the e90:: kCeid* range; the handler receives a
// best-effort decode (NoState for axes the CEID doesn't update).
using SubstrateEventHandler =
std::function<void(uint32_t ceid, SubstrateState location,
SubstrateProcessingState processing)>;
explicit HostHandler(std::shared_ptr<hsms::Connection> conn);
@@ -55,6 +63,9 @@ class HostHandler : public std::enable_shared_from_this<HostHandler> {
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); }
void set_substrate_event_handler(SubstrateEventHandler h) {
on_substrate_ = std::move(h);
}
// Wire the inbound message handler into the underlying Connection.
// Call once before Connection::start() so the dispatch table is live
@@ -176,11 +187,12 @@ class HostHandler : public std::enable_shared_from_this<HostHandler> {
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_;
EventHandler on_event_;
AlarmHandler on_alarm_;
TerminalHandler on_terminal_;
S9Handler on_s9_;
LogHandler on_log_;
SubstrateEventHandler on_substrate_;
};
} // namespace secsgem::gem
+21
View File
@@ -30,6 +30,27 @@ std::optional<s2::Message> HostHandler::handle_inbound(const s2::Message& msg) {
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);
}