fixed: 告警手动清除功能的bug修复 - 当删除告警配置后,迅速点击手动清除,会导致告警显示卡死
continuous-integration/drone/tag Build is passing
Details
continuous-integration/drone/tag Build is passing
Details
This commit is contained in:
parent
4211482650
commit
f44c8e1ae9
|
|
@ -1,4 +1,4 @@
|
|||
// alarm/alarm_service.cpp
|
||||
|
||||
#include "alarm_service.h"
|
||||
|
||||
#include <chrono>
|
||||
|
|
@ -31,7 +31,7 @@ void AlarmService::stop() {
|
|||
std::lock_guard<std::mutex> 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<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.");
|
||||
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::seconds>(
|
||||
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<std::mutex> 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<std::mutex> 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: {}",
|
||||
|
|
|
|||
Loading…
Reference in New Issue