增加配置管理服务

This commit is contained in:
GuanYuankai 2025-10-23 08:15:09 +00:00
parent 1365e95c19
commit 374e8b586d
5 changed files with 258 additions and 26 deletions

View File

@ -4,7 +4,6 @@ project(EdgeProxy LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# --- spdlog ---
find_package(spdlog REQUIRED)
find_package(Boost REQUIRED COMPONENTS system thread)
message(STATUS "Found Boost version: ${Boost_VERSION}")
@ -14,8 +13,7 @@ find_package(SQLite3 REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GST REQUIRED gstreamer-1.0 gstreamer-app-1.0)
find_package(OpenCV REQUIRED)
# Crow CMake
# Crow 使 Boost Asio
add_subdirectory(src/vendor/crow)
add_library(nlohmann_json INTERFACE)
@ -26,27 +24,21 @@ target_include_directories(nlohmann_json INTERFACE
add_library(edge_proxy_lib STATIC
# TCP
src/network/tcp_server.cc
# --- ---
src/deviceManager/device_manager.cc
#
src/utils/mqtt_topic_matcher.cpp
# --- MQTT ---
src/mqtt/mqtt_client.cpp
src/mqtt/mqtt_router.cpp
src/mqtt/handler/data_handler.cpp
src/mqtt/handler/command_handler.cpp
# --- ---
src/protocol/protocol.cc
src/protocol/private_protocol_adapter.cc
src/protocol/protocol_factory.cc
#
src/systemMonitor/system_monitor.cc
#modbus
src/modbus/modbus_rtu_client.cc
src/modbus/modbus_rtu_bus_service.cc
@ -54,19 +46,17 @@ add_library(edge_proxy_lib STATIC
# --- Modbus Master ---
src/protocol/modbus/modbus_protocol.cc
src/modbus/modbus_master_poller.cc
# /
src/dataCache/data_cache.cc
src/dataCache/cache_uploader.cc
#web
src/web/web_server.cc
#SQL
src/dataStorage/data_storage.cc
#tts
src/tts/piper_tts_interface.cc
#config
src/config/config_manager.cc
)
target_include_directories(edge_proxy_lib PUBLIC
@ -88,9 +78,9 @@ target_link_libraries(edge_proxy_lib PUBLIC
Crow
nlohmann_json
)
# =================================================================
# Main Application Target (UNCOMMENTED AND ACTIVATED)
# =================================================================
# ==================================
# Main Application Target
# ==================================
add_executable(edge_proxy
src/main.cpp
)
@ -115,7 +105,7 @@ target_include_directories(edge_streamer PRIVATE
${GST_INCLUDE_DIRS}
)
# =================#================================================
# =================================================================
#
# =================================================================
# add_executable(test

14
config/config.json Normal file
View File

@ -0,0 +1,14 @@
{
"config_base_path": "/app/config/",
"data_cache_db_path": "edge_data_cache.db",
"data_storage_db_path": "edge_proxy_data.db",
"device_id": "rk3588-proxy-001",
"log_level": "info",
"mqtt_broker": "tcp://localhost:1883",
"mqtt_client_id_prefix": "edge-proxy-",
"tcp_server_ports": [
12345,
502
],
"web_server_port": 8080
}

View File

@ -0,0 +1,123 @@
#include "config_manager.h"
#include <fstream>
ConfigManager& ConfigManager::getInstance() {
static ConfigManager instance;
return instance;
}
json ConfigManager::createDefaultConfig() {
return json {
{"device_id", "default-edge-proxy-01"},
{"config_base_path", "/app/config/"},
{"mqtt_broker", "tcp://localhost:1883"},
{"mqtt_client_id_prefix", "edge-proxy-"},
{"data_storage_db_path", "edge_proxy_data.db"},
{"data_cache_db_path", "edge_data_cache.db"},
{"tcp_server_ports", {12345}},
{"web_server_port", 8080},
{"log_level", "debug"}
// **新特性**:您可以在这里添加任何新的默认值
};
}
// 私有辅助函数:保存(必须由已持有 unique_lock 的函数调用)
bool ConfigManager::save_unlocked() {
std::ofstream ofs(m_configFilePath);
if (!ofs.is_open()) {
spdlog::error("Failed to open config file '{}' for writing.", m_configFilePath);
return false;
}
try {
ofs << m_config_json.dump(4); // 格式化输出
return true;
} catch (const json::exception& e) {
spdlog::error("Failed to serialize config. Error: {}", e.what());
return false;
}
}
bool ConfigManager::load(const std::string& configFilePath) {
std::unique_lock<std::shared_mutex> lock(m_mutex); // 写操作,使用 unique_lock
m_configFilePath = configFilePath;
std::ifstream ifs(m_configFilePath);
if (!ifs.is_open()) {
spdlog::warn("Config file '{}' not found. Creating with default values.", m_configFilePath);
m_config_json = createDefaultConfig();
if(save_unlocked()) {
spdlog::info("Default config file created at '{}'.", m_configFilePath);
return true;
} else {
spdlog::error("Failed to create default config file.");
return false;
}
}
try {
ifs >> m_config_json;
// **重要**合并默认值。确保JSON文件中缺失的键被默认值补全。
json defaults = createDefaultConfig();
defaults.merge_patch(m_config_json); // 已加载的值会覆盖默认值
m_config_json = defaults; //
spdlog::info("Successfully loaded config from '{}'. Device ID: {}", m_configFilePath, m_config_json.value("device_id", "N/A"));
// 检查是否需要重新保存(
if (save_unlocked()) {
spdlog::debug("Config file updated with new default keys if any.");
}
return true;
} catch (const json::exception& e) {
spdlog::error("Failed to parse config file '{}'. Error: {}. Using default values.", m_configFilePath, e.what());
m_config_json = createDefaultConfig();
return false;
}
}
bool ConfigManager::save() {
std::unique_lock<std::shared_mutex> lock(m_mutex);
return save_unlocked();
}
std::string ConfigManager::getDeviceID() {
return get<std::string>("device_id", "default-edge-proxy-01");
}
std::string ConfigManager::getConfigBasePath() {
return get<std::string>("config_base_path", "/app/config/");
}
std::string ConfigManager::getMqttBroker() {
return get<std::string>("mqtt_broker", "tcp://localhost:1883");
}
std::string ConfigManager::getMqttClientID() {
// 逻辑保持不变
return get<std::string>("mqtt_client_id_prefix", "edge-proxy-") + getDeviceID();
}
std::string ConfigManager::getDataStorageDbPath() {
return getConfigBasePath() + get<std::string>("data_storage_db_path", "edge_proxy_data.db");
}
std::string ConfigManager::getDataCacheDbPath() {
return getConfigBasePath() + get<std::string>("data_cache_db_path", "edge_data_cache.db");
}
std::string ConfigManager::getDevicesConfigPath() {
return getConfigBasePath() + "devices.json";
}
int ConfigManager::getWebServerPort() {
return get<int>("web_server_port", 8080);
}
std::vector<uint16_t> ConfigManager::getTcpServerPorts() {
return get<std::vector<uint16_t>>("tcp_server_ports", {12345});
}
std::string ConfigManager::getLogLevel() {
return get<std::string>("log_level", "debug");
}

View File

@ -0,0 +1,97 @@
#pragma once
#include <string>
#include <vector>
#include <shared_mutex>
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h> // 包含了 spdlog
#include <typeinfo> // 用于 typeid
// 使用 nlohmann::json 命名空间
using json = nlohmann::json;
// 移除 AppConfig 结构体
class ConfigManager {
public:
static ConfigManager& getInstance();
bool load(const std::string& configFilePath);
bool save();
/**
* @brief [] 线 GET
* @tparam T (e.g., std::string, int, bool)
* @param key JSON中的键
* @param default_value
* @return T
*/
template<typename T>
T get(const std::string& key, const T& default_value) {
std::shared_lock<std::shared_mutex> lock(m_mutex);
if (!m_config_json.contains(key)) {
spdlog::debug("Config key '{}' not found, using default value.", key);
return default_value;
}
try {
return m_config_json.at(key).get<T>();
} catch (const json::type_error& e) {
spdlog::warn("Config type mismatch for key '{}'. Expected '{}', found '{}'. Using default. Error: {}",
key,
typeid(T).name(),
m_config_json.at(key).type_name(),
e.what());
return default_value;
}
}
/**
* @brief [] 线 SET
* @tparam T
* @param key
* @param value
*/
template<typename T>
void set(const std::string& key, const T& value) {
{
std::unique_lock<std::shared_mutex> lock(m_mutex);
// 无论键是否存在,都会被创建或覆盖
m_config_json[key] = value;
spdlog::info("Config updated: [{}] set.", key);
} // 锁在这里释放
save(); // 自动保存到文件
// **特殊处理**: 某些配置需要立即生效
if (key == "log_level") {
spdlog::set_level(spdlog::level::from_str(value));
}
}
std::string getDeviceID();
std::string getConfigBasePath();
std::string getMqttBroker();
std::string getMqttClientID();
std::string getDataStorageDbPath();
std::string getDataCacheDbPath();
std::string getDevicesConfigPath();
int getWebServerPort();
std::vector<uint16_t> getTcpServerPorts();
std::string getLogLevel();
// ... 您可以继续为其他核心配置添加包装器 ...
private:
ConfigManager() = default;
~ConfigManager() = default;
ConfigManager(const ConfigManager&) = delete;
ConfigManager& operator=(const ConfigManager&) = delete;
json createDefaultConfig();
bool save_unlocked();
std::string m_configFilePath;
json m_config_json;
mutable std::shared_mutex m_mutex;
};

View File

@ -1,5 +1,4 @@
// main.cpp
#include "network/tcp_server.h"
#include "mqtt/mqtt_client.h"
#include "mqtt/mqtt_router.h"
@ -11,6 +10,7 @@
#include "web/web_server.h"
#include "dataCache/live_data_cache.h"
#include "dataStorage/data_storage.h"
#include "config/config_manager.h"
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
@ -45,8 +45,15 @@ void poll_system_metrics(
int main(int argc, char* argv[]) {
const std::string config_path = "/app/config/config.json";
if (!ConfigManager::getInstance().load(config_path)) {
std::cerr << "Failed to load configuration from " << config_path
<< ". Running with defaults, but this may cause issues." << std::endl;
}
try {
spdlog::set_level(spdlog::level::debug);
spdlog::set_level(spdlog::level::from_str(ConfigManager::getInstance().getLogLevel()));
spdlog::info("Edge Proxy starting up...");
} catch (const spdlog::spdlog_ex& ex) {
std::cerr << "Log initialization failed: " << ex.what() << std::endl;
@ -54,7 +61,7 @@ int main(int argc, char* argv[]) {
}
spdlog::info("Initializing Data Storage...");
if (!DataStorage::getInstance().initialize("edge_proxy_data.db")) {
if (!DataStorage::getInstance().initialize(ConfigManager::getInstance().getDataStorageDbPath())) {
spdlog::critical("Failed to initialize DataStorage. Exiting.");
return 1;
}
@ -62,9 +69,10 @@ int main(int argc, char* argv[]) {
try {
DataCache data_cache;
LiveDataCache live_data_cache;
MqttClient mqtt_client("tcp://localhost:1883", "edge-proxy-main-client");
MqttClient mqtt_client(ConfigManager::getInstance().getMqttBroker(),
ConfigManager::getInstance().getMqttClientID()
);
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 {
@ -86,8 +94,8 @@ int main(int argc, char* argv[]) {
};
DeviceManager device_manager(g_io_context, report_to_mqtt);
MqttRouter mqtt_router(mqtt_client, device_manager, "../config/devices.json");
std::vector<uint16_t> listen_ports = { 12345 };
MqttRouter mqtt_router(mqtt_client, device_manager, ConfigManager::getInstance().getDevicesConfigPath());
std::vector<uint16_t> listen_ports = ConfigManager::getInstance().getTcpServerPorts();
TCPServer tcp_server(g_io_context, listen_ports, mqtt_client);
SystemMonitor::SystemMonitor monitor;
@ -111,9 +119,9 @@ int main(int argc, char* argv[]) {
system_monitor_timer.async_wait(std::bind(poll_system_metrics, std::ref(system_monitor_timer), std::ref(monitor), std::ref(mqtt_client)));
device_manager.load_and_start("../config/devices.json");
device_manager.load_and_start(ConfigManager::getInstance().getDevicesConfigPath());
WebServer web_server(monitor, device_manager, live_data_cache, 8080);
WebServer web_server(monitor, device_manager, live_data_cache, ConfigManager::getInstance().getWebServerPort());
web_server.start();
boost::asio::signal_set signals(g_io_context, SIGINT, SIGTERM);