D3: CarrierStore + LoadPortStore
Per-CARRIERID and per-PORTID stores wrap the D1 FSMs, mirroring the ProcessJobStore / ExceptionStore pattern: heap-allocated state machines keyed in a std::map, non-movable to keep this-capture lambdas safe, synthetic create() that wires per-row change handlers into the store's top-level callbacks. CarrierStore: create(carrierid, port_id, capacity) — default 25-slot map fire_id_event / fire_slot_map_event / fire_access_event set_id_handler / set_slot_map_handler / set_access_handler LoadPortStore: create(port_id) associate(pid, carrierid) / disassociate(pid) fire_transfer_event / fire_reservation_event set_transfer_handler / set_reservation_handler / set_association_handler Both join EquipmentDataModel alongside process_jobs / control_jobs / exceptions. Six test cases cover create-dedup, ID-status change observation, slot-map / access independence, port association, transfer lifecycle, and reservation handler firing. Server-side dispatch (S3F17 -> CarrierStore::fire_id_event, S3F25 -> LoadPortStore transfer) lands in D4. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "secsgem/gem/store/carriers.hpp"
|
||||
|
||||
using namespace secsgem::gem;
|
||||
|
||||
TEST_CASE("CarrierStore: create / get / remove") {
|
||||
CarrierStore s;
|
||||
CHECK(s.create("CAR-1", /*port=*/1) == CarrierStore::CreateResult::Created);
|
||||
CHECK(s.create("CAR-1") == CarrierStore::CreateResult::Denied_AlreadyExists);
|
||||
REQUIRE(s.has("CAR-1"));
|
||||
CHECK(s.get("CAR-1")->port_id == 1);
|
||||
CHECK(s.get("CAR-1")->slots.size() == 25);
|
||||
CHECK(s.remove("CAR-1"));
|
||||
CHECK_FALSE(s.has("CAR-1"));
|
||||
}
|
||||
|
||||
TEST_CASE("CarrierStore: id-status handler observes transitions") {
|
||||
CarrierStore s;
|
||||
std::vector<std::tuple<std::string, CarrierIDStatus, CarrierIDStatus>> log;
|
||||
s.set_id_handler([&](const std::string& id, CarrierIDStatus f,
|
||||
CarrierIDStatus t, CarrierIDEvent) {
|
||||
log.emplace_back(id, f, t);
|
||||
});
|
||||
s.create("CAR-A");
|
||||
CHECK(s.fire_id_event("CAR-A", CarrierIDEvent::ProceedWithCarrier));
|
||||
REQUIRE(log.size() == 1);
|
||||
CHECK(std::get<0>(log[0]) == "CAR-A");
|
||||
CHECK(std::get<1>(log[0]) == CarrierIDStatus::NotConfirmed);
|
||||
CHECK(std::get<2>(log[0]) == CarrierIDStatus::Confirmed);
|
||||
}
|
||||
|
||||
TEST_CASE("CarrierStore: slot-map and access events are independent") {
|
||||
CarrierStore s;
|
||||
s.create("CAR-B");
|
||||
CHECK(s.fire_slot_map_event("CAR-B", SlotMapEvent::Read));
|
||||
CHECK(s.get("CAR-B")->fsm->slot_map_status() == SlotMapStatus::Read);
|
||||
CHECK(s.fire_access_event("CAR-B", CarrierAccessEvent::BeginAccess));
|
||||
CHECK(s.get("CAR-B")->fsm->access_status() == CarrierAccessStatus::InAccess);
|
||||
// ID state untouched.
|
||||
CHECK(s.get("CAR-B")->fsm->id_status() == CarrierIDStatus::NotConfirmed);
|
||||
}
|
||||
|
||||
// ---- LoadPortStore -----------------------------------------------------
|
||||
|
||||
TEST_CASE("LoadPortStore: create + associate/disassociate") {
|
||||
LoadPortStore p;
|
||||
CHECK(p.create(1));
|
||||
CHECK_FALSE(p.create(1)); // already exists
|
||||
CHECK(p.associate(1, "CAR-X"));
|
||||
CHECK(p.get(1)->carrierid == "CAR-X");
|
||||
CHECK(p.get(1)->fsm->association_state() ==
|
||||
LoadPortAssociationStatus::Associated);
|
||||
CHECK(p.disassociate(1));
|
||||
CHECK(p.get(1)->carrierid.empty());
|
||||
CHECK(p.get(1)->fsm->association_state() ==
|
||||
LoadPortAssociationStatus::NotAssociated);
|
||||
}
|
||||
|
||||
TEST_CASE("LoadPortStore: transfer events progress through Loading/Unloading") {
|
||||
LoadPortStore p;
|
||||
p.create(2);
|
||||
CHECK(p.fire_transfer_event(2, LoadPortTransferEvent::StartLoading));
|
||||
CHECK(p.get(2)->fsm->transfer_state() == LoadPortTransferState::Loading);
|
||||
CHECK(p.fire_transfer_event(2, LoadPortTransferEvent::CompleteLoading));
|
||||
CHECK(p.get(2)->fsm->transfer_state() == LoadPortTransferState::InService);
|
||||
}
|
||||
|
||||
TEST_CASE("LoadPortStore: reservation toggle observed by handler") {
|
||||
LoadPortStore p;
|
||||
int calls = 0;
|
||||
p.set_reservation_handler([&](uint8_t, LoadPortReservationStatus,
|
||||
LoadPortReservationStatus,
|
||||
LoadPortReservationEvent) { ++calls; });
|
||||
p.create(3);
|
||||
p.fire_reservation_event(3, LoadPortReservationEvent::Reserve);
|
||||
CHECK(calls == 1);
|
||||
p.fire_reservation_event(3, LoadPortReservationEvent::Release);
|
||||
CHECK(calls == 2);
|
||||
}
|
||||
Reference in New Issue
Block a user