#pragma once #include #include #include #include #include #include #include #include "secsgem/secs2/item.hpp" // E120 ยง6 Common Equipment Model. E120 defines a generic object // hierarchy (Equipment -> Subsystem -> IODevice / Module / Material // Location) for equipment self-description. Hosts query the tree via // the generic E14 Object Service messages (S14F1-F4 GetAttr / SetAttr). // // We model the hierarchy with a single CemObject node type whose // `objtype` is one of a small enum, parented by `parent_objid`, and // carrying a flat attribute map per E14. Real CEM trees // are rarely deeper than four levels; a flat map keyed by OBJSPEC // (parent.child.grand...) is the canonical traversal shape. namespace secsgem::gem { namespace s2 = secsgem::secs2; enum class CemObjectType : uint8_t { Equipment = 0, // top of the tree (one per tool) Subsystem = 1, // major functional unit IODevice = 2, // I/O device Module = 3, // process module (E157 ties in here) MaterialLoc = 4, // material location (load port, internal store) Other = 255, }; const char* cem_object_type_name(CemObjectType t); struct CemObject { std::string objid; // unique within the equipment tree CemObjectType objtype; std::string parent_objid; // empty = root (Equipment) std::map attributes; }; class CemObjectStore { public: enum class CreateResult { Created, Denied_AlreadyExists, Denied_BadParent }; CreateResult add(CemObject obj) { if (objs_.count(obj.objid)) return CreateResult::Denied_AlreadyExists; if (!obj.parent_objid.empty() && !objs_.count(obj.parent_objid)) return CreateResult::Denied_BadParent; objs_.emplace(obj.objid, std::move(obj)); return CreateResult::Created; } bool has(const std::string& objid) const { return objs_.count(objid) > 0; } const CemObject* get(const std::string& objid) const { auto it = objs_.find(objid); return it == objs_.end() ? nullptr : &it->second; } CemObject* get(const std::string& objid) { auto it = objs_.find(objid); return it == objs_.end() ? nullptr : &it->second; } // E14 GetAttr / SetAttr: returns nullopt for unknown object or attr. std::optional get_attr(const std::string& objid, const std::string& name) const { const auto* o = get(objid); if (!o) return std::nullopt; auto it = o->attributes.find(name); if (it == o->attributes.end()) return std::nullopt; return it->second; } bool set_attr(const std::string& objid, std::string name, s2::Item value) { auto* o = get(objid); if (!o) return false; o->attributes.insert_or_assign(std::move(name), std::move(value)); return true; } // Children of `parent_objid` (empty string returns roots). std::vector children(const std::string& parent_objid) const { std::vector out; for (const auto& kv : objs_) { if (kv.second.parent_objid == parent_objid) out.push_back(&kv.second); } return out; } std::size_t size() const { return objs_.size(); } std::vector ids() const { std::vector out; out.reserve(objs_.size()); for (const auto& kv : objs_) out.push_back(kv.first); return out; } private: std::map objs_; }; } // namespace secsgem::gem