K2: SubstrateIDStatus (third E90 axis)

Adds the substrate-ID verification FSM that E90 §6.4.6 calls for:

  NotConfirmed   initial; equipment hasn't read the ID yet
  WaitingForHost ID has been read; awaiting host accept/reject
  Confirmed      host confirmed (or force-bound)
  Mismatched     host rejected — recoverable via Bind

Events:
  Read     NotConfirmed -> WaitingForHost
  Confirm  WaitingForHost -> Confirmed
  Mismatch WaitingForHost -> Mismatched
  Bind     any -> Confirmed (force-bind)
  Reset    any -> NotConfirmed

Wire-byte values pinned via static_assert.  The third axis is now
exposed on SubstrateStateMachine alongside location_state() and
processing_state(); set_id_handler() observes transitions.  Existing
two-axis API is unchanged.

4 new test cases cover the happy path, Mismatch+Bind recovery, Reset
from any state, and same-state event handler suppression.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 08:46:10 +02:00
parent a28a8b5982
commit a52d44ade5
3 changed files with 134 additions and 1 deletions
+34
View File
@@ -68,13 +68,38 @@ enum class SubstrateProcessingEvent {
const char* substrate_processing_event_name(SubstrateProcessingEvent e);
// Substrate ID Status (E90 §6.4.6) — the third independent axis.
// Captures whether the equipment has read & confirmed the wafer's ID.
enum class SubstrateIDStatus : uint8_t {
NotConfirmed = 0,
WaitingForHost = 1, // ID read; awaiting host confirmation
Confirmed = 2,
Mismatched = 3,
NoState = 255,
};
const char* substrate_id_status_name(SubstrateIDStatus s);
enum class SubstrateIDEvent {
Read, // NotConfirmed -> WaitingForHost (equipment scanned ID)
Confirm, // WaitingForHost -> Confirmed (host accepted)
Mismatch, // WaitingForHost -> Mismatched (host rejected)
Bind, // any -> Confirmed (host force-bind)
Reset, // any -> NotConfirmed
};
const char* substrate_id_event_name(SubstrateIDEvent e);
using SubstrateTable =
CarrierTransitionTable<SubstrateState, SubstrateEvent>;
using SubstrateProcessingTable =
CarrierTransitionTable<SubstrateProcessingState, SubstrateProcessingEvent>;
using SubstrateIDTable =
CarrierTransitionTable<SubstrateIDStatus, SubstrateIDEvent>;
SubstrateTable default_substrate_table();
SubstrateProcessingTable default_substrate_processing_table();
SubstrateIDTable default_substrate_id_table();
class SubstrateStateMachine {
public:
@@ -84,25 +109,34 @@ class SubstrateStateMachine {
std::function<void(SubstrateProcessingState from,
SubstrateProcessingState to,
SubstrateProcessingEvent)>;
using IDChangeHandler =
std::function<void(SubstrateIDStatus from, SubstrateIDStatus to,
SubstrateIDEvent)>;
SubstrateStateMachine();
SubstrateState location_state() const { return loc_; }
SubstrateProcessingState processing_state() const { return proc_; }
SubstrateIDStatus id_state() const { return id_; }
void set_location_handler(LocationChangeHandler h) { on_loc_ = std::move(h); }
void set_processing_handler(ProcessingChangeHandler h) { on_proc_ = std::move(h); }
void set_id_handler(IDChangeHandler h) { on_id_ = std::move(h); }
bool on_location_event(SubstrateEvent e);
bool on_processing_event(SubstrateProcessingEvent e);
bool on_id_event(SubstrateIDEvent e);
private:
SubstrateTable loc_table_;
SubstrateProcessingTable proc_table_;
SubstrateIDTable id_table_;
SubstrateState loc_ = SubstrateState::AtSource;
SubstrateProcessingState proc_ = SubstrateProcessingState::NeedsProcessing;
SubstrateIDStatus id_ = SubstrateIDStatus::NotConfirmed;
LocationChangeHandler on_loc_;
ProcessingChangeHandler on_proc_;
IDChangeHandler on_id_;
};
} // namespace secsgem::gem