From 777fa5e9f994f1faa29d67b8a4670ed05a7a00ef Mon Sep 17 00:00:00 2001 From: Raphael Maenle Date: Mon, 8 Jun 2026 01:23:11 +0200 Subject: [PATCH] E3: Host-side E90 substrate event observer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 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 --- include/secsgem/gem/host_handler.hpp | 22 +++++++++++++++++----- src/gem/host_handler.cpp | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/include/secsgem/gem/host_handler.hpp b/include/secsgem/gem/host_handler.hpp index 3cf4c22..7b9e9c7 100644 --- a/include/secsgem/gem/host_handler.hpp +++ b/include/secsgem/gem/host_handler.hpp @@ -8,9 +8,11 @@ #include #include +#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 { using S9Handler = std::function& mhead)>; using LogHandler = std::function; + // 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; explicit HostHandler(std::shared_ptr conn); @@ -55,6 +63,9 @@ class HostHandler : public std::enable_shared_from_this { 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 { void log(const std::string& msg); std::shared_ptr 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 diff --git a/src/gem/host_handler.cpp b/src/gem/host_handler.cpp index 5e83a30..d5c644b 100644 --- a/src/gem/host_handler.cpp +++ b/src/gem/host_handler.cpp @@ -30,6 +30,27 @@ std::optional 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); }