P: E84 Parallel I/O handoff signaling
The biggest single gap I called out in the GEM300 audit — closed.
E84 is the digital handshake between AMHS (Automated Material
Handling System) and the equipment for carrier load/unload. Unlike
the rest of GEM300, this isn't SECS messaging; it's a fixed set of
ten parallel boolean wires that follow a strict sequencing protocol
(E84-0710 §6.3).
Adds:
E84Signal enum CS_0/CS_1/VALID/TR_REQ/BUSY/COMPT/L_REQ/U_REQ/
READY/ES
E84SignalSet 10-bit bitmap with bool get/set
E84State Idle / CarrierPresent / ValidAsserted /
LoadReady / UnloadReady / Transferring /
Complete / EmergencyStop
E84StateMachine re-evaluates state on every signal change,
observable via set_state_change_handler
Joins EquipmentDataModel as `e84` (top-level — there's one per tool,
not per port). ES (emergency stop) dominates regardless of other
signals; COMPT and BUSY override the VALID-handshake states. Same
FSM drives real opto-isolated I/O lines (when wired through an
asio digital input adapter) and the back-to-back test simulation.
Six test cases cover the full load handshake trace (six transitions,
including the transient LoadReady-after-BUSY-drops state), the
unload variant via U_REQ, ES dominance + recovery, reset(), and
no-op suppression for idempotent signal writes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
#include "secsgem/gem/e84_state.hpp"
|
||||
|
||||
namespace secsgem::gem {
|
||||
|
||||
const char* e84_signal_name(E84Signal s) {
|
||||
switch (s) {
|
||||
case E84Signal::CS_0: return "CS_0";
|
||||
case E84Signal::CS_1: return "CS_1";
|
||||
case E84Signal::VALID: return "VALID";
|
||||
case E84Signal::TR_REQ: return "TR_REQ";
|
||||
case E84Signal::BUSY: return "BUSY";
|
||||
case E84Signal::COMPT: return "COMPT";
|
||||
case E84Signal::L_REQ: return "L_REQ";
|
||||
case E84Signal::U_REQ: return "U_REQ";
|
||||
case E84Signal::READY: return "READY";
|
||||
case E84Signal::ES: return "ES";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
const char* e84_state_name(E84State s) {
|
||||
switch (s) {
|
||||
case E84State::Idle: return "Idle";
|
||||
case E84State::CarrierPresent: return "CarrierPresent";
|
||||
case E84State::ValidAsserted: return "ValidAsserted";
|
||||
case E84State::LoadReady: return "LoadReady";
|
||||
case E84State::UnloadReady: return "UnloadReady";
|
||||
case E84State::Transferring: return "Transferring";
|
||||
case E84State::Complete: return "Complete";
|
||||
case E84State::EmergencyStop: return "EmergencyStop";
|
||||
case E84State::NoState: return "NoState";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
void E84StateMachine::on_signal_change(E84Signal s, bool value) {
|
||||
signals_.set(s, value);
|
||||
reevaluate(s);
|
||||
}
|
||||
|
||||
void E84StateMachine::reset() {
|
||||
signals_.clear();
|
||||
if (state_ != E84State::Idle) {
|
||||
const auto prev = state_;
|
||||
state_ = E84State::Idle;
|
||||
if (on_change_) on_change_(prev, state_, E84Signal::ES); // trigger is arbitrary
|
||||
}
|
||||
}
|
||||
|
||||
void E84StateMachine::reevaluate(E84Signal trigger) {
|
||||
E84State next = state_;
|
||||
|
||||
// Emergency stop dominates.
|
||||
if (signals_.get(E84Signal::ES)) {
|
||||
next = E84State::EmergencyStop;
|
||||
} else if (signals_.get(E84Signal::COMPT)) {
|
||||
next = E84State::Complete;
|
||||
} else if (signals_.get(E84Signal::BUSY)) {
|
||||
next = E84State::Transferring;
|
||||
} else if (signals_.get(E84Signal::VALID)) {
|
||||
if (signals_.get(E84Signal::L_REQ)) next = E84State::LoadReady;
|
||||
else if (signals_.get(E84Signal::U_REQ)) next = E84State::UnloadReady;
|
||||
else next = E84State::ValidAsserted;
|
||||
} else if (signals_.get(E84Signal::CS_0) || signals_.get(E84Signal::CS_1)) {
|
||||
next = E84State::CarrierPresent;
|
||||
} else {
|
||||
next = E84State::Idle;
|
||||
}
|
||||
|
||||
if (next != state_) {
|
||||
const auto prev = state_;
|
||||
state_ = next;
|
||||
if (on_change_) on_change_(prev, state_, trigger);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace secsgem::gem
|
||||
Reference in New Issue
Block a user