1548b49afd
Same pattern as carriers: per-substrate binary record (.sub) with atomic .tmp+rename, replay on enable, delete on remove. Records current state across all three E90 axes (location / processing / ID-status), plus substid / carrierid / slot / free-form location label. History is deliberately NOT journaled — it's an in-memory ring buffer and rebuilding from replayed state would mislead. Five new tests cover full-axis replay, every terminal processing state, remove-deletes-journal, corrupt-record drop, and the history-is-transient invariant. Closes #2 in the test-gap backlog. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
152 lines
5.3 KiB
C++
152 lines
5.3 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "secsgem/gem/carrier_state.hpp" // shared CarrierTransition template
|
|
|
|
// E90 §6 Substrate Tracking — per-substrate state model.
|
|
//
|
|
// E90 actually defines several related state models per substrate:
|
|
//
|
|
// STS Substrate State the processing lifecycle
|
|
// SLS Substrate Location where the wafer physically is
|
|
// IDS Substrate ID Status has the ID been verified
|
|
//
|
|
// This first pass covers the most-used STS model (E90-0716 §10.3.1) at
|
|
// full fidelity and exposes a Location string the application can
|
|
// update as it sees substrate moves. IDS / cross-state guards mirror
|
|
// the CIDS pattern from E87 and remain a follow-on.
|
|
namespace secsgem::gem {
|
|
|
|
// Wire-byte values per E90-0716 §10.3.1 STS.
|
|
enum class SubstrateState : uint8_t {
|
|
AtSource = 0,
|
|
AtWork = 1, // in-process at a module
|
|
AtDestination = 2, // moved to final location
|
|
NoState = 255,
|
|
};
|
|
|
|
const char* substrate_state_name(SubstrateState s);
|
|
std::optional<SubstrateState> parse_substrate_state(const std::string& s);
|
|
|
|
// "Processing" lifecycle (E90 §6.4.4); orthogonal to STS.
|
|
enum class SubstrateProcessingState : uint8_t {
|
|
NeedsProcessing = 0,
|
|
InProcess = 1,
|
|
Processed = 2,
|
|
Aborted = 3,
|
|
Stopped = 4,
|
|
Rejected = 5,
|
|
Lost = 6,
|
|
Skipped = 7,
|
|
NoState = 255,
|
|
};
|
|
|
|
const char* substrate_processing_state_name(SubstrateProcessingState s);
|
|
|
|
enum class SubstrateEvent {
|
|
Acquire, // AtSource -> AtWork (machine picked up the wafer)
|
|
Release, // AtWork -> AtDestination (deposited)
|
|
Return, // AtWork -> AtSource (returned without processing)
|
|
};
|
|
|
|
const char* substrate_event_name(SubstrateEvent e);
|
|
|
|
enum class SubstrateProcessingEvent {
|
|
StartProcessing, // NeedsProcessing -> InProcess
|
|
EndProcessing, // InProcess -> Processed
|
|
Abort, // any -> Aborted
|
|
Stop, // any -> Stopped
|
|
Reject, // any -> Rejected
|
|
ReportLost, // any -> Lost
|
|
Skip, // any -> Skipped
|
|
};
|
|
|
|
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:
|
|
using LocationChangeHandler =
|
|
std::function<void(SubstrateState from, SubstrateState to, SubstrateEvent)>;
|
|
using ProcessingChangeHandler =
|
|
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);
|
|
|
|
// Direct state-restore (persistence replay only). Bypasses the
|
|
// transition tables and does NOT fire change handlers.
|
|
void restore_state(SubstrateState loc, SubstrateProcessingState proc,
|
|
SubstrateIDStatus id) {
|
|
loc_ = loc;
|
|
proc_ = proc;
|
|
id_ = id;
|
|
}
|
|
|
|
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
|