100%/C: Limits Monitoring (S2F45–F48, E30 §6.21)
tests / build-and-test (push) Failing after 35s

New LimitMonitorStore keyed by VID; each entry is a vector of
LimitDefinition (LIMITID + upper/lower deadband as arbitrary Items).
S2F45/F46 set, S2F47/F48 read.  VLAACK validates each VID exists.

Four new SxFy in the catalog; codegen handles the nested
list-of-(VID, list-of-LimitDefinition) shape.  LimitDefinition is
defined in store/limits.hpp and referenced as external_struct so the
data model and the message codecs share one type.

The actual "value crossed limit" detection + CEID emission is left to
the application's set_value path (E30 §6.21 leaves *how* the equipment
detects crossings up to the implementer).

COMPLIANCE.md: Limits Monitoring Additional capability flips .
Tests: 80 cases / 465 assertions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 02:08:19 +02:00
parent 65db38d9f2
commit 224130d99f
6 changed files with 202 additions and 2 deletions
+28
View File
@@ -290,6 +290,34 @@ int main(int argc, char** argv) {
return gem::s2f42_host_command_ack(result.ack, {});
});
router.on(2, 45, [model, logfn](const s2::Message& msg) {
auto req = gem::parse_s2f45(msg);
auto ack = gem::LimitMonitorAck::Accept;
if (!req) {
ack = gem::LimitMonitorAck::LimitValueError;
} else {
for (const auto& entry : req->entries) {
if (!model->vid_exists(entry.vid)) { ack = gem::LimitMonitorAck::VidNotExist; break; }
}
if (ack == gem::LimitMonitorAck::Accept) {
for (const auto& entry : req->entries)
model->limits.set_for_vid(entry.vid, entry.limits);
}
}
logfn("S2F45 -> S2F46 VLAACK=" + std::to_string(static_cast<int>(ack)));
return gem::s2f46_define_variable_limits_ack(ack);
});
router.on(2, 47, [model, logfn](const s2::Message& msg) {
auto vids = gem::parse_s2f47(msg);
std::vector<gem::VidLimitsEntry> rows;
if (vids) {
const auto target = vids->empty() ? model->limits.all_vids() : *vids;
for (auto v : target) rows.push_back({v, model->limits.get_for_vid(v)});
}
logfn("S2F47 -> S2F48 (" + std::to_string(rows.size()) + " entries)");
return gem::s2f48_variable_limit_attribute_data(rows);
});
router.on(2, 43, [model, logfn](const s2::Message& msg) {
auto streams = gem::parse_s2f43(msg);
auto ack = gem::ResetSpoolAck::Accept;