#include "secsgem/gem/carrier_state.hpp" namespace secsgem::gem { // Pin the wire-byte values per E87-0716 ยง10.3. static_assert(static_cast(CarrierIDStatus::NotConfirmed) == 0); static_assert(static_cast(CarrierIDStatus::Confirmed) == 1); static_assert(static_cast(CarrierIDStatus::Mismatched) == 2); static_assert(static_cast(CarrierIDStatus::Unknown) == 3); static_assert(static_cast(SlotMapStatus::NotRead) == 0); static_assert(static_cast(SlotMapStatus::Read) == 1); static_assert(static_cast(SlotMapStatus::Mismatched) == 2); static_assert(static_cast(CarrierAccessStatus::NotAccessed) == 0); static_assert(static_cast(CarrierAccessStatus::InAccess) == 1); static_assert(static_cast(CarrierAccessStatus::Complete) == 2); const char* carrier_id_status_name(CarrierIDStatus s) { switch (s) { case CarrierIDStatus::NotConfirmed: return "NotConfirmed"; case CarrierIDStatus::Confirmed: return "Confirmed"; case CarrierIDStatus::Mismatched: return "Mismatched"; case CarrierIDStatus::Unknown: return "Unknown"; case CarrierIDStatus::NoState: return "NoState"; } return "?"; } std::optional parse_carrier_id_status(const std::string& s) { if (s == "NotConfirmed") return CarrierIDStatus::NotConfirmed; if (s == "Confirmed") return CarrierIDStatus::Confirmed; if (s == "Mismatched") return CarrierIDStatus::Mismatched; if (s == "Unknown") return CarrierIDStatus::Unknown; if (s == "NoState") return CarrierIDStatus::NoState; return std::nullopt; } const char* carrier_id_event_name(CarrierIDEvent e) { switch (e) { case CarrierIDEvent::Read: return "Read"; case CarrierIDEvent::ProceedWithCarrier: return "ProceedWithCarrier"; case CarrierIDEvent::CancelCarrier: return "CancelCarrier"; case CarrierIDEvent::IDFailed: return "IDFailed"; case CarrierIDEvent::Bind: return "Bind"; } return "?"; } const char* slot_map_status_name(SlotMapStatus s) { switch (s) { case SlotMapStatus::NotRead: return "NotRead"; case SlotMapStatus::Read: return "Read"; case SlotMapStatus::Mismatched: return "Mismatched"; case SlotMapStatus::NoState: return "NoState"; } return "?"; } const char* slot_map_event_name(SlotMapEvent e) { switch (e) { case SlotMapEvent::Read: return "Read"; case SlotMapEvent::Confirm: return "Confirm"; case SlotMapEvent::Mismatch: return "Mismatch"; case SlotMapEvent::Reset: return "Reset"; } return "?"; } const char* carrier_access_status_name(CarrierAccessStatus s) { switch (s) { case CarrierAccessStatus::NotAccessed: return "NotAccessed"; case CarrierAccessStatus::InAccess: return "InAccess"; case CarrierAccessStatus::Complete: return "Complete"; case CarrierAccessStatus::NoState: return "NoState"; } return "?"; } const char* carrier_access_event_name(CarrierAccessEvent e) { switch (e) { case CarrierAccessEvent::BeginAccess: return "BeginAccess"; case CarrierAccessEvent::EndAccess: return "EndAccess"; case CarrierAccessEvent::Cancel: return "Cancel"; } return "?"; } CarrierIDTable default_carrier_id_table() { using S = CarrierIDStatus; using E = CarrierIDEvent; CarrierIDTable t; // NotConfirmed (initial) t.add({S::NotConfirmed, E::Read, S::NotConfirmed, std::nullopt}); t.add({S::NotConfirmed, E::ProceedWithCarrier, S::Confirmed, std::nullopt}); t.add({S::NotConfirmed, E::Bind, S::Confirmed, std::nullopt}); t.add({S::NotConfirmed, E::IDFailed, S::Unknown, std::nullopt}); t.add({S::NotConfirmed, E::CancelCarrier, S::NotConfirmed, std::nullopt}); // Confirmed t.add({S::Confirmed, E::CancelCarrier, S::NotConfirmed, std::nullopt}); // Mismatched t.add({S::Mismatched, E::ProceedWithCarrier, S::Confirmed, std::nullopt}); t.add({S::Mismatched, E::CancelCarrier, S::NotConfirmed, std::nullopt}); t.add({S::Mismatched, E::Bind, S::Confirmed, std::nullopt}); // Unknown t.add({S::Unknown, E::Bind, S::Confirmed, std::nullopt}); t.add({S::Unknown, E::CancelCarrier, S::NotConfirmed, std::nullopt}); return t; } SlotMapTable default_slot_map_table() { using S = SlotMapStatus; using E = SlotMapEvent; SlotMapTable t; t.add({S::NotRead, E::Read, S::Read, std::nullopt}); t.add({S::Read, E::Confirm, S::Read, std::nullopt}); // ack-only t.add({S::Read, E::Mismatch, S::Mismatched, std::nullopt}); t.add({S::Read, E::Reset, S::NotRead, std::nullopt}); t.add({S::Mismatched, E::Reset, S::NotRead, std::nullopt}); t.add({S::Mismatched, E::Read, S::Read, std::nullopt}); // re-scan return t; } CarrierAccessTable default_carrier_access_table() { using S = CarrierAccessStatus; using E = CarrierAccessEvent; CarrierAccessTable t; t.add({S::NotAccessed, E::BeginAccess, S::InAccess, std::nullopt}); t.add({S::NotAccessed, E::Cancel, S::Complete, std::nullopt}); t.add({S::InAccess, E::EndAccess, S::Complete, std::nullopt}); t.add({S::InAccess, E::Cancel, S::Complete, std::nullopt}); return t; } CarrierStateMachine::CarrierStateMachine() : id_table_(default_carrier_id_table()), sm_table_(default_slot_map_table()), acc_table_(default_carrier_access_table()) {} bool CarrierStateMachine::on_id_event(CarrierIDEvent e) { const auto* row = id_table_.find(id_state_, e); if (!row) return false; if (row->to && *row->to != id_state_) { auto prev = id_state_; id_state_ = *row->to; if (on_id_change_) on_id_change_(prev, id_state_, e); } return true; } bool CarrierStateMachine::on_slot_map_event(SlotMapEvent e) { const auto* row = sm_table_.find(sm_state_, e); if (!row) return false; if (row->to && *row->to != sm_state_) { auto prev = sm_state_; sm_state_ = *row->to; if (on_sm_change_) on_sm_change_(prev, sm_state_, e); } return true; } bool CarrierStateMachine::on_access_event(CarrierAccessEvent e) { const auto* row = acc_table_.find(acc_state_, e); if (!row) return false; if (row->to && *row->to != acc_state_) { auto prev = acc_state_; acc_state_ = *row->to; if (on_acc_change_) on_acc_change_(prev, acc_state_, e); } return true; } } // namespace secsgem::gem