#include "secsgem/gem/exception_state.hpp" namespace secsgem::gem { const char* exception_state_name(ExceptionState s) { switch (s) { case ExceptionState::Posted: return "Posted"; case ExceptionState::Recovering: return "Recovering"; case ExceptionState::RecoverFailed: return "RecoverFailed"; case ExceptionState::Cleared: return "Cleared"; case ExceptionState::NoState: return "NoState"; } return "?"; } std::optional parse_exception_state(const std::string& s) { if (s == "Posted") return ExceptionState::Posted; if (s == "Recovering") return ExceptionState::Recovering; if (s == "RecoverFailed") return ExceptionState::RecoverFailed; if (s == "Cleared") return ExceptionState::Cleared; if (s == "NoState") return ExceptionState::NoState; return std::nullopt; } const char* exception_event_name(ExceptionEvent e) { switch (e) { case ExceptionEvent::Created: return "Created"; case ExceptionEvent::Recover: return "Recover"; case ExceptionEvent::RecoveryComplete: return "RecoveryComplete"; case ExceptionEvent::RecoveryFailed: return "RecoveryFailed"; case ExceptionEvent::RecoveryAbort: return "RecoveryAbort"; case ExceptionEvent::Clear: return "Clear"; } return "?"; } std::optional parse_exception_event(const std::string& s) { // `created` is synthetic — never legal in a table row. if (s == "recover") return ExceptionEvent::Recover; if (s == "recovery_complete") return ExceptionEvent::RecoveryComplete; if (s == "recovery_failed") return ExceptionEvent::RecoveryFailed; if (s == "recovery_abort") return ExceptionEvent::RecoveryAbort; if (s == "clear") return ExceptionEvent::Clear; return std::nullopt; } void ExceptionTransitionTable::add(ExceptionTransition row) { rows_.push_back(row); } const ExceptionTransition* ExceptionTransitionTable::find( ExceptionState from, ExceptionEvent on) const { for (const auto& r : rows_) { if (r.from == from && r.on == on) return &r; } return nullptr; } ExceptionTransitionTable ExceptionTransitionTable::default_table() { using S = ExceptionState; using E = ExceptionEvent; ExceptionTransitionTable t; // POSTED ---------------------------------------------------------- t.add({S::Posted, E::Recover, S::Recovering, std::nullopt}); t.add({S::Posted, E::Clear, S::Cleared, std::nullopt}); // RECOVERING ------------------------------------------------------ t.add({S::Recovering, E::RecoveryComplete, S::Cleared, std::nullopt}); t.add({S::Recovering, E::RecoveryFailed, S::RecoverFailed, std::nullopt}); t.add({S::Recovering, E::RecoveryAbort, S::Posted, std::nullopt}); // RECOVER-FAILED -------------------------------------------------- t.add({S::RecoverFailed, E::Recover, S::Recovering, std::nullopt}); t.add({S::RecoverFailed, E::Clear, S::Cleared, std::nullopt}); // CLEARED is terminal — the store removes the exception entry. return t; } ExceptionStateMachine::ExceptionStateMachine() : ExceptionStateMachine(ExceptionTransitionTable::default_table(), ExceptionState::Posted) {} ExceptionStateMachine::ExceptionStateMachine(ExceptionTransitionTable table, ExceptionState initial) : table_(std::move(table)), state_(initial) {} void ExceptionStateMachine::transition(ExceptionState next, ExceptionEvent trig) { if (state_ == next) return; const ExceptionState prev = state_; state_ = next; if (on_change_) on_change_(prev, next, trig); } const ExceptionTransition* ExceptionStateMachine::fire(ExceptionEvent on) { const ExceptionTransition* row = table_.find(state_, on); if (!row) return nullptr; if (row->to) transition(*row->to, on); return row; } AlarmAck ExceptionStateMachine::on_host_command(ExceptionEvent event) { const auto* row = fire(event); if (!row) return AlarmAck::Error; if (row->ack_code) return static_cast(*row->ack_code); return AlarmAck::Accept; } bool ExceptionStateMachine::on_internal(ExceptionEvent event) { return fire(event) != nullptr; } } // namespace secsgem::gem