#pragma once #include #include #include #include #include #include #include "secsgem/gem/data_model.hpp" #include "secsgem/secs2/item.hpp" #include "secsgem/secs2/message.hpp" // Hand-written helpers used by the generated messages.hpp: scalar accessors // (one per SECS-II type), a few list helpers, and the two special-case // messages whose shape doesn't fit the codegen schema (S1F4 needs per-row // optional values; S5F6 needs a callback to compute ALCD per alarm). namespace secsgem::gem { namespace s2 = secsgem::secs2; namespace secs2 = secsgem::secs2; // alias used by generated code // ---- Scalar accessors ---------------------------------------------------- inline std::optional as_ascii(const s2::Item& item) { if (item.format() != s2::Format::ASCII) return std::nullopt; return item.as_ascii(); } inline std::optional as_binary_string(const s2::Item& item) { if (item.format() != s2::Format::Binary) return std::nullopt; const auto& v = item.as_bytes(); return std::string(v.begin(), v.end()); } inline std::optional as_binary_first(const s2::Item& item) { if (item.format() != s2::Format::Binary) return std::nullopt; const auto& v = item.as_bytes(); if (v.empty()) return std::nullopt; return v.front(); } inline std::optional as_boolean(const s2::Item& item) { if (item.format() != s2::Format::Boolean) return std::nullopt; const auto& v = item.as_bytes(); if (v.empty()) return std::nullopt; return v.front() != 0; } // Templated typed-scalar accessor — one specialization per SECS-II numeric. template inline std::optional first_or_none(const s2::Item& item, s2::Format want) { if (item.format() != want) return std::nullopt; const auto& v = std::get(item.storage()); if (v.empty()) return std::nullopt; return v.front(); } inline std::optional as_u1_scalar(const s2::Item& i) { return first_or_none>(i, s2::Format::U1); } inline std::optional as_u2_scalar(const s2::Item& i) { return first_or_none>(i, s2::Format::U2); } inline std::optional as_u4_scalar(const s2::Item& i) { return first_or_none>(i, s2::Format::U4); } inline std::optional as_u8_scalar(const s2::Item& i) { return first_or_none>(i, s2::Format::U8); } inline std::optional as_i1_scalar(const s2::Item& i) { return first_or_none>(i, s2::Format::I1); } inline std::optional as_i2_scalar(const s2::Item& i) { return first_or_none>(i, s2::Format::I2); } inline std::optional as_i4_scalar(const s2::Item& i) { return first_or_none>(i, s2::Format::I4); } inline std::optional as_i8_scalar(const s2::Item& i) { return first_or_none>(i, s2::Format::I8); } inline std::optional as_f4_scalar(const s2::Item& i) { return first_or_none>(i, s2::Format::F4); } inline std::optional as_f8_scalar(const s2::Item& i) { return first_or_none>(i, s2::Format::F8); } // ---- List helpers -------------------------------------------------------- inline s2::Item u4_list_item(const std::vector& ids) { s2::Item::List children; children.reserve(ids.size()); for (auto id : ids) children.push_back(s2::Item::u4(id)); return s2::Item::list(std::move(children)); } inline std::optional> parse_u4_list_body(const s2::Message& m) { if (!m.body || !m.body->is_list()) return std::nullopt; std::vector out; for (const auto& c : m.body->as_list()) { auto v = as_u4_scalar(c); if (!v) return std::nullopt; out.push_back(*v); } return out; } // Generic "body is " reader, used by tests + apps as a quick helper. inline std::optional ack_byte(const s2::Message& m) { if (!m.body) return std::nullopt; return as_binary_first(*m.body); } // ---- S1F4: list of values, nullopt -> ----------------------------- inline s2::Message s1f4_selected_status_data( const std::vector>& values) { s2::Item::List children; children.reserve(values.size()); for (const auto& v : values) { children.push_back(v ? *v : s2::Item::list({})); } return s2::Message(1, 4, false, s2::Item::list(std::move(children))); } // ---- S5F6: alarm directory; ALCD bit-7 from per-row callback ------------ inline s2::Message s5f6_list_alarms_data(const std::vector& alarms, const std::function& active) { s2::Item::List rows; rows.reserve(alarms.size()); for (const auto& a : alarms) { const uint8_t alcd = (a.severity_category & 0x7F) | static_cast(active(a.id) ? 0x80 : 0x00); rows.push_back(s2::Item::list( {s2::Item::binary({alcd}), s2::Item::u4(a.id), s2::Item::ascii(a.text)})); } return s2::Message(5, 6, false, s2::Item::list(std::move(rows))); } // ---- ALED byte constants for S5F3 --------------------------------------- inline constexpr uint8_t kAlarmEnableByte = 0x80; inline constexpr uint8_t kAlarmDisableByte = 0x00; } // namespace secsgem::gem