Files
secs-gem/include/secsgem/gem/store/limits.hpp
raphael 224130d99f
tests / build-and-test (push) Failing after 35s
100%/C: Limits Monitoring (S2F45–F48, E30 §6.21)
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>
2026-06-07 02:08:19 +02:00

65 lines
1.7 KiB
C++

#pragma once
#include <cstdint>
#include <map>
#include <utility>
#include <vector>
#include "secsgem/secs2/item.hpp"
// E30 §6.21 Limits Monitoring.
//
// A limit definition attaches an upper/lower deadband pair to a VID under
// some limit id. Multiple definitions per VID are allowed (different
// LIMITIDs can monitor different ranges). Store is pure data; the actual
// "value crossed limit" detection and CEID emission is the application's
// job (typically wired into the SVID/DVID set_value path).
namespace secsgem::gem {
namespace s2 = secsgem::secs2;
// Defined here (not in the generated messages.hpp) so the data model can
// own the type and the codegen can reference it as external_struct.
struct LimitDefinition {
uint8_t limitid;
s2::Item upper;
s2::Item lower;
};
enum class LimitMonitorAck : uint8_t { // S2F46 VLAACK
Accept = 0,
LimitsNotEqualToExisting = 1,
CannotChangeAttributes = 2,
LimitValueError = 3,
VidNotExist = 4,
VidNoLimitSupport = 5,
};
class LimitMonitorStore {
public:
void set_for_vid(uint32_t vid, std::vector<LimitDefinition> defs) {
by_vid_.insert_or_assign(vid, std::move(defs));
}
void clear() { by_vid_.clear(); }
std::vector<LimitDefinition> get_for_vid(uint32_t vid) const {
auto it = by_vid_.find(vid);
if (it == by_vid_.end()) return {};
return it->second;
}
std::vector<uint32_t> all_vids() const {
std::vector<uint32_t> out;
out.reserve(by_vid_.size());
for (const auto& [vid, _] : by_vid_) out.push_back(vid);
return out;
}
bool has(uint32_t vid) const { return by_vid_.count(vid) > 0; }
std::size_t size() const { return by_vid_.size(); }
private:
std::map<uint32_t, std::vector<LimitDefinition>> by_vid_;
};
} // namespace secsgem::gem