增加告警热重载功能

This commit is contained in:
GuanYuankai 2025-11-04 01:40:09 +00:00
parent 4e8cb2e4a0
commit 59436351cd
2 changed files with 418 additions and 337 deletions

View File

@ -1,18 +1,17 @@
// alarm/alarm_service.cpp // alarm/alarm_service.cpp
#include "alarm_service.h" #include "alarm_service.h"
#include <fstream>
#include <chrono> #include <chrono>
#include <fstream>
AlarmService::AlarmService(boost::asio::io_context& io, AlarmService::AlarmService(boost::asio::io_context& io,
PiperTTSInterface& tts_service, PiperTTSInterface& tts_service,
MqttClient& mqtt_client, MqttClient& mqtt_client, DataStorage& data_storage)
DataStorage& data_storage)
: m_io_context(io), : m_io_context(io),
m_tts_service(tts_service), m_tts_service(tts_service),
m_mqtt_client(mqtt_client), m_mqtt_client(mqtt_client),
m_data_storage(data_storage), m_data_storage(data_storage),
m_tts_running(true) { m_tts_running(true) {
m_tts_thread = std::thread(&AlarmService::tts_worker, this); m_tts_thread = std::thread(&AlarmService::tts_worker, this);
spdlog::info("AlarmService created and TTS worker thread started."); spdlog::info("AlarmService created and TTS worker thread started.");
} }
@ -36,66 +35,70 @@ void AlarmService::stop() {
} }
bool AlarmService::load_rules(const std::string& config_path) { bool AlarmService::load_rules(const std::string& config_path) {
std::ifstream ifs(config_path); m_rules_config_path = config_path;
if (!ifs.is_open()) {
spdlog::error("Failed to open alarm rules file: {}", config_path); std::vector<AlarmRule> new_rules;
std::map<std::string, AlarmState> new_states;
if (!parse_rules_from_file(config_path, new_rules, new_states)) {
return false; return false;
} }
try { {
json j_rules = json::parse(ifs); std::lock_guard<std::mutex> lock(m_rules_mutex);
for (const auto& j_rule : j_rules) { m_rules = std::move(new_rules);
AlarmRule rule; m_alarm_states = std::move(new_states);
rule.rule_id = j_rule.at("rule_id");
rule.device_id = j_rule.at("device_id");
rule.data_point_name = j_rule.at("data_point_name");
rule.compare_type = AlarmRule::stringToCompareType(j_rule.at("compare_type"));
rule.threshold = j_rule.at("threshold");
rule.level = AlarmRule::stringToLevel(j_rule.at("level"));
rule.message_template = j_rule.at("message_template");
// 可选字段
rule.debounce_seconds = j_rule.value("debounce_seconds", 0);
rule.alarm_mqtt_topic = j_rule.value("alarm_mqtt_topic", "");
rule.clear_message_template = j_rule.value("clear_message_template", "");
if (rule.compare_type == CompareType::UNKNOWN) {
spdlog::warn("Skipping rule '{}': Unknown compare_type.", rule.rule_id);
continue;
} }
m_rules.push_back(std::move(rule)); spdlog::info("Successfully loaded {} alarm rules from '{}'.", m_rules.size(),
config_path);
// 为每条规则初始化一个状态机
m_alarm_states.emplace(
std::piecewise_construct,
std::forward_as_tuple(rule.rule_id),
std::forward_as_tuple(m_io_context)
);
}
} catch (const json::exception& e) {
spdlog::error("Failed to parse alarm rules file '{}': {}", config_path, e.what());
return false;
}
spdlog::info("Successfully loaded {} alarm rules from '{}'.", m_rules.size(), config_path);
return true; return true;
} }
void AlarmService::process_device_data(const std::string& device_id, const std::string& data_json) { bool AlarmService::reload_rules() {
if (m_rules_config_path.empty()) {
spdlog::error(
"Cannot reload rules: config path not set (load_rules was never "
"called?).");
return false;
}
spdlog::info("Attempting to reload alarm rules from '{}'...",
m_rules_config_path);
std::vector<AlarmRule> new_rules;
std::map<std::string, AlarmState> new_states;
if (!parse_rules_from_file(m_rules_config_path, new_rules, new_states)) {
spdlog::error(
"Failed to parse new rules file, aborting reload. Service continues "
"with old rules.");
return false;
}
{
std::lock_guard<std::mutex> lock(m_rules_mutex);
m_rules = std::move(new_rules);
m_alarm_states = std::move(new_states);
}
spdlog::info("Successfully reloaded {} alarm rules.", m_rules.size());
return true;
}
void AlarmService::process_device_data(const std::string& device_id,
const std::string& data_json) {
std::lock_guard<std::mutex> lock(m_rules_mutex);
try { try {
json j_data = json::parse(data_json); json j_data = json::parse(data_json);
if (!j_data.is_object()) return; if (!j_data.is_object()) return;
// 遍历所有规则
for (auto& rule : m_rules) { for (auto& rule : m_rules) {
// 匹配 device_id (支持通配符 * 或 "proxy_system")
if (rule.device_id == device_id || rule.device_id == "*") { if (rule.device_id == device_id || rule.device_id == "*") {
// 检查规则关心的数据点是否存在
if (j_data.contains(rule.data_point_name)) { if (j_data.contains(rule.data_point_name)) {
if (j_data[rule.data_point_name].is_number()) { if (j_data[rule.data_point_name].is_number()) {
double value = j_data[rule.data_point_name].get<double>(); double value = j_data[rule.data_point_name].get<double>();
// 调用核心逻辑
check_rule_against_value(rule, value, device_id); check_rule_against_value(rule, value, device_id);
} }
} }
@ -107,6 +110,7 @@ void AlarmService::process_device_data(const std::string& device_id, const std::
} }
void AlarmService::process_system_data(const std::string& system_data_json) { void AlarmService::process_system_data(const std::string& system_data_json) {
std::lock_guard<std::mutex> lock(m_rules_mutex);
try { try {
json j_data = json::parse(system_data_json); json j_data = json::parse(system_data_json);
if (!j_data.is_object()) return; if (!j_data.is_object()) return;
@ -114,7 +118,6 @@ void AlarmService::process_system_data(const std::string& system_data_json) {
const std::string device_id = "proxy_system"; // 系统数据的固定ID const std::string device_id = "proxy_system"; // 系统数据的固定ID
for (auto& rule : m_rules) { for (auto& rule : m_rules) {
// 只匹配 "proxy_system"
if (rule.device_id == device_id) { if (rule.device_id == device_id) {
if (j_data.contains(rule.data_point_name)) { if (j_data.contains(rule.data_point_name)) {
if (j_data[rule.data_point_name].is_number()) { if (j_data[rule.data_point_name].is_number()) {
@ -129,108 +132,117 @@ void AlarmService::process_system_data(const std::string& system_data_json) {
} }
} }
void AlarmService::check_rule_against_value(AlarmRule& rule, double value, const std::string& actual_device_id) { void AlarmService::check_rule_against_value(
AlarmRule& rule, double value, const std::string& actual_device_id) {
bool condition_met = false; bool condition_met = false;
switch (rule.compare_type) { switch (rule.compare_type) {
case CompareType::GT: condition_met = (value > rule.threshold); break; case CompareType::GT:
case CompareType::LT: condition_met = (value < rule.threshold); break; condition_met = (value > rule.threshold);
case CompareType::EQ: condition_met = (std::abs(value - rule.threshold) < 1e-9); break; break;
default: break; case CompareType::LT:
condition_met = (value < rule.threshold);
break;
case CompareType::EQ:
condition_met = (std::abs(value - rule.threshold) < 1e-9);
break;
default:
break;
} }
auto it = m_alarm_states.find(rule.rule_id);
auto it = m_alarm_states.find(rule.rule_id); if (it == m_alarm_states.end()) {
spdlog::warn(
"AlarmState for rule '{}' was missing. Creating it on-the-fly.",
rule.rule_id);
if (it == m_alarm_states.end()) { auto result = m_alarm_states.emplace(std::piecewise_construct,
spdlog::warn("AlarmState for rule '{}' was missing. Creating it on-the-fly.", rule.rule_id);
auto result = m_alarm_states.emplace(
std::piecewise_construct,
std::forward_as_tuple(rule.rule_id), std::forward_as_tuple(rule.rule_id),
std::forward_as_tuple(m_io_context) std::forward_as_tuple(m_io_context));
); it = result.first;
it = result.first; // 'it' 现在指向新创建的元素 }
}
AlarmState& state = it->second; AlarmState& state = it->second;
if (condition_met) { if (condition_met) {
// 条件满足
if (state.current_state == AlarmStateType::NORMAL) { if (state.current_state == AlarmStateType::NORMAL) {
state.current_state = AlarmStateType::PENDING; state.current_state = AlarmStateType::PENDING;
if (rule.debounce_seconds <= 0) { if (rule.debounce_seconds <= 0) {
// 立即触发
state.current_state = AlarmStateType::ACTIVE; state.current_state = AlarmStateType::ACTIVE;
trigger_alarm_action(rule, value, actual_device_id); trigger_alarm_action(rule, value, actual_device_id);
} else { } else {
// 启动防抖计时器
spdlog::debug("Alarm PENDING: {}", rule.rule_id); spdlog::debug("Alarm PENDING: {}", rule.rule_id);
state.debounce_timer.expires_after(std::chrono::seconds(rule.debounce_seconds)); state.debounce_timer.expires_after(
std::chrono::seconds(rule.debounce_seconds));
std::string rule_id = rule.rule_id; // 捕获 rule_id std::string rule_id = rule.rule_id;
state.debounce_timer.async_wait( state.debounce_timer.async_wait([this, rule_id, value,
[this, rule_id, value, actual_device_id](const boost::system::error_code& ec) { actual_device_id](
const boost::system::error_code&
ec) {
std::lock_guard<std::mutex> lock(m_rules_mutex);
if (ec == boost::asio::error::operation_aborted) { if (ec == boost::asio::error::operation_aborted) {
// Timer被取消了 (因为数据恢复正常) spdlog::debug("Alarm PENDING cancelled (or reloaded): {}", rule_id);
spdlog::debug("Alarm PENDING cancelled: {}", rule_id);
return; return;
} }
auto it = m_alarm_states.find(rule_id);
if (it == m_alarm_states.end()) {
spdlog::debug(
"Alarm timer fired, but rule '{}' no longer exists (reloaded).",
rule_id);
return;
}
AlarmState& current_state = it->second;
// 计时器正常结束,告警激活
AlarmState& current_state = m_alarm_states.at(rule_id);
// 查找原始规则 (必须重新查找,因为 &rule 引用可能已失效)
AlarmRule* current_rule = nullptr; AlarmRule* current_rule = nullptr;
for(auto& r : m_rules) { if (r.rule_id == rule_id) { current_rule = &r; break; } } for (auto& r : m_rules) {
if (r.rule_id == rule_id) {
current_rule = &r;
break;
}
}
if (current_rule && current_state.current_state == AlarmStateType::PENDING) { if (current_rule &&
current_state.current_state == AlarmStateType::PENDING) {
current_state.current_state = AlarmStateType::ACTIVE; current_state.current_state = AlarmStateType::ACTIVE;
trigger_alarm_action(*current_rule, value, actual_device_id); trigger_alarm_action(*current_rule, value, actual_device_id);
} }
}); });
} }
} }
// else: 如果已经是 PENDING 或 ACTIVE保持不变 (防止重复触发)
} else { } else {
// 条件不满足 (数据恢复正常)
if (state.current_state == AlarmStateType::PENDING) { if (state.current_state == AlarmStateType::PENDING) {
// 状态PENDING -> NORMAL
// 在防抖期间恢复了,取消计时器
state.debounce_timer.cancel(); state.debounce_timer.cancel();
state.current_state = AlarmStateType::NORMAL; state.current_state = AlarmStateType::NORMAL;
} else if (state.current_state == AlarmStateType::ACTIVE) { } else if (state.current_state == AlarmStateType::ACTIVE) {
// 状态ACTIVE -> NORMAL
// 告警已激活,现在恢复了
state.current_state = AlarmStateType::NORMAL; state.current_state = AlarmStateType::NORMAL;
clear_alarm(rule, value, actual_device_id); clear_alarm(rule, value, actual_device_id);
} }
// else: 已经是 NORMAL保持不变
} }
} }
std::string AlarmService::format_message(const AlarmRule& rule, const std::string& template_str, double value, const std::string& actual_device_id) { std::string AlarmService::format_message(const AlarmRule& rule,
const std::string& template_str,
double value,
const std::string& actual_device_id) {
std::string msg = template_str; std::string msg = template_str;
// 替换 {device_id}
size_t pos = msg.find("{device_id}"); size_t pos = msg.find("{device_id}");
if (pos != std::string::npos) { if (pos != std::string::npos) {
msg.replace(pos, 11, actual_device_id); msg.replace(pos, 11, actual_device_id);
} }
// 替换 {value}
pos = msg.find("{value}"); pos = msg.find("{value}");
if (pos != std::string::npos) { if (pos != std::string::npos) {
// 格式化浮点数保留2位小数
char buffer[32]; char buffer[32];
std::snprintf(buffer, sizeof(buffer), "%.2f", value); std::snprintf(buffer, sizeof(buffer), "%.2f", value);
msg.replace(pos, 7, buffer); msg.replace(pos, 7, buffer);
} }
// 替换 {threshold}
pos = msg.find("{threshold}"); pos = msg.find("{threshold}");
if (pos != std::string::npos) { if (pos != std::string::npos) {
char buffer[32]; char buffer[32];
@ -240,8 +252,10 @@ std::string AlarmService::format_message(const AlarmRule& rule, const std::strin
return msg; return msg;
} }
void AlarmService::trigger_alarm_action(AlarmRule& rule, double value, const std::string& actual_device_id) { void AlarmService::trigger_alarm_action(AlarmRule& rule, double value,
std::string message = format_message(rule, rule.message_template, value, actual_device_id); const std::string& actual_device_id) {
std::string message =
format_message(rule, rule.message_template, value, actual_device_id);
spdlog::warn("[ALARM ACTIVE] (Rule: {}) {}", rule.rule_id, message); spdlog::warn("[ALARM ACTIVE] (Rule: {}) {}", rule.rule_id, message);
AlarmEvent event; AlarmEvent event;
@ -252,23 +266,26 @@ void AlarmService::trigger_alarm_action(AlarmRule& rule, double value, const std
event.message = message; event.message = message;
event.trigger_value = value; event.trigger_value = value;
event.timestamp_ms = std::chrono::duration_cast<std::chrono::seconds>( event.timestamp_ms = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch() std::chrono::system_clock::now().time_since_epoch())
).count(); .count();
// 1. 语音播报 (加入队列) // 1. 语音播报 (加入队列)
schedule_tts(message); schedule_tts(message);
// 2. 发布到MQTT // 2. 发布到MQTT
std::string topic = rule.alarm_mqtt_topic.empty() ? "proxy/alarms" : rule.alarm_mqtt_topic; std::string topic =
rule.alarm_mqtt_topic.empty() ? "proxy/alarms" : rule.alarm_mqtt_topic;
m_mqtt_client.publish(topic, AlarmEvent::toJson(event).dump(), 1, false); m_mqtt_client.publish(topic, AlarmEvent::toJson(event).dump(), 1, false);
// 3. 写入数据库 // 3. 写入数据库
if (!m_data_storage.storeAlarmEvent(event)) { if (!m_data_storage.storeAlarmEvent(event)) {
spdlog::error("Failed to store ACTIVE alarm event for rule: {}", rule.rule_id); spdlog::error("Failed to store ACTIVE alarm event for rule: {}",
rule.rule_id);
} }
} }
void AlarmService::clear_alarm(AlarmRule& rule, double value, const std::string& actual_device_id) { void AlarmService::clear_alarm(AlarmRule& rule, double value,
const std::string& actual_device_id) {
spdlog::info("[ALARM CLEARED] (Rule: {})", rule.rule_id); spdlog::info("[ALARM CLEARED] (Rule: {})", rule.rule_id);
// 检查是否有解除消息模板 // 检查是否有解除消息模板
@ -276,7 +293,8 @@ void AlarmService::clear_alarm(AlarmRule& rule, double value, const std::string&
return; // 没有模板,安静地解除 return; // 没有模板,安静地解除
} }
std::string message = format_message(rule, rule.clear_message_template, value, actual_device_id); std::string message = format_message(rule, rule.clear_message_template, value,
actual_device_id);
AlarmEvent event; AlarmEvent event;
event.rule_id = rule.rule_id; event.rule_id = rule.rule_id;
@ -286,19 +304,21 @@ void AlarmService::clear_alarm(AlarmRule& rule, double value, const std::string&
event.message = message; event.message = message;
event.trigger_value = value; event.trigger_value = value;
event.timestamp_ms = std::chrono::duration_cast<std::chrono::seconds>( event.timestamp_ms = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch() std::chrono::system_clock::now().time_since_epoch())
).count(); .count();
// 1. 语音播报 (加入队列) // 1. 语音播报 (加入队列)
schedule_tts(message); schedule_tts(message);
// 2. 发布到MQTT // 2. 发布到MQTT
std::string topic = rule.alarm_mqtt_topic.empty() ? "proxy/alarms" : rule.alarm_mqtt_topic; std::string topic =
rule.alarm_mqtt_topic.empty() ? "proxy/alarms" : rule.alarm_mqtt_topic;
m_mqtt_client.publish(topic, AlarmEvent::toJson(event).dump(), 1, false); m_mqtt_client.publish(topic, AlarmEvent::toJson(event).dump(), 1, false);
// 3. 写入数据库 // 3. 写入数据库
if (!m_data_storage.storeAlarmEvent(event)) { if (!m_data_storage.storeAlarmEvent(event)) {
spdlog::error("Failed to store CLEARED alarm event for rule: {}", rule.rule_id); spdlog::error("Failed to store CLEARED alarm event for rule: {}",
rule.rule_id);
} }
} }
@ -310,7 +330,8 @@ void AlarmService::tts_worker() {
{ {
std::unique_lock<std::mutex> lock(m_tts_queue_mutex); std::unique_lock<std::mutex> lock(m_tts_queue_mutex);
// 等待队列中有消息 或 线程被通知停止 // 等待队列中有消息 或 线程被通知停止
m_tts_cv.wait(lock, [this]{ return !m_tts_queue.empty() || !m_tts_running; }); m_tts_cv.wait(lock,
[this] { return !m_tts_queue.empty() || !m_tts_running; });
if (!m_tts_running && m_tts_queue.empty()) { if (!m_tts_running && m_tts_queue.empty()) {
break; // 退出 break; // 退出
@ -343,7 +364,7 @@ void AlarmService::schedule_tts(const std::string& text) {
nlohmann::json AlarmService::getActiveAlarmsJson() { nlohmann::json AlarmService::getActiveAlarmsJson() {
auto alarms = m_data_storage.getActiveAlarms(); auto alarms = m_data_storage.getActiveAlarms();
json j_alarms = json::array(); json j_alarms = json::array();
for(const auto& alarm : alarms) { for (const auto& alarm : alarms) {
j_alarms.push_back(AlarmEvent::toJson(alarm)); j_alarms.push_back(AlarmEvent::toJson(alarm));
} }
return j_alarms; return j_alarms;
@ -352,8 +373,61 @@ nlohmann::json AlarmService::getActiveAlarmsJson() {
nlohmann::json AlarmService::getAlarmHistoryJson(int limit) { nlohmann::json AlarmService::getAlarmHistoryJson(int limit) {
auto alarms = m_data_storage.getAlarmHistory(limit); auto alarms = m_data_storage.getAlarmHistory(limit);
json j_alarms = json::array(); json j_alarms = json::array();
for(const auto& alarm : alarms) { for (const auto& alarm : alarms) {
j_alarms.push_back(AlarmEvent::toJson(alarm)); j_alarms.push_back(AlarmEvent::toJson(alarm));
} }
return j_alarms; return j_alarms;
} }
bool AlarmService::parse_rules_from_file(
const std::string& config_path, std::vector<AlarmRule>& out_rules,
std::map<std::string, AlarmState>& out_states) {
std::ifstream ifs(config_path);
if (!ifs.is_open()) {
spdlog::error("Failed to open alarm rules file: {}", config_path);
return false;
}
try {
json j_rules = json::parse(ifs);
// 清空临时容器,准备接收新规则
out_rules.clear();
out_states.clear();
for (const auto& j_rule : j_rules) {
AlarmRule rule;
rule.rule_id = j_rule.at("rule_id");
rule.device_id = j_rule.at("device_id");
rule.data_point_name = j_rule.at("data_point_name");
rule.compare_type =
AlarmRule::stringToCompareType(j_rule.at("compare_type"));
rule.threshold = j_rule.at("threshold");
rule.level = AlarmRule::stringToLevel(j_rule.at("level"));
rule.message_template = j_rule.at("message_template");
rule.debounce_seconds = j_rule.value("debounce_seconds", 0);
rule.alarm_mqtt_topic = j_rule.value("alarm_mqtt_topic", "");
rule.clear_message_template = j_rule.value("clear_message_template", "");
if (rule.compare_type == CompareType::UNKNOWN) {
spdlog::warn("Skipping rule '{}': Unknown compare_type.", rule.rule_id);
continue;
}
out_rules.push_back(std::move(rule));
// 为每条规则准备一个状态机
out_states.emplace(std::piecewise_construct,
std::forward_as_tuple(rule.rule_id),
std::forward_as_tuple(
m_io_context) // m_io_context 是类成员,可以访问
);
}
} catch (const json::exception& e) {
spdlog::error("Failed to parse alarm rules file '{}': {}", config_path,
e.what());
return false;
}
return true;
}

View File

@ -1,56 +1,60 @@
// alarm/alarm_service.h // alarm/alarm_service.h
#pragma once #pragma once
#include <boost/asio/io_context.hpp>
#include <condition_variable>
#include <map>
#include <mutex>
#include <nlohmann/json.hpp>
#include <queue>
#include <string>
#include <thread>
#include <vector>
#include "alarm_defs.h" #include "alarm_defs.h"
#include "alarm_event.h" #include "alarm_event.h"
#include "tts/piper_tts_interface.h" // 你的 TTS 接口
#include "dataStorage/data_storage.h" #include "dataStorage/data_storage.h"
#include "mqtt/mqtt_client.h" #include "mqtt/mqtt_client.h"
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include <boost/asio/io_context.hpp> #include "tts/piper_tts_interface.h" // 你的 TTS 接口
#include <nlohmann/json.hpp>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
using json = nlohmann::json; using json = nlohmann::json;
class AlarmService { class AlarmService {
public: public:
AlarmService(boost::asio::io_context& io, AlarmService(boost::asio::io_context& io, PiperTTSInterface& tts_service,
PiperTTSInterface& tts_service, MqttClient& mqtt_client, DataStorage& data_storage);
MqttClient& mqtt_client,
DataStorage& data_storage);
~AlarmService(); ~AlarmService();
void stop(); void stop();
bool load_rules(const std::string& config_path); bool load_rules(const std::string& config_path);
bool reload_rules();
void process_device_data(const std::string& device_id, const std::string& data_json); void process_device_data(const std::string& device_id,
const std::string& data_json);
void process_system_data(const std::string& system_data_json); void process_system_data(const std::string& system_data_json);
nlohmann::json getActiveAlarmsJson(); nlohmann::json getActiveAlarmsJson();
nlohmann::json getAlarmHistoryJson(int limit); nlohmann::json getAlarmHistoryJson(int limit);
private: private:
void check_rule_against_value(AlarmRule& rule, double value, const std::string& actual_device_id); void check_rule_against_value(AlarmRule& rule, double value,
const std::string& actual_device_id);
std::string format_message(const AlarmRule& rule, const std::string& template_str, double value, const std::string& actual_device_id); std::string format_message(const AlarmRule& rule,
const std::string& template_str, double value,
void trigger_alarm_action(AlarmRule& rule, double value, const std::string& actual_device_id); const std::string& actual_device_id);
void trigger_alarm_action(AlarmRule& rule, double value,
void clear_alarm(AlarmRule& rule, double value, const std::string& actual_device_id); const std::string& actual_device_id);
void clear_alarm(AlarmRule& rule, double value,
const std::string& actual_device_id);
void tts_worker(); void tts_worker();
void schedule_tts(const std::string& text); void schedule_tts(const std::string& text);
bool parse_rules_from_file(const std::string& config_path,
std::vector<AlarmRule>& out_rules,
std::map<std::string, AlarmState>& out_states);
boost::asio::io_context& m_io_context; boost::asio::io_context& m_io_context;
PiperTTSInterface& m_tts_service; PiperTTSInterface& m_tts_service;
@ -60,6 +64,9 @@ private:
std::vector<AlarmRule> m_rules; std::vector<AlarmRule> m_rules;
std::map<std::string, AlarmState> m_alarm_states; std::map<std::string, AlarmState> m_alarm_states;
std::string m_rules_config_path; // <--- 新增:存储配置文件路径
std::mutex m_rules_mutex; // <--- 新增:保护 m_rules 和 m_alarm_states
// TTS 播报队列 // TTS 播报队列
std::mutex m_tts_queue_mutex; std::mutex m_tts_queue_mutex;
std::condition_variable m_tts_cv; std::condition_variable m_tts_cv;