e84: asio adapter for handshake timers + wall-clock test
The E84StateMachine timers landed last commit but stayed theoretical — arming was delivered via abstract callbacks the application had to glue to a real clock. This commit ships the canonical glue: - include/secsgem/gem/e84_asio_timers.hpp: header-only E84AsioTimers wraps three asio::steady_timers, wires set_timer_handlers on attach(), routes async_wait expiry back into fsm.on_timeout(). detach() cancels everything cleanly. - tests/test_e84_asio_timers.cpp: four scenarios exercised through a real asio::io_context with wall-clock timers — TA1 expiry, signal-driven cancel before TA1 fires, TA3 expiry from the Transferring state, and detach() halting further transitions. These cover the integration the synthetic unit tests in test_e84_timers.cpp can't reach. - INTEGRATION.md §4.6: the vendor-side recipe — create the port, set timeouts, make_shared<E84AsioTimers>(...)::attach(), feed signals from your I/O bridge. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+44
-1
@@ -267,7 +267,50 @@ model->alarms.set(1, false); // emits S5F1(ALCD=0x04)
|
||||
|
||||
The dispatcher takes care of the wire frame — you just toggle.
|
||||
|
||||
### 4.6. Recoverable exceptions (E5 §9, S5F9–F18)
|
||||
### 4.6. E84 parallel I/O handoff (AMHS)
|
||||
|
||||
For each load port that talks to the AMHS robot:
|
||||
|
||||
```cpp
|
||||
#include "secsgem/gem/e84_asio_timers.hpp"
|
||||
|
||||
auto* fsm = model->e84_ports.get(/*port_id=*/1);
|
||||
if (!fsm) { model->e84_ports.create(1); fsm = model->e84_ports.get(1); }
|
||||
|
||||
// SEMI E84 §6 handshake timers. Defaults below are spec-typical; tune
|
||||
// per port. TA1=AMHS waits for L_REQ/U_REQ after VALID; TA2=equipment
|
||||
// waits for BUSY after port is ready; TA3=BUSY phase budget.
|
||||
fsm->set_timeouts({std::chrono::seconds(2),
|
||||
std::chrono::seconds(2),
|
||||
std::chrono::seconds(60)});
|
||||
|
||||
// Wire arm/cancel into asio so the FSM polices the real wall clock.
|
||||
auto driver = std::make_shared<gem::E84AsioTimers>(io.get_executor(), *fsm);
|
||||
driver->attach();
|
||||
// Keep `driver` alive for the lifetime of the FSM (e.g. as a member
|
||||
// of your per-port object).
|
||||
|
||||
// Optional: log handoff faults.
|
||||
fsm->set_fault_handler([port_id = 1](gem::E84Fault reason) {
|
||||
log("E84 port " + std::to_string(port_id) + " fault: " +
|
||||
gem::e84_fault_name(reason));
|
||||
});
|
||||
|
||||
// Now feed signal changes from your I/O bridge. On a real AMHS the
|
||||
// bridge polls or interrupts on the parallel-I/O lines:
|
||||
model->e84_ports.on_signal_change(1, gem::E84Signal::CS_0, true);
|
||||
model->e84_ports.on_signal_change(1, gem::E84Signal::VALID, true);
|
||||
// equipment side asserts when port is physically ready:
|
||||
model->e84_ports.on_signal_change(1, gem::E84Signal::L_REQ, true);
|
||||
// ... AMHS continues with BUSY / COMPT.
|
||||
```
|
||||
|
||||
If TA1, TA2, or TA3 expires the FSM transitions to `HandoffFault` and
|
||||
the fault handler fires with the precise `E84Fault` reason. Your
|
||||
application is then responsible for whatever the tool's fault policy is
|
||||
(typically: assert your local ES line and raise an alarm).
|
||||
|
||||
### 4.7. Recoverable exceptions (E5 §9, S5F9–F18)
|
||||
|
||||
For faults where you want a host/equipment recovery dialogue:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user