feat(daemon): Phase D — GEM300 in-the-loop (jobs, recipes, EC changes)
Semantics settled and documented: v1 is observe-and-report. The engine keeps acking S16/S3/S7/S2F15 from its FSM tables — exactly the behaviour both reference implementations validated — while the tool observes lifecycle events on the Subscribe stream and reports physical progress back. Gating stays the documented v2 deferred-reply item. Engine: two new store observers (HandlerSlot pattern) — RecipeStore fires (ppid, body) after an add (S7F3 downloads), EquipmentConstantStore fires (id, value) on ACCEPTED S2F15 writes only. Unit-tested. Daemon: the service registers PJ/recipe/EC observers (io thread; add_ observers coexist with register_default_handlers' primaries) and fans the new HostRequest variants out via push_request (fire-and-forget, no- buffering contract). ProcessJob carries action (Start->START, Resume-> RESUME, Paused->PAUSE, Stopping->STOP, Aborting->ABORT) + recipe + material bindings read store-side on the io thread. ReportProcessJob maps SETTING_UP ->SetupComplete, COMPLETE->ProcessComplete, ABORTED->AbortComplete via read_sync; PROCESSING is informational; unknown job => INVALID_OBJECT, table-rejected transition => CANNOT_DO_NOW. Carriers deferred (CarrierStore has no observer machinery; ReportCarrier stays UNIMPLEMENTED) — roadmap. Python client: on_process_job / on_recipe / on_constant_change decorators + report_job(job_id, state); ProcessJob dataclass exported. Tests: daemon suite 141 -> 175 assertions — the full in-process loop (S16F11 create -> tool setup -> S16F5 PJSTART -> stream ProcessJob with recipe+carriers -> ReportProcessJob(COMPLETE) -> FSM at ProcessComplete), rejection paths, S7F3 -> ProcessProgram, S2F15 -> ConstantChange with the configured name. Core 475/3097 (observer units). Live regression: daemon interop 20 checks + pyclient 13 checks still green against the running daemon. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
@@ -9,6 +10,7 @@
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "secsgem/gem/handler_slot.hpp"
|
||||
#include "secsgem/secs2/item.hpp"
|
||||
|
||||
namespace secsgem::gem {
|
||||
@@ -59,10 +61,18 @@ class EquipmentConstantStore {
|
||||
if (it == by_id_.end()) return EquipmentAck::Denied_UnknownEcid;
|
||||
if (!in_range(it->second, value)) return EquipmentAck::Denied_OutOfRange;
|
||||
it->second.value = std::move(value);
|
||||
if (on_changed_) on_changed_(id, it->second.value);
|
||||
return EquipmentAck::Accept;
|
||||
}
|
||||
|
||||
// Observe ACCEPTED host writes (S2F15): fires after the value is stored.
|
||||
// The tool reacts to process-parameter tuning; rejected writes don't fire.
|
||||
using ChangedHandler = std::function<void(uint32_t, const s2::Item&)>;
|
||||
void add_changed_handler(ChangedHandler h) { on_changed_.add(std::move(h)); }
|
||||
|
||||
private:
|
||||
HandlerSlot<uint32_t, const s2::Item&> on_changed_;
|
||||
|
||||
// For numeric formats, parse min_str / max_str as integers / doubles and
|
||||
// compare against the first value of the array (we don't support setting
|
||||
// multi-element ECs in this implementation).
|
||||
|
||||
Reference in New Issue
Block a user