#pragma once #include #include #include #include #include #include "secsgem/gem/e84_state.hpp" // asio-backed driver for the SEMI E84 §6 handshake timers (TA1/TA2/TA3). // // `E84StateMachine` is I/O-free by design: it tells the application to // arm or cancel a timer via callbacks and expects the application to // drive a real clock and call `on_timeout(id)` on expiry. This // header-only adapter is the canonical asio glue — three steady_timers, // wired so that an arm() restarts the matching timer and a cancel() // stops it; expiry routes back into `fsm.on_timeout(id)`. // // Usage (one adapter per E84StateMachine, one E84StateMachine per // load port via `E84PortStore::get(port_id)`): // // E84StateMachine* fsm = model->e84_ports.get(port_id); // auto driver = std::make_shared(io.get_executor(), *fsm); // driver->attach(); // wires set_timer_handlers // fsm->set_timeouts({2s, 2s, 60s}); // SEMI defaults // // Keep the shared_ptr alive for as long as the FSM may transition — // typically tied to the load-port lifetime. Detach (or just drop the // shared_ptr) before destroying the FSM. namespace secsgem::gem { class E84AsioTimers : public std::enable_shared_from_this { public: E84AsioTimers(asio::any_io_executor ex, E84StateMachine& fsm) : fsm_(fsm), timers_{{asio::steady_timer{ex}, asio::steady_timer{ex}, asio::steady_timer{ex}}} {} E84AsioTimers(const E84AsioTimers&) = delete; E84AsioTimers& operator=(const E84AsioTimers&) = delete; // Wires the FSM's arm/cancel callbacks to the internal timers. Must // be called after construction (we need shared_from_this for the // expiry continuation). void attach() { auto self = shared_from_this(); fsm_.set_timer_handlers( [self](E84TimerId id, std::chrono::milliseconds d) { self->arm_(id, d); }, [self](E84TimerId id) { self->cancel_(id); }); } // Detach so timers stop driving the FSM. Pending expiries are // cancelled; in-flight asio handlers see ec == operation_aborted and // return early. void detach() { for (auto& t : timers_) t.cancel(); fsm_.set_timer_handlers({}, {}); } private: void arm_(E84TimerId id, std::chrono::milliseconds d) { auto& t = timer_for_(id); t.expires_after(d); auto self = shared_from_this(); t.async_wait([self, id](std::error_code ec) { if (ec) return; // cancelled or timer destroyed self->fsm_.on_timeout(id); }); } void cancel_(E84TimerId id) { timer_for_(id).cancel(); } asio::steady_timer& timer_for_(E84TimerId id) { return timers_[static_cast(id) - 1]; } E84StateMachine& fsm_; std::array timers_; }; } // namespace secsgem::gem