E1: SubstrateStateMachine + SubstrateStore (E90 §6)
Per-substrate dual FSM with two orthogonal axes:
Location (STS):
AtSource -> AtWork (Acquire) -> AtDestination (Release)
AtWork -> AtSource (Return; processing aborted before completion)
Processing:
NeedsProcessing -> InProcess (Start) -> Processed (End)
InProcess -> {Aborted, Stopped, Rejected, Lost} terminal
NeedsProcessing -> {Skipped, Lost} terminal
Wire-byte values pinned via static_assert to E90-0716 §10.3.
SubstrateStore mirrors the CarrierStore pattern: non-movable, per-row
SubstrateStateMachine heap-allocated with handlers dispatching through
the store's location/processing callbacks; fire_location_event accepts
an optional new_location string so the application can carry
equipment-specific module names alongside the FSM state.
Joins EquipmentDataModel alongside carriers / load_ports. 9 test
cases cover initial state, full location lifecycle, all five
processing exits, and store-level dual-axis observer firing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
#include "secsgem/gem/substrate_state.hpp"
|
||||
|
||||
namespace secsgem::gem {
|
||||
|
||||
static_assert(static_cast<uint8_t>(SubstrateState::AtSource) == 0);
|
||||
static_assert(static_cast<uint8_t>(SubstrateState::AtWork) == 1);
|
||||
static_assert(static_cast<uint8_t>(SubstrateState::AtDestination) == 2);
|
||||
static_assert(static_cast<uint8_t>(SubstrateProcessingState::NeedsProcessing) == 0);
|
||||
static_assert(static_cast<uint8_t>(SubstrateProcessingState::InProcess) == 1);
|
||||
static_assert(static_cast<uint8_t>(SubstrateProcessingState::Processed) == 2);
|
||||
static_assert(static_cast<uint8_t>(SubstrateProcessingState::Aborted) == 3);
|
||||
static_assert(static_cast<uint8_t>(SubstrateProcessingState::Stopped) == 4);
|
||||
static_assert(static_cast<uint8_t>(SubstrateProcessingState::Rejected) == 5);
|
||||
static_assert(static_cast<uint8_t>(SubstrateProcessingState::Lost) == 6);
|
||||
static_assert(static_cast<uint8_t>(SubstrateProcessingState::Skipped) == 7);
|
||||
|
||||
const char* substrate_state_name(SubstrateState s) {
|
||||
switch (s) {
|
||||
case SubstrateState::AtSource: return "AtSource";
|
||||
case SubstrateState::AtWork: return "AtWork";
|
||||
case SubstrateState::AtDestination: return "AtDestination";
|
||||
case SubstrateState::NoState: return "NoState";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
std::optional<SubstrateState> parse_substrate_state(const std::string& s) {
|
||||
if (s == "AtSource") return SubstrateState::AtSource;
|
||||
if (s == "AtWork") return SubstrateState::AtWork;
|
||||
if (s == "AtDestination") return SubstrateState::AtDestination;
|
||||
if (s == "NoState") return SubstrateState::NoState;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const char* substrate_processing_state_name(SubstrateProcessingState s) {
|
||||
switch (s) {
|
||||
case SubstrateProcessingState::NeedsProcessing: return "NeedsProcessing";
|
||||
case SubstrateProcessingState::InProcess: return "InProcess";
|
||||
case SubstrateProcessingState::Processed: return "Processed";
|
||||
case SubstrateProcessingState::Aborted: return "Aborted";
|
||||
case SubstrateProcessingState::Stopped: return "Stopped";
|
||||
case SubstrateProcessingState::Rejected: return "Rejected";
|
||||
case SubstrateProcessingState::Lost: return "Lost";
|
||||
case SubstrateProcessingState::Skipped: return "Skipped";
|
||||
case SubstrateProcessingState::NoState: return "NoState";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
const char* substrate_event_name(SubstrateEvent e) {
|
||||
switch (e) {
|
||||
case SubstrateEvent::Acquire: return "Acquire";
|
||||
case SubstrateEvent::Release: return "Release";
|
||||
case SubstrateEvent::Return: return "Return";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
const char* substrate_processing_event_name(SubstrateProcessingEvent e) {
|
||||
switch (e) {
|
||||
case SubstrateProcessingEvent::StartProcessing: return "StartProcessing";
|
||||
case SubstrateProcessingEvent::EndProcessing: return "EndProcessing";
|
||||
case SubstrateProcessingEvent::Abort: return "Abort";
|
||||
case SubstrateProcessingEvent::Stop: return "Stop";
|
||||
case SubstrateProcessingEvent::Reject: return "Reject";
|
||||
case SubstrateProcessingEvent::ReportLost: return "ReportLost";
|
||||
case SubstrateProcessingEvent::Skip: return "Skip";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
SubstrateTable default_substrate_table() {
|
||||
using S = SubstrateState;
|
||||
using E = SubstrateEvent;
|
||||
SubstrateTable t;
|
||||
t.add({S::AtSource, E::Acquire, S::AtWork, std::nullopt});
|
||||
t.add({S::AtWork, E::Release, S::AtDestination, std::nullopt});
|
||||
t.add({S::AtWork, E::Return, S::AtSource, std::nullopt});
|
||||
return t;
|
||||
}
|
||||
|
||||
SubstrateProcessingTable default_substrate_processing_table() {
|
||||
using S = SubstrateProcessingState;
|
||||
using E = SubstrateProcessingEvent;
|
||||
SubstrateProcessingTable t;
|
||||
// From NeedsProcessing.
|
||||
t.add({S::NeedsProcessing, E::StartProcessing, S::InProcess, std::nullopt});
|
||||
t.add({S::NeedsProcessing, E::Skip, S::Skipped, std::nullopt});
|
||||
t.add({S::NeedsProcessing, E::ReportLost, S::Lost, std::nullopt});
|
||||
// From InProcess.
|
||||
t.add({S::InProcess, E::EndProcessing, S::Processed, std::nullopt});
|
||||
t.add({S::InProcess, E::Abort, S::Aborted, std::nullopt});
|
||||
t.add({S::InProcess, E::Stop, S::Stopped, std::nullopt});
|
||||
t.add({S::InProcess, E::Reject, S::Rejected, std::nullopt});
|
||||
t.add({S::InProcess, E::ReportLost, S::Lost, std::nullopt});
|
||||
// Processed/Aborted/etc are terminal.
|
||||
return t;
|
||||
}
|
||||
|
||||
SubstrateStateMachine::SubstrateStateMachine()
|
||||
: loc_table_(default_substrate_table()),
|
||||
proc_table_(default_substrate_processing_table()) {}
|
||||
|
||||
bool SubstrateStateMachine::on_location_event(SubstrateEvent e) {
|
||||
const auto* row = loc_table_.find(loc_, e);
|
||||
if (!row) return false;
|
||||
if (row->to && *row->to != loc_) {
|
||||
auto prev = loc_;
|
||||
loc_ = *row->to;
|
||||
if (on_loc_) on_loc_(prev, loc_, e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SubstrateStateMachine::on_processing_event(SubstrateProcessingEvent e) {
|
||||
const auto* row = proc_table_.find(proc_, e);
|
||||
if (!row) return false;
|
||||
if (row->to && *row->to != proc_) {
|
||||
auto prev = proc_;
|
||||
proc_ = *row->to;
|
||||
if (on_proc_) on_proc_(prev, proc_, e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace secsgem::gem
|
||||
Reference in New Issue
Block a user