#pragma once #include #include #include #include #include #include #include #include #include "secsgem/secs2/item.hpp" namespace secsgem::gem { namespace s2 = secsgem::secs2; struct CollectionEvent { uint32_t id; std::string name; }; struct Report { uint32_t id; std::vector vids; }; struct ReportData { uint32_t rptid; std::vector values; }; enum class DefineReportAck : uint8_t { Accept = 0, InsufficientSpace = 1, InvalidFormat = 2, RptidAlreadyDefined = 3, InvalidVid = 5, }; enum class LinkEventAck : uint8_t { Accept = 0, InsufficientSpace = 1, InvalidFormat = 2, UnknownCeid = 3, UnknownRptid = 4, CeidAlreadyLinked = 5, }; enum class EnableEventAck : uint8_t { Accept = 0, UnknownCeid = 1, }; // VID resolver: a callable that maps a VID (which may be an SVID or DVID) to // its current value, or std::nullopt if unknown. Injected at S2F33 validation // and again at compose_reports_for emission time; that way the subscription // service stays pure data (no back-reference to the SVID / DVID stores). using VidLookup = std::function(uint32_t)>; using VidExists = std::function; // E30 ยง6.6 dynamic event reporting state: // * the catalog of registered CEIDs (defined by the equipment), // * the host-defined reports (RPTID -> VID list), // * the CEID -> RPTID links, // * and the set of CEIDs the host has enabled. // // Pure data; no IO. class EventReportSubscriptions { public: // --- CEID catalog ----------------------------------------------------- void register_event(CollectionEvent ce) { by_ceid_.insert_or_assign(ce.id, std::move(ce)); } bool has_event(uint32_t ceid) const { return by_ceid_.count(ceid) > 0; } std::vector all_events() const { std::vector out; out.reserve(by_ceid_.size()); for (const auto& [_, e] : by_ceid_) out.push_back(e); return out; } // --- S2F33 define reports -------------------------------------------- DefineReportAck define_reports( const std::vector>>& rows, const VidExists& vid_exists) { if (rows.empty()) { reports_.clear(); links_.clear(); return DefineReportAck::Accept; } for (const auto& [rptid, vids] : rows) { if (vids.empty()) continue; for (auto v : vids) if (!vid_exists(v)) return DefineReportAck::InvalidVid; } for (const auto& [rptid, vids] : rows) { if (vids.empty()) { reports_.erase(rptid); for (auto& [_, rpts] : links_) rpts.erase(std::remove(rpts.begin(), rpts.end(), rptid), rpts.end()); } else { reports_.insert_or_assign(rptid, Report{rptid, vids}); } } return DefineReportAck::Accept; } std::vector all_reports() const { std::vector out; out.reserve(reports_.size()); for (const auto& [_, r] : reports_) out.push_back(r); return out; } // --- S2F35 link event report ----------------------------------------- LinkEventAck link_event_reports( const std::vector>>& rows) { if (rows.empty()) { links_.clear(); return LinkEventAck::Accept; } for (const auto& [ceid, rpts] : rows) { if (!has_event(ceid)) return LinkEventAck::UnknownCeid; for (auto r : rpts) if (!reports_.count(r)) return LinkEventAck::UnknownRptid; } for (const auto& [ceid, rpts] : rows) { if (rpts.empty()) links_.erase(ceid); else links_[ceid] = rpts; } return LinkEventAck::Accept; } // --- S2F37 enable / disable event ----------------------------------- EnableEventAck enable_events(bool enable, const std::vector& ceids) { if (ceids.empty()) { if (enable) for (const auto& [id, _] : by_ceid_) enabled_.insert(id); else enabled_.clear(); return EnableEventAck::Accept; } for (auto id : ceids) if (!has_event(id)) return EnableEventAck::UnknownCeid; for (auto id : ceids) { if (enable) enabled_.insert(id); else enabled_.erase(id); } return EnableEventAck::Accept; } bool is_enabled(uint32_t ceid) const { return enabled_.count(ceid) > 0; } // --- S6F11 emission -------------------------------------------------- std::vector compose_for(uint32_t ceid, const VidLookup& lookup) const { std::vector out; auto it = links_.find(ceid); if (it == links_.end()) return out; for (auto rptid : it->second) { auto rit = reports_.find(rptid); if (rit == reports_.end()) continue; ReportData rd{rptid, {}}; for (auto vid : rit->second.vids) { auto v = lookup(vid); rd.values.push_back(v ? *v : s2::Item::list({})); } out.push_back(std::move(rd)); } return out; } private: std::map by_ceid_; std::map reports_; std::map> links_; std::set enabled_; }; } // namespace secsgem::gem