#pragma once #include #include #include #include #include #include #include #include #include "secsgem/gem/carrier_state.hpp" #include "secsgem/gem/load_port_state.hpp" namespace secsgem::gem { // One slot in a carrier slot map. E87-0716 slot states: Empty (0), // NotEmpty (1), Undefined (3) — we keep the wire byte directly rather // than wrapping it in an enum, since the application may carry tool- // specific extensions. struct CarrierSlot { uint8_t state = 3; // undefined by default }; struct Carrier { std::string carrierid; uint8_t port_id = 0; // 0 = unbound; ports are 1-based std::vector slots; // length == capacity (typ. 25) std::unique_ptr fsm; }; struct LoadPort { uint8_t port_id; std::string carrierid; // empty when nothing associated std::unique_ptr fsm; }; // Both stores are non-movable for the same reason ProcessJobStore is — // per-row FSMs close over `this` via their change-handler lambdas. class CarrierStore { public: using IDChangeHandler = std::function; using SlotMapChangeHandler = std::function; using AccessChangeHandler = std::function; CarrierStore() = default; CarrierStore(const CarrierStore&) = delete; CarrierStore& operator=(const CarrierStore&) = delete; CarrierStore(CarrierStore&&) = delete; CarrierStore& operator=(CarrierStore&&) = delete; void set_id_handler(IDChangeHandler h) { on_id_ = std::move(h); } void set_slot_map_handler(SlotMapChangeHandler h) { on_sm_ = std::move(h); } void set_access_handler(AccessChangeHandler h) { on_acc_ = std::move(h); } enum class CreateResult { Created, Denied_AlreadyExists }; CreateResult create(std::string carrierid, uint8_t port_id = 0, std::size_t capacity = 25) { if (carriers_.count(carrierid)) return CreateResult::Denied_AlreadyExists; auto fsm = std::make_unique(); const std::string id = carrierid; fsm->set_id_handler([this, id](CarrierIDStatus f, CarrierIDStatus t, CarrierIDEvent e) { if (on_id_) on_id_(id, f, t, e); }); fsm->set_slot_map_handler([this, id](SlotMapStatus f, SlotMapStatus t, SlotMapEvent e) { if (on_sm_) on_sm_(id, f, t, e); }); fsm->set_access_handler([this, id](CarrierAccessStatus f, CarrierAccessStatus t, CarrierAccessEvent e) { if (on_acc_) on_acc_(id, f, t, e); }); carriers_.emplace(carrierid, Carrier{carrierid, port_id, std::vector(capacity), std::move(fsm)}); return CreateResult::Created; } bool has(const std::string& cid) const { return carriers_.count(cid) > 0; } const Carrier* get(const std::string& cid) const { auto it = carriers_.find(cid); return it == carriers_.end() ? nullptr : &it->second; } Carrier* get(const std::string& cid) { auto it = carriers_.find(cid); return it == carriers_.end() ? nullptr : &it->second; } bool fire_id_event(const std::string& cid, CarrierIDEvent e) { auto* c = get(cid); return c && c->fsm->on_id_event(e); } bool fire_slot_map_event(const std::string& cid, SlotMapEvent e) { auto* c = get(cid); return c && c->fsm->on_slot_map_event(e); } bool fire_access_event(const std::string& cid, CarrierAccessEvent e) { auto* c = get(cid); return c && c->fsm->on_access_event(e); } bool remove(const std::string& cid) { return carriers_.erase(cid) > 0; } std::size_t size() const { return carriers_.size(); } std::vector ids() const { std::vector out; out.reserve(carriers_.size()); for (const auto& kv : carriers_) out.push_back(kv.first); return out; } private: std::map carriers_; IDChangeHandler on_id_; SlotMapChangeHandler on_sm_; AccessChangeHandler on_acc_; }; class LoadPortStore { public: using TransferChangeHandler = std::function; using ReservationChangeHandler = std::function; using AssociationChangeHandler = std::function; LoadPortStore() = default; LoadPortStore(const LoadPortStore&) = delete; LoadPortStore& operator=(const LoadPortStore&) = delete; LoadPortStore(LoadPortStore&&) = delete; LoadPortStore& operator=(LoadPortStore&&) = delete; void set_transfer_handler(TransferChangeHandler h) { on_tx_ = std::move(h); } void set_reservation_handler(ReservationChangeHandler h) { on_rs_ = std::move(h); } void set_association_handler(AssociationChangeHandler h) { on_as_ = std::move(h); } // Idempotent: re-create a port_id is a no-op. bool create(uint8_t port_id) { if (ports_.count(port_id)) return false; auto fsm = std::make_unique(); const uint8_t pid = port_id; fsm->set_transfer_handler([this, pid](LoadPortTransferState f, LoadPortTransferState t, LoadPortTransferEvent e) { if (on_tx_) on_tx_(pid, f, t, e); }); fsm->set_reservation_handler([this, pid](LoadPortReservationStatus f, LoadPortReservationStatus t, LoadPortReservationEvent e) { if (on_rs_) on_rs_(pid, f, t, e); }); fsm->set_association_handler([this, pid](LoadPortAssociationStatus f, LoadPortAssociationStatus t, LoadPortAssociationEvent e) { if (on_as_) on_as_(pid, f, t, e); }); ports_.emplace(port_id, LoadPort{port_id, "", std::move(fsm)}); return true; } bool has(uint8_t pid) const { return ports_.count(pid) > 0; } const LoadPort* get(uint8_t pid) const { auto it = ports_.find(pid); return it == ports_.end() ? nullptr : &it->second; } LoadPort* get(uint8_t pid) { auto it = ports_.find(pid); return it == ports_.end() ? nullptr : &it->second; } bool associate(uint8_t pid, std::string carrierid) { auto* p = get(pid); if (!p) return false; p->carrierid = std::move(carrierid); return p->fsm->on_association_event(LoadPortAssociationEvent::Associate); } bool disassociate(uint8_t pid) { auto* p = get(pid); if (!p) return false; p->carrierid.clear(); return p->fsm->on_association_event(LoadPortAssociationEvent::Disassociate); } bool fire_transfer_event(uint8_t pid, LoadPortTransferEvent e) { auto* p = get(pid); return p && p->fsm->on_transfer_event(e); } bool fire_reservation_event(uint8_t pid, LoadPortReservationEvent e) { auto* p = get(pid); return p && p->fsm->on_reservation_event(e); } std::size_t size() const { return ports_.size(); } std::vector ids() const { std::vector out; out.reserve(ports_.size()); for (const auto& kv : ports_) out.push_back(kv.first); return out; } private: std::map ports_; TransferChangeHandler on_tx_; ReservationChangeHandler on_rs_; AssociationChangeHandler on_as_; }; } // namespace secsgem::gem