G: CemObjectStore (E120 Common Equipment Model)
Hierarchical object tree for equipment self-description. Each object carries a CemObjectType (Equipment / Subsystem / IODevice / Module / MaterialLocation / Other), an optional parent_objid, and a flat attribute map keyed by name (the wire shape S14F1 / F3 returns). Operations covered: add(CemObject) - dedup'd, validates parent exists get / has - lookup by objid get_attr / set_attr - E14 GetAttr / SetAttr semantics children(parent) - tree traversal; empty parent = roots The flat-map representation matches how E14 ObjectService traffic addresses nodes (by OBJSPEC string). Wiring S14F1/F2 GetAttr and S14F3/F4 SetAttr to this store is a downstream commit; the data model is what was missing. Joins EquipmentDataModel alongside the other top-level stores. Three test cases cover hierarchical add+dedup, children() traversal, and get/set/missing attribute semantics. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
#include <doctest/doctest.h>
|
||||
|
||||
#include "secsgem/gem/store/cem_objects.hpp"
|
||||
|
||||
using namespace secsgem::gem;
|
||||
namespace s2 = secsgem::secs2;
|
||||
|
||||
TEST_CASE("CemObjectStore: add / get / size") {
|
||||
CemObjectStore s;
|
||||
CHECK(s.add({"EQ-1", CemObjectType::Equipment, "", {}}) ==
|
||||
CemObjectStore::CreateResult::Created);
|
||||
CHECK(s.add({"EQ-1", CemObjectType::Equipment, "", {}}) ==
|
||||
CemObjectStore::CreateResult::Denied_AlreadyExists);
|
||||
CHECK(s.add({"SUB-1", CemObjectType::Subsystem, "EQ-1", {}}) ==
|
||||
CemObjectStore::CreateResult::Created);
|
||||
CHECK(s.add({"orphan", CemObjectType::Module, "ghost", {}}) ==
|
||||
CemObjectStore::CreateResult::Denied_BadParent);
|
||||
CHECK(s.size() == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("CemObjectStore: tree traversal via children()") {
|
||||
CemObjectStore s;
|
||||
s.add({"EQ-1", CemObjectType::Equipment, "", {}});
|
||||
s.add({"SUB-A", CemObjectType::Subsystem, "EQ-1", {}});
|
||||
s.add({"SUB-B", CemObjectType::Subsystem, "EQ-1", {}});
|
||||
s.add({"MOD-1", CemObjectType::Module, "SUB-A", {}});
|
||||
CHECK(s.children("").size() == 1); // one root
|
||||
CHECK(s.children("EQ-1").size() == 2); // two subsystems
|
||||
CHECK(s.children("SUB-A").size() == 1); // one module
|
||||
CHECK(s.children("SUB-B").empty());
|
||||
}
|
||||
|
||||
TEST_CASE("CemObjectStore: GetAttr / SetAttr E14 semantics") {
|
||||
CemObjectStore s;
|
||||
s.add({"EQ-1", CemObjectType::Equipment, "",
|
||||
{{"MDLN", s2::Item::ascii("SECS-DEMO")}}});
|
||||
auto mdln = s.get_attr("EQ-1", "MDLN");
|
||||
REQUIRE(mdln.has_value());
|
||||
CHECK(mdln->as_ascii() == "SECS-DEMO");
|
||||
// SetAttr on existing attr overwrites.
|
||||
CHECK(s.set_attr("EQ-1", "MDLN", s2::Item::ascii("v2")));
|
||||
CHECK(s.get_attr("EQ-1", "MDLN")->as_ascii() == "v2");
|
||||
// Unknown object.
|
||||
CHECK_FALSE(s.set_attr("ghost", "x", s2::Item::ascii("y")));
|
||||
CHECK_FALSE(s.get_attr("EQ-1", "UNKNOWN_ATTR").has_value());
|
||||
}
|
||||
Reference in New Issue
Block a user