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 "alarm_service.h"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
@ -31,7 +31,7 @@ void AlarmService::stop() {
|
||||||
std::lock_guard<std::mutex> lock(m_tts_queue_mutex);
|
std::lock_guard<std::mutex> lock(m_tts_queue_mutex);
|
||||||
m_tts_running = false;
|
m_tts_running = false;
|
||||||
}
|
}
|
||||||
m_tts_cv.notify_one(); // 唤醒工作线程使其退出
|
m_tts_cv.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AlarmService::load_rules(const std::string &config_path) {
|
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() {
|
bool AlarmService::reload_rules() {
|
||||||
if (m_rules_config_path.empty()) {
|
if (m_rules_config_path.empty()) {
|
||||||
spdlog::error(
|
spdlog::error("Cannot reload rules: config path not set.");
|
||||||
"Cannot reload rules: config path not set (load_rules was never "
|
|
||||||
"called?).");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,15 +68,51 @@ bool AlarmService::reload_rules() {
|
||||||
std::map<std::string, AlarmState> new_states;
|
std::map<std::string, AlarmState> new_states;
|
||||||
|
|
||||||
if (!parse_rules_from_file(m_rules_config_path, new_rules, new_states)) {
|
if (!parse_rules_from_file(m_rules_config_path, new_rules, new_states)) {
|
||||||
spdlog::error(
|
spdlog::error("Failed to parse new rules file, aborting reload.");
|
||||||
"Failed to parse new rules file, aborting reload. Service continues "
|
|
||||||
"with old rules.");
|
|
||||||
return false;
|
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);
|
std::lock_guard<std::mutex> lock(m_rules_mutex);
|
||||||
|
|
||||||
m_rules = std::move(new_rules);
|
m_rules = std::move(new_rules);
|
||||||
m_alarm_states = std::move(new_states);
|
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())
|
if (!j_data.is_object())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const std::string device_id = "proxy_system"; // 系统数据的固定ID
|
const std::string device_id = "proxy_system";
|
||||||
|
|
||||||
for (auto &rule : m_rules) {
|
for (auto &rule : m_rules) {
|
||||||
if (rule.device_id == device_id) {
|
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())
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
// 1. 语音播报 (加入队列)
|
|
||||||
// schedule_tts(message);
|
|
||||||
|
|
||||||
// 2. 发布到MQTT
|
|
||||||
std::string topic =
|
std::string topic =
|
||||||
rule.alarm_mqtt_topic.empty() ? "proxy/alarms" : rule.alarm_mqtt_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. 写入数据库
|
|
||||||
if (!DataStorage::getInstance().storeAlarmEvent(event)) {
|
if (!DataStorage::getInstance().storeAlarmEvent(event)) {
|
||||||
spdlog::error("Failed to store ACTIVE alarm event for rule: {}",
|
spdlog::error("Failed to store ACTIVE alarm event for rule: {}",
|
||||||
rule.rule_id);
|
rule.rule_id);
|
||||||
|
|
@ -290,9 +319,8 @@ void AlarmService::clear_alarm(AlarmRule &rule, double value,
|
||||||
const std::string &actual_device_id) {
|
const std::string &actual_device_id) {
|
||||||
spdlog::info("[ALARM CLEARED] (Rule: {})", rule.rule_id);
|
spdlog::info("[ALARM CLEARED] (Rule: {})", rule.rule_id);
|
||||||
|
|
||||||
// 检查是否有解除消息模板
|
|
||||||
if (rule.clear_message_template.empty()) {
|
if (rule.clear_message_template.empty()) {
|
||||||
return; // 没有模板,安静地解除
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string message = format_message(rule, rule.clear_message_template, value,
|
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())
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
// 1. 语音播报 (加入队列)
|
|
||||||
schedule_tts(message);
|
schedule_tts(message);
|
||||||
|
|
||||||
// 2. 发布到MQTT
|
|
||||||
std::string topic =
|
std::string topic =
|
||||||
rule.alarm_mqtt_topic.empty() ? "proxy/alarms" : rule.alarm_mqtt_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. 写入数据库
|
|
||||||
if (!DataStorage::getInstance().storeAlarmEvent(event)) {
|
if (!DataStorage::getInstance().storeAlarmEvent(event)) {
|
||||||
spdlog::error("Failed to store CLEARED alarm event for rule: {}",
|
spdlog::error("Failed to store CLEARED alarm event for rule: {}",
|
||||||
rule.rule_id);
|
rule.rule_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- TTS 队列和 Web API 实现 ---
|
|
||||||
|
|
||||||
void AlarmService::tts_worker() {
|
void AlarmService::tts_worker() {
|
||||||
while (m_tts_running) {
|
while (m_tts_running) {
|
||||||
std::string message_to_play;
|
std::string message_to_play;
|
||||||
{
|
{
|
||||||
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,
|
m_tts_cv.wait(lock,
|
||||||
[this] { return !m_tts_queue.empty() || !m_tts_running; });
|
[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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_tts_queue.empty()) {
|
if (!m_tts_queue.empty()) {
|
||||||
message_to_play = m_tts_queue.front();
|
message_to_play = m_tts_queue.front();
|
||||||
m_tts_queue.pop();
|
m_tts_queue.pop();
|
||||||
}
|
}
|
||||||
} // 释放锁
|
}
|
||||||
|
|
||||||
if (!message_to_play.empty()) {
|
if (!message_to_play.empty()) {
|
||||||
spdlog::debug("TTS worker playing: {}", message_to_play);
|
spdlog::debug("TTS worker playing: {}", message_to_play);
|
||||||
// 这是阻塞调用,但在独立线程中,不会影响主 io_context
|
|
||||||
m_tts_service.say_text_and_play(message_to_play);
|
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,
|
bool AlarmService::manually_clear_alarm(const std::string &rule_id,
|
||||||
const std::string &device_id) {
|
const std::string &device_id) {
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
auto it = m_alarm_states.find(rule_id);
|
auto it = m_alarm_states.find(rule_id);
|
||||||
if (it == m_alarm_states.end()) {
|
if (it != m_alarm_states.end()) {
|
||||||
spdlog::warn(
|
|
||||||
"Cannot manually clear alarm: Rule '{}' not found in state map.",
|
|
||||||
rule_id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AlarmState &state = it->second;
|
AlarmState &state = it->second;
|
||||||
state.current_state = AlarmStateType::NORMAL;
|
state.current_state = AlarmStateType::NORMAL;
|
||||||
state.debounce_timer.cancel();
|
state.debounce_timer.cancel();
|
||||||
|
} else {
|
||||||
|
spdlog::warn("Manually clearing alarm for deleted/missing rule '{}'. "
|
||||||
|
"Cleaning up DB record only.",
|
||||||
|
rule_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spdlog::info("[ALARM MANUALLY CLEARED] (Rule: {}) for Device: {}", rule_id,
|
spdlog::info("[ALARM MANUALLY CLEARED] (Rule: {}) for Device: {}", rule_id,
|
||||||
device_id);
|
device_id);
|
||||||
|
|
||||||
// 2. 写入数据库日志
|
|
||||||
AlarmEvent event;
|
AlarmEvent event;
|
||||||
event.rule_id = rule_id;
|
event.rule_id = rule_id;
|
||||||
event.device_id = device_id;
|
event.device_id = device_id;
|
||||||
|
|
@ -409,14 +436,14 @@ bool AlarmService::manually_clear_alarm(const std::string &rule_id,
|
||||||
std::chrono::system_clock::now().time_since_epoch())
|
std::chrono::system_clock::now().time_since_epoch())
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
|
event.level = "INFO";
|
||||||
|
{
|
||||||
for (const auto &rule : m_rules) {
|
for (const auto &rule : m_rules) {
|
||||||
if (rule.rule_id == rule_id) {
|
if (rule.rule_id == rule_id) {
|
||||||
event.level = AlarmRule::levelToString(rule.level);
|
event.level = AlarmRule::levelToString(rule.level);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (event.level.empty()) {
|
|
||||||
event.level = "INFO";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DataStorage::getInstance().storeAlarmEvent(event)) {
|
if (!DataStorage::getInstance().storeAlarmEvent(event)) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue