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:
@@ -824,6 +824,51 @@ int main(int argc, char** argv) {
|
||||
return gem::s7f20_current_eppd_data(list);
|
||||
});
|
||||
|
||||
// ---- E39 generic ObjectService ----------------------------------------
|
||||
// S14F1 GetAttr / S14F3 SetAttr against the CemObjectStore. OBJTYPE
|
||||
// is validated against the stored object's type name (case-sensitive).
|
||||
router.on(14, 1, [model, logfn](const s2::Message& msg) {
|
||||
auto req = gem::parse_s14f1(msg);
|
||||
if (!req) return gem::s14f2_get_attr_data({}, gem::ObjectAck::Error);
|
||||
auto* obj = model->cem.get(req->objspec);
|
||||
if (!obj)
|
||||
return gem::s14f2_get_attr_data({}, gem::ObjectAck::Denied_UnknownObject);
|
||||
if (gem::cem_object_type_name(obj->objtype) != req->objtype)
|
||||
return gem::s14f2_get_attr_data({}, gem::ObjectAck::Denied_InvalidAttribute);
|
||||
std::vector<gem::AttrValue> attrs;
|
||||
attrs.reserve(req->attrids.size());
|
||||
for (const auto& id : req->attrids) {
|
||||
auto v = model->cem.get_attr(req->objspec, id);
|
||||
attrs.push_back({id, v.value_or(s2::Item::ascii(""))});
|
||||
}
|
||||
logfn("S14F1 " + req->objspec + " (" +
|
||||
std::to_string(req->attrids.size()) + " attrs) -> S14F2");
|
||||
return gem::s14f2_get_attr_data(attrs, gem::ObjectAck::Success);
|
||||
});
|
||||
router.on(14, 3, [model, logfn](const s2::Message& msg) {
|
||||
auto req = gem::parse_s14f3(msg);
|
||||
if (!req)
|
||||
return gem::s14f4_set_attr_ack({}, gem::ObjectAck::Error);
|
||||
auto* obj = model->cem.get(req->objspec);
|
||||
if (!obj)
|
||||
return gem::s14f4_set_attr_ack({}, gem::ObjectAck::Denied_UnknownObject);
|
||||
if (gem::cem_object_type_name(obj->objtype) != req->objtype)
|
||||
return gem::s14f4_set_attr_ack({}, gem::ObjectAck::Denied_InvalidAttribute);
|
||||
for (const auto& a : req->attrs) {
|
||||
model->cem.set_attr(req->objspec, a.attrid, a.value);
|
||||
}
|
||||
// Reply echoes back the now-stored values.
|
||||
std::vector<gem::AttrValue> reply;
|
||||
reply.reserve(req->attrs.size());
|
||||
for (const auto& a : req->attrs) {
|
||||
auto v = model->cem.get_attr(req->objspec, a.attrid);
|
||||
reply.push_back({a.attrid, v.value_or(s2::Item::ascii(""))});
|
||||
}
|
||||
logfn("S14F3 " + req->objspec + " (" +
|
||||
std::to_string(req->attrs.size()) + " attrs) -> S14F4");
|
||||
return gem::s14f4_set_attr_ack(reply, gem::ObjectAck::Success);
|
||||
});
|
||||
|
||||
// ---- E40 / E94 -------------------------------------------------------
|
||||
router.on(14, 9, [model, logfn, run_cj_lifecycle](const s2::Message& msg) {
|
||||
(void)run_cj_lifecycle;
|
||||
|
||||
Reference in New Issue
Block a user