diff --git a/src/alarm/alarm_service.cc b/src/alarm/alarm_service.cc index 762e5e6..dd640bf 100644 --- a/src/alarm/alarm_service.cc +++ b/src/alarm/alarm_service.cc @@ -1,4 +1,4 @@ -// alarm/alarm_service.cpp + #include "alarm_service.h" #include @@ -31,7 +31,7 @@ void AlarmService::stop() { std::lock_guard lock(m_tts_queue_mutex); m_tts_running = false; } - m_tts_cv.notify_one(); // 唤醒工作线程使其退出 + m_tts_cv.notify_one(); } bool AlarmService::load_rules(const std::string &config_path) { @@ -57,9 +57,7 @@ bool AlarmService::load_rules(const std::string &config_path) { bool AlarmService::reload_rules() { if (m_rules_config_path.empty()) { - spdlog::error( - "Cannot reload rules: config path not set (load_rules was never " - "called?)."); + spdlog::error("Cannot reload rules: config path not set."); return false; } @@ -70,15 +68,51 @@ bool AlarmService::reload_rules() { std::map 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."); + spdlog::error("Failed to parse new rules file, aborting reload."); return false; } + try { + + auto active_alarms = DataStorage::getInstance().getActiveAlarms(); + + for (const auto &alarm : active_alarms) { + bool rule_exists = false; + + for (const auto &new_rule : new_rules) { + if (new_rule.rule_id == alarm.rule_id) { + rule_exists = true; + break; + } + } + + if (!rule_exists) { + spdlog::info("Reload Cleanup: Removing zombie alarm for deleted rule " + "'{}' on device '{}'.", + alarm.rule_id, alarm.device_id); + + AlarmEvent clear_event; + clear_event.rule_id = alarm.rule_id; + clear_event.device_id = alarm.device_id; + clear_event.status = AlarmEventStatus::CLEARED; + clear_event.message = + "Rule deleted from config. Auto-cleared by system."; + clear_event.level = "INFO"; + clear_event.trigger_value = 0.0; + clear_event.timestamp_ms = + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + DataStorage::getInstance().storeAlarmEvent(clear_event); + } + } + } catch (const std::exception &e) { + spdlog::warn("Error during zombie alarm cleanup: {}", e.what()); + } + { std::lock_guard lock(m_rules_mutex); - m_rules = std::move(new_rules); m_alarm_states = std::move(new_states); } @@ -117,7 +151,7 @@ void AlarmService::process_system_data(const std::string &system_data_json) { if (!j_data.is_object()) return; - const std::string device_id = "proxy_system"; // 系统数据的固定ID + const std::string device_id = "proxy_system"; for (auto &rule : m_rules) { if (rule.device_id == device_id) { @@ -271,15 +305,10 @@ void AlarmService::trigger_alarm_action(AlarmRule &rule, double value, std::chrono::system_clock::now().time_since_epoch()) .count(); - // 1. 语音播报 (加入队列) - // schedule_tts(message); - - // 2. 发布到MQTT 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); - // 3. 写入数据库 if (!DataStorage::getInstance().storeAlarmEvent(event)) { spdlog::error("Failed to store ACTIVE alarm event for rule: {}", rule.rule_id); @@ -290,9 +319,8 @@ void AlarmService::clear_alarm(AlarmRule &rule, double value, const std::string &actual_device_id) { spdlog::info("[ALARM CLEARED] (Rule: {})", rule.rule_id); - // 检查是否有解除消息模板 if (rule.clear_message_template.empty()) { - return; // 没有模板,安静地解除 + return; } std::string message = format_message(rule, rule.clear_message_template, value, @@ -309,45 +337,40 @@ void AlarmService::clear_alarm(AlarmRule &rule, double value, std::chrono::system_clock::now().time_since_epoch()) .count(); - // 1. 语音播报 (加入队列) schedule_tts(message); - // 2. 发布到MQTT 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); - // 3. 写入数据库 if (!DataStorage::getInstance().storeAlarmEvent(event)) { spdlog::error("Failed to store CLEARED alarm event for rule: {}", rule.rule_id); } } -// --- TTS 队列和 Web API 实现 --- - void AlarmService::tts_worker() { while (m_tts_running) { std::string message_to_play; { std::unique_lock lock(m_tts_queue_mutex); - // 等待队列中有消息 或 线程被通知停止 + m_tts_cv.wait(lock, [this] { return !m_tts_queue.empty() || !m_tts_running; }); if (!m_tts_running && m_tts_queue.empty()) { - break; // 退出 + break; } if (!m_tts_queue.empty()) { message_to_play = m_tts_queue.front(); m_tts_queue.pop(); } - } // 释放锁 + } if (!message_to_play.empty()) { spdlog::debug("TTS worker playing: {}", message_to_play); - // 这是阻塞调用,但在独立线程中,不会影响主 io_context + m_tts_service.say_text_and_play(message_to_play); } } @@ -384,21 +407,25 @@ nlohmann::json AlarmService::getAlarmHistoryJson(int limit) { bool AlarmService::manually_clear_alarm(const std::string &rule_id, const std::string &device_id) { - auto it = m_alarm_states.find(rule_id); - if (it == m_alarm_states.end()) { - spdlog::warn( - "Cannot manually clear alarm: Rule '{}' not found in state map.", - rule_id); - return false; + + { + + auto it = m_alarm_states.find(rule_id); + if (it != m_alarm_states.end()) { + + AlarmState &state = it->second; + state.current_state = AlarmStateType::NORMAL; + state.debounce_timer.cancel(); + } else { + spdlog::warn("Manually clearing alarm for deleted/missing rule '{}'. " + "Cleaning up DB record only.", + rule_id); + } } - AlarmState &state = it->second; - state.current_state = AlarmStateType::NORMAL; - state.debounce_timer.cancel(); spdlog::info("[ALARM MANUALLY CLEARED] (Rule: {}) for Device: {}", rule_id, device_id); - // 2. 写入数据库日志 AlarmEvent event; event.rule_id = rule_id; event.device_id = device_id; @@ -409,15 +436,15 @@ bool AlarmService::manually_clear_alarm(const std::string &rule_id, std::chrono::system_clock::now().time_since_epoch()) .count(); - for (const auto &rule : m_rules) { - if (rule.rule_id == rule_id) { - event.level = AlarmRule::levelToString(rule.level); - break; + event.level = "INFO"; + { + for (const auto &rule : m_rules) { + if (rule.rule_id == rule_id) { + event.level = AlarmRule::levelToString(rule.level); + break; + } } } - if (event.level.empty()) { - event.level = "INFO"; - } if (!DataStorage::getInstance().storeAlarmEvent(event)) { spdlog::error("Failed to store MANUALLY CLEARED alarm event for rule: {}",