N: E39 generic ObjectService — S14F1/F2 GetAttr + S14F3/F4 SetAttr
The catalog had S14F9/F10 / F11/F12 specialized for E94 ControlJob;
this commit adds the generic E14 attribute access pair, the most-
queried half of the E39 surface area, backed by the CemObjectStore.
S14F1 / F2 GetAttr — OBJSPEC + OBJTYPE + ATTRID list ->
(ATTRID, VALUE) pairs + OBJACK
S14F3 / F4 SetAttr — same addressing, applies ATTRID/VALUE pairs,
reply echoes stored values + OBJACK
Server dispatches both into the CemObjectStore added in tranche G.
OBJTYPE validation is case-sensitive against the CemObjectType name
(Equipment / Subsystem / IODevice / Module / MaterialLocation).
Unknown objects return Denied_UnknownObject; type mismatches return
Denied_InvalidAttribute.
The shared AttrValue struct is declared external_struct: true on
F3/F4 so both directions share the same C++ type.
Two round-trip tests cover both pairs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -584,6 +584,43 @@ TEST_CASE("S7F19 / S7F20 EPPD list") {
|
||||
CHECK(*parsed == std::vector<std::string>{"RECIPE-A", "RECIPE-B"});
|
||||
}
|
||||
|
||||
TEST_CASE("S14F1 / S14F2 GetAttr round-trip") {
|
||||
auto m = s14f1_get_attr("EQ-1", "Equipment", {"MDLN", "SOFTREV"});
|
||||
CHECK(m.stream == 14);
|
||||
CHECK(m.function == 1);
|
||||
auto parsed = parse_s14f1(m);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(parsed->objspec == "EQ-1");
|
||||
CHECK(parsed->objtype == "Equipment");
|
||||
REQUIRE(parsed->attrids.size() == 2);
|
||||
|
||||
std::vector<AttrValue> reply{
|
||||
{"MDLN", s2::Item::ascii("DEMO")},
|
||||
{"SOFTREV", s2::Item::ascii("1.0")},
|
||||
};
|
||||
auto r = s14f2_get_attr_data(reply, ObjectAck::Success);
|
||||
auto pr = parse_s14f2(r);
|
||||
REQUIRE(pr.has_value());
|
||||
CHECK(pr->ack == ObjectAck::Success);
|
||||
REQUIRE(pr->attrs.size() == 2);
|
||||
CHECK(pr->attrs[0].attrid == "MDLN");
|
||||
}
|
||||
|
||||
TEST_CASE("S14F3 / S14F4 SetAttr round-trip") {
|
||||
std::vector<AttrValue> attrs{{"PORT_STATE", s2::Item::ascii("OPEN")}};
|
||||
auto m = s14f3_set_attr("LP-1", "MaterialLocation", attrs);
|
||||
auto parsed = parse_s14f3(m);
|
||||
REQUIRE(parsed.has_value());
|
||||
CHECK(parsed->objspec == "LP-1");
|
||||
REQUIRE(parsed->attrs.size() == 1);
|
||||
CHECK(parsed->attrs[0].attrid == "PORT_STATE");
|
||||
|
||||
auto r = s14f4_set_attr_ack(attrs, ObjectAck::Success);
|
||||
auto pr = parse_s14f4(r);
|
||||
REQUIRE(pr.has_value());
|
||||
CHECK(pr->ack == ObjectAck::Success);
|
||||
}
|
||||
|
||||
TEST_CASE("S14F9 / S14F10 CreateControlJob round-trip") {
|
||||
auto req = s14f9_create_control_job("CJ-1", {"PJ-1", "PJ-2"});
|
||||
CHECK(req.stream == 14);
|
||||
|
||||
Reference in New Issue
Block a user