bonus-edge-proxy/src/test.cc

279 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// #include "tts/piper_tts_interface.h"
// #include <string>
// #include <iostream>
// int main() {
// // 使用默认配置 (piper 和 /app/piper_models/zh_CN-huayan-medium.onnx)
// PiperTTSInterface tts_speaker;
// // 如果你的piper可执行文件不在PATH中或者模型路径不同你可以这样指定
// // PiperTTSInterface tts_speaker("/path/to/your/piper_executable", "/path/to/your/model.onnx");
// std::string text_to_say = "请执行上述调试步骤,特别是禁用文件删除后手动检查和播放 WAV 文件,这将提供更多线索";
// if (tts_speaker.say_text_and_play(text_to_say)) {
// std::cout << "语音播报完成。" << std::endl;
// } else {
// std::cerr << "语音播报失败,请检查日志。" << std::endl;
// }
// return 0;
// }
// #include <iostream>
// #include <chrono>
// #include <thread>
// #include "systemMonitor/iio_sensor.h" // 包含我们刚刚创建的头文件
// int main() {
// // --- 配置参数 ---
// // !!! 修改这里以匹配你的设备和通道 !!!
// const std::string iio_device_path = "/sys/bus/iio/devices/iio:device0";
// const std::string iio_channel_name = "in_voltage2_raw"; // 例如,连接到 IN2 通道
// const double adc_reference_voltage = 1.8; // ADC参考电压
// const int64_t adc_resolution = 4096; // ADC分辨率 (e.g., 2^12 for 12-bit)
// const double signal_gain = 11.0; // 信号通路的增益
// std::cout << "--- Starting IIO Sensor Reader (Version 2) ---" << std::endl;
// std::cout << "Reading from device: " << iio_device_path << std::endl;
// std::cout << "Reading channel: " << iio_channel_name << std::endl;
// std::cout << "-----------------------------------------------" << std::endl;
// // 注意:传感器对象在 try 块外创建,如果构造失败(找不到文件),会立即被捕获
// IioSensor sensor(iio_device_path, iio_channel_name);
// while (true) {
// try {
// // 封装后的调用方式非常简洁!
// // 每次调用 readVoltage内部都会重新打开文件读取一次
// double actual_voltage = sensor.readVoltage(adc_reference_voltage, adc_resolution, signal_gain);
// // 输出结果
// std::cout << "Actual Voltage: " << actual_voltage << " V" << std::endl;
// } catch (const IioSensorError& e) {
// // 如果单次读取失败(例如文件内容格式错误、无权限等),打印错误但继续循环
// std::cerr << "[!] Read Error: " << e.what() << std::endl;
// }
// // 每秒钟读取一次
// std::this_thread::sleep_for(std::chrono::seconds(1));
// }
// return 0;
// }
// main.cpp
#include "network/tcp_server.h"
#include "mqtt/mqtt_client.h"
#include "mqtt/mqtt_router.h"
#include "systemMonitor/system_monitor.h"
#include "spdlog/spdlog.h"
#include "deviceManager/device_manager.h"
#include "dataCache/data_cache.h"
#include "dataCache/cache_uploader.h"
#include "web/web_server.h"
#include "dataCache/live_data_cache.h"
#include "dataStorage/data_storage.h"
#include "alert/alarm_manager.h" // <-- 添加报警管理器头文件
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
#include <csignal>
#include <iostream>
#include <functional>
#include <nlohmann/json.hpp> // 用于解析data_json
// 用于 ASIO 服务的全局 io_context
boost::asio::io_context g_io_context;
// !!! 你提供的 UnifiedData 结构确认 !!!
struct UnifiedData {
std::string device_id; // 产生数据的设备唯一ID
int64_t timestamp_ms; // 数据产生的时间戳 (毫秒级UTC)
std::string data_json; // 采用JSON字符串格式具有良好的灵活性和可扩展性
};
/**
* @brief 周期性轮询系统状态并发布到 MQTT
*/
void poll_system_metrics(
boost::asio::steady_timer& timer,
SystemMonitor::SystemMonitor& monitor,
MqttClient& mqtt_client,
AlarmManager& alarm_manager // <-- 传递 alarm_manager
) {
if (g_io_context.stopped()) return;
auto cpu_util = monitor.getCpuUtilization();
auto mem_info = monitor.getMemoryInfo();
double mem_total_gb = mem_info.total_kb / 1024.0 / 1024.0;
double mem_free_gb = mem_info.free_kb / 1024.0 / 1024.0;
double mem_usage_percentage = (mem_total_gb - mem_free_gb) / mem_total_gb * 100.0;
std::string topic = "proxy/system_status";
nlohmann::json system_status_json;
system_status_json["device_id"] = "proxy_system"; // 统一一个设备ID用于报警
system_status_json["timestamp_ms"] = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()
).count();
system_status_json["cpu_usage"] = cpu_util.totalUsagePercentage;
system_status_json["mem_total_gb"] = mem_total_gb;
system_status_json["mem_usage_percentage"] = mem_usage_percentage;
std::string payload = system_status_json.dump();
mqtt_client.publish(topic, payload);
spdlog::debug("System metrics published.");
// !!! 对系统指标进行报警检查 !!!
alarm_manager.check_data_for_alarms("proxy_system", system_status_json);
timer.expires_at(timer.expiry() + std::chrono::seconds(15));
timer.async_wait(std::bind(poll_system_metrics, std::ref(timer), std::ref(monitor), std::ref(mqtt_client), std::ref(alarm_manager)));
}
int main(int argc, char* argv[]) {
try {
spdlog::set_level(spdlog::level::debug);
spdlog::info("Edge Proxy starting up...");
} catch (const spdlog::spdlog_ex& ex) {
std::cerr << "Log initialization failed: " << ex.what() << std::endl;
return 1;
}
spdlog::info("Initializing Data Storage...");
if (!DataStorage::getInstance().initialize("edge_proxy_data.db")) {
spdlog::critical("Failed to initialize DataStorage. Exiting.");
return 1;
}
try {
DataCache data_cache;
LiveDataCache live_data_cache;
MqttClient mqtt_client("tcp://localhost:1883", "edge-proxy-main-client");
// --- 报警管理器初始化 ---
AlarmManager alarm_manager;
// 加载报警规则
if (!alarm_manager.load_rules("../config/alarms.json")) {
spdlog::critical("Failed to load alarm rules. Exiting.");
return 1;
}
// 注册报警回调函数:当报警触发时,通过 MQTT 发布报警消息
alarm_manager.register_alarm_callback([&](const AlarmEvent& event) {
std::string alarm_topic = "alerts/device/" + event.device_id + "/" + AlarmManager::alarm_level_to_string(event.level);
nlohmann::json alarm_payload;
alarm_payload["event_id"] = event.event_id;
alarm_payload["rule_id"] = event.rule_id;
alarm_payload["device_id"] = event.device_id;
alarm_payload["data_point_name"] = event.data_point_name;
alarm_payload["current_value"] = event.current_value;
alarm_payload["threshold"] = event.threshold;
alarm_payload["level"] = AlarmManager::alarm_level_to_string(event.level);
alarm_payload["timestamp"] = event.timestamp;
alarm_payload["message"] = event.message;
// 使用 g_io_context.post 将 MQTT 发布操作调度到主线程,避免在回调中直接阻塞
g_io_context.post([&, alarm_topic, payload_str = alarm_payload.dump()]() {
mqtt_client.publish(alarm_topic, payload_str, 1, false); // QoS 1, 不保留
});
spdlog::info("Published alarm: TOPIC={} PAYLOAD={}", alarm_topic, alarm_payload.dump());
});
// --- 报警管理器初始化结束 ---
auto report_to_mqtt = [&](const UnifiedData& data) {
// 使用 DataStorage 单例存储处理后的 UnifiedData 对象
if (DataStorage::getInstance().storeProcessedData(data)) {
spdlog::debug("Successfully stored PROCESSED data for device '{}'", data.device_id);
} else {
spdlog::error("Failed to store PROCESSED data for device '{}'", data.device_id);
}
// 更新实时数据缓存
live_data_cache.update_data(data.device_id, data.data_json);
// !!! 新增:进行数据比较和报警检查 !!!
try {
// 将 UnifiedData::data_json (string) 解析为 nlohmann::json 对象
// 这假设 data.data_json 总是有效的 JSON
nlohmann::json parsed_data_json = nlohmann::json::parse(data.data_json);
alarm_manager.check_data_for_alarms(data.device_id, parsed_data_json);
} catch (const nlohmann::json::exception& e) {
spdlog::error("Failed to parse data_json for alarm checking for device '{}': {}", data.device_id, e.what());
}
if (mqtt_client.is_connected()) {
// 网络正常,直接上报
std::string topic = "devices/" + data.device_id + "/data";
g_io_context.post([&, topic, payload = data.data_json]() {
mqtt_client.publish(topic, payload, 1, false);
});
} else {
// 网络断开,写入缓存
spdlog::warn("MQTT disconnected. Caching data for device '{}'.", data.device_id);
data_cache.add(data);
}
};
DeviceManager device_manager(g_io_context, report_to_mqtt);
MqttRouter mqtt_router(mqtt_client, device_manager);
std::vector<uint16_t> listen_ports = { 12345 };
TCPServer tcp_server(g_io_context, listen_ports, mqtt_client);
SystemMonitor::SystemMonitor monitor;
if (!data_cache.open("edge_data_cache.db")) {
spdlog::critical("Failed to initialize data cache. Exiting.");
return 1;
}
CacheUploader cache_uploader(g_io_context, mqtt_client, data_cache);
mqtt_client.set_connected_handler([&](const std::string& cause){
spdlog::info("MQTT client connected: {}", cause);
cache_uploader.start_upload();
});
mqtt_client.connect();
mqtt_router.start();
monitor.getCpuUtilization();
boost::asio::steady_timer system_monitor_timer(g_io_context, std::chrono::seconds(15));
// 将 alarm_manager 传递给 poll_system_metrics
system_monitor_timer.async_wait(std::bind(poll_system_metrics, std::ref(system_monitor_timer), std::ref(monitor), std::ref(mqtt_client), std::ref(alarm_manager)));
device_manager.load_and_start("../config/devices.json");
WebServer web_server(monitor, device_manager, live_data_cache, 8080);
web_server.start();
boost::asio::signal_set signals(g_io_context, SIGINT, SIGTERM);
signals.async_wait([&](const boost::system::error_code& error, int signal_number) {
spdlog::warn("Interrupt signal ({}) received. Shutting down.", signal_number);
// a. 首先停止所有数据采集线程
spdlog::info("[Shutdown] A. Stopping device manager services...");
device_manager.stop_all();
// b. 停止Web服务器线程
spdlog::info("[Shutdown] B. Stopping web server...");
web_server.stop();
// c. 断开MQTT连接 (这将释放它对io_context的'劫持')
spdlog::info("[Shutdown] C. Disconnecting from MQTT broker...");
mqtt_client.disconnect();
// d. 最后安全地停止io_context
spdlog::info("[Shutdown] D. Stopping main event loop...");
g_io_context.stop();
});
spdlog::info("All services are running. Press Ctrl+C to exit.");
g_io_context.run();
} catch (const std::exception& e) {
spdlog::critical("An unhandled exception occurred: {}", e.what());
return 1;
}
spdlog::info("Server has been shut down gracefully. Exiting.");
return 0;
}