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:
@@ -4,6 +4,7 @@
|
||||
#include "secsgem/gem/store/carriers.hpp"
|
||||
#include "secsgem/gem/store/cem_objects.hpp"
|
||||
#include "secsgem/gem/store/clock.hpp"
|
||||
#include "secsgem/gem/e84_state.hpp"
|
||||
#include "secsgem/gem/ept_state.hpp"
|
||||
#include "secsgem/gem/store/control_jobs.hpp"
|
||||
#include "secsgem/gem/store/equipment_constants.hpp"
|
||||
@@ -47,6 +48,7 @@ struct EquipmentDataModel {
|
||||
EptStateMachine ept;
|
||||
CemObjectStore cem;
|
||||
ModuleStore modules;
|
||||
E84StateMachine e84;
|
||||
|
||||
// Convenience: VID -> value lookup spanning SVIDs and DVIDs.
|
||||
std::optional<s2::Item> vid_value(uint32_t vid) const {
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
// E84 §6 Parallel I/O — the digital handshake between AMHS (Automated
|
||||
// Material Handling System) and equipment for carrier load/unload.
|
||||
//
|
||||
// E84 is signal-level, not SECS: ten parallel boolean wires between the
|
||||
// AMHS robot and the equipment, sequenced in a strict handshake. This
|
||||
// FSM models the signal bitmap and the handshake state, accepting
|
||||
// signal-change events as input and exposing state transitions for
|
||||
// observation. Real wiring uses opto-isolated 24V lines; we abstract
|
||||
// it as bool getters/setters so the same FSM drives both real hardware
|
||||
// and back-to-back testing.
|
||||
namespace secsgem::gem {
|
||||
|
||||
enum class E84Signal : uint8_t {
|
||||
CS_0 = 0, // AMHS -> equip: carrier stage select 0
|
||||
CS_1 = 1, // AMHS -> equip: carrier stage select 1
|
||||
VALID = 2, // AMHS -> equip: handshake start
|
||||
TR_REQ = 3, // AMHS -> equip: transfer request
|
||||
BUSY = 4, // AMHS -> equip: transfer in progress
|
||||
COMPT = 5, // AMHS -> equip: transfer complete
|
||||
L_REQ = 6, // equip -> AMHS: load request (port ready to receive)
|
||||
U_REQ = 7, // equip -> AMHS: unload request (port ready to release)
|
||||
READY = 8, // equip -> AMHS: ready
|
||||
ES = 9, // either direction: emergency stop
|
||||
};
|
||||
|
||||
const char* e84_signal_name(E84Signal s);
|
||||
|
||||
// 10-bit signal bitmap with bool get/set.
|
||||
class E84SignalSet {
|
||||
public:
|
||||
bool get(E84Signal s) const {
|
||||
return (bits_ & (uint16_t{1} << static_cast<uint8_t>(s))) != 0;
|
||||
}
|
||||
void set(E84Signal s, bool v) {
|
||||
const uint16_t mask = uint16_t{1} << static_cast<uint8_t>(s);
|
||||
if (v) bits_ |= mask;
|
||||
else bits_ &= static_cast<uint16_t>(~mask);
|
||||
}
|
||||
uint16_t raw() const { return bits_; }
|
||||
void clear() { bits_ = 0; }
|
||||
|
||||
private:
|
||||
uint16_t bits_ = 0;
|
||||
};
|
||||
|
||||
// E84 handoff handshake state (E84 §6.3). Names are short for log
|
||||
// readability; semantics in comments.
|
||||
enum class E84State : uint8_t {
|
||||
Idle = 0, // no signals asserted (or carrier absent)
|
||||
CarrierPresent = 1, // CS_0 or CS_1 asserted; no VALID yet
|
||||
ValidAsserted = 2, // CS && VALID; equipment hasn't acknowledged
|
||||
LoadReady = 3, // VALID && L_REQ; ready to receive carrier
|
||||
UnloadReady = 4, // VALID && U_REQ; ready to release carrier
|
||||
Transferring = 5, // BUSY asserted; transfer in progress
|
||||
Complete = 6, // COMPT asserted; AMHS reports done
|
||||
EmergencyStop = 7, // ES asserted
|
||||
NoState = 255,
|
||||
};
|
||||
|
||||
const char* e84_state_name(E84State s);
|
||||
|
||||
class E84StateMachine {
|
||||
public:
|
||||
using StateChangeHandler =
|
||||
std::function<void(E84State from, E84State to, E84Signal trigger)>;
|
||||
|
||||
E84State state() const { return state_; }
|
||||
const E84SignalSet& signals() const { return signals_; }
|
||||
bool signal(E84Signal s) const { return signals_.get(s); }
|
||||
|
||||
void set_state_change_handler(StateChangeHandler h) { on_change_ = std::move(h); }
|
||||
|
||||
// Apply a single signal change. Re-evaluates the handshake state
|
||||
// and fires the change handler on transition. Order of signal
|
||||
// changes matters for the AMHS-equipment handshake; the FSM accepts
|
||||
// any order and just reports the resulting state.
|
||||
void on_signal_change(E84Signal s, bool value);
|
||||
|
||||
// Convenience: clear all signals; resets state to Idle.
|
||||
void reset();
|
||||
|
||||
private:
|
||||
void reevaluate(E84Signal trigger);
|
||||
|
||||
E84SignalSet signals_;
|
||||
E84State state_ = E84State::Idle;
|
||||
StateChangeHandler on_change_;
|
||||
};
|
||||
|
||||
} // namespace secsgem::gem
|
||||
Reference in New Issue
Block a user