web服务器文件开发
This commit is contained in:
parent
1cd0d68115
commit
cb6d594e49
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
# =================================================================
|
# =================================================================
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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.
|
|
@ -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"
|
||||||
|
}
|
||||||
32
src/main.cpp
32
src/main.cpp
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]{ ... });
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
Loading…
Reference in New Issue