告警手动清除,告警api端点开发
This commit is contained in:
parent
cff2c9fae7
commit
f69a389353
|
|
@ -1,46 +1,58 @@
|
|||
[
|
||||
{
|
||||
"rule_id": "RTU_TEMP_HIGH_CRITICAL",
|
||||
"device_id": "rtu_temp_sensor_lab",
|
||||
"data_point_name": "temperature",
|
||||
"compare_type": "GT",
|
||||
"threshold": 30.0,
|
||||
"level": "CRITICAL",
|
||||
"device_id": "rtu_temp_sensor_lab",
|
||||
"data_point_name": "temperature",
|
||||
"compare_type": "GT",
|
||||
"threshold": 30.0,
|
||||
"level": "CRITICAL",
|
||||
"debounce_seconds": 60,
|
||||
"alarm_mqtt_topic": "alarms/rtu_lab",
|
||||
"alarm_mqtt_topic": "alarms/rtu_lab",
|
||||
"message_template": "RTU温度传感器'{device_id}' 检测到温度过高 ({value}°C),已达临界值 {threshold}°C!",
|
||||
"clear_message_template": "RTU温度传感器'{device_id}' 温度已恢复正常 ({value}°C)。"
|
||||
"clear_message_template": "RTU温度传感器'{device_id}' 温度已恢复正常 ({value}°C)。"
|
||||
},
|
||||
{
|
||||
"rule_id": "TEST_TEMP_GT_10_WARNING",
|
||||
"device_id": "rtu_temp_sensor_lab",
|
||||
"data_point_name": "temperature",
|
||||
"compare_type": "GT",
|
||||
"threshold": 10.0,
|
||||
"level": "CRITICAL",
|
||||
"debounce_seconds": 0,
|
||||
"alarm_mqtt_topic": "alarms/rtu_lab",
|
||||
"message_template": "测试告警 设备 '{device_id}' 温度 ({value}°C) 高于测试阈值 {threshold}°C!",
|
||||
"clear_message_template": "【测试告警解除】设备 '{device_id}' 温度 ({value}°C) 已恢复正常。"
|
||||
},
|
||||
{
|
||||
"rule_id": "ANY_TEMP_CRITICAL",
|
||||
"device_id": "*",
|
||||
"device_id": "*",
|
||||
"data_point_name": "temperature",
|
||||
"compare_type": "GT",
|
||||
"compare_type": "GT",
|
||||
"threshold": 40.0,
|
||||
"level": "CRITICAL",
|
||||
"alarm_mqtt_topic": "alarms/critical",
|
||||
"alarm_mqtt_topic": "alarms/critical",
|
||||
"message_template": "【全局报警】任意设备 '{device_id}' 的温度 ({value}°C) 达到或超过 {threshold}°C。",
|
||||
"clear_message_template": "【全局报警解除】设备 '{device_id}' 的温度 ({value}°C) 已恢复。"
|
||||
},
|
||||
{
|
||||
"rule_id": "SYSTEM_CPU_HIGH",
|
||||
"device_id": "proxy_system",
|
||||
"data_point_name": "cpu_usage",
|
||||
"compare_type": "GT",
|
||||
"threshold": 90.0,
|
||||
"level": "WARNING",
|
||||
"debounce_seconds": 30,
|
||||
"device_id": "proxy_system",
|
||||
"data_point_name": "cpu_usage",
|
||||
"compare_type": "GT",
|
||||
"threshold": 90.0,
|
||||
"level": "WARNING",
|
||||
"debounce_seconds": 30,
|
||||
"message_template": "代理系统 CPU 利用率 ({value}%) 超过 {threshold}%,请检查!",
|
||||
"clear_message_template": "代理系统 CPU 利用率已恢复 ({value}%)。"
|
||||
},
|
||||
{
|
||||
"rule_id": "SYSTEM_MEM_HIGH",
|
||||
"device_id": "proxy_system",
|
||||
"data_point_name": "mem_usage_percentage",
|
||||
"compare_type": "GT",
|
||||
"threshold": 80.0,
|
||||
"level": "WARNING",
|
||||
"debounce_seconds": 30,
|
||||
"device_id": "proxy_system",
|
||||
"data_point_name": "mem_usage_percentage",
|
||||
"compare_type": "GT",
|
||||
"threshold": 80.0,
|
||||
"level": "WARNING",
|
||||
"debounce_seconds": 30,
|
||||
"message_template": "代理系统内存使用率 ({value}%) 超过 {threshold}%,请检查!",
|
||||
"clear_message_template": "代理系统内存使用率已恢复 ({value}%)。"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"device_id": "rotary encoder",
|
||||
"port_path": "/dev/ttyS7",
|
||||
"baud_rate": 9600,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// alarm/alarm_service.cpp
|
||||
#include "alarm_service.h"
|
||||
#include "dataStorage/data_storage.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
|
||||
#include "dataStorage/data_storage.h"
|
||||
|
||||
AlarmService::AlarmService(boost::asio::io_context& io,
|
||||
PiperTTSInterface& tts_service,
|
||||
MqttClient& mqtt_client)
|
||||
|
|
@ -270,7 +271,7 @@ void AlarmService::trigger_alarm_action(AlarmRule& rule, double value,
|
|||
.count();
|
||||
|
||||
// 1. 语音播报 (加入队列)
|
||||
schedule_tts(message);
|
||||
// schedule_tts(message);
|
||||
|
||||
// 2. 发布到MQTT
|
||||
std::string topic =
|
||||
|
|
@ -429,5 +430,51 @@ bool AlarmService::parse_rules_from_file(
|
|||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
event.status = AlarmEventStatus::CLEARED; // 标记为 CLEARED
|
||||
event.message = "Alarm manually cleared by user.";
|
||||
event.trigger_value = 0.0;
|
||||
event.timestamp_ms = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (event.level.empty()) {
|
||||
event.level = "INFO";
|
||||
}
|
||||
|
||||
if (!DataStorage::getInstance().storeAlarmEvent(event)) {
|
||||
spdlog::error("Failed to store MANUALLY CLEARED alarm event for rule: {}",
|
||||
rule_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
#include "alarm_event.h"
|
||||
#include "mqtt/mqtt_client.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "tts/piper_tts_interface.h"
|
||||
#include "tts/piper_tts_interface.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
|
|
@ -39,6 +39,15 @@ class AlarmService {
|
|||
nlohmann::json getActiveAlarmsJson();
|
||||
nlohmann::json getAlarmHistoryJson(int limit);
|
||||
|
||||
/**
|
||||
* @brief [新增] 手动清除一个当前激活的告警
|
||||
* @param rule_id 要清除的规则ID
|
||||
* @param device_id 触发该规则的设备ID
|
||||
* @return 成功则返回 true
|
||||
*/
|
||||
bool manually_clear_alarm(const std::string& rule_id,
|
||||
const std::string& device_id);
|
||||
|
||||
private:
|
||||
void check_rule_against_value(AlarmRule& rule, double value,
|
||||
const std::string& actual_device_id);
|
||||
|
|
@ -62,8 +71,8 @@ class AlarmService {
|
|||
std::vector<AlarmRule> m_rules;
|
||||
std::map<std::string, AlarmState> m_alarm_states;
|
||||
|
||||
std::string m_rules_config_path;
|
||||
std::mutex m_rules_mutex;
|
||||
std::string m_rules_config_path;
|
||||
std::mutex m_rules_mutex;
|
||||
|
||||
// TTS 播报队列
|
||||
std::mutex m_tts_queue_mutex;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -154,4 +154,69 @@ void WebServer::setup_routes() {
|
|||
return res;
|
||||
}
|
||||
});
|
||||
|
||||
CROW_ROUTE((*this), "/api/alarms/reload").methods("POST"_method)([this]() {
|
||||
spdlog::info("Web API: Received request to reload alarm rules...");
|
||||
|
||||
bool success = m_alarm_service.reload_rules();
|
||||
|
||||
if (success) {
|
||||
crow::json::wvalue response_json;
|
||||
response_json["status"] = "success";
|
||||
response_json["message"] = "Alarm rules reloaded successfully.";
|
||||
|
||||
auto res = crow::response(200, response_json.dump());
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
|
||||
} else {
|
||||
crow::json::wvalue error_json;
|
||||
error_json["status"] = "error";
|
||||
error_json["message"] =
|
||||
"Failed to reload alarm rules. Check service logs for details.";
|
||||
auto res = crow::response(500, error_json.dump());
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
}
|
||||
});
|
||||
|
||||
CROW_ROUTE((*this), "/api/alarms/clear")
|
||||
.methods("POST"_method)([this](const crow::request& req) {
|
||||
crow::json::rvalue j_body;
|
||||
try {
|
||||
j_body = crow::json::load(req.body);
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::warn("Failed to parse request body for /api/alarms/clear: {}",
|
||||
e.what());
|
||||
auto res = crow::response(400, "{\"error\":\"Invalid JSON body.\"}");
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!j_body.has("rule_id") || !j_body.has("device_id")) {
|
||||
auto res = crow::response(
|
||||
400, "{\"error\":\"Missing 'rule_id' or 'device_id'.\"}");
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string rule_id = j_body["rule_id"].s();
|
||||
std::string device_id = j_body["device_id"].s();
|
||||
|
||||
bool success = m_alarm_service.manually_clear_alarm(rule_id, device_id);
|
||||
|
||||
if (success) {
|
||||
auto res = crow::response(
|
||||
200, "{\"status\":\"success\", \"message\":\"Alarm cleared.\"}");
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
} else {
|
||||
auto res =
|
||||
crow::response(404,
|
||||
"{\"status\":\"error\", \"message\":\"Alarm not "
|
||||
"found or not active.\"}");
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
}
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue