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:
@@ -5,6 +5,7 @@
|
||||
#include "secsgem/gem/store/equipment_constants.hpp"
|
||||
#include "secsgem/gem/store/event_reports.hpp"
|
||||
#include "secsgem/gem/store/host_commands.hpp"
|
||||
#include "secsgem/gem/store/limits.hpp"
|
||||
#include "secsgem/gem/store/recipes.hpp"
|
||||
#include "secsgem/gem/store/spool.hpp"
|
||||
#include "secsgem/gem/store/status_variables.hpp"
|
||||
@@ -26,6 +27,7 @@ struct EquipmentDataModel {
|
||||
Clock clock;
|
||||
HostCommandRegistry commands;
|
||||
SpoolStore spool;
|
||||
LimitMonitorStore limits;
|
||||
|
||||
// Convenience: VID -> value lookup spanning SVIDs and DVIDs.
|
||||
std::optional<s2::Item> vid_value(uint32_t vid) const {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user