增加配置管理端点
This commit is contained in:
parent
b14e2cba8e
commit
0b4fa12325
|
|
@ -7,41 +7,41 @@
|
|||
"enabled": true,
|
||||
"id": "cam_01_intrusion",
|
||||
"input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1301",
|
||||
"module_type": "intrusion_detection",
|
||||
"output_rtsp": "rtsp://127.0.0.1:8554/ch1301",
|
||||
"module_config": {
|
||||
"model_path": "/app/edge-proxy/models/RK3588/yolov5s-640-640.rknn",
|
||||
"rknn_thread_num": 3,
|
||||
"class_num": 80,
|
||||
"intrusion_zone": [
|
||||
100,
|
||||
100,
|
||||
300,
|
||||
300
|
||||
1820,
|
||||
1820
|
||||
],
|
||||
"time_threshold_sec": 3.0,
|
||||
"class_num": 80,
|
||||
"label_path": "/app/edge-proxy/models/coco_80_labels_list.txt"
|
||||
}
|
||||
"label_path": "/app/edge-proxy/models/coco_80_labels_list.txt",
|
||||
"model_path": "/app/edge-proxy/models/RK3588/yolov5s-640-640.rknn",
|
||||
"rknn_thread_num": 3,
|
||||
"time_threshold_sec": 3
|
||||
},
|
||||
"module_type": "intrusion_detection",
|
||||
"output_rtsp": "rtsp://127.0.0.1:8554/ch1301"
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"id": "cam_03_intrusion",
|
||||
"input_url": "rtsp://admin:hzx12345@192.168.1.10:554/Streaming/Channels/1501",
|
||||
"module_type": "intrusion_detection",
|
||||
"output_rtsp": "rtsp://127.0.0.1:8554/ch1501",
|
||||
"module_config": {
|
||||
"model_path": "/app/edge-proxy/models/RK3588/yolov5s-640-640.rknn",
|
||||
"rknn_thread_num": 3,
|
||||
"class_num": 80,
|
||||
"intrusion_zone": [
|
||||
100,
|
||||
100,
|
||||
300,
|
||||
300
|
||||
],
|
||||
"time_threshold_sec": 3.0,
|
||||
"class_num": 80,
|
||||
"label_path": "/app/edge-proxy/models/coco_80_labels_list.txt"
|
||||
}
|
||||
"label_path": "/app/edge-proxy/models/coco_80_labels_list.txt",
|
||||
"model_path": "/app/edge-proxy/models/RK3588/yolov5s-640-640.rknn",
|
||||
"rknn_thread_num": 3,
|
||||
"time_threshold_sec": 3
|
||||
},
|
||||
"module_type": "intrusion_detection",
|
||||
"output_rtsp": "rtsp://127.0.0.1:8554/ch1501"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -140,56 +140,7 @@ std::string ConfigManager::getVideoConfigPath() {
|
|||
get<std::string>("video_config_path", "video_config.json");
|
||||
}
|
||||
|
||||
// bool ConfigManager::getIsVideoServiceEnabled() const {
|
||||
// std::shared_lock<std::shared_mutex> lock(m_mutex);
|
||||
// try {
|
||||
// if (m_config_json.contains("video_service")) {
|
||||
// return m_config_json["video_service"].value("enabled", false);
|
||||
// }
|
||||
// } catch (const json::type_error &e) {
|
||||
// spdlog::warn(
|
||||
// "Config type mismatch for key 'video_service.enabled'. Error: {}",
|
||||
// e.what());
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// std::vector<ConfigManager::VideoStreamConfig>
|
||||
// ConfigManager::getVideoStreamConfigs() const {
|
||||
// std::vector<VideoStreamConfig> configs;
|
||||
|
||||
// std::shared_lock<std::shared_mutex> lock(m_mutex);
|
||||
|
||||
// try {
|
||||
// if (m_config_json.contains("video_streams") &&
|
||||
// m_config_json["video_streams"].is_array()) {
|
||||
// for (const auto &stream_json : m_config_json["video_streams"]) {
|
||||
// VideoStreamConfig cfg;
|
||||
// cfg.id = stream_json.value("id", "");
|
||||
// cfg.enabled = stream_json.value("enabled", false);
|
||||
// cfg.input_url = stream_json.value("input_url", "");
|
||||
// cfg.output_rtsp = stream_json.value("output_rtsp", "");
|
||||
|
||||
// cfg.module_type = stream_json.value("module_type", "");
|
||||
// cfg.module_config = stream_json.value("module_config",
|
||||
// json::object());
|
||||
|
||||
// if (cfg.module_type.empty()) {
|
||||
// spdlog::warn("Video stream '{}' has no 'module_type' defined. It
|
||||
// may "
|
||||
// "fail to start.",
|
||||
// cfg.id);
|
||||
// }
|
||||
|
||||
// configs.push_back(cfg);
|
||||
// }
|
||||
// } else {
|
||||
// spdlog::warn("Config key 'video_streams' not found or is not an
|
||||
// array.");
|
||||
// }
|
||||
// } catch (const json::exception &e) {
|
||||
// spdlog::error("Error parsing 'video_streams': {}", e.what());
|
||||
// }
|
||||
|
||||
// return configs;
|
||||
// }
|
||||
std::string ConfigManager::getConfigFilePath() const {
|
||||
std::shared_lock<std::shared_mutex> lock(m_mutex);
|
||||
return m_configFilePath;
|
||||
}
|
||||
|
|
@ -76,6 +76,7 @@ public:
|
|||
// bool getIsVideoServiceEnabled() const;
|
||||
// std::vector<VideoStreamConfig> getVideoStreamConfigs() const;
|
||||
std::string getVideoConfigPath();
|
||||
std::string getConfigFilePath() const;
|
||||
|
||||
private:
|
||||
ConfigManager() = default;
|
||||
|
|
|
|||
|
|
@ -44,16 +44,13 @@ void WebServer::stop() {
|
|||
}
|
||||
}
|
||||
|
||||
// [新增] set_shutdown_handler 的实现
|
||||
void WebServer::set_shutdown_handler(std::function<void()> handler) {
|
||||
m_shutdown_handler = handler;
|
||||
}
|
||||
|
||||
// [新增] validate_video_config 的实现
|
||||
bool WebServer::validate_video_config(const std::string &json_string,
|
||||
std::string &error_message) {
|
||||
try {
|
||||
// 我们使用 nlohmann::json 来解析和验证
|
||||
auto config = nlohmann::json::parse(json_string);
|
||||
|
||||
if (!config.is_object()) {
|
||||
|
|
@ -140,9 +137,62 @@ bool WebServer::validate_video_config(const std::string &json_string,
|
|||
}
|
||||
}
|
||||
|
||||
void WebServer::setup_routes() {
|
||||
bool WebServer::validate_main_config(const std::string &json_string,
|
||||
std::string &error_message) {
|
||||
try {
|
||||
auto config = nlohmann::json::parse(json_string);
|
||||
|
||||
// --- 原始路由 (未省略) ---
|
||||
if (!config.is_object()) {
|
||||
error_message = "Root is not an object.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. 必需的字符串类型
|
||||
for (const char *key :
|
||||
{"device_id", "config_base_path", "mqtt_broker",
|
||||
"mqtt_client_id_prefix", "data_storage_db_path", "data_cache_db_path",
|
||||
"log_level", "alarm_rules_path", "piper_executable_path",
|
||||
"piper_model_path", "video_config_path"}) {
|
||||
if (!config.contains(key) || !config[key].is_string() ||
|
||||
config[key].get<std::string>().empty()) {
|
||||
error_message =
|
||||
"Missing or invalid/empty string key: '" + std::string(key) + "'.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 必需的整数类型
|
||||
if (!config.contains("web_server_port") ||
|
||||
!config["web_server_port"].is_number_integer()) {
|
||||
error_message = "Missing or invalid 'web_server_port' (must be integer).";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 必需的数组类型
|
||||
if (!config.contains("tcp_server_ports") ||
|
||||
!config["tcp_server_ports"].is_array()) {
|
||||
error_message = "Missing or invalid 'tcp_server_ports' (must be array).";
|
||||
return false;
|
||||
}
|
||||
for (const auto &port : config["tcp_server_ports"]) {
|
||||
if (!port.is_number_integer()) {
|
||||
error_message = "Item in 'tcp_server_ports' is not an integer.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true; // 所有检查通过
|
||||
|
||||
} catch (const nlohmann::json::parse_error &e) {
|
||||
error_message = "Invalid JSON: " + std::string(e.what());
|
||||
return false;
|
||||
} catch (const std::exception &e) {
|
||||
error_message = "Validation error: " + std::string(e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void WebServer::setup_routes() {
|
||||
|
||||
CROW_ROUTE((*this), "/api/system/id").methods("GET"_method)([this] {
|
||||
auto deviceID = ConfigManager::getInstance().getDeviceID();
|
||||
|
|
@ -354,8 +404,6 @@ void WebServer::setup_routes() {
|
|||
}
|
||||
});
|
||||
|
||||
// --- [!! 原始路由: 设备配置管理 !!] ---
|
||||
|
||||
/**
|
||||
* @brief GET /api/devices/config
|
||||
* 获取设备配置 (devices.json) 的原始 JSON 字符串
|
||||
|
|
@ -431,10 +479,6 @@ void WebServer::setup_routes() {
|
|||
}
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// [!! 新增: 视频配置路由 (完整版) !!]
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief GET /api/video_config
|
||||
* 获取 video_config.json 的原始 JSON 字符串
|
||||
|
|
@ -442,7 +486,7 @@ void WebServer::setup_routes() {
|
|||
CROW_ROUTE((*this), "/api/video_config").methods("GET"_method)([this]() {
|
||||
std::string config_path;
|
||||
try {
|
||||
// 从 ConfigManager 获取 video_config.json 的路径
|
||||
|
||||
config_path = ConfigManager::getInstance().getVideoConfigPath();
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::error("Failed to get video config path from ConfigManager: {}",
|
||||
|
|
@ -463,7 +507,6 @@ void WebServer::setup_routes() {
|
|||
return res;
|
||||
}
|
||||
|
||||
// 读取文件全部内容
|
||||
std::string content((std::istreambuf_iterator<char>(ifs)),
|
||||
(std::istreambuf_iterator<char>()));
|
||||
|
||||
|
|
@ -481,7 +524,6 @@ void WebServer::setup_routes() {
|
|||
const std::string &new_config_content = req.body;
|
||||
std::string error_msg;
|
||||
|
||||
// 1. [!! 验证 !!]
|
||||
if (!validate_video_config(new_config_content, error_msg)) {
|
||||
spdlog::warn("Web API: Failed to save video_config: {}", error_msg);
|
||||
crow::json::wvalue error_json;
|
||||
|
|
@ -492,11 +534,9 @@ void WebServer::setup_routes() {
|
|||
return res;
|
||||
}
|
||||
|
||||
// 2. 获取路径
|
||||
std::string config_path =
|
||||
ConfigManager::getInstance().getVideoConfigPath();
|
||||
|
||||
// 3. 写入文件
|
||||
std::ofstream ofs(config_path);
|
||||
if (!ofs.is_open()) {
|
||||
spdlog::error("Failed to open video config file for writing: {}",
|
||||
|
|
@ -507,8 +547,6 @@ void WebServer::setup_routes() {
|
|||
return res;
|
||||
}
|
||||
|
||||
// 我们将 nlohmann::json 解析后的内容重新 dump 写入
|
||||
// 这样可以确保写入的 JSON 格式是规范的 (例如,格式化)
|
||||
auto json_data = nlohmann::json::parse(new_config_content);
|
||||
ofs << json_data.dump(4); // 格式化写入
|
||||
ofs.close();
|
||||
|
|
@ -525,7 +563,6 @@ void WebServer::setup_routes() {
|
|||
return res;
|
||||
});
|
||||
|
||||
|
||||
CROW_ROUTE((*this), "/api/service/reload").methods("POST"_method)([this]() {
|
||||
if (m_shutdown_handler) {
|
||||
spdlog::info("Web API: Received request to reload service...");
|
||||
|
|
@ -545,4 +582,109 @@ void WebServer::setup_routes() {
|
|||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @brief GET /api/config
|
||||
* 获取主 config.json 的原始 JSON 字符串
|
||||
*/
|
||||
CROW_ROUTE((*this), "/api/config").methods("GET"_method)([this]() {
|
||||
std::string config_path;
|
||||
try {
|
||||
// 1. 从 ConfigManager 获取路径
|
||||
config_path = ConfigManager::getInstance().getConfigFilePath();
|
||||
if (config_path.empty()) {
|
||||
throw std::runtime_error("ConfigManager returned an empty path.");
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::error("Failed to get main config path from ConfigManager: {}",
|
||||
e.what());
|
||||
auto res = crow::response(500, "{\"error\":\"Server configuration error: "
|
||||
"cannot determine config path.\"}");
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
}
|
||||
|
||||
// 2. 读取文件
|
||||
std::ifstream ifs(config_path);
|
||||
if (!ifs.is_open()) {
|
||||
spdlog::error("Failed to open main config file for reading: {}",
|
||||
config_path);
|
||||
auto res =
|
||||
crow::response(404, "{\"error\":\"Main config file not found.\"}");
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
}
|
||||
|
||||
// 3. 返回内容
|
||||
std::string content((std::istreambuf_iterator<char>(ifs)),
|
||||
(std::istreambuf_iterator<char>()));
|
||||
|
||||
auto res = crow::response(200, content);
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
});
|
||||
|
||||
/**
|
||||
* @brief POST /api/config
|
||||
* 验证并保存主 config.json
|
||||
*/
|
||||
CROW_ROUTE((*this), "/api/config")
|
||||
.methods("POST"_method)([this](const crow::request &req) {
|
||||
const std::string &new_config_content = req.body;
|
||||
std::string error_msg;
|
||||
|
||||
if (!validate_main_config(new_config_content, error_msg)) {
|
||||
spdlog::warn("Web API: Failed to save main config: {}", error_msg);
|
||||
crow::json::wvalue error_json;
|
||||
error_json["status"] = "error";
|
||||
error_json["message"] = "Invalid JSON format or schema: " + error_msg;
|
||||
auto res = crow::response(400, error_json.dump());
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string config_path =
|
||||
ConfigManager::getInstance().getConfigFilePath();
|
||||
if (config_path.empty()) {
|
||||
auto res = crow::response(
|
||||
500, "{\"error\":\"Failed to get config file path on server.\"}");
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
}
|
||||
|
||||
std::ofstream ofs(config_path);
|
||||
if (!ofs.is_open()) {
|
||||
spdlog::error("Failed to open main config file for writing: {}",
|
||||
config_path);
|
||||
auto res = crow::response(
|
||||
500, "{\"error\":\"Failed to write config file on server.\"}");
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
}
|
||||
|
||||
try {
|
||||
auto json_data = nlohmann::json::parse(new_config_content);
|
||||
ofs << json_data.dump(4);
|
||||
ofs.close();
|
||||
} catch (const std::exception &e) {
|
||||
spdlog::error("Failed to re-parse and dump main config: {}",
|
||||
e.what());
|
||||
auto res = crow::response(
|
||||
500, "{\"error\":\"Failed to serialize config for writing.\"}");
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
}
|
||||
|
||||
spdlog::info("Main config successfully updated via API at {}",
|
||||
config_path);
|
||||
crow::json::wvalue response_json;
|
||||
response_json["status"] = "success";
|
||||
response_json["message"] =
|
||||
"Main config saved. A full service restart (e.g., via "
|
||||
"/api/service/reload) is required to apply changes.";
|
||||
auto res = crow::response(200, response_json.dump());
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
|
@ -34,6 +34,9 @@ private:
|
|||
bool validate_video_config(const std::string &json_string,
|
||||
std::string &error_message);
|
||||
|
||||
bool validate_main_config(const std::string &json_string,
|
||||
std::string &error_message);
|
||||
|
||||
SystemMonitor::SystemMonitor &m_monitor;
|
||||
DeviceManager &m_device_manager;
|
||||
LiveDataCache &m_live_data_cache;
|
||||
|
|
|
|||
Loading…
Reference in New Issue