Files
secs-gem/include/secsgem/gem/store/alarms.hpp
T
raphael c163d2060f C3: AlarmSeverity bit-flag enum + classification helpers
ALCD's lower 7 bits are a bitmap of category flags per E5 §10.3 and
E30 §6.13; a single alarm may carry multiple categories at once
(e.g. an irrecoverable equipment-safety fault is 0x10 | 0x02).
Adds:

  enum class AlarmSeverity : uint8_t
    PersonalSafety  EquipmentSafety  ParameterError  ParameterWarning
    Irrecoverable   EquipmentStatus  Attention

  has_severity(alcd, bit), severity_bits(alcd)
  Alarm::has(bit), Alarm::is_safety()
  constexpr severity_mask = 0x7F

Tests cover single-category alarms, multi-category combos, and that
the bit-7 SET/CLEAR flag is correctly excluded from category bits.

Closes Tranche C (E5 alarm/exception state model).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 23:52:09 +02:00

102 lines
3.1 KiB
C++

#pragma once
#include <cstdint>
#include <map>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>
namespace secsgem::gem {
// ALCD bit-flag categories (E5 §10.3 / E30 §6.13). The lower 7 bits
// of ALCD are a bitmap — an alarm may carry multiple categories at
// once (e.g. an irrecoverable equipment-safety fault is 0x10 | 0x02).
// Bit 7 is the alarm SET/CLEAR flag, applied at emit time and not
// part of the category bitmap.
enum class AlarmSeverity : uint8_t {
PersonalSafety = 0x01,
EquipmentSafety = 0x02,
ParameterError = 0x04, // parameter control error
ParameterWarning = 0x08, // parameter control warning
Irrecoverable = 0x10,
EquipmentStatus = 0x20, // equipment status warning
Attention = 0x40, // attention flag (lowest-priority)
};
inline constexpr uint8_t severity_mask = 0x7F;
inline bool has_severity(uint8_t alcd, AlarmSeverity bit) {
return (alcd & static_cast<uint8_t>(bit)) != 0;
}
inline uint8_t severity_bits(uint8_t alcd) { return alcd & severity_mask; }
struct Alarm {
uint32_t id;
std::string text;
// Lower 7 bits of ALCD: severity bitmap (see AlarmSeverity). Bit 7
// marks set vs cleared and is applied at emit time.
uint8_t severity_category;
bool has(AlarmSeverity bit) const { return has_severity(severity_category, bit); }
bool is_safety() const {
return has(AlarmSeverity::PersonalSafety) || has(AlarmSeverity::EquipmentSafety);
}
};
enum class AlarmAck : uint8_t {
Accept = 0,
Error = 1,
};
class AlarmRegistry {
public:
void add(Alarm a) { by_id_.insert_or_assign(a.id, std::move(a)); }
std::optional<Alarm> get(uint32_t id) const {
auto it = by_id_.find(id);
if (it == by_id_.end()) return std::nullopt;
return it->second;
}
std::vector<Alarm> all() const {
std::vector<Alarm> out;
out.reserve(by_id_.size());
for (const auto& [_, a] : by_id_) out.push_back(a);
return out;
}
bool has(uint32_t id) const { return by_id_.count(id) > 0; }
// S5F3 enable / disable.
AlarmAck set_enabled(uint32_t id, bool enable) {
if (!by_id_.count(id)) return AlarmAck::Error;
if (enable) enabled_.insert(id);
else enabled_.erase(id);
return AlarmAck::Accept;
}
bool enabled(uint32_t id) const { return enabled_.count(id) > 0; }
// Trigger set / clear; returns the ALCD byte to put on the wire (bit 7
// is the set flag, lower 7 carry the category). std::nullopt on unknown.
std::optional<uint8_t> set_active(uint32_t id) {
auto it = by_id_.find(id);
if (it == by_id_.end()) return std::nullopt;
active_.insert(id);
return static_cast<uint8_t>((it->second.severity_category & 0x7F) | 0x80);
}
std::optional<uint8_t> clear_active(uint32_t id) {
auto it = by_id_.find(id);
if (it == by_id_.end()) return std::nullopt;
active_.erase(id);
return static_cast<uint8_t>(it->second.severity_category & 0x7F);
}
bool active(uint32_t id) const { return active_.count(id) > 0; }
private:
std::map<uint32_t, Alarm> by_id_;
std::set<uint32_t> enabled_;
std::set<uint32_t> active_;
};
} // namespace secsgem::gem