From c163d2060f35240e50b8af722f7ea7c65704c2db Mon Sep 17 00:00:00 2001 From: Raphael Maenle Date: Sun, 7 Jun 2026 23:52:09 +0200 Subject: [PATCH] C3: AlarmSeverity bit-flag enum + classification helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- include/secsgem/gem/store/alarms.hpp | 33 +++++++++++++++++++++++++--- tests/test_data_model.cpp | 28 +++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/include/secsgem/gem/store/alarms.hpp b/include/secsgem/gem/store/alarms.hpp index 60755eb..c609095 100644 --- a/include/secsgem/gem/store/alarms.hpp +++ b/include/secsgem/gem/store/alarms.hpp @@ -10,13 +10,40 @@ 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(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 category (1=personal safety, - // 2=equipment safety, 4=parameter control error, ...). Bit 7 marks - // set vs cleared and is applied at emit time. + // 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 { diff --git a/tests/test_data_model.cpp b/tests/test_data_model.cpp index 869c906..a6a2395 100644 --- a/tests/test_data_model.cpp +++ b/tests/test_data_model.cpp @@ -169,6 +169,34 @@ TEST_CASE("alarm enable / set / clear / list") { CHECK(r.all().size() == 2); } +TEST_CASE("AlarmSeverity bit-flag helpers (E5 §10.3 / E30 §6.13)") { + // Single-category alarms. + Alarm safety{1, "door open", static_cast(AlarmSeverity::PersonalSafety)}; + CHECK(safety.has(AlarmSeverity::PersonalSafety)); + CHECK_FALSE(safety.has(AlarmSeverity::EquipmentSafety)); + CHECK(safety.is_safety()); + + Alarm temp_warn{2, "chiller high", + static_cast(AlarmSeverity::ParameterWarning)}; + CHECK_FALSE(temp_warn.is_safety()); + CHECK(temp_warn.has(AlarmSeverity::ParameterWarning)); + + // Multi-category alarms: combine Irrecoverable + EquipmentSafety. + Alarm combo{3, "spindle seized", + static_cast( + static_cast(AlarmSeverity::EquipmentSafety) | + static_cast(AlarmSeverity::Irrecoverable))}; + CHECK(combo.is_safety()); + CHECK(combo.has(AlarmSeverity::Irrecoverable)); + CHECK(combo.has(AlarmSeverity::EquipmentSafety)); + CHECK_FALSE(combo.has(AlarmSeverity::PersonalSafety)); + + // The ALCD set-bit is *not* part of the category bitmap. + const uint8_t set_alcd = static_cast(combo.severity_category | 0x80); + CHECK(severity_bits(set_alcd) == combo.severity_category); + CHECK(has_severity(set_alcd, AlarmSeverity::Irrecoverable)); +} + // ---- Process programs ---------------------------------------------------- TEST_CASE("recipe CRUD") {