279 lines
12 KiB
C++
279 lines
12 KiB
C++
// #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;
|
||
}
|