82fac6fd17
Per-module process-tracking state machine. An E157 instance models a
single recipe step at a single module, with the canonical lifecycle:
NotExecuting -> GeneralExecuting (StartGeneral)
-> StepExecuting (StartStep)
-> StepCompleted (CompleteStep)
Plus universal escape hatches: Reset returns any state to
NotExecuting; Abort terminates from any state to StepCompleted.
ModuleStore wraps the FSM with the now-standard pattern:
- non-movable (this-capture lambdas)
- per-module bind() carries current_substid + recipe_step
- fire(module_id, event) delegates to the FSM
- set_state_change_handler observes every transition with module_id
Joins EquipmentDataModel. 5 test cases cover happy path, Reset from
each interior state, Abort, store-level create dedup + bind, and the
multi-module change handler keying.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
87 lines
2.5 KiB
C++
87 lines
2.5 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "secsgem/gem/module_state.hpp"
|
|
|
|
namespace secsgem::gem {
|
|
|
|
struct Module {
|
|
std::string module_id;
|
|
std::string recipe_step; // RCPSPEC the step is executing
|
|
std::string current_substid; // SUBSTID being processed; empty when idle
|
|
std::unique_ptr<ModuleStateMachine> fsm;
|
|
};
|
|
|
|
class ModuleStore {
|
|
public:
|
|
using StateChangeHandler =
|
|
std::function<void(const std::string& module_id, ModuleState from,
|
|
ModuleState to, ModuleEvent)>;
|
|
|
|
ModuleStore() = default;
|
|
ModuleStore(const ModuleStore&) = delete;
|
|
ModuleStore& operator=(const ModuleStore&) = delete;
|
|
ModuleStore(ModuleStore&&) = delete;
|
|
ModuleStore& operator=(ModuleStore&&) = delete;
|
|
|
|
void set_state_change_handler(StateChangeHandler h) { on_change_ = std::move(h); }
|
|
|
|
bool create(std::string module_id) {
|
|
if (mods_.count(module_id)) return false;
|
|
auto fsm = std::make_unique<ModuleStateMachine>();
|
|
const std::string id = module_id;
|
|
fsm->set_state_change_handler(
|
|
[this, id](ModuleState f, ModuleState t, ModuleEvent e) {
|
|
if (on_change_) on_change_(id, f, t, e);
|
|
});
|
|
mods_.emplace(module_id, Module{module_id, "", "", std::move(fsm)});
|
|
return true;
|
|
}
|
|
|
|
bool has(const std::string& mid) const { return mods_.count(mid) > 0; }
|
|
const Module* get(const std::string& mid) const {
|
|
auto it = mods_.find(mid);
|
|
return it == mods_.end() ? nullptr : &it->second;
|
|
}
|
|
Module* get(const std::string& mid) {
|
|
auto it = mods_.find(mid);
|
|
return it == mods_.end() ? nullptr : &it->second;
|
|
}
|
|
|
|
// Bind a substrate + recipe step to the module for the next step
|
|
// (typically called immediately before StartGeneral).
|
|
bool bind(const std::string& mid, std::string substid, std::string recipe_step) {
|
|
auto* m = get(mid);
|
|
if (!m) return false;
|
|
m->current_substid = std::move(substid);
|
|
m->recipe_step = std::move(recipe_step);
|
|
return true;
|
|
}
|
|
|
|
bool fire(const std::string& mid, ModuleEvent e) {
|
|
auto* m = get(mid);
|
|
return m && m->fsm->on_event(e);
|
|
}
|
|
|
|
std::size_t size() const { return mods_.size(); }
|
|
std::vector<std::string> ids() const {
|
|
std::vector<std::string> out;
|
|
out.reserve(mods_.size());
|
|
for (const auto& kv : mods_) out.push_back(kv.first);
|
|
return out;
|
|
}
|
|
|
|
private:
|
|
std::map<std::string, Module> mods_;
|
|
StateChangeHandler on_change_;
|
|
};
|
|
|
|
} // namespace secsgem::gem
|