From fb57d51ee526dae122856ce1d70b9324b93bfc0b Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Thu, 16 Jul 2026 09:44:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B8=B8=E8=A7=84=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.json | 1 + .../Data_Source/Data_Source_Handler.cpp | 58 +- module/Local_Server/server/Config.h | 1 + module/Local_Server/server/io_coro.cpp | 546 +++++---- module/Local_Server/server/server.cpp | 1022 ++++++++--------- todolist.txt | 6 + 6 files changed, 795 insertions(+), 839 deletions(-) create mode 100644 todolist.txt diff --git a/config/config.json b/config/config.json index 581e810..f7d7ea9 100644 --- a/config/config.json +++ b/config/config.json @@ -60,6 +60,7 @@ ] }, "mode_acs": { + "wait_process_msg": true, "time_space_filter": true, "speed_filter": true, "timeout_seconds": 160, diff --git a/module/Local_Server/Data_Source/Data_Source_Handler.cpp b/module/Local_Server/Data_Source/Data_Source_Handler.cpp index 66d78c9..1d912d8 100644 --- a/module/Local_Server/Data_Source/Data_Source_Handler.cpp +++ b/module/Local_Server/Data_Source/Data_Source_Handler.cpp @@ -2,6 +2,7 @@ #include "Data_Source.h" #include "Local_Server/server/Global.h" #include +#include "Local_Server/server/io_coro.h" using namespace Psc; std::shared_ptr ds(Data_Source_Handler* dsh) { return dynamic_cast(dsh)->that(); @@ -130,28 +131,41 @@ size_t Data_Source_Handler::process_mode_acs_data(std::string_view origin_data) std::cout << source->key + " read:[mode_s_serial]:" << mem2hex(mode_data) << std::endl; } - SSR::Binary_Format_handle_buffer( - source->buffer, mode_data, [this, source, &ret](std::string& packet) { - ret++; - auto msg = create_msg(packet); - if (!msg) return; - source->push_to_feed(msg); - auto mt = msg->type; - bool mode_s = mt == SSR::Msg::S7 || mt == SSR::Msg::S14; - if (mt == SSR::Msg::HULC_Status) { - source->handle_HULC(packet); - } - else if (mt == SSR::Msg::Radarcape_status) { - auto radarcape_msg = SSR::create_Radarcape_STATUS_Message(packet); - std::cout << radarcape_msg.toJson().to_json_string() << std::endl; - SSR::mode_s_logger->debug("Radarcape_status/radarcape", {}, - radarcape_msg.toJson().to_json_string()); - } - else if (mode_s) { - // 拓展点 - handle_mode_s(std::dynamic_pointer_cast(msg)); - } - }); + auto handle_packet = [this, source, &ret](std::string& packet) { + ret++; + auto msg = create_msg(packet); + if (!msg) return; + source->push_to_feed(msg); + auto mt = msg->type; + bool mode_s = mt == SSR::Msg::S7 || mt == SSR::Msg::S14; + if (mt == SSR::Msg::HULC_Status) { + source->handle_HULC(packet); + } + else if (mt == SSR::Msg::Radarcape_status) { + auto radarcape_msg = SSR::create_Radarcape_STATUS_Message(packet); + std::cout << radarcape_msg.toJson().to_json_string() << std::endl; + SSR::mode_s_logger->debug("Radarcape_status/radarcape", {}, + radarcape_msg.toJson().to_json_string()); + } + else if (mode_s) { + // 拓展点 + handle_mode_s(std::dynamic_pointer_cast(msg)); + } + }; + auto f = [this, source, handle_packet](std::string& packet) { + auto& wait = Global::instance()->mode_acs.wait_process_msg; + if (wait) { + handle_packet(packet); + } + else { + auto co = Coro::instance(); + auto executor = co->process_data->get_executor(); + asio::post(executor, [handle_packet, packet = std::move(packet)]() mutable { + handle_packet(packet); + }); + } + }; + SSR::Binary_Format_handle_buffer(source->buffer, mode_data, f); return ret; } void Data_Source_Handler::handle_mode_s(std::shared_ptr mode_s_msg) { diff --git a/module/Local_Server/server/Config.h b/module/Local_Server/server/Config.h index 25bc1da..c0d5aa3 100644 --- a/module/Local_Server/server/Config.h +++ b/module/Local_Server/server/Config.h @@ -47,6 +47,7 @@ struct Mode_ACS_Config_Base_Data { PSC_USE_JSON }; struct Mode_ACS_Config_Data { + Psc::Copyable_Atomic wait_process_msg; Psc::Copyable_Atomic time_space_filter; Psc::Copyable_Atomic speed_filter; Psc::Copyable_Atomic max_track_point_size{}; diff --git a/module/Local_Server/server/io_coro.cpp b/module/Local_Server/server/io_coro.cpp index 325ae05..ebeb3dd 100644 --- a/module/Local_Server/server/io_coro.cpp +++ b/module/Local_Server/server/io_coro.cpp @@ -14,299 +14,293 @@ #include #include #include - static std::string thread_id_str() { - std::ostringstream oss; - oss << std::this_thread::get_id(); - return oss.str(); + std::ostringstream oss; + oss << std::this_thread::get_id(); + return oss.str(); } - -std::vector > get_all() { - auto g = Global::instance(); - std::vector > ret; - auto sources = g->mode_acs.data_source_config.map.list(); - for (auto &source: sources) { - ret.emplace_back(source); - } - auto feeds = g->mode_acs.data_feed_config.map.list(); - for (auto &feed: feeds) { - ret.emplace_back(feed); - } - return ret; +std::vector> get_all() { + auto g = Global::instance(); + std::vector> ret; + auto sources = g->mode_acs.data_source_config.map.list(); + for (auto& source : sources) { + ret.emplace_back(source); + } + auto feeds = g->mode_acs.data_feed_config.map.list(); + for (auto& feed : feeds) { + ret.emplace_back(feed); + } + return ret; } - bool Coro::has_running_loop_tasks() { - auto list = get_all(); - for (auto &li: list) { - if (li->running()) { - return true; - } - } - return false; + auto list = get_all(); + for (auto& li : list) { + if (li->running()) { + return true; + } + } + return false; } - bool data_feed_debug = false; bool data_source_debug = false; // 不开启wait 性能巨差 数据积压 -bool wait = true; - void Coro::start() { - std::cout << "1================ start_io_coro" << std::endl; - running.store(true, std::memory_order_release); - io.restart(); - io_work = std::make_unique >(io.get_executor()); - io_thread = std::thread([this] { - try { - std::cout << std::format("协程io:{} 协程启动!\n", thread_id_str()); - io.run(); - std::cout << std::format("协程io:{} 销毁!\n", thread_id_str()); - } catch (const std::exception &e) { - std::cerr << std::format("协程io异常: {}\n", e.what()); - std::terminate(); - } catch (...) { - std::cerr << "协程io未知异常\n"; - std::terminate(); - } - }); - auto n = std::max(1, std::thread::hardware_concurrency()); - auto other_need = std::max(2, n / 2); - if (n > other_need) { - n -= other_need; - } else { - n = n / 2; - } - n = std::max(1, n); - process_data = std::make_unique(n); - std::cout << std::format("协程已经启动 process_data concurrency: 在n个线程上{}\n", n); - - auto promise = std::make_shared >(); - loop_task = std::make_unique >(promise->get_future()); - asio::co_spawn(io, coro_thread(), [promise](std::exception_ptr ep) { - std::cout << "发生了异常" << std::endl; - if (ep) { - promise->set_exception(ep); - std::rethrow_exception(ep); - } - promise->set_value(); - }); - std::cout << std::flush; + std::cout << "1================ start_io_coro" << std::endl; + running.store(true, std::memory_order_release); + io.restart(); + io_work = std::make_unique>(io.get_executor()); + io_thread = std::thread([this] { + try { + std::cout << std::format("协程io:{} 协程启动!\n", thread_id_str()); + io.run(); + std::cout << std::format("协程io:{} 销毁!\n", thread_id_str()); + } + catch (const std::exception& e) { + std::cerr << std::format("协程io异常: {}\n", e.what()); + std::terminate(); + } + catch (...) { + std::cerr << "协程io未知异常\n"; + std::terminate(); + } + }); + auto n = std::max(1, std::thread::hardware_concurrency()); + auto other_need = std::max(2, n / 2); + if (n > other_need) { + n -= other_need; + } + else { + n = n / 2; + } + n = std::max(1, n); + process_data = std::make_unique(n); + std::cout << std::format("协程已经启动 process_data concurrency: 在n个线程上{}\n", n); + auto promise = std::make_shared>(); + loop_task = std::make_unique>(promise->get_future()); + asio::co_spawn(io, coro_thread(), [promise](std::exception_ptr ep) { + std::cout << "发生了异常" << std::endl; + if (ep) { + promise->set_exception(ep); + std::rethrow_exception(ep); + } + promise->set_value(); + }); + std::cout << std::flush; } - asio::awaitable Coro::sleep_for(std::chrono::milliseconds ms) { - auto executor = co_await asio::this_coro::executor; - asio::steady_timer timer(executor, ms); - co_await timer.async_wait(asio::use_awaitable); - co_return; + auto executor = co_await asio::this_coro::executor; + asio::steady_timer timer(executor, ms); + co_await timer.async_wait(asio::use_awaitable); + co_return; } - void Coro::stop() { - std::cout << "stop_io_coro begin" << std::endl; - auto list = get_all(); - for (auto &li: list) { - if (li->running()) { - std::cout << std::format("请求停止 {}:{} 当前状态为{}\n", li->type, li->key, Psc::to_string(li->state)); - li->async_stop(); - } - } - // 同步等待 - for (auto &li: list) { - li->sync_wait(); - } - // 停止状态更新状态机 - running = false; - asio::post(io, [] { - }); - if (loop_task) { - std::cout << "等待 coro_thread 退出" << std::endl; - loop_task->get(); - loop_task.reset(); - std::cout << "coro_thread 已退出" << std::endl; - } - // 清理资源 - for (auto &li: list) { - li->loop_task.reset(); - } - if (process_data) { - process_data->join(); - process_data.reset(); - } - io_work.reset(); - io.stop(); - if (io_thread.joinable()) { - io_thread.join(); - } - std::cout << "stop_io_coro end" << std::endl; + std::cout << "stop_io_coro begin" << std::endl; + auto list = get_all(); + for (auto& li : list) { + if (li->running()) { + std::cout << std::format("请求停止 {}:{} 当前状态为{}\n", li->type, li->key, Psc::to_string(li->state)); + li->async_stop(); + } + } + // 同步等待 + for (auto& li : list) { + li->sync_wait(); + } + // 停止状态更新状态机 + running = false; + asio::post(io, [] {}); + if (loop_task) { + std::cout << "等待 coro_thread 退出" << std::endl; + loop_task->get(); + loop_task.reset(); + std::cout << "coro_thread 已退出" << std::endl; + } + // 清理资源 + for (auto& li : list) { + li->loop_task.reset(); + } + if (process_data) { + process_data->join(); + process_data.reset(); + } + io_work.reset(); + io.stop(); + if (io_thread.joinable()) { + io_thread.join(); + } + std::cout << "stop_io_coro end" << std::endl; } - asio::awaitable Coro::coro_thread() { - while (running.load(std::memory_order_acquire)) { - auto list = get_all(); - for (auto &li: list) { - co_await li->tick(); //状态机驱动函数 - } - co_await sleep_for(std::chrono::milliseconds(10)); - } - co_return; + while (running.load(std::memory_order_acquire)) { + auto list = get_all(); + for (auto& li : list) { + co_await li->tick(); //状态机驱动函数 + } + co_await sleep_for(std::chrono::milliseconds(10)); + } + co_return; } - - asio::awaitable Data_Feed::loop_coro() { - auto feed = this; - auto co = Coro::instance(); - auto &mode_acs = Global::instance()->mode_acs; - auto &cfg = mode_acs.data_feed_config; - auto &pool = cfg.pool_; - auto &report_data_feed_msg_mum = mode_acs.report_data_feed_msg_mum; - while (feed->running()) { - if (data_feed_debug) { - std::cout << std::format("{} feed loop begin {}\n", key, thread_id_str()); - } - std::vector messages; - { - if (!feed->running() || !feed->registered()) { - break; - } - co_await feed->handle_in_loop_coro(); - auto s_num = feed->msg_buffer.mode_s_msg_num.load(); - auto other_num = feed->msg_buffer.mode_other_msg_num.load(); - const std::vector &all = feed->msg_buffer.get_all(); - Pool_Guard pg(&pool, all); - auto num = report_data_feed_msg_mum.load(); - auto monitor_msg_live = mode_acs.monitor_msg_live.load(); - if (monitor_msg_live && num != 0 && all.size() > num && too_many_msg_limit.test()) { - std::ostringstream oss; - oss << "feed_key:" << feed->key << " "; - oss << "recv_s:" << s_num << " "; - oss << "recv_other:" << other_num << " "; - std::cout << oss.str() << std::endl; - } - messages.reserve(all.size()); - for (const auto *str: all) { - if (!str || str->empty()) { - std::cout << "empty feed message:" << feed->key << std::endl; - continue; - } - messages.emplace_back(*str); - } - } - if (messages.empty()) { - co_await co->sleep_for(std::chrono::milliseconds(10)); - continue; - } - if (!feed->running() || !feed->registered()) { - break; - } - for (auto &msg: messages) { - if (!feed->enable) { - break; - } - if (data_feed_debug) { - std::cout << std::format("{} before send {}\n", key, thread_id_str()); - } - co_await feed->send_coro(msg); - if (data_feed_debug) { - std::cout << std::format("{} after send {}\n", key, thread_id_str()); - } - } - co_await resume_on(co->io.get_executor()); - } - std::cout << std::format("{} feed loop exit {}\n", key, thread_id_str()); - co_return; + auto feed = this; + auto co = Coro::instance(); + auto& mode_acs = Global::instance()->mode_acs; + auto& cfg = mode_acs.data_feed_config; + auto& pool = cfg.pool_; + auto& report_data_feed_msg_mum = mode_acs.report_data_feed_msg_mum; + while (feed->running()) { + if (data_feed_debug) { + std::cout << std::format("{} feed loop begin {}\n", key, thread_id_str()); + } + std::vector messages; + { + if (!feed->running() || !feed->registered()) { + break; + } + co_await feed->handle_in_loop_coro(); + auto s_num = feed->msg_buffer.mode_s_msg_num.load(); + auto other_num = feed->msg_buffer.mode_other_msg_num.load(); + const std::vector& all = feed->msg_buffer.get_all(); + Pool_Guard pg(&pool, all); + auto num = report_data_feed_msg_mum.load(); + auto monitor_msg_live = mode_acs.monitor_msg_live.load(); + if (monitor_msg_live && num != 0 && all.size() > num && too_many_msg_limit.test()) { + std::ostringstream oss; + oss << "feed_key:" << feed->key << " "; + oss << "recv_s:" << s_num << " "; + oss << "recv_other:" << other_num << " "; + std::cout << oss.str() << std::endl; + } + messages.reserve(all.size()); + for (const auto* str : all) { + if (!str || str->empty()) { + std::cout << "empty feed message:" << feed->key << std::endl; + continue; + } + messages.emplace_back(*str); + } + } + if (messages.empty()) { + co_await co->sleep_for(std::chrono::milliseconds(10)); + continue; + } + if (!feed->running() || !feed->registered()) { + break; + } + for (auto& msg : messages) { + if (!feed->enable) { + break; + } + if (data_feed_debug) { + std::cout << std::format("{} before send {}\n", key, thread_id_str()); + } + co_await feed->send_coro(msg); + if (data_feed_debug) { + std::cout << std::format("{} after send {}\n", key, thread_id_str()); + } + } + co_await resume_on(co->io.get_executor()); + } + std::cout << std::format("{} feed loop exit {}\n", key, thread_id_str()); + co_return; } - - asio::awaitable Data_Source::loop_coro() { - auto source = this; - auto co = Coro::instance(); - auto process_strand = std::make_shared >( - co->process_data->get_executor()); - while (source->running()) { - if (data_source_debug) { - std::cout << std::format("[1] ds:{} source loop begin {}\n", key, thread_id_str()) << std::flush;; - } - if (!source->running() || !source->registered()) { - break; - } - if (data_source_debug) { - std::cout << std::format("[2] ds:{} before handle thread:{}\n", key, thread_id_str()) << std::flush;; - } - co_await source->handle_in_loop_coro(); - if (!enable || !source->running() || !source->registered()) { - break; - } - if (data_source_debug) { - std::cout << std::format("[3] ds:{} before read thread:{}\n", key, thread_id_str()) << std::flush;; - } - auto mode_data = co_await source->read_coro(); - if (data_source_debug) { - std::cout << std::format("[4] ds:{} after read thread:{}\n", key, thread_id_str()) << std::flush;; - } - if (wait) { - co_await resume_on(co->process_data->get_executor()); - if (data_source_debug) { - std::cout << std::format("wait [5] ds:{} before process {} thread:{} \n", key, thread_id_str(), - mode_data.size()) << - std::flush;; - } - auto num = source->process_mode_acs_data(mode_data); - if (data_source_debug) { - std::cout << std::format("[6] ds:{} after process {} {}\n", key, num, thread_id_str()) << std::flush;; - } - co_await resume_on(co->io.get_executor()); - static Frequency_Limit_Multi mt(0.1); - auto name = type + ":" + key; - if (mt.test(name)) { - if (num == 0) { - if (data_source_debug) - std::cout << std::format("[7] {} 没解析到数据睡眠10ms {} {}\n", name, num, - thread_id_str()) << std::flush;; - ask_sleep = true; - } else { - if (data_source_debug) - std::cout << std::format("[7] {} 解析到数据 {} {}\n", name, num, - thread_id_str()) << std::flush;; - } - } - if (num == 0) { - co_await co->sleep_for(std::chrono::milliseconds(10)); - ask_sleep = false; - } - } else { - asio::post(*process_strand, [this, source, mode_data = std::move(mode_data)]() mutable { - try { - if (data_source_debug) { - std::cout << std::format("no wait [5] ds:{} before process {} size:{}\n", source->key, - thread_id_str(), - mode_data.size()); - } - auto num = source->process_mode_acs_data(mode_data); - if (data_source_debug) { - std::cout << std::format("[6] ds:{} after process {} {}\n", source->key, num, thread_id_str()); - } - static Frequency_Limit_Multi mt(0.1); - auto name = source->type + ":" + source->key; - if (mt.test(name)) { - if (num == 0) { - std::cout << std::format("[7] {} 没解析到数据 {} {}\n", name, num, thread_id_str()); - ask_sleep = true; - } else { - std::cout << std::format("[8] {} 解析到数据 {} {}\n", name, num, thread_id_str()); - } - } - } catch (const std::exception &e) { - std::cout << std::format("[9] {} process exception: {}\n", source->key, e.what()); - } catch (...) { - std::cout << std::format("[9] {} process unknown exception\n", source->key); - } - }); - co_await asio::post(co->io, asio::use_awaitable); - if (ask_sleep) { - co_await co->sleep_for(std::chrono::milliseconds(10)); - ask_sleep = false; - } - } - std::cout << std::flush; - } - std::cout << std::format("{} source loop exit {}\n", key, thread_id_str()); - co_return; + auto co = Coro::instance(); + auto process_strand = std::make_shared>(co->process_data->get_executor()); + while (running()) { + if (data_source_debug) { + std::cout << std::format("[1] ds:{} source loop begin {}\n", key, thread_id_str()) << std::flush;; + } + if (!running() || !registered()) { + break; + } + if (data_source_debug) { + std::cout << std::format("[2] ds:{} before handle thread:{}\n", key, thread_id_str()) << std::flush;; + } + co_await handle_in_loop_coro(); + if (!enable || !running() || !registered()) { + break; + } + if (data_source_debug) { + std::cout << std::format("[3] ds:{} before read thread:{}\n", key, thread_id_str()) << std::flush;; + } + auto mode_data = co_await read_coro(); + if (data_source_debug) { + std::cout << std::format("[4] ds:{} after read thread:{}\n", key, thread_id_str()) << std::flush;; + } + auto& wait = Global::instance()->mode_acs.wait_process_msg; + if (wait) { + // 调度到别的线程 + co_await resume_on(co->process_data->get_executor()); + if (data_source_debug) { + std::cout << std::format("wait [5] ds:{} before process {} thread:{} \n", key, thread_id_str(), + mode_data.size()) << + std::flush;; + } + auto num = process_mode_acs_data(mode_data); + if (data_source_debug) { + std::cout << std::format("[6] ds:{} after process {} {}\n", key, num, thread_id_str()) << std::flush;; + } + // 调度回io主线程 + co_await resume_on(co->io.get_executor()); + static Frequency_Limit_Multi mt(0.1); + auto name = type + ":" + key; + if (mt.test(name)) { + if (num == 0) { + if (data_source_debug) + std::cout << std::format("[7] {} 没解析到数据睡眠10ms {} {}\n", name, num, + thread_id_str()) << std::flush;; + ask_sleep = true; + } + else { + if (data_source_debug) + std::cout << std::format("[7] {} 解析到数据 {} {}\n", name, num, + thread_id_str()) << std::flush;; + } + } + if (num == 0) { + co_await co->sleep_for(std::chrono::milliseconds(10)); + ask_sleep = false; + } + } + else { + asio::post(*process_strand, [this, mode_data = std::move(mode_data)]() mutable { + try { + if (data_source_debug) { + std::cout << std::format("no wait [5] ds:{} before process {} size:{}\n", key, + thread_id_str(), + mode_data.size()); + } + auto num = process_mode_acs_data(mode_data); + if (data_source_debug) { + std::cout << std::format("[6] ds:{} after process {} {}\n", key, num, thread_id_str()); + } + static Frequency_Limit_Multi mt(0.1); + auto name = type + ":" + key; + if (mt.test(name)) { + if (num == 0) { + std::cout << std::format("[7] {} 没解析到数据 {} {}\n", name, num, thread_id_str()); + ask_sleep = true; + } + else { + std::cout << std::format("[8] {} 解析到数据 {} {}\n", name, num, thread_id_str()); + } + } + } + catch (const std::exception& e) { + std::cout << std::format("[9] {} process exception: {}\n", key, e.what()); + } + catch (...) { + std::cout << std::format("[9] {} process unknown exception\n", key); + } + }); + co_await asio::post(co->io, asio::use_awaitable); + if (ask_sleep) { + co_await co->sleep_for(std::chrono::milliseconds(10)); + ask_sleep = false; + } + } + std::cout << std::flush; + } + std::cout << std::format("{} source loop exit {}\n", key, thread_id_str()); + co_return; } diff --git a/module/Local_Server/server/server.cpp b/module/Local_Server/server/server.cpp index aa1563b..5e2714f 100644 --- a/module/Local_Server/server/server.cpp +++ b/module/Local_Server/server/server.cpp @@ -1,619 +1,559 @@ #include "Global.h" #include "Performance_Monitor.h" - #include #include #include - namespace { - constexpr std::size_t kMaxLogRequestBodySize = 8 * 1024; - - void Log_Request_Body_If_Small(const drogon::HttpRequestPtr &request) { - const auto &body = request->getBody(); +constexpr std::size_t kMaxLogRequestBodySize = 8 * 1024; +void Log_Request_Body_If_Small(const drogon::HttpRequestPtr& request) { + const auto& body = request->getBody(); if (body.empty()) { - return; + return; } - if (body.size() <= kMaxLogRequestBodySize) { - LOG_INFO << "[request body] " - << "path=" << request->path() << ", size=" << body.size() - << ", body=" << std::string(body.data(), body.size()); - } else { - LOG_INFO << "[request body skipped] " - << "path=" << request->path() << ", size=" << body.size() - << ", max=" << kMaxLogRequestBodySize; + LOG_INFO << "[request body] " + << "path=" << request->path() << ", size=" << body.size() + << ", body=" << std::string(body.data(), body.size()); } - } - - bool Is_Invalid_Http_Param(const std::exception &e) { - return dynamic_cast(&e) != nullptr || - dynamic_cast(&e) != nullptr || - dynamic_cast(&e) != nullptr; - } - - bool Is_Safe_Version_Directory_Name(std::string_view name) { + else { + LOG_INFO << "[request body skipped] " + << "path=" << request->path() << ", size=" << body.size() + << ", max=" << kMaxLogRequestBodySize; + } +} +bool Is_Invalid_Http_Param(const std::exception& e) { + return dynamic_cast(&e) != nullptr || + dynamic_cast(&e) != nullptr || + dynamic_cast(&e) != nullptr; +} +bool Is_Safe_Version_Directory_Name(std::string_view name) { if (name.empty() || name == "." || name == "..") { - return false; + return false; } return std::ranges::all_of(name, [](unsigned char ch) { - return std::isalnum(ch) != 0 || ch == '.' || ch == '_' || ch == '-'; + return std::isalnum(ch) != 0 || ch == '.' || ch == '_' || ch == '-'; }); - } - - bool Is_Safe_Archive_Extension(std::string_view extension) { +} +bool Is_Safe_Archive_Extension(std::string_view extension) { if (extension.empty() || extension.front() == '.' || extension.back() == '.') { - return false; + return false; } return std::ranges::all_of(extension, [](unsigned char ch) { - return std::isalnum(ch) != 0 || ch == '.' || ch == '_' || ch == '-'; + return std::isalnum(ch) != 0 || ch == '.' || ch == '_' || ch == '-'; }); - } - - std::string Normalize_Archive_Extension(std::string_view extension, - std::string_view fallback = "zip") { +} +std::string Normalize_Archive_Extension(std::string_view extension, + std::string_view fallback = "zip") { auto normalized = std::string(extension); while (!normalized.empty() && normalized.front() == '.') { - normalized.erase(normalized.begin()); + normalized.erase(normalized.begin()); } if (normalized.empty()) { - normalized = std::string(fallback); + normalized = std::string(fallback); } if (!Is_Safe_Archive_Extension(normalized)) { - throw std::invalid_argument("invalid archive extension: " + normalized); + throw std::invalid_argument("invalid archive extension: " + normalized); } return normalized; - } - - std::optional Try_Get_String_Field(const JSON &object, - std::string_view key) { - const JSON *value = object.get(key); +} +std::optional Try_Get_String_Field(const JSON& object, + std::string_view key) { + const JSON* value = object.get(key); if (value == nullptr) { - return std::nullopt; + return std::nullopt; } if (value->valueType != Psc::String) { - throw std::invalid_argument(std::string(key) + " must be a string"); + throw std::invalid_argument(std::string(key) + " must be a string"); } return value->val; - } - - struct Archive_Export_Request { +} +struct Archive_Export_Request { std::string version; Archive_Write_Options archive; - }; - - Archive_Export_Request - Parse_Archive_Export_Request(const drogon::HttpRequestPtr &req, - std::string_view default_version) { +}; +Archive_Export_Request +Parse_Archive_Export_Request(const drogon::HttpRequestPtr& req, + std::string_view default_version) { Archive_Export_Request result; result.version = std::move(default_version); - - const auto &body = req->getBody(); + const auto& body = req->getBody(); if (body.empty()) { - return result; + return result; } - auto parsed = Psc::try_parse_json(body.data()); if (!parsed.has_value()) { - throw_invalid_http_param("request body is not valid JSON"); + throw_invalid_http_param("request body is not valid JSON"); } - - const JSON ¶ms = parsed.value(); + const JSON& params = parsed.value(); if (auto version = Try_Get_String_Field(params, "version")) { - if (!version->empty()) { - result.version = *version; - } + if (!version->empty()) { + result.version = *version; + } } if (!Is_Safe_Version_Directory_Name(result.version)) { - throw std::invalid_argument("invalid version name: " + result.version); + throw std::invalid_argument("invalid version name: " + result.version); } - if (auto format = Try_Get_String_Field(params, "format")) { - if (!format->empty()) { - result.archive.format = *format; - } + if (!format->empty()) { + result.archive.format = *format; + } } if (auto filter = Try_Get_String_Field(params, "filter")) { - result.archive.filter = *filter; + result.archive.filter = *filter; } if (auto extension = Try_Get_String_Field(params, "extension")) { - if (!extension->empty()) { - result.archive.extension = Normalize_Archive_Extension(*extension); - } - } else { - result.archive.extension = - Normalize_Archive_Extension(result.archive.extension); + if (!extension->empty()) { + result.archive.extension = Normalize_Archive_Extension(*extension); + } + } + else { + result.archive.extension = + Normalize_Archive_Extension(result.archive.extension); } - return result; - } - - const Archive_Layout_Rules &Upgrade_Zip_Layout_Rules() { +} +const Archive_Layout_Rules& Upgrade_Zip_Layout_Rules() { static const Archive_Layout_Rules rules = [] { - Archive_Layout_Rules value; - value.directories = { - { - "", - {"adsb_cfg.ini", "config.json", "ecap_server", "lib9002config.json"}, - {"lib9002", "wwwroot"}, - {}, - {} - }, - {"lib9002", {"lib9002.so", "testserver"}, {"env"}, {}, {}} - }; - return value; + Archive_Layout_Rules value; + value.directories = { + { + "", + {"adsb_cfg.ini", "config.json", "ecap_server", "lib9002config.json"}, + {"lib9002", "wwwroot"}, + {}, + {} + }, + {"lib9002", {"lib9002.so", "testserver"}, {"env"}, {}, {}} + }; + return value; }(); return rules; - } +} } // namespace - void setupCors(); - void Global::init_web_server() { - register_http_performance_monitor( - http_monitor_config.access_log, - http_monitor_config.slow_request_threshold_ms); - setupCors(); - static auto &web_server = web_server_config; - Web_Server &svr = this->svr; - auto &da = drogon::app(); - auto return_file = [](std::string_view path) { - auto res = drogon::HttpResponse::newHttpResponse(); - if (std::filesystem::exists(path)) { - std::string content; - httplib::detail::read_file(path, content); - auto content_type = httplib::detail::find_content_type( - path, {}, "application/octet-stream"); - res->setContentTypeString(content_type); - res->setBody(content); - // res.set_file_content(path); - } else { - res->setStatusCode(drogon::k404NotFound); - JSON ret = JSON::object(); - ret.append({"LOG_POS", LOG_POS}); - ret.append({"not_found_path", path}); - res->setContentTypeCode(drogon::CT_TEXT_PLAIN); - res->setBody(ret.to_json_string()); - } - return res; - }; - auto add_path = [return_file](std::string_view path) { + register_http_performance_monitor( + http_monitor_config.access_log, + http_monitor_config.slow_request_threshold_ms); + setupCors(); + static auto& web_server = web_server_config; + Web_Server& svr = this->svr; + auto& da = drogon::app(); + auto return_file = [](std::string_view path) { + auto res = drogon::HttpResponse::newHttpResponse(); + if (std::filesystem::exists(path)) { + std::string content; + httplib::detail::read_file(path, content); + auto content_type = httplib::detail::find_content_type( + path, {}, "application/octet-stream"); + res->setContentTypeString(content_type); + res->setBody(content); + // res.set_file_content(path); + } + else { + res->setStatusCode(drogon::k404NotFound); + JSON ret = JSON::object(); + ret.append({"LOG_POS", LOG_POS}); + ret.append({"not_found_path", path}); + res->setContentTypeCode(drogon::CT_TEXT_PLAIN); + res->setBody(ret.to_json_string()); + } + return res; + }; + auto add_path = [return_file](std::string_view path) { + drogon::app().registerHandlerViaRegex( + std::string(path), + [return_file]( + const drogon::HttpRequestPtr& req, + std::function&& callback) { + callback(return_file(web_server.get_true_webapp() + "/index.html")); + }, + {drogon::Get}); + }; drogon::app().registerHandlerViaRegex( - std::string(path), - [return_file]( - const drogon::HttpRequestPtr &req, - std::function &&callback) { - callback(return_file(web_server.get_true_webapp() + "/index.html")); - }, - {drogon::Get}); - }; - - drogon::app().registerHandlerViaRegex( - R"(^/tiles/.*$)", - [return_file]( - const drogon::HttpRequestPtr &req, - std::function &&callback) { - auto path = web_server.get_true_tiles() + req->getPath().substr(6); - callback(return_file(path)); - }, - {drogon::Get}); - - drogon::app().registerHandlerViaRegex( - "/", - [return_file]( - const drogon::HttpRequestPtr &req, - std::function &&callback) { - callback(return_file(web_server.get_true_webapp() + "/index.html")); - }, - {drogon::Get}); - - add_path(R"(^/settings)"); - add_path(R"(^/map)"); - add_path(R"(^/dsp)"); - add_path(R"(^/aircraftlist)"); - drogon::app().registerHandlerViaRegex( - R"(^/ui(/.*)?$)", - [return_file]( - const drogon::HttpRequestPtr &req, - std::function &&callback) { - std::cout << req->path() << std::endl; - auto res = drogon::HttpResponse::newHttpResponse(); - auto path = web_server.get_true_webapp(); - auto rp = "/" + req->path().substr(3); - if (rp == "/") { - path += "/index.html"; - } else { - path += rp; - } - callback(return_file(path)); + R"(^/tiles/.*$)", + [return_file]( + const drogon::HttpRequestPtr& req, + std::function&& callback) { + auto path = web_server.get_true_tiles() + req->getPath().substr(6); + auto resp = return_file(path); + resp->addHeader("Cache-Control", "public, max-age=31536000, immutable"); + callback(resp); + }, + {drogon::Get}); + drogon::app().registerHandlerViaRegex( + "/", + [return_file]( + const drogon::HttpRequestPtr& req, + std::function&& callback) { + callback(return_file(web_server.get_true_webapp() + "/index.html")); + }, + {drogon::Get}); + add_path(R"(^/settings)"); + add_path(R"(^/map)"); + add_path(R"(^/dsp)"); + add_path(R"(^/aircraftlist)"); + drogon::app().registerHandlerViaRegex( + R"(^/ui(/.*)?$)", + [return_file]( + const drogon::HttpRequestPtr& req, + std::function&& callback) { + std::cout << req->path() << std::endl; + auto res = drogon::HttpResponse::newHttpResponse(); + auto path = web_server.get_true_webapp(); + auto rp = "/" + req->path().substr(3); + if (rp == "/") { + path += "/index.html"; + } + else { + path += rp; + } + callback(return_file(path)); + }); + State_Report::instance(); + svr.Get(R"(/get_debug_info)", [](HTTP_Param) { + JSON arr = JSON::array(); + auto& sp = Global::instance()->mode_acs.data_feed_config.pool_; + arr.append(JSON::object( + {{"remain_size", sp.remain_size()}, {"free_size", sp.free_size()}})); + res->setBody(arr.to_json_string("", "\n", "")); }); - - State_Report::instance(); - svr.Get(R"(/get_debug_info)", [](HTTP_Param) { - JSON arr = JSON::array(); - auto &sp = Global::instance()->mode_acs.data_feed_config.pool_; - arr.append(JSON::object( - {{"remain_size", sp.remain_size()}, {"free_size", sp.free_size()}})); - res->setBody(arr.to_json_string("", "\n", "")); - }); - - mlat.server(this); - database_server(this); - external_resources_manager.server(this); - device_config.server(this); - mode_acs.server(this); - dsp_config.server(this); - - - // s.w.init(); - - drogon::app().registerHandler( - api + "export_logs", - [](const drogon::HttpRequestPtr &req, - std::function &&cb) { - try { - auto export_request = - Parse_Archive_Export_Request(req, Global::instance()->version); - std::string name = export_request.version; - auto version_dir = std::filesystem::path(get_exe_dir() + "/.."); - std::string data = archive_directory_to_memory( - version_dir / name / "logs", {}, export_request.archive); - auto resp = drogon::HttpResponse::newHttpResponse(); - resp->setBody(data); - resp->setContentTypeString("application/octet-stream"); - resp->addHeader("Content-Disposition", - "attachment; filename*=UTF-8''" + name + "_logs." + - std::string(export_request.archive.extension)); - resp->addHeader("ok", "true"); - cb(resp); - } catch (const std::exception &e) { - auto resp = drogon::HttpResponse::newHttpResponse(); - resp->addHeader("ok", "false"); - Psc::JSON json = - JSON::object({{"message", Psc::platform_2_utf8(e.what())}}); - resp->setContentTypeCode(drogon::CT_APPLICATION_JSON); - resp->setBody(json.to_json_string()); - cb(resp); - } - }); - - drogon::app().registerHandler( - api + "export_version", - [](const drogon::HttpRequestPtr &req, - std::function &&cb) { - try { - auto export_request = - Parse_Archive_Export_Request(req, Global::instance()->version); - std::string name = export_request.version; - auto version_dir = std::filesystem::path(get_exe_dir() + "/.."); - - std::cout << "准备压缩:" << std::endl; - std::string data = archive_directory_to_memory( - version_dir / name, - { - "logs/", "uploads/", "drogon_uploads/", - "backups/", // ghw的什么文件路径 - "stack_trace.txt", "pt.log", "ecap_server.log", "Core", "core", - name + ".zip", name + "." + std::string(export_request.archive.extension) - }, - export_request.archive); - std::cout << "压缩成功:" << data.size() << std::endl; - auto resp = drogon::HttpResponse::newHttpResponse(); - resp->setBody(data); - resp->setContentTypeString("application/octet-stream"); - resp->addHeader("Content-Disposition", - "attachment; filename*=UTF-8''" + name + "." + - std::string(export_request.archive.extension)); - resp->addHeader("ok", "true"); - cb(resp); - } catch (const std::exception &e) { - auto message = Psc::platform_2_utf8(e.what()); - std::cout << "导出异常触发:" << message << " " << LOG_POS - << std::endl; - auto resp = drogon::HttpResponse::newHttpResponse(); - - Psc::JSON json = JSON::object({{"message", message}}); - resp->setContentTypeCode(drogon::CT_APPLICATION_JSON); - resp->setBody(json.to_json_string()); - resp->addHeader("ok", "false"); - cb(resp); - } - }); - - drogon::app().registerHandler(api + "restart_device", [](const drogon:: - HttpRequestPtr - &req, - std::function - &&cb) { + mlat.server(this); + database_server(this); + external_resources_manager.server(this); + device_config.server(this); + mode_acs.server(this); + dsp_config.server(this); + // s.w.init(); + drogon::app().registerHandler( + api + "export_logs", + [](const drogon::HttpRequestPtr& req, + std::function&& cb) { + try { + auto export_request = + Parse_Archive_Export_Request(req, Global::instance()->version); + std::string name = export_request.version; + auto version_dir = std::filesystem::path(get_exe_dir() + "/.."); + std::string data = archive_directory_to_memory( + version_dir / name / "logs", {}, export_request.archive); + auto resp = drogon::HttpResponse::newHttpResponse(); + resp->setBody(data); + resp->setContentTypeString("application/octet-stream"); + resp->addHeader("Content-Disposition", + "attachment; filename*=UTF-8''" + name + "_logs." + + std::string(export_request.archive.extension)); + resp->addHeader("ok", "true"); + cb(resp); + } + catch (const std::exception& e) { + auto resp = drogon::HttpResponse::newHttpResponse(); + resp->addHeader("ok", "false"); + Psc::JSON json = + JSON::object({{"message", Psc::platform_2_utf8(e.what())}}); + resp->setContentTypeCode(drogon::CT_APPLICATION_JSON); + resp->setBody(json.to_json_string()); + cb(resp); + } + }); + drogon::app().registerHandler( + api + "export_version", + [](const drogon::HttpRequestPtr& req, + std::function&& cb) { + try { + auto export_request = + Parse_Archive_Export_Request(req, Global::instance()->version); + std::string name = export_request.version; + auto version_dir = std::filesystem::path(get_exe_dir() + "/.."); + std::cout << "准备压缩:" << std::endl; + std::string data = archive_directory_to_memory( + version_dir / name, + { + "logs/", "uploads/", "drogon_uploads/", + "backups/", // ghw的什么文件路径 + "stack_trace.txt", "pt.log", "ecap_server.log", "Core", "core", + name + ".zip", name + "." + std::string(export_request.archive.extension) + }, + export_request.archive); + std::cout << "压缩成功:" << data.size() << std::endl; + auto resp = drogon::HttpResponse::newHttpResponse(); + resp->setBody(data); + resp->setContentTypeString("application/octet-stream"); + resp->addHeader("Content-Disposition", + "attachment; filename*=UTF-8''" + name + "." + + std::string(export_request.archive.extension)); + resp->addHeader("ok", "true"); + cb(resp); + } + catch (const std::exception& e) { + auto message = Psc::platform_2_utf8(e.what()); + std::cout << "导出异常触发:" << message << " " << LOG_POS + << std::endl; + auto resp = drogon::HttpResponse::newHttpResponse(); + Psc::JSON json = JSON::object({{"message", message}}); + resp->setContentTypeCode(drogon::CT_APPLICATION_JSON); + resp->setBody(json.to_json_string()); + resp->addHeader("ok", "false"); + cb(resp); + } + }); + drogon::app().registerHandler(api + "restart_device", [](const drogon:: + HttpRequestPtr + & req, + std::function + && cb) { #ifdef __linux__ - std::string root_dir = Psc::get_exe_dir() + "/.."; - - std::string log_file = fmt::format("{}/restart_device.log", root_dir); - std::string cmd = fmt::format( - R"(systemd-run --scope --slice=system.slice sudo bash "{}/restart_device.bash" > {} 2>&1 &)", - root_dir, log_file); - std::cout << "Generated Command: " << cmd << std::endl; - std::system(cmd.c_str()); -#endif - auto resp = drogon::HttpResponse::newHttpResponse(); - resp->setContentTypeCode(drogon::CT_APPLICATION_JSON); - resp->setBody(R"({"ok":true})"); - cb(resp); - }); - - drogon::app().registerHandler( - api + "import_version", - [](const drogon::HttpRequestPtr &req, - std::function &&cb) { - try { - // ---------- 参数校验 ---------- - auto dir = req->getHeader("dir"); - if (!Is_Safe_Version_Directory_Name(dir)) { - throw std::invalid_argument("invalid or missing header: dir"); - } - - const auto &body = req->getBody(); - auto archive_layout_result = validate_archive_layout_from_memory( - body.data(), body.size(), Upgrade_Zip_Layout_Rules()); - if (!archive_layout_result) { - throw Invalid_Archive_Error(archive_layout_result.error()); - } - - auto version_dir = std::filesystem::path(get_exe_dir() + "/.."); - // ---------- 路径准备 ---------- - auto targetDir = version_dir / dir; - - std::cout << "========dir====" << dir << std::endl; - std::cout << "========version_dir====" << version_dir << std::endl; - std::cout << "========targetDir====" << targetDir << std::endl; - - std::filesystem::create_directories(targetDir); - - const auto archive_extension = - Normalize_Archive_Extension(req->getHeader("archive-extension")); - auto archivePath = targetDir / (dir + "." + archive_extension); - - // ---------- 写 zip ---------- - std::ofstream ofs(archivePath, std::ios::binary); - if (!ofs) { - throw std::runtime_error("open archive failed: " + - archivePath.string()); - } - - ofs.write(body.data(), body.size()); - if (!ofs) { - throw std::runtime_error("write archive failed: " + - archivePath.string()); - } - ofs.close(); - if (!ofs) { - throw std::runtime_error("close archive failed: " + - archivePath.string()); - } - -#ifdef _WIN32 - std::string root_dir = version_dir.string(); - std::string old_version = - Global::instance()->version; // Global version info - std::string new_version = dir; - std::string log_file = fmt::format( - "{}/upgrade_{}_to_{}.log", root_dir, old_version, new_version); + std::string root_dir = Psc::get_exe_dir() + "/.."; + std::string log_file = fmt::format("{}/restart_device.log", root_dir); std::string cmd = fmt::format( - R"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {{ . '{}/upgrade.legacy' '{}' '{}' > '{}' 2>&1 }}" )", - root_dir, old_version, new_version, log_file); -#else - std::string root_dir = version_dir.string(); - std::string old_version = - Global::instance()->version; // Global version info - std::string new_version = dir; - std::string log_file = fmt::format( - "{}/upgrade_{}_to_{}.log", root_dir, old_version, new_version); - std::string cmd = fmt::format( - R"(systemd-run --scope --slice=system.slice sudo bash "{}/upgrade.bash" "{}" "{}" > {} 2>&1 &)", - root_dir, old_version, new_version, log_file); -#endif + R"(systemd-run --scope --slice=system.slice sudo bash "{}/restart_device.bash" > {} 2>&1 &)", + root_dir, log_file); std::cout << "Generated Command: " << cmd << std::endl; std::system(cmd.c_str()); - // ---------- 成功响应 ---------- +#endif auto resp = drogon::HttpResponse::newHttpResponse(); resp->setContentTypeCode(drogon::CT_APPLICATION_JSON); resp->setBody(R"({"ok":true})"); cb(resp); - } catch (const std::exception &e) { - auto resp = drogon::HttpResponse::newHttpResponse(); - const bool invalid_input = - dynamic_cast(&e) != nullptr || - dynamic_cast(&e) != nullptr; - resp->setStatusCode(invalid_input - ? drogon::k400BadRequest - : drogon::k500InternalServerError); - resp->setContentTypeCode(drogon::CT_APPLICATION_JSON); - auto reason = Psc::platform_2_utf8(e.what()); - std::cout << "异常" << reason << std::endl; - Psc::JSON json = JSON::object({{"ok", false}, {"error", reason}}); - resp->setBody(json.to_json_string()); - cb(resp); - } - }, - {drogon::Post}); - - svr.Post(api + "get_debug_log", [](HTTP_Param) { - CHECK_JSON_PARAM - HTTP_REQUIRE_VALUE( - start_serial_number, - params.try_get_number("serial_number")) - auto log = + }); + drogon::app().registerHandler( + api + "import_version", + [](const drogon::HttpRequestPtr& req, + std::function&& cb) { + try { + // ---------- 参数校验 ---------- + auto dir = req->getHeader("dir"); + if (!Is_Safe_Version_Directory_Name(dir)) { + throw std::invalid_argument("invalid or missing header: dir"); + } + const auto& body = req->getBody(); + auto archive_layout_result = validate_archive_layout_from_memory( + body.data(), body.size(), Upgrade_Zip_Layout_Rules()); + if (!archive_layout_result) { + throw Invalid_Archive_Error(archive_layout_result.error()); + } + auto version_dir = std::filesystem::path(get_exe_dir() + "/.."); + // ---------- 路径准备 ---------- + auto targetDir = version_dir / dir; + std::cout << "========dir====" << dir << std::endl; + std::cout << "========version_dir====" << version_dir << std::endl; + std::cout << "========targetDir====" << targetDir << std::endl; + std::filesystem::create_directories(targetDir); + const auto archive_extension = + Normalize_Archive_Extension(req->getHeader("archive-extension")); + auto archivePath = targetDir / (dir + "." + archive_extension); + // ---------- 写 zip ---------- + std::ofstream ofs(archivePath, std::ios::binary); + if (!ofs) { + throw std::runtime_error("open archive failed: " + + archivePath.string()); + } + ofs.write(body.data(), body.size()); + if (!ofs) { + throw std::runtime_error("write archive failed: " + + archivePath.string()); + } + ofs.close(); + if (!ofs) { + throw std::runtime_error("close archive failed: " + + archivePath.string()); + } +#ifdef _WIN32 + std::string root_dir = version_dir.string(); + std::string old_version = + Global::instance()->version; // Global version info + std::string new_version = dir; + std::string log_file = fmt::format( + "{}/upgrade_{}_to_{}.log", root_dir, old_version, new_version); + std::string cmd = fmt::format( + R"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {{ . '{}/upgrade.legacy' '{}' '{}' > '{}' 2>&1 }}" )", + root_dir, old_version, new_version, log_file); +#else + std::string root_dir = version_dir.string(); + std::string old_version = + Global::instance()->version; // Global version info + std::string new_version = dir; + std::string log_file = fmt::format( + "{}/upgrade_{}_to_{}.log", root_dir, old_version, new_version); + std::string cmd = fmt::format( + R"(systemd-run --scope --slice=system.slice sudo bash "{}/upgrade.bash" "{}" "{}" > {} 2>&1 &)", + root_dir, old_version, new_version, log_file); +#endif + std::cout << "Generated Command: " << cmd << std::endl; + std::system(cmd.c_str()); + // ---------- 成功响应 ---------- + auto resp = drogon::HttpResponse::newHttpResponse(); + resp->setContentTypeCode(drogon::CT_APPLICATION_JSON); + resp->setBody(R"({"ok":true})"); + cb(resp); + } + catch (const std::exception& e) { + auto resp = drogon::HttpResponse::newHttpResponse(); + const bool invalid_input = + dynamic_cast(&e) != nullptr || + dynamic_cast(&e) != nullptr; + resp->setStatusCode(invalid_input + ? drogon::k400BadRequest + : drogon::k500InternalServerError); + resp->setContentTypeCode(drogon::CT_APPLICATION_JSON); + auto reason = Psc::platform_2_utf8(e.what()); + std::cout << "异常" << reason << std::endl; + Psc::JSON json = JSON::object({{"ok", false}, {"error", reason}}); + resp->setBody(json.to_json_string()); + cb(resp); + } + }, + {drogon::Post}); + svr.Post(api + "get_debug_log", [](HTTP_Param) { + CHECK_JSON_PARAM + HTTP_REQUIRE_VALUE( + start_serial_number, + params.try_get_number("serial_number")) + auto log = base64_encode(debug_logger_info_manager.get_info(start_serial_number)); - JSON ret = JSON::object(); - ret.append({"log", log}); - ret.append( - {"serial_number", debug_logger_info_manager.get_cur_serial_number()}); - ret.append({"start_serial_number", start_serial_number}); - res->setBody(warp(ret).to_json_string()); - }); - - svr.Post(api + "get_general_info", [](HTTP_Param) { - auto g = Global::instance(); - // auto &db = *Global::instance()->total_source(); - auto ret = JSON::object(); - // ret.append({"total_source key", db.key}); - // ret.append({"飞机总数", db.get_aircraft_num()}); - // ret.append({"解出位置的数量", db.have_pos_aircraft_num}); - JSON arr = JSON::object(); - for (auto &ds: g->mode_acs.data_source_config.map.list()) { - if (!ds->enable) - continue; - arr.append({ds->key, ds->statistic_json()}); - } - ret.append({"统计信息", arr}); - // ret.append({"基站信息", base_station.to_Json()}); - res->setBody(warp(ret).to_json_string()); - }); - - svr.Post(api + "version", [this](HTTP_Param) { - auto ret = JSON::object(); - ret.append({"version", version}); - res->setBody(warp(ret).to_json_string()); - }); - - auto now = std::chrono::system_clock::now(); - start_server_time = std::chrono::system_clock::to_time_t(now); - svr.Post(api + "start_device_time", [this](HTTP_Param) { - auto ret = JSON::object(); - ret.append({"start_device_time", start_server_time}); - res->setBody(warp(ret).to_json_string()); - }); - - da.setDefaultHandler( - [](const drogon::HttpRequestPtr &req, - std::function &&callback) { - LOG_WARN << "[http route not found] " - << "path=" << req->path(); - - auto res = drogon::HttpResponse::newHttpResponse(); - - res->setStatusCode(drogon::k404NotFound); - res->setBody("not found"); - res->addHeader("Content-Type", "text/plain; charset=utf-8"); - - callback(res); + JSON ret = JSON::object(); + ret.append({"log", log}); + ret.append( + {"serial_number", debug_logger_info_manager.get_cur_serial_number()}); + ret.append({"start_serial_number", start_serial_number}); + res->setBody(warp(ret).to_json_string()); }); - - da.setCustomErrorHandler( - [](drogon::HttpStatusCode status, - const drogon::HttpRequestPtr &req) -> drogon::HttpResponsePtr { - LOG_WARN << "[http error] " - << "path=" << req->path() - << ", status=" << static_cast(status); - - auto res = drogon::HttpResponse::newHttpResponse(); - - res->setStatusCode(status); - res->setBody("http error"); - res->addHeader("Content-Type", "text/plain; charset=utf-8"); - - return res; + svr.Post(api + "get_general_info", [](HTTP_Param) { + auto g = Global::instance(); + // auto &db = *Global::instance()->total_source(); + auto ret = JSON::object(); + // ret.append({"total_source key", db.key}); + // ret.append({"飞机总数", db.get_aircraft_num()}); + // ret.append({"解出位置的数量", db.have_pos_aircraft_num}); + JSON arr = JSON::object(); + for (auto& ds : g->mode_acs.data_source_config.map.list()) { + if (!ds->enable) + continue; + arr.append({ds->key, ds->statistic_json()}); + } + ret.append({"统计信息", arr}); + // ret.append({"基站信息", base_station.to_Json()}); + res->setBody(warp(ret).to_json_string()); }); - - da.setExceptionHandler( - [](const std::exception &e, const drogon::HttpRequestPtr &req, - std::function &&callback) { - auto res = drogon::HttpResponse::newHttpResponse(); - - if (Is_Invalid_Http_Param(e)) { - LOG_WARN << "[invalid http parameters] " - << "path=" << req->path() << ", detail=" << e.what(); - Log_Request_Body_If_Small(req); - - res->setStatusCode(drogon::k400BadRequest); - res->setContentTypeCode(drogon::CT_APPLICATION_JSON); - res->setBody(R"({"code":400,"message":"invalid http parameters"})"); - } else { - LOG_ERROR << "[handler exception] " - << "path=" << req->path() << ", what=" << e.what(); - - res->setStatusCode(drogon::k500InternalServerError); - res->setContentTypeCode(drogon::CT_APPLICATION_JSON); - res->setBody(R"({"code":500,"message":"internal server error"})"); - } - - callback(res); + svr.Post(api + "version", [this](HTTP_Param) { + auto ret = JSON::object(); + ret.append({"version", version}); + res->setBody(warp(ret).to_json_string()); }); - - da.setUploadPath(get_exe_dir() + "/drogon_uploads"); - std::cout << "上传路径: " << da.getUploadPath() << std::endl; - - init_ok = true; + auto now = std::chrono::system_clock::now(); + start_server_time = std::chrono::system_clock::to_time_t(now); + svr.Post(api + "start_device_time", [this](HTTP_Param) { + auto ret = JSON::object(); + ret.append({"start_device_time", start_server_time}); + res->setBody(warp(ret).to_json_string()); + }); + da.setDefaultHandler( + [](const drogon::HttpRequestPtr& req, + std::function&& callback) { + LOG_WARN << "[http route not found] " + << "path=" << req->path(); + auto res = drogon::HttpResponse::newHttpResponse(); + res->setStatusCode(drogon::k404NotFound); + res->setBody("not found"); + res->addHeader("Content-Type", "text/plain; charset=utf-8"); + callback(res); + }); + da.setCustomErrorHandler( + [](drogon::HttpStatusCode status, + const drogon::HttpRequestPtr& req) -> drogon::HttpResponsePtr { + LOG_WARN << "[http error] " + << "path=" << req->path() + << ", status=" << static_cast(status); + auto res = drogon::HttpResponse::newHttpResponse(); + res->setStatusCode(status); + res->setBody("http error"); + res->addHeader("Content-Type", "text/plain; charset=utf-8"); + return res; + }); + da.setExceptionHandler( + [](const std::exception& e, const drogon::HttpRequestPtr& req, + std::function&& callback) { + auto res = drogon::HttpResponse::newHttpResponse(); + if (Is_Invalid_Http_Param(e)) { + LOG_WARN << "[invalid http parameters] " + << "path=" << req->path() << ", detail=" << e.what(); + Log_Request_Body_If_Small(req); + res->setStatusCode(drogon::k400BadRequest); + res->setContentTypeCode(drogon::CT_APPLICATION_JSON); + res->setBody(R"({"code":400,"message":"invalid http parameters"})"); + } + else { + LOG_ERROR << "[handler exception] " + << "path=" << req->path() << ", what=" << e.what(); + res->setStatusCode(drogon::k500InternalServerError); + res->setContentTypeCode(drogon::CT_APPLICATION_JSON); + res->setBody(R"({"code":500,"message":"internal server error"})"); + } + callback(res); + }); + da.setUploadPath(get_exe_dir() + "/drogon_uploads"); + std::cout << "上传路径: " << da.getUploadPath() << std::endl; + init_ok = true; } - void setupCors() { - // Register sync advice to handle CORS preflight (OPTIONS) requests - drogon::app().registerSyncAdvice( - [](const drogon::HttpRequestPtr &req) -> drogon::HttpResponsePtr { - if (req->method() == drogon::HttpMethod::Options) { - auto resp = drogon::HttpResponse::newHttpResponse(); - - // Set Access-Control-Allow-Origin header based on the Origin - // request header - const auto &origin = req->getHeader("Origin"); - if (!origin.empty()) { - resp->addHeader("Access-Control-Allow-Origin", origin); - } - - // Set Access-Control-Allow-Methods based on the requested method - const auto &requestMethod = + // Register sync advice to handle CORS preflight (OPTIONS) requests + drogon::app().registerSyncAdvice( + [](const drogon::HttpRequestPtr& req) -> drogon::HttpResponsePtr { + if (req->method() == drogon::HttpMethod::Options) { + auto resp = drogon::HttpResponse::newHttpResponse(); + // Set Access-Control-Allow-Origin header based on the Origin + // request header + const auto& origin = req->getHeader("Origin"); + if (!origin.empty()) { + resp->addHeader("Access-Control-Allow-Origin", origin); + } + // Set Access-Control-Allow-Methods based on the requested method + const auto& requestMethod = + req->getHeader("Access-Control-Request-Method"); + if (!requestMethod.empty()) { + resp->addHeader("Access-Control-Allow-Methods", requestMethod); + } + // Allow credentials to be included in cross-origin requests + resp->addHeader("Access-Control-Allow-Credentials", "true"); + // Set allowed headers from the Access-Control-Request-Headers + // header + const auto& requestHeaders = + req->getHeader("Access-Control-Request-Headers"); + if (!requestHeaders.empty()) { + resp->addHeader("Access-Control-Allow-Headers", requestHeaders); + } + return std::move(resp); + } + return {}; + }); + // Register post-handling advice to add CORS headers to all responses + drogon::app().registerPostHandlingAdvice( + [](const drogon::HttpRequestPtr& req, + const drogon::HttpResponsePtr& resp) -> void { + // Set Access-Control-Allow-Origin based on the Origin request + // header + const auto& origin = req->getHeader("Origin"); + if (!origin.empty()) { + resp->addHeader("Access-Control-Allow-Origin", origin); + } + // Reflect the requested Access-Control-Request-Method back in the + // response + const auto& requestMethod = req->getHeader("Access-Control-Request-Method"); - if (!requestMethod.empty()) { - resp->addHeader("Access-Control-Allow-Methods", requestMethod); - } - - // Allow credentials to be included in cross-origin requests - resp->addHeader("Access-Control-Allow-Credentials", "true"); - - // Set allowed headers from the Access-Control-Request-Headers - // header - const auto &requestHeaders = + if (!requestMethod.empty()) { + resp->addHeader("Access-Control-Allow-Methods", requestMethod); + } + // Allow credentials to be included in cross-origin requests + resp->addHeader("Access-Control-Allow-Credentials", "true"); + // Reflect the requested Access-Control-Request-Headers back + const auto& requestHeaders = req->getHeader("Access-Control-Request-Headers"); - if (!requestHeaders.empty()) { - resp->addHeader("Access-Control-Allow-Headers", requestHeaders); - } - - return std::move(resp); - } - return {}; - }); - - // Register post-handling advice to add CORS headers to all responses - drogon::app().registerPostHandlingAdvice( - [](const drogon::HttpRequestPtr &req, - const drogon::HttpResponsePtr &resp) -> void { - // Set Access-Control-Allow-Origin based on the Origin request - // header - const auto &origin = req->getHeader("Origin"); - if (!origin.empty()) { - resp->addHeader("Access-Control-Allow-Origin", origin); - } - - // Reflect the requested Access-Control-Request-Method back in the - // response - const auto &requestMethod = - req->getHeader("Access-Control-Request-Method"); - if (!requestMethod.empty()) { - resp->addHeader("Access-Control-Allow-Methods", requestMethod); - } - - // Allow credentials to be included in cross-origin requests - resp->addHeader("Access-Control-Allow-Credentials", "true"); - - // Reflect the requested Access-Control-Request-Headers back - const auto &requestHeaders = - req->getHeader("Access-Control-Request-Headers"); - if (!requestHeaders.empty()) { - resp->addHeader("Access-Control-Allow-Headers", requestHeaders); - } - }); + if (!requestHeaders.empty()) { + resp->addHeader("Access-Control-Allow-Headers", requestHeaders); + } + }); } diff --git a/todolist.txt b/todolist.txt new file mode 100644 index 0000000..c9b5957 --- /dev/null +++ b/todolist.txt @@ -0,0 +1,6 @@ +网页显示的开启服务时间 同时得知系统的开机时间 系统的内存占用 cpu占用 线程的信息 显示在网页上 +先调研前端有什么图标库做这个事情 + +梯子的网页配置来源是从哪里 前端缓存瓦片 + +后台处理发送消息 分流