Adds the GEM "Documentation" Fundamental capability: the equipment now
self-reports which GEM capabilities it supports, and the host can
discover the DVID namelist with the same shape used for SVIDs.
Catalog (data/messages.yaml -> generated messages.hpp)
S1F19 W header-only Get GEM Compliance Request
S1F20 <L,3 <A SOFTREV>
<A EQPTYP>
<L,a <L,2 <U1 CCODE> <A CDESC>>>>
Get GEM Compliance Data
S1F21 W <L,n <U4 VID>> DVID Namelist Request (n=0 = all)
S1F22 <L,n <L,3 <U4 VID>
<A VNAME>
<A UNITS>>> DVID Namelist Data
Codegen emits CapabilityEntry and GemCompliance structs. S1F22 reuses
S1F12's StatusName struct (same wire shape; dedup avoids redefinition).
Equipment data dictionary (data/equipment.yaml)
device: Adds `equipment_type: "EQUIPMENT"`
for the S1F20 EQPTYP field.
capabilities: New section. List of
- {code, name} (CCODE, CDESC) pairs honestly
reflecting what the codebase
implements: 1, 2, 3, 5, 6, 7, 8,
9, 11, 12, 14 (partial), 15.
dvids: New section, same schema as
svids:. Demo populates two:
- WaferCounter (U4, units wafer)
- ChamberPressure (F4, units Torr)
Loader (src/config/loader.cpp + include/secsgem/config/loader.hpp)
EquipmentDescriptor gains equipment_type and capabilities (vector of
(uint8_t, string) pairs). load_equipment now reads `capabilities:`
into the descriptor and `dvids:` into model.dvids.
Server (apps/secs_server.cpp)
router.on(1, 19) returns S1F20 with desc.software_rev,
desc.equipment_type, and desc.capabilities converted to
vector<CapabilityEntry>.
router.on(1, 21) returns S1F22 built from model.dvids.all().
Client (apps/secs_client.cpp)
Two new demo steps after Request Online and before SVID discovery:
S1F19 -> S1F20: logs SOFTREV, EQPTYP, and every (CCODE, CDESC)
the equipment claims.
S1F21 -> S1F22: logs each DVID with units.
Tests
tests/test_messages.cpp Round-trip S1F19/F20 with a 3-entry
capability list; round-trip S1F22 with two
DVIDs.
tests/test_loader.cpp Asserts equipment_type, the capabilities
list contains CCODE 14 (Spooling), and the
two DVIDs land in model.dvids.
COMPLIANCE.md
"Documentation" Fundamental moves from ⬜ to ✅.
S1F19/F20 + S1F21/F22 rows in the coverage matrix flip to ✅.
The "what would it take" list drops the documentation-messages bullet.
Verified
- Tests: 77 cases / 444 assertions pass.
- Demo: client logs the full capability list received from the
equipment, including CCODE 14 "Spooling (partial; S2F43/F44 +
S6F23/F24)" — the equipment honestly reports its partial
implementation rather than overclaiming.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -147,6 +147,42 @@ int main(int argc, char** argv) {
|
||||
});
|
||||
});
|
||||
|
||||
// 2a. Ask the equipment to self-report its GEM compliance.
|
||||
seq->steps.push_back([conn, logfn, fail](auto next) {
|
||||
conn->send_request(gem::s1f19_get_gem_compliance_request(),
|
||||
[logfn, fail, next](std::error_code ec, const s2::Message& reply) {
|
||||
if (ec) { fail("S1F19", ec); return; }
|
||||
auto parsed = gem::parse_s1f20(reply);
|
||||
if (parsed) {
|
||||
logfn("S1F20 SOFTREV=" + parsed->softrev +
|
||||
" TYPE=" + parsed->equipment_type +
|
||||
" (" + std::to_string(parsed->capabilities.size()) +
|
||||
" capabilities)");
|
||||
for (const auto& c : parsed->capabilities) {
|
||||
logfn(" CCODE " + std::to_string(c.ccode) + " " + c.cdesc);
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
// 2b. Discover DVIDs.
|
||||
seq->steps.push_back([conn, logfn, fail](auto next) {
|
||||
conn->send_request(gem::s1f21_data_variable_namelist_request({}),
|
||||
[logfn, fail, next](std::error_code ec, const s2::Message& reply) {
|
||||
if (ec) { fail("S1F21", ec); return; }
|
||||
auto parsed = gem::parse_s1f22(reply);
|
||||
if (parsed) {
|
||||
logfn("S1F22 (" + std::to_string(parsed->size()) + " DVIDs)");
|
||||
for (const auto& dn : *parsed) {
|
||||
logfn(" DVID " + std::to_string(dn.id) + " " + dn.name +
|
||||
(dn.units.empty() ? "" : " [" + dn.units + "]"));
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
// 3. Discover SVIDs.
|
||||
seq->steps.push_back([conn, logfn, fail, svids](auto next) {
|
||||
conn->send_request(gem::s1f11_status_namelist_request({}),
|
||||
|
||||
@@ -178,6 +178,19 @@ int main(int argc, char** argv) {
|
||||
logfn("S1F17 -> S1F18 ONLACK=" + std::to_string(static_cast<int>(ack)));
|
||||
return gem::s1f18_online_ack(ack);
|
||||
});
|
||||
router.on(1, 19, [desc, logfn](const s2::Message&) {
|
||||
std::vector<gem::CapabilityEntry> caps;
|
||||
for (const auto& c : desc.capabilities) caps.push_back({c.first, c.second});
|
||||
logfn("S1F19 -> S1F20 (" + std::to_string(caps.size()) + " capabilities)");
|
||||
return gem::s1f20_get_gem_compliance_data(desc.software_rev, desc.equipment_type, caps);
|
||||
});
|
||||
router.on(1, 21, [model, logfn](const s2::Message&) {
|
||||
std::vector<gem::StatusName> rows;
|
||||
for (const auto& dv : model->dvids.all())
|
||||
rows.push_back({dv.id, dv.name, dv.units});
|
||||
logfn("S1F21 -> S1F22 (" + std::to_string(rows.size()) + " DVIDs)");
|
||||
return gem::s1f22_data_variable_namelist_data(rows);
|
||||
});
|
||||
|
||||
router.on(2, 13, [model, logfn](const s2::Message& msg) -> std::optional<s2::Message> {
|
||||
auto ids = gem::parse_u4_list_body(msg);
|
||||
|
||||
Reference in New Issue
Block a user