|
@@ -12,7 +12,29 @@ namespace jtjai_media {
|
|
|
HttpServer::HttpServer(const std::string& output_directory, int port)
|
|
|
: output_directory_(output_directory)
|
|
|
, port_(port)
|
|
|
- , is_running_(false) {
|
|
|
+ , is_running_(false)
|
|
|
+ , config_manager_(nullptr) {
|
|
|
+}
|
|
|
+
|
|
|
+HttpServer::HttpServer(const std::string& output_directory,
|
|
|
+ std::shared_ptr<ConfigManager> config_manager,
|
|
|
+ int port)
|
|
|
+ : output_directory_(output_directory)
|
|
|
+ , port_(port)
|
|
|
+ , is_running_(false)
|
|
|
+ , config_manager_(config_manager)
|
|
|
+ , scheduler_(nullptr) {
|
|
|
+}
|
|
|
+
|
|
|
+HttpServer::HttpServer(const std::string& output_directory,
|
|
|
+ std::shared_ptr<ConfigManager> config_manager,
|
|
|
+ std::shared_ptr<StreamScheduler> scheduler,
|
|
|
+ int port)
|
|
|
+ : output_directory_(output_directory)
|
|
|
+ , port_(port)
|
|
|
+ , is_running_(false)
|
|
|
+ , config_manager_(config_manager)
|
|
|
+ , scheduler_(scheduler) {
|
|
|
}
|
|
|
|
|
|
HttpServer::~HttpServer() {
|
|
@@ -37,6 +59,31 @@ bool HttpServer::start() {
|
|
|
std::cout << " GET /api/report/{timestamp} - 获取指定时间戳的轮询报告" << std::endl;
|
|
|
std::cout << " DELETE /api/video?path={path} - 删除指定视频文件" << std::endl;
|
|
|
std::cout << " DELETE /api/timestamp/{timestamp} - 删除指定时间戳目录及其所有文件" << std::endl;
|
|
|
+
|
|
|
+ if (config_manager_) {
|
|
|
+ std::cout << "配置管理API:" << std::endl;
|
|
|
+ std::cout << " GET /api/config - 获取完整配置" << std::endl;
|
|
|
+ std::cout << " PUT /api/config - 更新完整配置" << std::endl;
|
|
|
+ std::cout << " GET /api/config/global - 获取全局配置" << std::endl;
|
|
|
+ std::cout << " PUT /api/config/global - 更新全局配置" << std::endl;
|
|
|
+ std::cout << " GET /api/config/streams - 获取流配置列表" << std::endl;
|
|
|
+ std::cout << " POST /api/config/streams - 添加新的流配置" << std::endl;
|
|
|
+ std::cout << " PUT /api/config/streams/{index} - 更新指定索引的流配置" << std::endl;
|
|
|
+ std::cout << " DELETE /api/config/streams/{index} - 删除指定索引的流配置" << std::endl;
|
|
|
+ std::cout << " POST /api/config/save - 保存配置到文件" << std::endl;
|
|
|
+ std::cout << " POST /api/config/reload - 从文件重新加载配置" << std::endl;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (scheduler_) {
|
|
|
+ std::cout << "轮询任务管理API:" << std::endl;
|
|
|
+ std::cout << " GET /api/tasks - 获取轮询任务列表" << std::endl;
|
|
|
+ std::cout << " GET /api/tasks/status - 获取实时任务状态" << std::endl;
|
|
|
+ std::cout << " GET /api/scheduler/status - 获取调度器状态" << std::endl;
|
|
|
+ std::cout << " GET /api/scheduler/cycle - 获取当前周期状态" << std::endl;
|
|
|
+ std::cout << " POST /api/scheduler/start - 启动调度器" << std::endl;
|
|
|
+ std::cout << " POST /api/scheduler/stop - 停止调度器" << std::endl;
|
|
|
+ }
|
|
|
+
|
|
|
std::cout << "Web界面:" << std::endl;
|
|
|
std::cout << " GET / - 主页" << std::endl;
|
|
|
std::cout << " GET /manager - 视频管理界面" << std::endl;
|
|
@@ -174,6 +221,19 @@ HttpRequest HttpServer::parse_request(const std::string& request_data) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 解析请求体(对于POST/PUT请求)
|
|
|
+ if (request.method == "POST" || request.method == "PUT") {
|
|
|
+ std::string remaining_content;
|
|
|
+ std::string body_line;
|
|
|
+ while (std::getline(stream, body_line)) {
|
|
|
+ if (!remaining_content.empty()) {
|
|
|
+ remaining_content += "\n";
|
|
|
+ }
|
|
|
+ remaining_content += body_line;
|
|
|
+ }
|
|
|
+ request.body = remaining_content;
|
|
|
+ }
|
|
|
+
|
|
|
return request;
|
|
|
}
|
|
|
|
|
@@ -218,6 +278,20 @@ HttpResponse HttpServer::route_request(const HttpRequest& request) {
|
|
|
return handle_list_timestamps(request);
|
|
|
} else if (request.path == "/api/reports") {
|
|
|
return handle_list_reports(request);
|
|
|
+ } else if (request.path == "/api/config") {
|
|
|
+ return handle_get_config(request);
|
|
|
+ } else if (request.path == "/api/config/global") {
|
|
|
+ return handle_get_global_config(request);
|
|
|
+ } else if (request.path == "/api/config/streams") {
|
|
|
+ return handle_get_stream_configs(request);
|
|
|
+ } else if (request.path == "/api/tasks") {
|
|
|
+ return handle_get_task_list(request);
|
|
|
+ } else if (request.path == "/api/tasks/status") {
|
|
|
+ return handle_get_task_status(request);
|
|
|
+ } else if (request.path == "/api/scheduler/status") {
|
|
|
+ return handle_get_scheduler_status(request);
|
|
|
+ } else if (request.path == "/api/scheduler/cycle") {
|
|
|
+ return handle_get_current_cycle_status(request);
|
|
|
} else if (request.path.find("/api/report/") == 0) {
|
|
|
return handle_get_report(request);
|
|
|
} else if (request.path.find("/api/videos/") == 0) {
|
|
@@ -234,11 +308,33 @@ HttpResponse HttpServer::route_request(const HttpRequest& request) {
|
|
|
// 处理静态文件访问(HTML、CSS、JS等)
|
|
|
return handle_static_file(request);
|
|
|
}
|
|
|
+ } else if (request.method == "PUT") {
|
|
|
+ if (request.path == "/api/config") {
|
|
|
+ return handle_update_config(request);
|
|
|
+ } else if (request.path == "/api/config/global") {
|
|
|
+ return handle_update_global_config(request);
|
|
|
+ } else if (request.path.find("/api/config/streams/") == 0) {
|
|
|
+ return handle_update_stream_config(request);
|
|
|
+ }
|
|
|
+ } else if (request.method == "POST") {
|
|
|
+ if (request.path == "/api/config/streams") {
|
|
|
+ return handle_add_stream_config(request);
|
|
|
+ } else if (request.path == "/api/config/save") {
|
|
|
+ return handle_save_config(request);
|
|
|
+ } else if (request.path == "/api/config/reload") {
|
|
|
+ return handle_reload_config(request);
|
|
|
+ } else if (request.path == "/api/scheduler/start") {
|
|
|
+ return handle_start_scheduler(request);
|
|
|
+ } else if (request.path == "/api/scheduler/stop") {
|
|
|
+ return handle_stop_scheduler(request);
|
|
|
+ }
|
|
|
} else if (request.method == "DELETE") {
|
|
|
if (request.path == "/api/video") {
|
|
|
return handle_delete_video(request);
|
|
|
} else if (request.path.find("/api/timestamp/") == 0) {
|
|
|
return handle_delete_timestamp(request);
|
|
|
+ } else if (request.path.find("/api/config/streams/") == 0) {
|
|
|
+ return handle_delete_stream_config(request);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -954,6 +1050,106 @@ HttpResponse HttpServer::generate_index_page() {
|
|
|
}
|
|
|
|
|
|
HttpResponse HttpServer::generate_api_doc() {
|
|
|
+ std::string config_section = "";
|
|
|
+ if (config_manager_) {
|
|
|
+ config_section = R"(
|
|
|
+ <div class="section" id="config-api">
|
|
|
+ <h2>⚙️ 配置管理API</h2>
|
|
|
+ <div class="status-codes">
|
|
|
+ <h4>🔧 核心功能:</h4>
|
|
|
+ <p>• 实时修改系统参数 • 热更新配置 • 流管理 • 持久化保存</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method get">GET</span>/api/config</h3>
|
|
|
+ <p>获取完整配置</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method get">GET</span>/api/config/global</h3>
|
|
|
+ <p>获取全局配置</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method put">PUT</span>/api/config/global</h3>
|
|
|
+ <p>更新全局配置</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method get">GET</span>/api/config/streams</h3>
|
|
|
+ <p>获取流配置列表</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method post">POST</span>/api/config/streams</h3>
|
|
|
+ <p>添加新的流配置</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method put">PUT</span>/api/config/streams/{index}</h3>
|
|
|
+ <p>更新指定索引的流配置</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method delete">DELETE</span>/api/config/streams/{index}</h3>
|
|
|
+ <p>删除指定索引的流配置</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method post">POST</span>/api/config/save</h3>
|
|
|
+ <p>保存配置到文件</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method post">POST</span>/api/config/reload</h3>
|
|
|
+ <p>从文件重新加载配置</p>
|
|
|
+ </div>
|
|
|
+ </div>)";
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string task_section = "";
|
|
|
+ if (scheduler_) {
|
|
|
+ task_section = R"(
|
|
|
+ <div class="section" id="task-api">
|
|
|
+ <h2>🔄 轮询任务管理API</h2>
|
|
|
+ <div class="status-codes">
|
|
|
+ <h4>🔍 状态码说明:</h4>
|
|
|
+ <p>• 0=空闲 • 1=连接中 • 2=已连接 • 3=录制中 • 4=完成</p>
|
|
|
+ <p>• 5=连接错误 • 6=录制错误 • 7=超时 • 8=已取消 • 9=流不可用</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method get">GET</span>/api/tasks</h3>
|
|
|
+ <p>获取任务列表和调度状态</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method get">GET</span>/api/tasks/status</h3>
|
|
|
+ <p>获取实时任务状态</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method get">GET</span>/api/scheduler/status</h3>
|
|
|
+ <p>获取调度器状态</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method get">GET</span>/api/scheduler/cycle</h3>
|
|
|
+ <p>获取当前周期状态</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method post">POST</span>/api/scheduler/start</h3>
|
|
|
+ <p>启动调度器</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method post">POST</span>/api/scheduler/stop</h3>
|
|
|
+ <p>停止调度器</p>
|
|
|
+ </div>
|
|
|
+ </div>)";
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string nav_config = config_manager_ ? R"(<a href="#config-api">⚙️ 配置管理</a>)" : "";
|
|
|
+ std::string nav_task = scheduler_ ? R"(<a href="#task-api">🔄 轮询任务</a>)" : "";
|
|
|
+
|
|
|
+ std::string example_config = config_manager_ ? R"(
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3>示例: 获取配置</h3>
|
|
|
+ <p><code>curl http://localhost:8080/api/config</code></p>
|
|
|
+ </div>)" : "";
|
|
|
+
|
|
|
+ std::string example_task = scheduler_ ? R"(
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3>示例: 获取任务状态</h3>
|
|
|
+ <p><code>curl http://localhost:8080/api/tasks</code></p>
|
|
|
+ </div>)" : "";
|
|
|
+
|
|
|
std::string html = R"(
|
|
|
<!DOCTYPE html>
|
|
|
<html lang="zh-CN">
|
|
@@ -962,28 +1158,102 @@ HttpResponse HttpServer::generate_api_doc() {
|
|
|
<title>API文档</title>
|
|
|
<style>
|
|
|
body { font-family: sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
|
|
|
- .container { max-width: 1000px; margin: 0 auto; background: white; padding: 40px; border-radius: 10px; }
|
|
|
- .endpoint { background: #f8f9fa; padding: 20px; margin: 20px 0; border-radius: 8px; }
|
|
|
- .method { display: inline-block; padding: 4px 8px; border-radius: 4px; color: white; font-weight: bold; margin-right: 10px; }
|
|
|
+ .container { max-width: 1000px; margin: 0 auto; background: white; padding: 40px; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
|
|
|
+ .endpoint { background: #f8f9fa; padding: 20px; margin: 15px 0; border-radius: 8px; border-left: 4px solid #007bff; }
|
|
|
+ .method { display: inline-block; padding: 4px 8px; border-radius: 4px; color: white; font-weight: bold; margin-right: 10px; font-size: 12px; }
|
|
|
.get { background: #28a745; }
|
|
|
+ .post { background: #007bff; }
|
|
|
+ .put { background: #ffc107; color: #212529; }
|
|
|
.delete { background: #dc3545; }
|
|
|
+ .section { margin: 30px 0; }
|
|
|
+ .section h2 { color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; margin-bottom: 20px; }
|
|
|
+ .nav-links { background: #e9ecef; padding: 15px; border-radius: 8px; margin-bottom: 30px; }
|
|
|
+ .nav-links a { display: inline-block; margin-right: 15px; color: #007bff; text-decoration: none; }
|
|
|
+ .nav-links a:hover { text-decoration: underline; }
|
|
|
+ .status-codes { background: #fff3cd; padding: 15px; border-radius: 8px; margin: 20px 0; }
|
|
|
+ .status-codes h4 { margin-top: 0; color: #856404; }
|
|
|
+ .endpoint p { margin: 5px 0; color: #6c757d; }
|
|
|
+ .highlight { background: #d4edda; padding: 10px; border-radius: 4px; margin: 10px 0; }
|
|
|
</style>
|
|
|
</head>
|
|
|
<body>
|
|
|
<div class="container">
|
|
|
<h1>📊 API文档</h1>
|
|
|
- <div class="endpoint">
|
|
|
- <h3><span class="method get">GET</span>/api/videos</h3>
|
|
|
- <p>列出所有视频文件</p>
|
|
|
+
|
|
|
+ <div class="nav-links">
|
|
|
+ <strong>📦 快速导航:</strong>
|
|
|
+ <a href="#video-api">🎥 视频管理</a>)" + nav_config + nav_task + R"(
|
|
|
+ <a href="/manager">🖥️ Web管理界面</a>
|
|
|
</div>
|
|
|
- <div class="endpoint">
|
|
|
- <h3><span class="method delete">DELETE</span>/api/video</h3>
|
|
|
- <p>删除指定视频文件</p>
|
|
|
+
|
|
|
+ <div class="highlight">
|
|
|
+ <strong>🎆 系统功能:</strong> RTSP视频流管理系统提供完整的视频文件管理、动态配置和实时任务监控能力
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="section" id="video-api">
|
|
|
+ <h2>🎥 视频管理API</h2>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method get">GET</span>/api/videos</h3>
|
|
|
+ <p>列出所有视频文件</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method get">GET</span>/api/timestamps</h3>
|
|
|
+ <p>列出所有时间戳目录</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method get">GET</span>/api/videos/{timestamp}</h3>
|
|
|
+ <p>列出指定时间戳目录的视频</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method get">GET</span>/api/reports</h3>
|
|
|
+ <p>列出所有轮询报告</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method get">GET</span>/api/report/{timestamp}</h3>
|
|
|
+ <p>获取指定时间戳的轮询报告</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method delete">DELETE</span>/api/video</h3>
|
|
|
+ <p>删除指定视频文件 (参数: path)</p>
|
|
|
+ </div>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3><span class="method delete">DELETE</span>/api/timestamp/{timestamp}</h3>
|
|
|
+ <p>删除整个时间戳目录</p>
|
|
|
+ </div>
|
|
|
+ </div>)" +
|
|
|
+ config_section +
|
|
|
+ task_section + R"(
|
|
|
+
|
|
|
+ <div class="section">
|
|
|
+ <h2>🔍 状态码说明</h2>
|
|
|
+ <div class="status-codes">
|
|
|
+ <h4>HTTP 状态码:</h4>
|
|
|
+ <p>• <strong>200 OK</strong>: 请求成功</p>
|
|
|
+ <p>• <strong>400 Bad Request</strong>: 请求参数错误</p>
|
|
|
+ <p>• <strong>404 Not Found</strong>: 资源不存在</p>
|
|
|
+ <p>• <strong>500 Internal Server Error</strong>: 服务器内部错误</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="section">
|
|
|
+ <h2>🚀 快速开始</h2>
|
|
|
+ <div class="endpoint">
|
|
|
+ <h3>示例: 获取所有视频</h3>
|
|
|
+ <p><code>curl http://localhost:8080/api/videos</code></p>
|
|
|
+ </div>)" + example_config + example_task + R"(
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="section">
|
|
|
+ <h2>🔗 相关链接</h2>
|
|
|
+ <div class="nav-links">
|
|
|
+ <a href="/">🏠 系统主页</a>
|
|
|
+ <a href="/manager">🖥️ Web管理界面</a>
|
|
|
+ <a href="https://github.com/your-repo">📚 文档仓库</a>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</body>
|
|
|
-</html>
|
|
|
- )";
|
|
|
+</html>)";
|
|
|
|
|
|
HttpResponse response;
|
|
|
response.status_code = 200;
|
|
@@ -991,6 +1261,822 @@ HttpResponse HttpServer::generate_api_doc() {
|
|
|
response.content_type = "text/html";
|
|
|
response.body = html;
|
|
|
return response;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// ===================== 配置管理API实现 =====================
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_get_config(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!config_manager_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "配置管理器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string config_json = config_manager_->to_json_string();
|
|
|
+ return HttpResponse(200, "OK", config_json);
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_update_config(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!config_manager_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "配置管理器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (request.body.empty()) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "请求体不能为空";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 尝试解析新配置
|
|
|
+ if (!config_manager_->load_from_json(request.body)) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "配置格式错误或验证失败";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["success"] = true;
|
|
|
+ result["message"] = "配置更新成功";
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_get_global_config(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!config_manager_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "配置管理器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto& global_config = config_manager_->get_global_config();
|
|
|
+
|
|
|
+ boost::json::object config_obj;
|
|
|
+ config_obj["total_poll_duration_seconds"] = global_config.total_poll_duration_seconds;
|
|
|
+ config_obj["max_concurrent_streams"] = global_config.max_concurrent_streams;
|
|
|
+ config_obj["output_directory"] = global_config.output_directory;
|
|
|
+ config_obj["report_filename"] = global_config.report_filename;
|
|
|
+ config_obj["connection_timeout_seconds"] = global_config.connection_timeout_seconds;
|
|
|
+ config_obj["read_timeout_seconds"] = global_config.read_timeout_seconds;
|
|
|
+ config_obj["poll_cycles"] = global_config.poll_cycles;
|
|
|
+ config_obj["cycle_interval_seconds"] = global_config.cycle_interval_seconds;
|
|
|
+
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(config_obj));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_update_global_config(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!config_manager_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "配置管理器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (request.body.empty()) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "请求体不能为空";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析JSON
|
|
|
+ auto json_value = boost::json::parse(request.body);
|
|
|
+ auto json_obj = json_value.as_object();
|
|
|
+
|
|
|
+ // 获取当前全局配置
|
|
|
+ GlobalConfig global_config = config_manager_->get_global_config();
|
|
|
+
|
|
|
+ // 更新各个字段(如果存在)
|
|
|
+ if (json_obj.contains("total_poll_duration_seconds")) {
|
|
|
+ global_config.total_poll_duration_seconds = static_cast<int>(json_obj.at("total_poll_duration_seconds").as_int64());
|
|
|
+ }
|
|
|
+ if (json_obj.contains("max_concurrent_streams")) {
|
|
|
+ global_config.max_concurrent_streams = static_cast<int>(json_obj.at("max_concurrent_streams").as_int64());
|
|
|
+ }
|
|
|
+ if (json_obj.contains("output_directory")) {
|
|
|
+ global_config.output_directory = json_obj.at("output_directory").as_string().c_str();
|
|
|
+ }
|
|
|
+ if (json_obj.contains("report_filename")) {
|
|
|
+ global_config.report_filename = json_obj.at("report_filename").as_string().c_str();
|
|
|
+ }
|
|
|
+ if (json_obj.contains("connection_timeout_seconds")) {
|
|
|
+ global_config.connection_timeout_seconds = static_cast<int>(json_obj.at("connection_timeout_seconds").as_int64());
|
|
|
+ }
|
|
|
+ if (json_obj.contains("read_timeout_seconds")) {
|
|
|
+ global_config.read_timeout_seconds = static_cast<int>(json_obj.at("read_timeout_seconds").as_int64());
|
|
|
+ }
|
|
|
+ if (json_obj.contains("poll_cycles")) {
|
|
|
+ global_config.poll_cycles = static_cast<int>(json_obj.at("poll_cycles").as_int64());
|
|
|
+ }
|
|
|
+ if (json_obj.contains("cycle_interval_seconds")) {
|
|
|
+ global_config.cycle_interval_seconds = static_cast<int>(json_obj.at("cycle_interval_seconds").as_int64());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置新配置
|
|
|
+ config_manager_->set_global_config(global_config);
|
|
|
+
|
|
|
+ // 验证配置
|
|
|
+ if (!config_manager_->validate()) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "全局配置验证失败";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["success"] = true;
|
|
|
+ result["message"] = "全局配置更新成功";
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_get_stream_configs(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!config_manager_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "配置管理器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto& stream_configs = config_manager_->get_stream_configs();
|
|
|
+
|
|
|
+ boost::json::array streams_array;
|
|
|
+ for (size_t i = 0; i < stream_configs.size(); ++i) {
|
|
|
+ const auto& stream = stream_configs[i];
|
|
|
+ boost::json::object stream_obj;
|
|
|
+ stream_obj["index"] = i;
|
|
|
+ stream_obj["rtsp_url"] = stream.rtsp_url;
|
|
|
+ stream_obj["duration_seconds"] = stream.duration_seconds;
|
|
|
+ stream_obj["weight"] = stream.weight;
|
|
|
+ stream_obj["output_filename"] = stream.output_filename;
|
|
|
+ streams_array.push_back(stream_obj);
|
|
|
+ }
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["count"] = stream_configs.size();
|
|
|
+ result["streams"] = streams_array;
|
|
|
+
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_add_stream_config(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!config_manager_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "配置管理器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (request.body.empty()) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "请求体不能为空";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析JSON
|
|
|
+ auto json_value = boost::json::parse(request.body);
|
|
|
+ auto json_obj = json_value.as_object();
|
|
|
+
|
|
|
+ // 创建新的流配置
|
|
|
+ StreamConfig new_stream;
|
|
|
+
|
|
|
+ if (json_obj.contains("rtsp_url")) {
|
|
|
+ new_stream.rtsp_url = json_obj.at("rtsp_url").as_string().c_str();
|
|
|
+ }
|
|
|
+ if (json_obj.contains("duration_seconds")) {
|
|
|
+ new_stream.duration_seconds = static_cast<int>(json_obj.at("duration_seconds").as_int64());
|
|
|
+ }
|
|
|
+ if (json_obj.contains("weight")) {
|
|
|
+ new_stream.weight = json_obj.at("weight").as_double();
|
|
|
+ }
|
|
|
+ if (json_obj.contains("output_filename")) {
|
|
|
+ new_stream.output_filename = json_obj.at("output_filename").as_string().c_str();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证新流配置
|
|
|
+ if (new_stream.rtsp_url.empty() || new_stream.duration_seconds <= 0 ||
|
|
|
+ new_stream.weight <= 0 || new_stream.output_filename.empty()) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "流配置参数不完整或不合法";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加新流配置
|
|
|
+ config_manager_->add_stream_config(new_stream);
|
|
|
+
|
|
|
+ // 验证整体配置
|
|
|
+ if (!config_manager_->validate()) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "流配置验证失败";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["success"] = true;
|
|
|
+ result["message"] = "流配置添加成功";
|
|
|
+ result["index"] = config_manager_->get_stream_count() - 1;
|
|
|
+
|
|
|
+ return HttpResponse(201, "Created", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_update_stream_config(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!config_manager_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "配置管理器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从路径中提取索引 /api/config/streams/{index}
|
|
|
+ std::string path = request.path;
|
|
|
+ size_t pos = path.find_last_of('/');
|
|
|
+ if (pos == std::string::npos) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "无效的请求路径";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string index_str = path.substr(pos + 1);
|
|
|
+ size_t index;
|
|
|
+ try {
|
|
|
+ index = std::stoull(index_str);
|
|
|
+ } catch (...) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "无效的流索引";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (request.body.empty()) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "请求体不能为空";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析JSON
|
|
|
+ auto json_value = boost::json::parse(request.body);
|
|
|
+ auto json_obj = json_value.as_object();
|
|
|
+
|
|
|
+ // 获取当前流配置
|
|
|
+ const auto& stream_configs = config_manager_->get_stream_configs();
|
|
|
+ if (index >= stream_configs.size()) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Not Found";
|
|
|
+ error_obj["message"] = "流配置索引不存在";
|
|
|
+ return HttpResponse(404, "Not Found", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ StreamConfig updated_stream = stream_configs[index];
|
|
|
+
|
|
|
+ // 更新各个字段(如果存在)
|
|
|
+ if (json_obj.contains("rtsp_url")) {
|
|
|
+ updated_stream.rtsp_url = json_obj.at("rtsp_url").as_string().c_str();
|
|
|
+ }
|
|
|
+ if (json_obj.contains("duration_seconds")) {
|
|
|
+ updated_stream.duration_seconds = static_cast<int>(json_obj.at("duration_seconds").as_int64());
|
|
|
+ }
|
|
|
+ if (json_obj.contains("weight")) {
|
|
|
+ updated_stream.weight = json_obj.at("weight").as_double();
|
|
|
+ }
|
|
|
+ if (json_obj.contains("output_filename")) {
|
|
|
+ updated_stream.output_filename = json_obj.at("output_filename").as_string().c_str();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新流配置
|
|
|
+ if (!config_manager_->update_stream_config(index, updated_stream)) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "流配置更新失败";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["success"] = true;
|
|
|
+ result["message"] = "流配置更新成功";
|
|
|
+ result["index"] = index;
|
|
|
+
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_delete_stream_config(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!config_manager_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "配置管理器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从路径中提取索引 /api/config/streams/{index}
|
|
|
+ std::string path = request.path;
|
|
|
+ size_t pos = path.find_last_of('/');
|
|
|
+ if (pos == std::string::npos) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "无效的请求路径";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string index_str = path.substr(pos + 1);
|
|
|
+ size_t index;
|
|
|
+ try {
|
|
|
+ index = std::stoull(index_str);
|
|
|
+ } catch (...) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "无效的流索引";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 删除流配置
|
|
|
+ if (!config_manager_->remove_stream_config(index)) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Not Found";
|
|
|
+ error_obj["message"] = "流配置索引不存在";
|
|
|
+ return HttpResponse(404, "Not Found", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证整体配置
|
|
|
+ if (!config_manager_->validate()) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Bad Request";
|
|
|
+ error_obj["message"] = "删除后配置验证失败";
|
|
|
+ return HttpResponse(400, "Bad Request", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["success"] = true;
|
|
|
+ result["message"] = "流配置删除成功";
|
|
|
+ result["deleted_index"] = index;
|
|
|
+
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_save_config(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!config_manager_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "配置管理器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ std::string config_file = config_manager_->get_config_file_path();
|
|
|
+ if (config_file.empty()) {
|
|
|
+ // 如果没有指定文件路径,使用默认路径
|
|
|
+ config_file = "config.json";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从请求体中获取文件路径(可选)
|
|
|
+ if (!request.body.empty()) {
|
|
|
+ try {
|
|
|
+ auto json_value = boost::json::parse(request.body);
|
|
|
+ auto json_obj = json_value.as_object();
|
|
|
+ if (json_obj.contains("file_path")) {
|
|
|
+ config_file = json_obj.at("file_path").as_string().c_str();
|
|
|
+ }
|
|
|
+ } catch (...) {
|
|
|
+ // 如果JSON解析失败,使用默认路径
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存配置
|
|
|
+ if (!config_manager_->save_to_file(config_file)) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = "保存配置文件失败";
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["success"] = true;
|
|
|
+ result["message"] = "配置保存成功";
|
|
|
+ result["file_path"] = config_file;
|
|
|
+
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_reload_config(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!config_manager_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "配置管理器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重新加载配置
|
|
|
+ if (!config_manager_->reload_from_file()) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = "重新加载配置失败";
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["success"] = true;
|
|
|
+ result["message"] = "配置重新加载成功";
|
|
|
+ result["file_path"] = config_manager_->get_config_file_path();
|
|
|
+
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ===================== 轮询任务管理API实现 =====================
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_get_task_list(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!scheduler_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "调度器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取调度方案信息
|
|
|
+ std::string schedule_info = scheduler_->get_schedule_info();
|
|
|
+
|
|
|
+ // 获取所有客户端统计信息
|
|
|
+ auto client_stats = scheduler_->get_all_stats();
|
|
|
+
|
|
|
+ boost::json::array tasks_array;
|
|
|
+ for (const auto& stats : client_stats) {
|
|
|
+ boost::json::object task_obj;
|
|
|
+ task_obj["task_id"] = stats.stream_index;
|
|
|
+ task_obj["stream_index"] = stats.stream_index;
|
|
|
+ task_obj["rtsp_url"] = stats.rtsp_url;
|
|
|
+ task_obj["output_file"] = stats.output_file;
|
|
|
+ task_obj["duration_seconds"] = stats.duration_seconds;
|
|
|
+ task_obj["status"] = static_cast<int>(stats.status);
|
|
|
+ task_obj["status_text"] = [&stats]() {
|
|
|
+ switch (stats.status) {
|
|
|
+ case RTSPClientStatus::IDLE: return "等待中";
|
|
|
+ case RTSPClientStatus::CONNECTING: return "连接中";
|
|
|
+ case RTSPClientStatus::CONNECTED: return "已连接";
|
|
|
+ case RTSPClientStatus::RECORDING: return "录制中";
|
|
|
+ case RTSPClientStatus::FINISHED: return "已完成";
|
|
|
+ case RTSPClientStatus::ERROR_CONNECT: return "连接错误";
|
|
|
+ case RTSPClientStatus::ERROR_RECORD: return "录制错误";
|
|
|
+ case RTSPClientStatus::TIMEOUT: return "超时";
|
|
|
+ case RTSPClientStatus::CANCELLED: return "已取消";
|
|
|
+ case RTSPClientStatus::STREAM_UNAVAILABLE: return "流不可用";
|
|
|
+ default: return "未知状态";
|
|
|
+ }
|
|
|
+ }();
|
|
|
+ // 转换时间点为字符串
|
|
|
+ auto start_time_t = std::chrono::system_clock::to_time_t(stats.start_time);
|
|
|
+ auto end_time_t = std::chrono::system_clock::to_time_t(stats.end_time);
|
|
|
+
|
|
|
+ std::ostringstream start_ss, end_ss;
|
|
|
+ start_ss << std::put_time(std::localtime(&start_time_t), "%Y-%m-%d %H:%M:%S");
|
|
|
+ end_ss << std::put_time(std::localtime(&end_time_t), "%Y-%m-%d %H:%M:%S");
|
|
|
+
|
|
|
+ task_obj["start_time"] = start_ss.str();
|
|
|
+ task_obj["end_time"] = end_ss.str();
|
|
|
+ task_obj["error_message"] = stats.error_message;
|
|
|
+ task_obj["bytes_received"] = stats.bytes_received;
|
|
|
+ task_obj["frames_received"] = stats.frames_received;
|
|
|
+
|
|
|
+ tasks_array.push_back(task_obj);
|
|
|
+ }
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["count"] = tasks_array.size();
|
|
|
+ result["tasks"] = tasks_array;
|
|
|
+ result["schedule_info"] = schedule_info;
|
|
|
+
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_get_task_status(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!scheduler_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "调度器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取实时任务状态
|
|
|
+ auto client_stats = scheduler_->get_all_stats();
|
|
|
+ auto scheduler_stats = scheduler_->get_scheduler_stats();
|
|
|
+
|
|
|
+ boost::json::array tasks_array;
|
|
|
+ for (const auto& stats : client_stats) {
|
|
|
+ boost::json::object task_obj;
|
|
|
+ task_obj["task_id"] = stats.stream_index;
|
|
|
+ task_obj["stream_index"] = stats.stream_index;
|
|
|
+ task_obj["status"] = static_cast<int>(stats.status);
|
|
|
+ task_obj["status_text"] = [&stats]() {
|
|
|
+ switch (stats.status) {
|
|
|
+ case RTSPClientStatus::IDLE: return "等待中";
|
|
|
+ case RTSPClientStatus::CONNECTING: return "连接中";
|
|
|
+ case RTSPClientStatus::CONNECTED: return "已连接";
|
|
|
+ case RTSPClientStatus::RECORDING: return "录制中";
|
|
|
+ case RTSPClientStatus::FINISHED: return "已完成";
|
|
|
+ case RTSPClientStatus::ERROR_CONNECT: return "连接错误";
|
|
|
+ case RTSPClientStatus::ERROR_RECORD: return "录制错误";
|
|
|
+ case RTSPClientStatus::TIMEOUT: return "超时";
|
|
|
+ case RTSPClientStatus::CANCELLED: return "已取消";
|
|
|
+ case RTSPClientStatus::STREAM_UNAVAILABLE: return "流不可用";
|
|
|
+ default: return "未知状态";
|
|
|
+ }
|
|
|
+ }();
|
|
|
+ task_obj["progress"] = stats.duration_seconds;
|
|
|
+ task_obj["bytes_received"] = stats.bytes_received;
|
|
|
+ task_obj["frames_received"] = stats.frames_received;
|
|
|
+ task_obj["error_message"] = stats.error_message;
|
|
|
+
|
|
|
+ tasks_array.push_back(task_obj);
|
|
|
+ }
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["scheduler_running"] = scheduler_->is_running();
|
|
|
+ result["total_tasks"] = scheduler_stats.total_tasks;
|
|
|
+ result["completed_tasks"] = scheduler_stats.completed_tasks;
|
|
|
+ result["failed_tasks"] = scheduler_stats.failed_tasks;
|
|
|
+ result["cancelled_tasks"] = scheduler_stats.cancelled_tasks;
|
|
|
+ result["max_concurrent_used"] = scheduler_stats.max_concurrent_used;
|
|
|
+ result["completion_rate"] = scheduler_stats.completion_rate;
|
|
|
+ result["tasks"] = tasks_array;
|
|
|
+
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_get_scheduler_status(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!scheduler_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "调度器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ auto scheduler_stats = scheduler_->get_scheduler_stats();
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["is_running"] = scheduler_->is_running();
|
|
|
+ result["total_tasks"] = scheduler_stats.total_tasks;
|
|
|
+ result["completed_tasks"] = scheduler_stats.completed_tasks;
|
|
|
+ result["failed_tasks"] = scheduler_stats.failed_tasks;
|
|
|
+ result["cancelled_tasks"] = scheduler_stats.cancelled_tasks;
|
|
|
+ result["max_concurrent_used"] = scheduler_stats.max_concurrent_used;
|
|
|
+ result["completion_rate"] = scheduler_stats.completion_rate;
|
|
|
+
|
|
|
+ // 时间信息
|
|
|
+ auto start_time = std::chrono::system_clock::to_time_t(scheduler_stats.start_time);
|
|
|
+ auto end_time = std::chrono::system_clock::to_time_t(scheduler_stats.end_time);
|
|
|
+
|
|
|
+ std::ostringstream start_ss, end_ss;
|
|
|
+ start_ss << std::put_time(std::localtime(&start_time), "%Y-%m-%d %H:%M:%S");
|
|
|
+
|
|
|
+ result["start_time"] = start_ss.str();
|
|
|
+ if (scheduler_stats.end_time != std::chrono::system_clock::time_point{}) {
|
|
|
+ end_ss << std::put_time(std::localtime(&end_time), "%Y-%m-%d %H:%M:%S");
|
|
|
+ result["end_time"] = end_ss.str();
|
|
|
+ } else {
|
|
|
+ result["end_time"] = "";
|
|
|
+ }
|
|
|
+
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_start_scheduler(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!scheduler_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "调度器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (scheduler_->is_running()) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Conflict";
|
|
|
+ error_obj["message"] = "调度器已经在运行中";
|
|
|
+ return HttpResponse(409, "Conflict", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ bool success = scheduler_->start_execution();
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["success"] = success;
|
|
|
+ result["message"] = success ? "调度器启动成功" : "调度器启动失败";
|
|
|
+ result["is_running"] = scheduler_->is_running();
|
|
|
+
|
|
|
+ return HttpResponse(success ? 200 : 500, success ? "OK" : "Internal Server Error", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_stop_scheduler(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!scheduler_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "调度器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!scheduler_->is_running()) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Conflict";
|
|
|
+ error_obj["message"] = "调度器未在运行";
|
|
|
+ return HttpResponse(409, "Conflict", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ scheduler_->stop_execution();
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["success"] = true;
|
|
|
+ result["message"] = "调度器停止成功";
|
|
|
+ result["is_running"] = scheduler_->is_running();
|
|
|
+
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+HttpResponse HttpServer::handle_get_current_cycle_status(const HttpRequest& request) {
|
|
|
+ try {
|
|
|
+ if (!scheduler_) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Service Unavailable";
|
|
|
+ error_obj["message"] = "调度器未初始化";
|
|
|
+ return HttpResponse(503, "Service Unavailable", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ auto scheduler_stats = scheduler_->get_scheduler_stats();
|
|
|
+ auto client_stats = scheduler_->get_all_stats();
|
|
|
+
|
|
|
+ // 计算当前周期的进度
|
|
|
+ auto now = std::chrono::system_clock::now();
|
|
|
+ auto elapsed_seconds = std::chrono::duration_cast<std::chrono::seconds>(
|
|
|
+ now - scheduler_stats.start_time).count();
|
|
|
+
|
|
|
+ // 获取配置信息
|
|
|
+ int total_duration = 60; // 默认值
|
|
|
+ if (config_manager_) {
|
|
|
+ total_duration = config_manager_->get_global_config().total_poll_duration_seconds;
|
|
|
+ }
|
|
|
+
|
|
|
+ boost::json::object result;
|
|
|
+ result["is_running"] = scheduler_->is_running();
|
|
|
+ result["current_cycle"] = 1; // 这里可以根据实际情况获取当前周期数
|
|
|
+ result["cycle_progress"] = {
|
|
|
+ {"elapsed_seconds", elapsed_seconds},
|
|
|
+ {"total_seconds", total_duration},
|
|
|
+ {"progress_percentage", std::min(100.0, (elapsed_seconds * 100.0) / total_duration)}
|
|
|
+ };
|
|
|
+ result["scheduler_stats"] = {
|
|
|
+ {"total_tasks", scheduler_stats.total_tasks},
|
|
|
+ {"completed_tasks", scheduler_stats.completed_tasks},
|
|
|
+ {"failed_tasks", scheduler_stats.failed_tasks},
|
|
|
+ {"cancelled_tasks", scheduler_stats.cancelled_tasks},
|
|
|
+ {"max_concurrent_used", scheduler_stats.max_concurrent_used},
|
|
|
+ {"completion_rate", scheduler_stats.completion_rate}
|
|
|
+ };
|
|
|
+
|
|
|
+ boost::json::array active_tasks;
|
|
|
+ for (const auto& stats : client_stats) {
|
|
|
+ if (stats.status == RTSPClientStatus::RECORDING ||
|
|
|
+ stats.status == RTSPClientStatus::CONNECTING ||
|
|
|
+ stats.status == RTSPClientStatus::CONNECTED) {
|
|
|
+ boost::json::object task_obj;
|
|
|
+ task_obj["stream_index"] = stats.stream_index;
|
|
|
+ task_obj["status"] = static_cast<int>(stats.status);
|
|
|
+ task_obj["progress_seconds"] = stats.duration_seconds;
|
|
|
+ task_obj["target_duration"] = stats.duration_seconds;
|
|
|
+ active_tasks.push_back(task_obj);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result["active_tasks"] = active_tasks;
|
|
|
+
|
|
|
+ return HttpResponse(200, "OK", boost::json::serialize(result));
|
|
|
+
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ boost::json::object error_obj;
|
|
|
+ error_obj["error"] = "Internal Server Error";
|
|
|
+ error_obj["message"] = e.what();
|
|
|
+ return HttpResponse(500, "Internal Server Error", boost::json::serialize(error_obj));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
} // namespace jtjai_media
|