web服务器文件开发

This commit is contained in:
GuanYuankai 2025-10-15 06:27:21 +00:00
parent 1cd0d68115
commit cb6d594e49
12 changed files with 673 additions and 20 deletions

View File

@ -3,9 +3,12 @@
{ {
"name": "Linux", "name": "Linux",
"includePath": [ "includePath": [
"${workspaceFolder}/**" "${workspaceFolder}/**",
"/usr/include"
],
"defines": [
"CROW_USE_BOOST"
], ],
"defines": [],
"compilerPath": "/usr/bin/gcc", "compilerPath": "/usr/bin/gcc",
"cStandard": "c17", "cStandard": "c17",
"cppStandard": "gnu++17", "cppStandard": "gnu++17",

View File

@ -9,7 +9,11 @@ find_package(spdlog REQUIRED)
find_package(Boost REQUIRED COMPONENTS system thread) find_package(Boost REQUIRED COMPONENTS system thread)
message(STATUS "Found Boost version: ${Boost_VERSION}") message(STATUS "Found Boost version: ${Boost_VERSION}")
find_package(PahoMqttCpp REQUIRED) find_package(PahoMqttCpp REQUIRED)
find_package(SQLite3 REQUIRED)
# Crow CMake
# Crow 使 Boost Asio
add_subdirectory(src/vendor/crow)
add_library(nlohmann_json INTERFACE) add_library(nlohmann_json INTERFACE)
target_include_directories(nlohmann_json INTERFACE target_include_directories(nlohmann_json INTERFACE
@ -54,6 +58,9 @@ add_library(edge_proxy_lib STATIC
# / # /
src/dataCache/data_cache.cc src/dataCache/data_cache.cc
src/dataCache/cache_uploader.cc src/dataCache/cache_uploader.cc
#web
src/web/web_server.cc
) )
target_include_directories(edge_proxy_lib PUBLIC target_include_directories(edge_proxy_lib PUBLIC
@ -65,11 +72,14 @@ target_link_libraries(edge_proxy_lib PRIVATE
Boost::system Boost::system
Boost::thread Boost::thread
PahoMqttCpp::paho-mqttpp3 PahoMqttCpp::paho-mqttpp3
sqlite3 SQLite::SQLite3
pthread pthread
nlohmann_json nlohmann_json
) )
target_link_libraries(edge_proxy_lib PUBLIC
Crow
)
# ================================================================= # =================================================================
# Main Application Target (UNCOMMENTED AND ACTIVATED) # Main Application Target (UNCOMMENTED AND ACTIVATED)
# ================================================================= # =================================================================

View File

@ -21,6 +21,7 @@ services:
- "8888:8888" - "8888:8888"
- "9999:9999" - "9999:9999"
- "502:502" - "502:502"
- "8080:8080"
command: sleep infinity command: sleep infinity
mqtt-broker: mqtt-broker:

View File

@ -21,6 +21,11 @@ RUN apt-get update && \
git \ git \
gdb \ gdb \
vim \ vim \
# Piper TTS 所需的系统库
espeak-ng-data \
libespeak1 \
python3 \
python3-pip \
# 开发库 # 开发库
libssl-dev \ libssl-dev \
libspdlog-dev \ libspdlog-dev \
@ -68,8 +73,16 @@ RUN cd /tmp/build-context/external/paho.mqtt.c && \
rm -rf /tmp/build-context && \ rm -rf /tmp/build-context && \
# 在所有 apt 操作完成后,最后进行清理 # 在所有 apt 操作完成后,最后进行清理
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
COPY piper_models/ /app/piper_models/
RUN pip install --no-cache-dir --user piper-tts
# 将 ~/.local/bin (包含 piper 二进制文件) 添加到 PATH
RUN echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bash_profile
# 现在dev 用户可以使用 `piper` 命令了,只要 shell 环境被正确加载。
# 我们可以将最终的 CMD 设置为加载环境然后执行一个命令,或者留空让用户交互。
# 5. 设置默认用户 # 5. 设置默认用户
# ================================= # =================================
# 容器的默认用户将是 'dev' # 容器的默认用户将是 'dev'
USER dev USER dev

Binary file not shown.

View File

@ -0,0 +1,487 @@
{
"audio": {
"sample_rate": 22050,
"quality": "medium"
},
"espeak": {
"voice": "cmn"
},
"inference": {
"noise_scale": 0.667,
"length_scale": 1,
"noise_w": 0.8
},
"phoneme_type": "espeak",
"phoneme_map": {},
"phoneme_id_map": {
"_": [
0
],
"^": [
1
],
"$": [
2
],
" ": [
3
],
"!": [
4
],
"'": [
5
],
"(": [
6
],
")": [
7
],
",": [
8
],
"-": [
9
],
".": [
10
],
":": [
11
],
";": [
12
],
"?": [
13
],
"a": [
14
],
"b": [
15
],
"c": [
16
],
"d": [
17
],
"e": [
18
],
"f": [
19
],
"h": [
20
],
"i": [
21
],
"j": [
22
],
"k": [
23
],
"l": [
24
],
"m": [
25
],
"n": [
26
],
"o": [
27
],
"p": [
28
],
"q": [
29
],
"r": [
30
],
"s": [
31
],
"t": [
32
],
"u": [
33
],
"v": [
34
],
"w": [
35
],
"x": [
36
],
"y": [
37
],
"z": [
38
],
"æ": [
39
],
"ç": [
40
],
"ð": [
41
],
"ø": [
42
],
"ħ": [
43
],
"ŋ": [
44
],
"œ": [
45
],
"ǀ": [
46
],
"ǁ": [
47
],
"ǂ": [
48
],
"ǃ": [
49
],
"ɐ": [
50
],
"ɑ": [
51
],
"ɒ": [
52
],
"ɓ": [
53
],
"ɔ": [
54
],
"ɕ": [
55
],
"ɖ": [
56
],
"ɗ": [
57
],
"ɘ": [
58
],
"ə": [
59
],
"ɚ": [
60
],
"ɛ": [
61
],
"ɜ": [
62
],
"ɞ": [
63
],
"ɟ": [
64
],
"ɠ": [
65
],
"ɡ": [
66
],
"ɢ": [
67
],
"ɣ": [
68
],
"ɤ": [
69
],
"ɥ": [
70
],
"ɦ": [
71
],
"ɧ": [
72
],
"ɨ": [
73
],
"ɪ": [
74
],
"ɫ": [
75
],
"ɬ": [
76
],
"ɭ": [
77
],
"ɮ": [
78
],
"ɯ": [
79
],
"ɰ": [
80
],
"ɱ": [
81
],
"ɲ": [
82
],
"ɳ": [
83
],
"ɴ": [
84
],
"ɵ": [
85
],
"ɶ": [
86
],
"ɸ": [
87
],
"ɹ": [
88
],
"ɺ": [
89
],
"ɻ": [
90
],
"ɽ": [
91
],
"ɾ": [
92
],
"ʀ": [
93
],
"ʁ": [
94
],
"ʂ": [
95
],
"ʃ": [
96
],
"ʄ": [
97
],
"ʈ": [
98
],
"ʉ": [
99
],
"ʊ": [
100
],
"ʋ": [
101
],
"ʌ": [
102
],
"ʍ": [
103
],
"ʎ": [
104
],
"ʏ": [
105
],
"ʐ": [
106
],
"ʑ": [
107
],
"ʒ": [
108
],
"ʔ": [
109
],
"ʕ": [
110
],
"ʘ": [
111
],
"ʙ": [
112
],
"ʛ": [
113
],
"ʜ": [
114
],
"ʝ": [
115
],
"ʟ": [
116
],
"ʡ": [
117
],
"ʢ": [
118
],
"ʲ": [
119
],
"ˈ": [
120
],
"ˌ": [
121
],
"ː": [
122
],
"ˑ": [
123
],
"˞": [
124
],
"β": [
125
],
"θ": [
126
],
"χ": [
127
],
"ᵻ": [
128
],
"ⱱ": [
129
],
"0": [
130
],
"1": [
131
],
"2": [
132
],
"3": [
133
],
"4": [
134
],
"5": [
135
],
"6": [
136
],
"7": [
137
],
"8": [
138
],
"9": [
139
],
"̧": [
140
],
"̃": [
141
],
"̪": [
142
],
"̯": [
143
],
"̩": [
144
],
"ʰ": [
145
],
"ˤ": [
146
],
"ε": [
147
],
"↓": [
148
],
"#": [
149
],
"\"": [
150
],
"↑": [
151
]
},
"num_symbols": 256,
"num_speakers": 1,
"speaker_id_map": {},
"piper_version": "1.0.0",
"language": {
"code": "zh_CN",
"family": "zh",
"region": "CN",
"name_native": "简体中文",
"name_english": "Chinese",
"country_english": "China"
},
"dataset": "huayan"
}

View File

@ -8,6 +8,7 @@
#include "deviceManager/device_manager.h" #include "deviceManager/device_manager.h"
#include "dataCache/data_cache.h" #include "dataCache/data_cache.h"
#include "dataCache/cache_uploader.h" #include "dataCache/cache_uploader.h"
#include "web/web_server.h"
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp> #include <boost/asio/steady_timer.hpp>
@ -34,7 +35,7 @@ void poll_system_metrics(
SystemMonitor::SystemMonitor& monitor, SystemMonitor::SystemMonitor& monitor,
MqttClient& mqtt_client MqttClient& mqtt_client
) { ) {
// ... 此函数内部逻辑保持不变 ... if (g_io_context.stopped()) return;
auto cpu_util = monitor.getCpuUtilization(); auto cpu_util = monitor.getCpuUtilization();
auto mem_info = monitor.getMemoryInfo(); auto mem_info = monitor.getMemoryInfo();
double mem_total_gb = mem_info.total_kb / 1024.0 / 1024.0; double mem_total_gb = mem_info.total_kb / 1024.0 / 1024.0;
@ -63,38 +64,33 @@ int main(int argc, char* argv[]) {
signal(SIGTERM, signalHandler); signal(SIGTERM, signalHandler);
try { try {
// --- 1. 初始化核心服务 ---
MqttClient mqtt_client("tcp://mqtt-broker:1883", "edge-proxy-main-client"); MqttClient mqtt_client("tcp://mqtt-broker:1883", "edge-proxy-main-client");
MqttRouter mqtt_router(mqtt_client); MqttRouter mqtt_router(mqtt_client);
std::vector<uint16_t> listen_ports = { 8888 }; std::vector<uint16_t> listen_ports = { 8888 };
TCPServer tcp_server(g_io_context, listen_ports, mqtt_client); TCPServer tcp_server(g_io_context, listen_ports, mqtt_client);
SystemMonitor::SystemMonitor monitor; SystemMonitor::SystemMonitor monitor;
// <<< MODIFIED: 初始化数据缓存和上传器 >>>
DataCache data_cache; DataCache data_cache;
if (!data_cache.open("edge_data_cache.db")) { // 数据库文件将创建在程序运行目录 if (!data_cache.open("edge_data_cache.db")) {
spdlog::critical("Failed to initialize data cache. Exiting."); spdlog::critical("Failed to initialize data cache. Exiting.");
return 1; return 1;
} }
CacheUploader cache_uploader(g_io_context, mqtt_client, data_cache); CacheUploader cache_uploader(g_io_context, mqtt_client, data_cache);
// <<< MODIFIED: 设置MQTT连接成功的回调用于触发断点续传 >>>
mqtt_client.set_connected_handler([&](const std::string& cause){ mqtt_client.set_connected_handler([&](const std::string& cause){
spdlog::info("MQTT client connected: {}", cause); spdlog::info("MQTT client connected: {}", cause);
// 当连接成功时,启动缓存上传
cache_uploader.start_upload(); cache_uploader.start_upload();
}); });
// 现在再连接MQTT
mqtt_client.connect(); mqtt_client.connect();
mqtt_router.start(); mqtt_router.start();
// --- 2. 启动系统状态监控定时器 ---
monitor.getCpuUtilization(); // 首次调用以初始化 monitor.getCpuUtilization();
boost::asio::steady_timer system_monitor_timer(g_io_context, std::chrono::seconds(15)); boost::asio::steady_timer system_monitor_timer(g_io_context, std::chrono::seconds(15));
system_monitor_timer.async_wait(std::bind(poll_system_metrics, std::ref(system_monitor_timer), std::ref(monitor), std::ref(mqtt_client))); system_monitor_timer.async_wait(std::bind(poll_system_metrics, std::ref(system_monitor_timer), std::ref(monitor), std::ref(mqtt_client)));
// --- 3. <<< MODIFIED: 创建包含缓存逻辑的统一数据上报回调函数 >>> // --- 创建包含缓存逻辑的统一数据上报回调函数 >>>
auto report_to_mqtt = [&](const UnifiedData& data) { auto report_to_mqtt = [&](const UnifiedData& data) {
if (mqtt_client.is_connected()) { if (mqtt_client.is_connected()) {
// 网络正常,直接上报 // 网络正常,直接上报
@ -109,19 +105,29 @@ int main(int argc, char* argv[]) {
} }
}; };
// --- 4. 实例化设备管理器并从文件加载所有设备 (使用新的回调) --- // 实例化设备管理器并从文件加载所有设备 (使用新的回调)
DeviceManager device_manager(g_io_context, report_to_mqtt); DeviceManager device_manager(g_io_context, report_to_mqtt);
// 默认从程序运行目录下的 "devices.json" 文件加载
device_manager.load_and_start("../config/devices.json"); device_manager.load_and_start("../config/devices.json");
// --- 5. 启动主事件循环 (程序将阻塞在这里直到被信号中断) --- WebServer web_server(monitor, 8080);
web_server.start();
spdlog::info("All services are running. Press Ctrl+C to exit."); spdlog::info("All services are running. Press Ctrl+C to exit.");
g_io_context.run(); g_io_context.run();
// --- 清理工作 --- // --- 清理工作 ---
spdlog::info("Shutting down MQTT client..."); spdlog::info("Shutting down MQTT client...");
spdlog::info("Stopping device manager services...");
device_manager.stop_all();
spdlog::info("Stopping web server...");
web_server.stop();
spdlog::info("Disconnecting from MQTT broker...");
mqtt_client.disconnect(); mqtt_client.disconnect();
// mqtt_client.disconnect();
// web_server.stop();
} catch (const std::exception& e) { } catch (const std::exception& e) {
spdlog::critical("An unhandled exception occurred: {}", e.what()); spdlog::critical("An unhandled exception occurred: {}", e.what());
return 1; return 1;

View File

@ -3,7 +3,7 @@
#define MODBUS_MASTER_POLLER_H #define MODBUS_MASTER_POLLER_H
#include "protocol/iprotocol_adapter.h" // For UnifiedData and ReportDataCallback #include "protocol/iprotocol_adapter.h" // For UnifiedData and ReportDataCallback
#include "modbus_common.h" // <<< MODIFIED: 包含通用配置定义 #include "modbus_common.h"
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <memory> #include <memory>
#include <string> #include <string>

View File

@ -64,12 +64,21 @@ MemoryInfo SystemMonitor::getMemoryInfo() const {
std::string meminfo_content = readFile("/proc/meminfo"); std::string meminfo_content = readFile("/proc/meminfo");
std::istringstream stream(meminfo_content); std::istringstream stream(meminfo_content);
std::string line; std::string line;
// 遍历/proc/meminfo的每一行
while (std::getline(stream, line)) { while (std::getline(stream, line)) {
// 查找并解析总内存
if (line.rfind("MemTotal:", 0) == 0) { if (line.rfind("MemTotal:", 0) == 0) {
std::sscanf(line.c_str(), "MemTotal: %lu kB", &info.total_kb); std::sscanf(line.c_str(), "MemTotal: %lu kB", &info.total_kb);
break; }
// 查找并解析可用内存 (MemAvailable通常比MemFree更准确)
else if (line.rfind("MemAvailable:", 0) == 0) {
std::sscanf(line.c_str(), "MemAvailable: %lu kB", &info.available_kb);
} }
} }
// 如果系统中没有 MemAvailable 字段 (非常老的内核), 则将 available_kb 设为0
// 我们的 web_server 代码会处理这种情况
return info; return info;
} }

View File

@ -27,6 +27,7 @@ struct StorageDevice {
struct MemoryInfo { struct MemoryInfo {
uint64_t total_kb = 0; uint64_t total_kb = 0;
uint64_t available_kb = 0;
// 未来可以添加 free, available, cached 等 // 未来可以添加 free, available, cached 等
}; };
@ -55,7 +56,6 @@ public:
std::string getKernelLogs(int last_n_lines = 20) const; std::string getKernelLogs(int last_n_lines = 20) const;
private: private:
// --- 核心工具函数(现在作为私有成员)---
std::string readFile(const std::string& filePath) const; std::string readFile(const std::string& filePath) const;
std::string execCommand(const char* cmd) const; std::string execCommand(const char* cmd) const;

68
src/web/web_server.cc Normal file
View File

@ -0,0 +1,68 @@
// 文件名: src/web/web_server.cc
#include "web_server.h"
#include "spdlog/spdlog.h"
WebServer::WebServer(SystemMonitor::SystemMonitor& monitor, uint16_t port)
: m_monitor(monitor), m_port(port)
{
// Crow默认会输出很多调试信息我们可以将其日志级别调高
// 以便只显示警告和错误,让我们的终端更干净。
m_app.loglevel(crow::LogLevel::Warning);
// 调用函数来设置所有的API路由
setup_routes();
}
WebServer::~WebServer() {
stop();
}
void WebServer::start() {
if (m_thread.joinable()) {
spdlog::warn("Web server is already running.");
return;
}
// Crow的 run() 方法是一个阻塞操作,它会一直运行直到被停止。
// 因此,我们必须在一个独立的线程中启动它,否则它会卡住我们的主程序。
m_thread = std::thread([this]() {
spdlog::info("Starting Web server on port {}", m_port);
m_app.port(m_port).run();
spdlog::info("Web server has stopped.");
});
}
void WebServer::stop() {
// a. 停止Crow应用
m_app.stop();
// b. 等待线程执行完毕
if (m_thread.joinable()) {
m_thread.join();
}
}
void WebServer::setup_routes() {
// ----------------------------------------------------------------
// 定义第一个API路由: GET /api/system/status
// ----------------------------------------------------------------
CROW_ROUTE(m_app, "/api/system/status")
([this] {
auto cpu_util = m_monitor.getCpuUtilization();
auto mem_info = m_monitor.getMemoryInfo();
crow::json::wvalue response;
response["cpu_usage_percentage"] = cpu_util.totalUsagePercentage;
response["memory_total_kb"] = mem_info.total_kb;
response["memory_free_kb"] = mem_info.available_kb;
response["memory_usage_percentage"] = (mem_info.total_kb > 0)
? (1.0 - static_cast<double>(mem_info.available_kb) / mem_info.total_kb) * 100.0
: 0.0;
return response;
});
// 未来在这里添加更多的路由,例如:
// CROW_ROUTE(m_app, "/api/devices")([this]{ ... });
}

56
src/web/web_server.h Normal file
View File

@ -0,0 +1,56 @@
// 文件名: src/web/web_server.h
#ifndef WEB_SERVER_H
#define WEB_SERVER_H
#include "crow.h" // 引入 Crow 库的头文件
#include "systemMonitor/system_monitor.h"
#include <thread>
/**
* @brief Web服务器模块
* * HTTP服务器RESTful API接口
* 线io_context事件循环
*/
class WebServer {
public:
/**
* @brief
* @param monitor SystemMonitor的引用
* @param port Web服务器监听的端口号8080
*/
WebServer(SystemMonitor::SystemMonitor& monitor, uint16_t port = 8080);
/**
* @brief
* * 退Web服务线程能够被安全地停止和清理
*/
~WebServer();
// 禁止拷贝和赋值,因为该类管理着一个线程资源
WebServer(const WebServer&) = delete;
WebServer& operator=(const WebServer&) = delete;
/**
* @brief 线Web服务
*/
void start();
/**
* @brief Web服务并等待线程退出
*/
void stop();
private:
/**
* @brief API路由URL路径
*/
void setup_routes();
crow::SimpleApp m_app; // Crow 应用实例
SystemMonitor::SystemMonitor& m_monitor;
uint16_t m_port;
std::thread m_thread; // 运行Web服务的后台线程
};
#endif // WEB_SERVER_H