H1: ModuleStateMachine + ModuleStore (E157 §6)
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>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
#include "secsgem/gem/module_state.hpp"
|
||||
|
||||
namespace secsgem::gem {
|
||||
|
||||
static_assert(static_cast<uint8_t>(ModuleState::NotExecuting) == 0);
|
||||
static_assert(static_cast<uint8_t>(ModuleState::GeneralExecuting) == 1);
|
||||
static_assert(static_cast<uint8_t>(ModuleState::StepExecuting) == 2);
|
||||
static_assert(static_cast<uint8_t>(ModuleState::StepCompleted) == 3);
|
||||
|
||||
const char* module_state_name(ModuleState s) {
|
||||
switch (s) {
|
||||
case ModuleState::NotExecuting: return "NotExecuting";
|
||||
case ModuleState::GeneralExecuting: return "GeneralExecuting";
|
||||
case ModuleState::StepExecuting: return "StepExecuting";
|
||||
case ModuleState::StepCompleted: return "StepCompleted";
|
||||
case ModuleState::NoState: return "NoState";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
std::optional<ModuleState> parse_module_state(const std::string& s) {
|
||||
if (s == "NotExecuting") return ModuleState::NotExecuting;
|
||||
if (s == "GeneralExecuting") return ModuleState::GeneralExecuting;
|
||||
if (s == "StepExecuting") return ModuleState::StepExecuting;
|
||||
if (s == "StepCompleted") return ModuleState::StepCompleted;
|
||||
if (s == "NoState") return ModuleState::NoState;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const char* module_event_name(ModuleEvent e) {
|
||||
switch (e) {
|
||||
case ModuleEvent::StartGeneral: return "StartGeneral";
|
||||
case ModuleEvent::StartStep: return "StartStep";
|
||||
case ModuleEvent::CompleteStep: return "CompleteStep";
|
||||
case ModuleEvent::Reset: return "Reset";
|
||||
case ModuleEvent::Abort: return "Abort";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
ModuleTable default_module_table() {
|
||||
using S = ModuleState;
|
||||
using E = ModuleEvent;
|
||||
ModuleTable t;
|
||||
t.add({S::NotExecuting, E::StartGeneral, S::GeneralExecuting, std::nullopt});
|
||||
t.add({S::GeneralExecuting, E::StartStep, S::StepExecuting, std::nullopt});
|
||||
t.add({S::StepExecuting, E::CompleteStep, S::StepCompleted, std::nullopt});
|
||||
|
||||
// Reset and Abort are universal escape hatches.
|
||||
for (auto src : {S::NotExecuting, S::GeneralExecuting,
|
||||
S::StepExecuting, S::StepCompleted}) {
|
||||
t.add({src, E::Reset, S::NotExecuting, std::nullopt});
|
||||
t.add({src, E::Abort, S::StepCompleted, std::nullopt});
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
ModuleStateMachine::ModuleStateMachine() : table_(default_module_table()) {}
|
||||
|
||||
bool ModuleStateMachine::on_event(ModuleEvent e) {
|
||||
const auto* row = table_.find(state_, e);
|
||||
if (!row) return false;
|
||||
if (row->to && *row->to != state_) {
|
||||
auto prev = state_;
|
||||
state_ = *row->to;
|
||||
if (on_change_) on_change_(prev, state_, e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace secsgem::gem
|
||||
Reference in New Issue
Block a user