#pragma once #include #include #include #include #include #include #include "secsgem/gem/control_job_state.hpp" namespace secsgem::gem { // One Control Job — owns an ordered list of PRJOBIDs (process jobs). struct ControlJob { std::string ctljobid; std::vector prjobids; std::unique_ptr fsm; }; class ControlJobStore { public: using TransitionTableFactory = std::function; using StateChangeHandler = std::function; ControlJobStore() : factory_([] { return ControlJobTransitionTable::default_table(); }) {} void set_table_factory(TransitionTableFactory f) { factory_ = std::move(f); } void set_state_change_handler(StateChangeHandler h) { on_change_ = std::move(h); } enum class CreateResult { Created, Denied_AlreadyExists, Denied_UnknownPRJob, Denied_Empty, }; CreateResult create(std::string ctljobid, std::vector prjobids, const std::function& pj_exists = [](const std::string&) { return true; }) { if (jobs_.count(ctljobid)) return CreateResult::Denied_AlreadyExists; if (prjobids.empty()) return CreateResult::Denied_Empty; for (const auto& id : prjobids) { if (!pj_exists(id)) return CreateResult::Denied_UnknownPRJob; } auto fsm = std::make_unique(factory_(), ControlJobState::Queued); const std::string id_for_handler = ctljobid; if (on_change_) { auto cb = on_change_; fsm->set_state_change_handler( [cb, id_for_handler](ControlJobState from, ControlJobState to, ControlJobEvent trig) { cb(id_for_handler, from, to, trig); }); } jobs_.emplace(ctljobid, ControlJob{ctljobid, std::move(prjobids), std::move(fsm)}); if (on_change_) { on_change_(id_for_handler, ControlJobState::NoState, ControlJobState::Queued, ControlJobEvent::Created); } return CreateResult::Created; } bool has(const std::string& ctljobid) const { return jobs_.count(ctljobid) > 0; } const ControlJob* get(const std::string& ctljobid) const { auto it = jobs_.find(ctljobid); return it == jobs_.end() ? nullptr : &it->second; } ControlJob* get(const std::string& ctljobid) { auto it = jobs_.find(ctljobid); return it == jobs_.end() ? nullptr : &it->second; } ControlJobState state(const std::string& ctljobid) const { auto it = jobs_.find(ctljobid); return it == jobs_.end() ? ControlJobState::NoState : it->second.fsm->state(); } HostCmdAck on_host_command(const std::string& ctljobid, ControlJobEvent event) { auto* cj = get(ctljobid); if (!cj) return HostCmdAck::InvalidObject; return cj->fsm->on_host_command(event); } bool fire_internal(const std::string& ctljobid, ControlJobEvent event) { auto* cj = get(ctljobid); if (!cj) return false; return cj->fsm->on_internal(event); } bool remove(const std::string& ctljobid) { return jobs_.erase(ctljobid) > 0; } std::size_t size() const { return jobs_.size(); } std::vector ids() const { std::vector out; out.reserve(jobs_.size()); for (const auto& kv : jobs_) out.push_back(kv.first); return out; } private: std::map jobs_; TransitionTableFactory factory_; StateChangeHandler on_change_; }; } // namespace secsgem::gem