告警管理模块

This commit is contained in:
GuanYuankai 2025-11-04 06:52:43 +00:00
parent f69a389353
commit 367f3a12a7
4 changed files with 287 additions and 178 deletions

View File

@ -1,10 +1,22 @@
[
{
"rule_id": "TeST",
"device_id": "rtu_temp_sensor_lab",
"data_point_name": "temperature",
"compare_type": "EQ",
"threshold": 5,
"level": "CRITICAL",
"debounce_seconds": 0,
"message_template": "测试用",
"clear_message_template": "测试解除",
"alarm_mqtt_topic": "alarms/rtu_test"
},
{
"rule_id": "RTU_TEMP_HIGH_CRITICAL",
"device_id": "rtu_temp_sensor_lab",
"data_point_name": "temperature",
"compare_type": "GT",
"threshold": 30.0,
"threshold": 30,
"level": "CRITICAL",
"debounce_seconds": 60,
"alarm_mqtt_topic": "alarms/rtu_lab",
@ -16,43 +28,22 @@
"device_id": "rtu_temp_sensor_lab",
"data_point_name": "temperature",
"compare_type": "GT",
"threshold": 10.0,
"threshold": 10,
"level": "CRITICAL",
"debounce_seconds": 0,
"alarm_mqtt_topic": "alarms/rtu_lab",
"alarm_mqtt_topic": "alarms/rtu_temp_sensor_lab",
"message_template": "测试告警 设备 '{device_id}' 温度 ({value}°C) 高于测试阈值 {threshold}°C!",
"clear_message_template": "【测试告警解除】设备 '{device_id}' 温度 ({value}°C) 已恢复正常。"
},
{
"rule_id": "ANY_TEMP_CRITICAL",
"device_id": "*",
"data_point_name": "temperature",
"compare_type": "GT",
"threshold": 40.0,
"level": "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,
"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,
"threshold": 80,
"level": "WARNING",
"debounce_seconds": 30,
"alarm_mqtt_topic": "alarms/proxy",
"message_template": "代理系统内存使用率 ({value}%) 超过 {threshold}%,请检查!",
"clear_message_template": "代理系统内存使用率已恢复 ({value}%)。"
}

View File

@ -3,15 +3,14 @@
#include <chrono>
#include <fstream>
#include <sstream>
#include "dataStorage/data_storage.h"
AlarmService::AlarmService(boost::asio::io_context& io,
PiperTTSInterface& tts_service,
MqttClient& mqtt_client)
: m_io_context(io),
m_tts_service(tts_service),
m_mqtt_client(mqtt_client),
AlarmService::AlarmService(boost::asio::io_context &io,
PiperTTSInterface &tts_service,
MqttClient &mqtt_client)
: m_io_context(io), m_tts_service(tts_service), m_mqtt_client(mqtt_client),
m_tts_running(true) {
m_tts_thread = std::thread(&AlarmService::tts_worker, this);
spdlog::info("AlarmService created and TTS worker thread started.");
@ -32,10 +31,10 @@ 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) {
bool AlarmService::load_rules(const std::string &config_path) {
m_rules_config_path = config_path;
std::vector<AlarmRule> new_rules;
@ -88,14 +87,15 @@ bool AlarmService::reload_rules() {
return true;
}
void AlarmService::process_device_data(const std::string& device_id,
const std::string& data_json) {
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 {
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) {
if (rule.device_id == device_id || rule.device_id == "*") {
if (j_data.contains(rule.data_point_name)) {
if (j_data[rule.data_point_name].is_number()) {
@ -105,20 +105,21 @@ void AlarmService::process_device_data(const std::string& device_id,
}
}
}
} catch (const json::exception& e) {
} catch (const json::exception &e) {
spdlog::error("Failed to parse device data for alarm: {}", e.what());
}
}
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 {
json j_data = json::parse(system_data_json);
if (!j_data.is_object()) return;
if (!j_data.is_object())
return;
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) {
if (rule.device_id == device_id) {
if (j_data.contains(rule.data_point_name)) {
if (j_data[rule.data_point_name].is_number()) {
@ -128,26 +129,26 @@ void AlarmService::process_system_data(const std::string& system_data_json) {
}
}
}
} catch (const json::exception& e) {
} catch (const json::exception &e) {
spdlog::error("Failed to parse system data for alarm: {}", e.what());
}
}
void AlarmService::check_rule_against_value(
AlarmRule& rule, double value, const std::string& actual_device_id) {
AlarmRule &rule, double value, const std::string &actual_device_id) {
bool condition_met = false;
switch (rule.compare_type) {
case CompareType::GT:
condition_met = (value > rule.threshold);
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;
case CompareType::GT:
condition_met = (value > rule.threshold);
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);
@ -163,7 +164,7 @@ void AlarmService::check_rule_against_value(
it = result.first;
}
AlarmState& state = it->second;
AlarmState &state = it->second;
if (condition_met) {
if (state.current_state == AlarmStateType::NORMAL) {
@ -181,8 +182,8 @@ void AlarmService::check_rule_against_value(
state.debounce_timer.async_wait([this, rule_id, value,
actual_device_id](
const boost::system::error_code&
ec) {
const boost::system::error_code
&ec) {
std::lock_guard<std::mutex> lock(m_rules_mutex);
if (ec == boost::asio::error::operation_aborted) {
@ -196,10 +197,10 @@ void AlarmService::check_rule_against_value(
rule_id);
return;
}
AlarmState& current_state = it->second;
AlarmState &current_state = it->second;
AlarmRule* current_rule = nullptr;
for (auto& r : m_rules) {
AlarmRule *current_rule = nullptr;
for (auto &r : m_rules) {
if (r.rule_id == rule_id) {
current_rule = &r;
break;
@ -226,10 +227,10 @@ void AlarmService::check_rule_against_value(
}
}
std::string AlarmService::format_message(const AlarmRule& rule,
const std::string& template_str,
std::string AlarmService::format_message(const AlarmRule &rule,
const std::string &template_str,
double value,
const std::string& actual_device_id) {
const std::string &actual_device_id) {
std::string msg = template_str;
size_t pos = msg.find("{device_id}");
@ -253,8 +254,8 @@ std::string AlarmService::format_message(const AlarmRule& rule,
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,
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);
@ -285,13 +286,13 @@ void AlarmService::trigger_alarm_action(AlarmRule& rule, double value,
}
}
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);
// 检查是否有解除消息模板
if (rule.clear_message_template.empty()) {
return; // 没有模板,安静地解除
return; // 没有模板,安静地解除
}
std::string message = format_message(rule, rule.clear_message_template, value,
@ -335,14 +336,14 @@ void AlarmService::tts_worker() {
[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);
@ -353,8 +354,9 @@ void AlarmService::tts_worker() {
spdlog::info("TTS worker thread stopped.");
}
void AlarmService::schedule_tts(const std::string& text) {
if (text.empty() || !m_tts_running) return;
void AlarmService::schedule_tts(const std::string &text) {
if (text.empty() || !m_tts_running)
return;
{
std::lock_guard<std::mutex> lock(m_tts_queue_mutex);
m_tts_queue.push(text);
@ -365,7 +367,7 @@ void AlarmService::schedule_tts(const std::string& text) {
nlohmann::json AlarmService::getActiveAlarmsJson() {
auto alarms = DataStorage::getInstance().getActiveAlarms();
json j_alarms = json::array();
for (const auto& alarm : alarms) {
for (const auto &alarm : alarms) {
j_alarms.push_back(AlarmEvent::toJson(alarm));
}
return j_alarms;
@ -374,29 +376,94 @@ nlohmann::json AlarmService::getActiveAlarmsJson() {
nlohmann::json AlarmService::getAlarmHistoryJson(int limit) {
auto alarms = DataStorage::getInstance().getAlarmHistory(limit);
json j_alarms = json::array();
for (const auto& alarm : alarms) {
for (const auto &alarm : alarms) {
j_alarms.push_back(AlarmEvent::toJson(alarm));
}
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);
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;
}
try {
json j_rules = json::parse(ifs);
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;
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;
}
std::string AlarmService::get_rules_as_json_string() {
std::lock_guard<std::mutex> lock(m_rules_mutex);
if (m_rules_config_path.empty()) {
spdlog::error("Cannot get rules: config path not set.");
return "[]";
}
std::ifstream ifs(m_rules_config_path);
if (!ifs.is_open()) {
spdlog::error("Failed to open alarm rules file for reading: {}",
m_rules_config_path);
return "[]";
}
std::stringstream buffer;
buffer << ifs.rdbuf();
return buffer.str();
}
bool AlarmService::parse_rules_from_content(
const std::string &json_content, std::vector<AlarmRule> &out_rules,
std::map<std::string, AlarmState> &out_states) {
try {
json j_rules = json::parse(json_content);
if (!j_rules.is_array()) {
spdlog::error("Failed to parse rules: JSON content is not an array.");
return false;
}
// 清空临时容器,准备接收新规则
out_rules.clear();
out_states.clear();
for (const auto& j_rule : j_rules) {
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");
@ -417,64 +484,65 @@ bool AlarmService::parse_rules_from_file(
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 是类成员,可以访问
);
std::forward_as_tuple(m_io_context));
}
} catch (const json::exception& e) {
spdlog::error("Failed to parse alarm rules file '{}': {}", config_path,
e.what());
} catch (const json::exception &e) {
spdlog::error("Failed to parse alarm rules content: {}", e.what());
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);
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;
}
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);
std::stringstream buffer;
buffer << ifs.rdbuf();
std::string file_content = buffer.str();
// 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();
return parse_rules_from_content(file_content, out_rules, out_states);
}
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";
}
bool AlarmService::save_rules_from_json_string(
const std::string &json_content) {
if (!DataStorage::getInstance().storeAlarmEvent(event)) {
spdlog::error("Failed to store MANUALLY CLEARED alarm event for rule: {}",
rule_id);
std::vector<AlarmRule> dummy_rules;
std::map<std::string, AlarmState> dummy_states;
if (!parse_rules_from_content(json_content, dummy_rules, dummy_states)) {
spdlog::warn("Failed to save rules: Invalid content or schema.");
return false;
}
std::lock_guard<std::mutex> lock(m_rules_mutex);
if (m_rules_config_path.empty()) {
spdlog::error("Cannot save rules: config path not set.");
return false;
}
std::ofstream ofs(m_rules_config_path, std::ios::trunc);
if (!ofs.is_open()) {
spdlog::error("Failed to open alarm rules file for writing: {}",
m_rules_config_path);
return false;
}
ofs << json_content;
ofs.close();
spdlog::info(
"Successfully saved new alarm rules to {}. (Reload is required to apply)",
m_rules_config_path);
return true;
}

View File

@ -20,53 +20,70 @@
using json = nlohmann::json;
class AlarmService {
public:
AlarmService(boost::asio::io_context& io, PiperTTSInterface& tts_service,
MqttClient& mqtt_client);
public:
AlarmService(boost::asio::io_context &io, PiperTTSInterface &tts_service,
MqttClient &mqtt_client);
~AlarmService();
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 getAlarmHistoryJson(int limit);
/**
* @brief []
* @param rule_id ID
* @param rule_id IDparse_rules_from_fil
* @param device_id ID
* @return true
*/
bool manually_clear_alarm(const std::string& rule_id,
const std::string& device_id);
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);
std::string format_message(const AlarmRule& rule,
const std::string& template_str, double value,
const std::string& actual_device_id);
void trigger_alarm_action(AlarmRule& rule, double value,
const std::string& actual_device_id);
void clear_alarm(AlarmRule& rule, double value,
const std::string& actual_device_id);
std::string get_rules_as_json_string();
/**
* @brief [] JSON
*
*/
bool parse_rules_from_content(const std::string &json_content,
std::vector<AlarmRule> &out_rules,
std::map<std::string, AlarmState> &out_states);
/**
* @brief []
* @param json_content JSON字符串
* @return (JSON有效且写入成功) true
*/
bool save_rules_from_json_string(const std::string &json_content);
private:
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);
void trigger_alarm_action(AlarmRule &rule, double value,
const std::string &actual_device_id);
void clear_alarm(AlarmRule &rule, double value,
const std::string &actual_device_id);
void tts_worker();
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);
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;
PiperTTSInterface& m_tts_service;
MqttClient& m_mqtt_client;
boost::asio::io_context &m_io_context;
PiperTTSInterface &m_tts_service;
MqttClient &m_mqtt_client;
std::vector<AlarmRule> m_rules;
std::map<std::string, AlarmState> m_alarm_states;

View File

@ -7,16 +7,13 @@
#include "config/config_manager.h"
#include "spdlog/spdlog.h"
WebServer::WebServer(SystemMonitor::SystemMonitor& monitor,
DeviceManager& deviceManager, LiveDataCache& liveDataCache,
AlarmService& alarm_service, uint16_t port)
: crow::Crow<crow::CORSHandler>(),
m_monitor(monitor),
m_device_manager(deviceManager),
m_live_data_cache(liveDataCache),
m_alarm_service(alarm_service),
m_port(port) {
auto& cors = this->get_middleware<crow::CORSHandler>();
WebServer::WebServer(SystemMonitor::SystemMonitor &monitor,
DeviceManager &deviceManager, LiveDataCache &liveDataCache,
AlarmService &alarm_service, uint16_t port)
: crow::Crow<crow::CORSHandler>(), m_monitor(monitor),
m_device_manager(deviceManager), m_live_data_cache(liveDataCache),
m_alarm_service(alarm_service), m_port(port) {
auto &cors = this->get_middleware<crow::CORSHandler>();
cors.global()
.origin("*")
.headers("Content-Type", "Authorization")
@ -77,7 +74,7 @@ void WebServer::setup_routes() {
auto devices_info = m_device_manager.get_all_device_info();
std::vector<crow::json::wvalue> devices_json;
for (const auto& info : devices_info) {
for (const auto &info : devices_info) {
crow::json::wvalue device_obj;
device_obj["id"] = info.id;
@ -85,7 +82,7 @@ void WebServer::setup_routes() {
device_obj["is_running"] = info.is_running;
crow::json::wvalue details_obj;
for (const auto& pair : info.connection_details) {
for (const auto &pair : info.connection_details) {
details_obj[pair.first] = pair.second;
}
device_obj["connection_details"] = std::move(details_obj);
@ -100,7 +97,7 @@ void WebServer::setup_routes() {
auto latest_data_map = m_live_data_cache.get_all_data();
crow::json::wvalue response;
for (const auto& pair : latest_data_map) {
for (const auto &pair : latest_data_map) {
response[pair.first] = crow::json::load(pair.second);
}
@ -115,7 +112,7 @@ void WebServer::setup_routes() {
res.set_header("Content-Type", "application/json");
return res;
} catch (const std::exception& e) {
} catch (const std::exception &e) {
spdlog::error("Error processing /api/alarms/active: {}", e.what());
crow::json::wvalue error_resp;
error_resp["error"] = "Failed to retrieve active alarms.";
@ -127,15 +124,16 @@ void WebServer::setup_routes() {
});
CROW_ROUTE((*this), "/api/alarms/history")
.methods("GET"_method)([this](const crow::request& req) {
.methods("GET"_method)([this](const crow::request &req) {
int limit = 100;
if (req.url_params.get("limit")) {
try {
limit = std::stoi(req.url_params.get("limit"));
} catch (const std::exception&) { /* ignore invalid */
} catch (const std::exception &) { /* ignore invalid */
}
}
if (limit <= 0) limit = 100;
if (limit <= 0)
limit = 100;
try {
auto json_string = m_alarm_service.getAlarmHistoryJson(limit).dump();
@ -144,7 +142,7 @@ void WebServer::setup_routes() {
res.set_header("Content-Type", "application/json");
return res;
} catch (const std::exception& e) {
} catch (const std::exception &e) {
spdlog::error("Error processing /api/alarms/history: {}", e.what());
crow::json::wvalue error_resp;
error_resp["error"] = "Failed to retrieve alarm history.";
@ -181,11 +179,11 @@ void WebServer::setup_routes() {
});
CROW_ROUTE((*this), "/api/alarms/clear")
.methods("POST"_method)([this](const crow::request& req) {
.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) {
} 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.\"}");
@ -211,10 +209,45 @@ void WebServer::setup_routes() {
res.set_header("Content-Type", "application/json");
return res;
} else {
auto res =
crow::response(404,
"{\"status\":\"error\", \"message\":\"Alarm not "
"found or not active.\"}");
auto res = crow::response(
404, "{\"status\":\"error\", \"message\":\"Alarm not "
"found or not active.\"}");
res.set_header("Content-Type", "application/json");
return res;
}
});
CROW_ROUTE((*this), "/api/alarms/config").methods("GET"_method)([this]() {
std::string rules_json_string = m_alarm_service.get_rules_as_json_string();
auto res = crow::response(200, rules_json_string);
res.set_header("Content-Type", "application/json");
return res;
});
CROW_ROUTE((*this), "/api/alarms/config")
.methods("POST"_method)([this](const crow::request &req) {
const std::string &new_rules_content = req.body;
bool success =
m_alarm_service.save_rules_from_json_string(new_rules_content);
if (success) {
crow::json::wvalue response_json;
response_json["status"] = "success";
response_json["message"] =
"Rules saved successfully. A reload is required to apply.";
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 save rules. Invalid JSON format or server error.";
auto res = crow::response(400, error_json.dump());
res.set_header("Content-Type", "application/json");
return res;
}