From 6fe0bed78e88bb75c11c303f0afb10eb87e82755 Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Sun, 9 Aug 2026 14:13:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.cmake | 16 +- module/Local_Server/DSP/DSP_Config.cpp | 2 +- module/Local_Server/Data_Feed/Data_Feed.cpp | 94 +- module/Local_Server/Data_Feed/Data_Feed.h | 187 +++- .../Local_Server/Data_Source/Data_Source.cpp | 111 +-- module/Local_Server/Data_Source/Data_Source.h | 251 +++-- .../Data_Source/Data_Source_Handler.cpp | 183 ++-- module/Local_Server/Data_Source/Database.cpp | 32 +- .../External_Database/External_Database.cpp | 10 +- module/Local_Server/global_include.h | 24 +- .../Local_Server/server/Adminive_Config.cpp | 940 ++++++++++++++++++ module/Local_Server/server/Adminive_Config.h | 5 + module/Local_Server/server/Config.cpp | 229 +++-- module/Local_Server/server/Config.h | 205 ++-- .../Local_Server/server/Config_Default.h.in | 3 + module/Local_Server/server/Global.h | 2 +- module/Local_Server/server/MLAT.cpp | 12 +- module/Local_Server/server/MLAT.h | 24 +- .../server/Source_Feed_Relation.cpp | 159 +-- .../server/Source_Feed_Relation.h | 104 +- module/Local_Server/server/With_Loop_Coro.cpp | 6 +- module/Local_Server/server/With_Loop_Coro.h | 44 +- module/Local_Server/server/io_coro.cpp | 12 +- module/Local_Server/server/server.cpp | 6 +- module/Local_Server/server/tiles.cpp | 11 +- module/Local_Server_main.cpp | 16 +- 26 files changed, 2007 insertions(+), 681 deletions(-) create mode 100644 module/Local_Server/server/Adminive_Config.cpp create mode 100644 module/Local_Server/server/Adminive_Config.h create mode 100644 module/Local_Server/server/Config_Default.h.in diff --git a/main.cmake b/main.cmake index a1dfc38..8f6f424 100644 --- a/main.cmake +++ b/main.cmake @@ -11,6 +11,11 @@ else () set(cfg_dir ${CMAKE_CURRENT_LIST_DIR}/config) endif () set(data_dir ${CMAKE_CURRENT_LIST_DIR}/data) +file(READ "${cfg_dir}/config.json" ecap_default_config_hex HEX) +string(REGEX REPLACE "([0-9A-Fa-f][0-9A-Fa-f])" "0x\\1,\n" ecap_default_config_bytes "${ecap_default_config_hex}") +set(ecap_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") +file(MAKE_DIRECTORY "${ecap_generated_dir}") +configure_file("${md}/Local_Server/server/Config_Default.h.in" "${ecap_generated_dir}/Config_Default.h" @ONLY) append_glob_source(dll_source_srcs "${md}/dll_source") add_library(dll_source SHARED ${dll_source_srcs}) add_executable(data_process "${dll_source_srcs}") @@ -29,12 +34,22 @@ target_link_libraries(mlat_source PUBLIC ${Core}) append_glob_source(ecap_server_srcs "${md}/Local_Server") add_executable(ecap_server "${md}/Local_Server_main.cpp" ${ecap_server_srcs}) target_include_directories(ecap_server PUBLIC "${md}") +target_include_directories(ecap_server PRIVATE "${ecap_generated_dir}") +target_include_directories(ecap_server PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/third_party/Adminive/backend/library/include" + "${CMAKE_CURRENT_LIST_DIR}/third_party/Adminive/backend/service/include" + "${CMAKE_CURRENT_LIST_DIR}/third_party/Adminive/backend/service/third_party/include" + "${CMAKE_CURRENT_LIST_DIR}/third_party/Structive/core/include" +) target_include_directories(ecap_server PRIVATE ${ZLIB_INCLUDE_DIR}) target_link_libraries(ecap_server PUBLIC SSR) target_link_libraries(ecap_server PUBLIC ${Core}) target_link_libraries(ecap_server PRIVATE Drogon::Drogon) target_link_libraries(ecap_server PRIVATE SQLiteCpp) target_link_libraries(ecap_server PRIVATE ${ZLIB_LIBRARY}) +if (MSVC) + target_compile_options(ecap_server PRIVATE /bigobj /utf-8) +endif () message("LibArchive_SUB_LIBRARIES {${LibArchive_SUB_LIBRARIES}}") target_link_libraries(ecap_server PRIVATE LibArchive::LibArchive @@ -43,7 +58,6 @@ target_link_libraries(ecap_server PRIVATE target_compile_definitions(ecap_server PRIVATE LIBARCHIVE_STATIC) target_compile_definitions(ecap_server PRIVATE NOMINMAX) target_compile_definitions(ecap_server PRIVATE INCL_EXTRA_HTON_FUNCTIONS) # drogon用 -_attach_post_build_file(ecap_server ${cfg_dir}/config.json) if (MSVC) target_compile_definitions(ecap_server PRIVATE _CRTDBG_MAP_ALLOC diff --git a/module/Local_Server/DSP/DSP_Config.cpp b/module/Local_Server/DSP/DSP_Config.cpp index 77fa546..4204bec 100644 --- a/module/Local_Server/DSP/DSP_Config.cpp +++ b/module/Local_Server/DSP/DSP_Config.cpp @@ -51,7 +51,7 @@ void DSP_Config::server(Global* g) { psc_set_less_30Mhz_fft_param(fft_win_type, fft_point_number, fft_period_ms, 1); #endif set_fft_point_number(this->fft_point_number); - g->save(); + g->save(Config_Section::dsp); res->setBody(warp(to_base_json()).to_json_string()); }); svr.Post(api + "get_dsp_config", [this](HTTP_Param) { diff --git a/module/Local_Server/Data_Feed/Data_Feed.cpp b/module/Local_Server/Data_Feed/Data_Feed.cpp index ce9cb0f..219bb6b 100644 --- a/module/Local_Server/Data_Feed/Data_Feed.cpp +++ b/module/Local_Server/Data_Feed/Data_Feed.cpp @@ -19,7 +19,8 @@ asio::awaitable Data_Feed_UDP_Server::send_coro(std::string_view data) { co_return; } asio::awaitable Data_Feed_UDP_Server::_open() { - svr.set_bind_address("0.0.0.0", port); + const auto value = specific.snapshot(); + svr.set_bind_address("0.0.0.0", value.port); svr.create(); server_logger->c_debug({}, {}, to_string() + "开启!"); co_return; @@ -35,100 +36,31 @@ JSON Data_feed_Config::get_feed(std::string_view key) { void Data_feed_Config::server(Global* g) { auto& svr = g->svr; auto& api = g->api; - std::string name = "data_feed"; svr.Post(api + "get_data_feed_state", [this](HTTP_Param) { CHECK_JSON_PARAM HTTP_REQUIRE_VALUE(key, params.try_get_string("key")) - auto t = map.get(key); + auto value = map.get(key); JSON ret{nullptr}; - if (t.has_value()) { - ret = t.value()->get_state_json(); - } + if (value.has_value()) + ret = value.value()->get_state_json(); res->setBody(warp(ret).to_json_string()); }); - svr.Post(api + "update_data_feed_config", [this](HTTP_Param) { - CHECK_JSON_PARAM - Get_J(packet_byte_size) Get_J(empty_wait_milliseconds) res->setBody(warp(to_json()).to_json_string()); - Global::save(); - }); - svr.Post(api + "get_data_feed_config", [this](HTTP_Param) { res->setBody(warp(to_json()).to_json_string()); }); svr.Post(api + "get_data_feed_server_connect", [this](HTTP_Param) { CHECK_JSON_PARAM HTTP_REQUIRE_VALUE(key, params.try_get_string("key")) - auto t = map.get(key); + auto value = map.get(key); JSON ret{nullptr}; - if (t.has_value()) { - auto dfs = dynamic_cast(t.value().get()); - if (dfs) { - ret = dfs->get_clients_json(); - } - auto dfs2 = dynamic_cast(t.value().get()); - if (dfs2) { - ret = dfs2->get_clients_json(); - } + if (value.has_value()) { + if (auto tcp = dynamic_cast(value.value().get())) + ret = tcp->get_clients_json(); + if (auto udp = dynamic_cast(value.value().get())) + ret = udp->get_clients_json(); } res->setBody(warp(ret).to_json_string()); }); - svr.Post(api + svr.search + name, [this](HTTP_Param) { - CHECK_JSON_PARAM - HTTP_REQUIRE_VALUE(type, params.try_get_string("key")) - res->setBody(warp(get_feed(type)).to_json_string()); - }); - svr.Post(api + svr.update + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - std::cout << "Data_Feed:" << req->path() << that_json->to_json_string(); - HTTP_REQUIRE_VALUE(key, params.try_get_string("key")) - HTTP_REQUIRE_VALUE(df, map.get(key)) - // 直接关闭 - // t->close(); - df->from_json(¶ms, true); - Global::save(); - g->mode_acs.source_feed_relation_config.set_need_refresh(); - }); - svr.Post(api + svr.insert + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - std::cout << "Data_Feed:" << req->path() << that_json->to_json_string(); - HTTP_REQUIRE_VALUE(index, params.try_get_number("index")) - HTTP_REQUIRE_PTR(data, params.get("data")) - HTTP_REQUIRE_VALUE(t, data->try_get_string("type")) - std::shared_ptr df = create_data_feed_from_type(t); - df->from_json(data, true); - HTTP_REQUIRE_VALUE(enable, data->try_get_bool("enable")) - HTTP_REQUIRE_TRUE(map.insert(index, df), "index") - res->setBody(warp(to_json()).to_json_string()); - Global::save(); - g->mode_acs.source_feed_relation_config.set_need_refresh(); - }); - svr.Post(api + svr.remove + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - std::cout << "Data_Feed:" << req->path() << that_json->to_json_string(); - HTTP_REQUIRE_VALUE(index, params.try_get_number("index")) - HTTP_REQUIRE_VALUE(df, map.try_get(index)) - df->async_stop(); - HTTP_REQUIRE_TRUE(map.remove(index), "index") - g->save(); - res->setBody(warp(to_json()).to_json_string()); - g->mode_acs.source_feed_relation_config.set_need_refresh(); - }); - svr.Post(api + svr.rise + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - HTTP_REQUIRE_VALUE(index, params.try_get_number("index")) - HTTP_REQUIRE_TRUE(map.swap(index, index - 1), "index") - g->save(); - res->setBody(warp(to_json()).to_json_string()); - g->mode_acs.source_feed_relation_config.set_need_refresh(); - }); - svr.Post(api + svr.fall + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - HTTP_REQUIRE_VALUE(index, params.try_get_number("index")) - HTTP_REQUIRE_TRUE(map.swap(index, index + 1), "index") - g->save(); - res->setBody(warp(to_json()).to_json_string()); - g->mode_acs.source_feed_relation_config.set_need_refresh(); - }); } void BIN_Msg_Buffer::push(std::string_view msg) { - auto output_packet_size = Global::instance()->mode_acs.data_feed_config.packet_byte_size.load(); + auto output_packet_size = Global::instance()->mode_acs.data_feed_config.settings.member<&Data_feed_Config_Data::packet_byte_size>().snapshot(); std::lock_guard g(mtx); auto& pool = Global::instance()->mode_acs.data_feed_config.pool_; if (msg_list_cache.empty() || msg_list_cache.back()->size() > output_packet_size) { @@ -153,7 +85,7 @@ BIN_Msg_Buffer::BIN_Msg_Buffer() { msg_list.reserve(8 * 1024); } Psc::JSON BIN_Msg_Buffer::state_json() { - auto output_packet_size = Global::instance()->mode_acs.data_feed_config.packet_byte_size.load(); + auto output_packet_size = Global::instance()->mode_acs.data_feed_config.settings.member<&Data_feed_Config_Data::packet_byte_size>().snapshot(); size_t size, cache_size, pool_capacity, pool_free_count; { std::lock_guard g(mtx); diff --git a/module/Local_Server/Data_Feed/Data_Feed.h b/module/Local_Server/Data_Feed/Data_Feed.h index be644ff..f5dd10c 100644 --- a/module/Local_Server/Data_Feed/Data_Feed.h +++ b/module/Local_Server/Data_Feed/Data_Feed.h @@ -7,16 +7,30 @@ enum class Output_Data_Format { AVR, AVR_MLAT, BIN, SBS, BIN_ID }; enum class Mode_S_Output_Type { DF_11_17_18, NO_POS_Mode_S, ALL_Mode_S }; struct Output_Format { Output_Data_Format type = Output_Data_Format::BIN; - Psc::Copyable_Atomic use_status = false; + bool use_status = false; Mode_S_Output_Type mode_s_output_type = Mode_S_Output_Type::DF_11_17_18; - Psc::Copyable_Atomic use_mode_ac = false; - Psc::Copyable_Atomic sbs_only_pos = false; + bool use_mode_ac = false; + bool sbs_only_pos = false; PSC_USE_JSON }; +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Output_Format; + return object("output_format", "输出格式", + ADMINIVE_FIELD_LABEL(T, type, "数据格式").creatable().editable().select_input(), + ADMINIVE_FIELD_LABEL(T, use_status, "输出状态消息").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, mode_s_output_type, "Mode S 输出类型").creatable().editable().select_input(), + ADMINIVE_FIELD_LABEL(T, use_mode_ac, "输出 Mode AC").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, sbs_only_pos, "SBS 仅位置").creatable().editable().boolean_input().visible_on("${$self.type == 'SBS'}")); + } +}; +} class Data_Feed : public With_Loop_Coro { public: BIN_Msg_Buffer msg_buffer{}; - Output_Format output_format; + adminive::Managed_Value output_format; Frequency_Limit_Multi sbs_flm{}; asio::awaitable loop_coro() final; virtual void handle_in_loop() { @@ -30,12 +44,16 @@ public: } virtual Psc::JSON to_json() { Psc::JSON ret = With_Loop_Coro::to_base_json(); - ret.append(Psc::JSON("output_format", output_format.to_base_json())); + ret.append(Psc::JSON("output_format", output_format.snapshot().to_base_json())); return ret; } virtual void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) { With_Loop_Coro::from_base_json(that_json, not_exist_use_default_value); - output_format.from_base_json(that_json->get("output_format"), not_exist_use_default_value); + auto value = output_format.snapshot(); + value.from_base_json(that_json->get("output_format"), not_exist_use_default_value); + output_format.write([&](auto& target) { + target = value; + }); } virtual Psc::JSON get_custom_state_json() { return Psc::JSON::object(); @@ -46,7 +64,7 @@ public: return ret; } std::string to_string() { - return "Data_Feed{" + VAR_STR_3(key, enable, type) + "}"; + return "Data_Feed{" + key + "," + (enabled() ? "true" : "false") + "," + type + "}"; } bool registered(); protected: @@ -60,16 +78,30 @@ public: size_t connect_system_buffer_size = 409600; PSC_USE_JSON }; -class Data_Feed_TCP_Server : public Data_Feed, public Data_Feed_TCP_Server_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Data_Feed_TCP_Server_Data; + return object("data_feed_tcp_server", "TCP 服务端配置", + ADMINIVE_FIELD_LABEL(T, port, "端口").creatable().editable().number_input(), + ADMINIVE_FIELD_LABEL(T, connect_user_buffer_size, "用户缓冲区").creatable().editable().number_input(), + ADMINIVE_FIELD_LABEL(T, connect_system_buffer_size, "系统缓冲区").creatable().editable().number_input()); + } +}; +} +class Data_Feed_TCP_Server : public Data_Feed { public: + adminive::Managed_Value specific; Psc::asio_socket::TCP_Server_Coro svr; Psc::JSON get_custom_state_json() override { auto& state = svr.state; - Psc::JSON ret = Psc::JSON::object(); - Ret_J(connect_system_buffer_size) Ret_J(connect_user_buffer_size) ret.key = "fixed"; - auto t = VAR_JSON_1(state); - t.append(ret); - return t; + auto value = specific.snapshot(); + auto ret = value.to_base_json(); + ret.key = "fixed"; + auto result = VAR_JSON_1(state); + result.append(ret); + return result; } ~Data_Feed_TCP_Server() override = default; asio::awaitable handle_in_loop_coro() override { @@ -83,23 +115,26 @@ public: } void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) override { Data_Feed::from_json(that_json, not_exist_use_default_value); - Data_Feed_TCP_Server_Data::from_base_json(that_json, not_exist_use_default_value); + auto value = specific.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + specific.write([&](auto& target) { + target = value; + }); } Psc::JSON to_json() override { - Psc::JSON ret = Data_Feed::to_json(); - ret += Data_Feed_TCP_Server_Data::to_base_json(); - return ret; + return Data_Feed::to_json() += specific.snapshot().to_base_json(); } asio::awaitable _close() override { co_await svr.close_coro(); co_return; } asio::awaitable _open() override { - svr.set_connect_user_buffer_size(connect_user_buffer_size); - svr.set_connect_system_buffer_size(connect_system_buffer_size); + const auto value = specific.snapshot(); + svr.set_connect_user_buffer_size(value.connect_user_buffer_size); + svr.set_connect_system_buffer_size(value.connect_system_buffer_size); svr.set_tcp_no_delay(false); svr.create(); - co_await svr.listen_coro("0.0.0.0", port); + co_await svr.listen_coro("0.0.0.0", value.port); co_return; } Psc::JSON get_clients_json() { @@ -138,8 +173,20 @@ public: std::uint16_t port{}; PSC_USE_JSON }; -class Data_Feed_TCP_Client : public Data_Feed, public Data_Feed_TCP_Client_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Data_Feed_TCP_Client_Data; + return object("data_feed_tcp_client", "TCP 客户端配置", + ADMINIVE_FIELD_LABEL(T, url, "IP 地址").creatable().editable().text_input(), + ADMINIVE_FIELD_LABEL(T, port, "端口").creatable().editable().number_input()); + } +}; +} +class Data_Feed_TCP_Client : public Data_Feed { public: + adminive::Managed_Value specific; asio::awaitable handle_in_loop_coro() override { co_await cli.tick_coro(); co_return; @@ -148,12 +195,10 @@ public: co_await cli.send_coro(data); co_return; } - std::string url; Psc::JSON get_custom_state_json() override { auto& state = cli.state; return VAR_JSON_1(state); } - std::uint16_t port{}; Psc::asio_socket::TCP_Client_Coro cli; ~Data_Feed_TCP_Client() override = default; asio::awaitable _close() override { @@ -161,9 +206,10 @@ public: co_return; } asio::awaitable _open() override { + const auto value = specific.snapshot(); Psc::asio_socket::Sockaddr_In sockaddr_in; - sockaddr_in.ip = url; - sockaddr_in.port = port; + sockaddr_in.ip = value.url; + sockaddr_in.port = value.port; cli.set_dest_address(sockaddr_in); cli.create(); co_await cli.connect_coro(); @@ -171,10 +217,14 @@ public: } void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) override { Data_Feed::from_json(that_json, not_exist_use_default_value); - Data_Feed_TCP_Client_Data::from_base_json(that_json, not_exist_use_default_value); + auto value = specific.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + specific.write([&](auto& target) { + target = value; + }); } Psc::JSON to_json() override { - return Data_Feed::to_json() += Data_Feed_TCP_Client_Data::to_base_json(); + return Data_Feed::to_json() += specific.snapshot().to_base_json(); } }; class Data_Feed_UDP_Server_Data { @@ -182,8 +232,19 @@ public: std::uint16_t port{}; PSC_USE_JSON }; -class Data_Feed_UDP_Server : public Data_Feed, public Data_Feed_UDP_Server_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Data_Feed_UDP_Server_Data; + return object("data_feed_udp_server", "UDP 服务端配置", + ADMINIVE_FIELD_LABEL(T, port, "端口").creatable().editable().number_input()); + } +}; +} +class Data_Feed_UDP_Server : public Data_Feed { public: + adminive::Managed_Value specific; Psc::JSON get_custom_state_json() override { auto& state = svr.state; return VAR_JSON_1(state); @@ -204,10 +265,14 @@ public: } void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) override { Data_Feed::from_json(that_json, not_exist_use_default_value); - Data_Feed_UDP_Server_Data::from_base_json(that_json, not_exist_use_default_value); + auto value = specific.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + specific.write([&](auto& target) { + target = value; + }); } Psc::JSON to_json() override { - return Data_Feed::to_json() += Data_Feed_UDP_Server_Data::to_base_json(); + return Data_Feed::to_json() += specific.snapshot().to_base_json(); } asio::awaitable _close() override { co_await svr.close_coro(); @@ -222,12 +287,20 @@ public: std::string url{}; PSC_USE_JSON }; -class Data_Feed_UDP_Client : public Data_Feed, public Data_Feed_UDP_Client_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Data_Feed_UDP_Client_Data; + return object("data_feed_udp_client", "UDP 客户端配置", + ADMINIVE_FIELD_LABEL(T, url, "IP 地址").creatable().editable().text_input(), + ADMINIVE_FIELD_LABEL(T, port, "端口").creatable().editable().number_input()); + } +}; +} +class Data_Feed_UDP_Client : public Data_Feed { public: - Data_Feed_UDP_Client() { - } - ~Data_Feed_UDP_Client() override { - } + adminive::Managed_Value specific; Psc::JSON get_custom_state_json() override { auto& state = cli.state; return VAR_JSON_1(state); @@ -243,10 +316,14 @@ public: Psc::asio_socket::UDP_Client_Coro cli; void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) override { Data_Feed::from_json(that_json, not_exist_use_default_value); - Data_Feed_UDP_Client_Data::from_base_json(that_json, not_exist_use_default_value); + auto value = specific.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + specific.write([&](auto& target) { + target = value; + }); } Psc::JSON to_json() override { - return Data_Feed::to_json() += Data_Feed_UDP_Client_Data::to_base_json(); + return Data_Feed::to_json() += specific.snapshot().to_base_json(); } asio::awaitable _close() override { std::cout << "udp client target address:" << cli.dest_address.to_string() << " closed" << std::endl; @@ -254,8 +331,9 @@ public: co_return; } asio::awaitable _open() override { + const auto value = specific.snapshot(); cli.create(); - cli.set_dest_address(url, port); + cli.set_dest_address(value.url, value.port); co_await cli.connect_coro(); co_return; } @@ -276,18 +354,33 @@ inline std::shared_ptr create_data_feed_from_type(std::string_view t) std::cout << oss.str(); throw std::invalid_argument(oss.str()); } + ret->type = std::string(t); return ret; } class Data_feed_Config_Data { public: - Psc::Copyable_Atomic empty_wait_milliseconds{}; - Psc::Copyable_Atomic packet_byte_size{}; + std::uint16_t empty_wait_milliseconds{}; + std::uint16_t packet_byte_size{}; size_t tcp_server_default_connect_user_buffer_size{}; size_t tcp_server_default_connect_system_buffer_size{}; PSC_USE_JSON }; -class Data_feed_Config : public Data_feed_Config_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Data_feed_Config_Data; + return object("data_feed_settings", "数据馈送全局配置", + ADMINIVE_FIELD_LABEL(T, empty_wait_milliseconds, "忙等待时间(毫秒)").editable().number_input(), + ADMINIVE_FIELD_LABEL(T, packet_byte_size, "最小批大小(byte)").editable().number_input(), + ADMINIVE_FIELD_LABEL(T, tcp_server_default_connect_user_buffer_size, "TCP 默认用户缓冲区").editable().number_input(), + ADMINIVE_FIELD_LABEL(T, tcp_server_default_connect_system_buffer_size, "TCP 默认系统缓冲区").editable().number_input()); + } +}; +} +class Data_feed_Config { public: + adminive::Managed_Value settings; Psc::String_Pool pool_ = Psc::String_Pool(1600, 8 * 1024); // 8192 * 1600 12.5 MB Ordered_Map> map; Data_feed_Config() = default; @@ -299,17 +392,21 @@ public: t->from_json(&it, not_exist_use_default_value); map.push_back(t); } - Data_feed_Config_Data::from_base_json(that_json, not_exist_use_default_value); + auto value = settings.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + settings.write([&](auto& target) { + target = value; + }); } - Psc::JSON list() { + Psc::JSON list() const { auto connect_json = Psc::JSON::array(); for (auto& it : map.list()) { connect_json.children.emplace_back(it->to_json()); } return connect_json; } - Psc::JSON to_json() { - Psc::JSON ret = Data_feed_Config_Data::to_base_json(); + Psc::JSON to_json() const { + Psc::JSON ret = settings.snapshot().to_base_json(); ret.append({"list", list()}); return ret; } diff --git a/module/Local_Server/Data_Source/Data_Source.cpp b/module/Local_Server/Data_Source/Data_Source.cpp index 4a50f2e..385c5cd 100644 --- a/module/Local_Server/Data_Source/Data_Source.cpp +++ b/module/Local_Server/Data_Source/Data_Source.cpp @@ -53,11 +53,13 @@ Data_Source::Data_Source() { // parse_format = std::make_shared(); // 写入到配置文件是懒加载 其他保存时他跟着保存 base_station.handle_when_updated = [this](SSR::HULC_Status_Message msg) { - if (this->update_form_gps) { - lat = msg.get_latitude(); - lon = msg.get_longitude(); - alt = msg.Alt; - } + if (!settings.member<&Data_Source_Data::update_form_gps>().snapshot()) + return; + settings.write([&](auto& value) { + value.lat = msg.get_latitude(); + value.lon = msg.get_longitude(); + value.alt = msg.Alt; + }); }; } std::string Data_Source::thread_key() const { @@ -177,14 +179,15 @@ std::optional File_Data_Source::get_raw_line(int& ret_index) { } std::optional log_info = std::nullopt; if (index == part_infos.size()) { - if (play_mode == Play_Mode::loop) { + const auto config = specific.snapshot(); + if (config.play_mode == Play_Mode::loop) { index = 0; } else { if (!have_report_play_back_all_success) { std::ostringstream oss; oss << SSR::get_current_date() << " " << SSR::get_current_time() - << " [进程:" << std::this_thread::get_id() << "] " << file_path + " 全部加载成功!\n" + << " [进程:" << std::this_thread::get_id() << "] " << config.file_path + " 全部加载成功!\n" << std::flush; std::cout << oss.str() << std::flush; have_report_play_back_all_success = true; @@ -305,13 +308,14 @@ asio::awaitable File_Data_Source::_close() { } asio::awaitable File_Data_Source::_open() { std::lock_guard g(mtx); - auto path = get_true_file_path(); + const auto config = specific.snapshot(); + auto path = Psc::get_abs_path(config.file_path); namespace fs = std::filesystem; if (!fs::exists(path)) { state = "文件不存在"; co_return; } - if (data_type == File_Data_Type::BIN) { + if (config.data_type == File_Data_Type::BIN) { part_infos = readBinaryFileAsString(path, 1000); } else { @@ -356,9 +360,10 @@ void File_Data_Source::origin_data_transform_mode_data(std::string& mode_data) { std::lock_guard g(mtx); assert(mode_data.size() == 0); int ret_index = 0; - if (play_mode != Play_Mode::analysis) { + const auto config = specific.snapshot(); + if (config.play_mode != Play_Mode::analysis) { std::optional log_info = get_raw_line(ret_index); - mode_data = log_info.has_value() ? get_true_from_raw_line(this, data_type, ret_index, log_info.value()) : ""; + mode_data = log_info.has_value() ? get_true_from_raw_line(this, config.data_type, ret_index, log_info.value()) : ""; } else { int num = 0; @@ -367,7 +372,7 @@ void File_Data_Source::origin_data_transform_mode_data(std::string& mode_data) { while (log_info.has_value()) { num++; auto data = log_info.value(); - auto cur = get_true_from_raw_line(this, data_type, ret_index, data); + auto cur = get_true_from_raw_line(this, config.data_type, ret_index, data); ret += cur; // 一定要在这个位置,否则会导致遗漏报文 if (num == 1000) { @@ -414,8 +419,9 @@ void Dll_Data_Source::origin_data_transform_mode_data(std::string& mode_data) { auto start = std::chrono::steady_clock::now(); if (read_func_ptr == nullptr) return; + const auto config = specific.snapshot(); auto buf = reinterpret_cast(buffer.data()); - auto len = read_func_ptr(buf, buffer_size); + auto len = read_func_ptr(buf, config.buffer_size); vs.update(len); std::string data(buf, len); if (data.empty()) { @@ -424,15 +430,16 @@ void Dll_Data_Source::origin_data_transform_mode_data(std::string& mode_data) { // std::cout << "Dll_Data_Source::read cost: " << cost << " us\n"; return; } - auto ret = get_true_from_raw_line(this, data_type, -1, data); + auto ret = get_true_from_raw_line(this, config.data_type, -1, data); auto end = std::chrono::steady_clock::now(); auto cost = std::chrono::duration_cast(end - start).count(); // std::cout << "Dll_Data_Source::read cost: " << cost << " us\n"; mode_data = ret; } asio::awaitable Shared_Memory_Data_Source::_open() { + const auto config = specific.snapshot(); sm = std::make_unique(); - sm->init(shared_memory_name, shared_memory_size); + sm->init(config.shared_memory_name, config.shared_memory_size); co_return; } asio::awaitable Shared_Memory_Data_Source::_close() { @@ -454,87 +461,29 @@ void Shared_Memory_Data_Source::origin_data_transform_mode_data(std::string& mod if (Global::instance()->console_config.mode_s_console) { std::cout << "read:" << data << std::endl; } - auto ret = get_true_from_raw_line(this, data_type, -1, data); + const auto config = specific.snapshot(); + auto ret = get_true_from_raw_line(this, config.data_type, -1, data); mode_data = ret; } void Data_Source_Config::server(Global* g) { auto& svr = g->svr; auto& api = g->api; - std::string name = "data_source"; - // 返回所有 JSON 数据的 API svr.Post(api + "get_data_source_connect", [this](HTTP_Param) { CHECK_JSON_PARAM HTTP_REQUIRE_VALUE(key, params.try_get_string("key")) - auto t = map.get(key); + auto value = map.get(key); JSON ret{nullptr}; - if (t.has_value()) { - ret = t.value()->get_all_connect_feed(); - } + if (value.has_value()) + ret = value.value()->get_all_connect_feed(); res->setBody(warp(ret).to_json_string()); }); svr.Post(api + "get_data_source_state", [this](HTTP_Param) { CHECK_JSON_PARAM HTTP_REQUIRE_VALUE(key, params.try_get_string("key")) - auto t = map.get(key); + auto value = map.get(key); JSON ret{nullptr}; - if (t.has_value()) { - ret = t.value()->get_state(); - } + if (value.has_value()) + ret = value.value()->get_state(); res->setBody(warp(ret).to_json_string()); }); - svr.Post(api + "get_mode_s_data_source_config", - [this](HTTP_Param) { res->setBody(warp(to_json()).to_json_string()); }); - svr.Post(api + svr.update + name, [this, g](HTTP_Param) { - CHECK_JSON_PARAM - std::cout << "Data_Source:" << req->path() << that_json->to_json_string(); - HTTP_REQUIRE_VALUE(key, params.try_get_string("key")) - HTTP_REQUIRE_VALUE(ds, map.get(key)) - ds->from_json(¶ms, true); - g->save(); - g->mode_acs.source_feed_relation_config.set_need_refresh(); - res->setBody(warp(ds->to_json()).to_json_string()); - }); - svr.Post(api + svr.insert + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - std::cout << "Data_Source:" << req->path() << that_json->to_json_string(); - HTTP_REQUIRE_VALUE(index, params.try_get_number("index")) - HTTP_REQUIRE_PTR(data, params.get("data")) - HTTP_REQUIRE_VALUE(t, data->try_get_string("type")) - std::cout << params.to_json_string() << std::endl; - std::shared_ptr ds = create_data_source_from_type(t); - ds->from_json(data, true); - HTTP_REQUIRE_VALUE(enable, data->try_get_bool("enable")) - (void)enable; - HTTP_REQUIRE_TRUE(map.insert(index, ds), "index"); - g->mode_acs.source_feed_relation_config.set_need_refresh(); - Config::save(); - res->setBody(warp(to_json()).to_json_string()); - }); - svr.Post(api + svr.remove + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - std::cout << "Data_Source:" << req->path() << that_json->to_json_string(); - HTTP_REQUIRE_VALUE(index, params.try_get_number("index")) - HTTP_REQUIRE_VALUE(ds, map.try_get(index)) - ds->async_stop(); - HTTP_REQUIRE_TRUE(map.remove(index), "index") - g->save(); - res->setBody(warp(to_json()).to_json_string()); - g->mode_acs.source_feed_relation_config.set_need_refresh(); - }); - svr.Post(api + svr.rise + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - HTTP_REQUIRE_VALUE(index, params.try_get_number("index")) - HTTP_REQUIRE_TRUE(map.swap(index, index - 1), "index") - g->save(); - res->setBody(warp(to_json()).to_json_string()); - g->mode_acs.source_feed_relation_config.set_need_refresh(); - }); - svr.Post(api + svr.fall + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - HTTP_REQUIRE_VALUE(index, params.try_get_number("index")) - HTTP_REQUIRE_TRUE(map.swap(index, index + 1), "index") - g->save(); - res->setBody(warp(to_json()).to_json_string()); - g->mode_acs.source_feed_relation_config.set_need_refresh(); - }); } diff --git a/module/Local_Server/Data_Source/Data_Source.h b/module/Local_Server/Data_Source/Data_Source.h index 0789707..f6d7a55 100644 --- a/module/Local_Server/Data_Source/Data_Source.h +++ b/module/Local_Server/Data_Source/Data_Source.h @@ -56,9 +56,9 @@ protected: }; class Data_Source_Map_Display_Data { public: - Psc::Copyable_Atomic base_station_show = true; - Psc::Copyable_Atomic aircraft_show = true; - Psc::Copyable_Atomic constant_screen_size = true; + bool base_station_show = true; + bool aircraft_show = true; + bool constant_screen_size = true; std::string color = "#1677ff"; std::string track_point_color = "#ffff00"; std::string base_station_color = "#1677ff"; @@ -67,9 +67,9 @@ public: std::uint32_t track_point_pixel_size = 8; double aircraft_scale = 1.0; double base_station_scale = 1.0; - Psc::Copyable_Atomic show_icao = true; - Psc::Copyable_Atomic show_call_sign = false; - Psc::Copyable_Atomic show_fly_status = false; + bool show_icao = true; + bool show_call_sign = false; + bool show_fly_status = false; PSC_USE_JSON }; class Data_Source_Map_Display_Config { @@ -83,23 +83,22 @@ public: return ret; } void from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) { - if (that_json == nullptr || that_json->valueType != Psc::Object) { + if (that_json == nullptr || that_json->valueType != Psc::Object) throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "map_display"); - } map2d.from_base_json(that_json->get("map2d"), not_exist_use_default_value); map3d.from_base_json(that_json->get("map3d"), not_exist_use_default_value); } }; class Data_Source_Data { public: - Psc::Copyable_Atomic base_station_has_valid_position{}; + bool base_station_has_valid_position{}; Data_Source_Map_Display_Config map_display; double lat{}; double lon{}; double alt{}; - Psc::Copyable_Atomic ignore_msg_time = false; // 忽略消息时间戳 如果启用 解码位置将不在判断消息自带时间戳 - Psc::Copyable_Atomic update_form_gps{}; - Psc::Copyable_Atomic keep_mode = true; + bool ignore_msg_time = false; + bool update_form_gps{}; + bool keep_mode = true; [[nodiscard]] Psc::JSON to_base_json() const { auto ret = Psc::JSON::object(); Ret_J(base_station_has_valid_position) ret.append({"map_display", map_display.to_base_json()}); @@ -111,10 +110,56 @@ public: Get_J(lat) Get_J(lon) Get_J(alt) Get_J(ignore_msg_time) Get_J(update_form_gps) Get_J(keep_mode) } }; -class Data_Source : public std::enable_shared_from_this, - public Data_Source_Handler, - public Data_Source_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Data_Source_Map_Display_Data; + return object("data_source_map_display", "地图显示", + ADMINIVE_FIELD_LABEL(T, base_station_show, "显示基站").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, aircraft_show, "显示飞机").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, constant_screen_size, "固定屏幕尺寸").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, color, "颜色").creatable().editable().color_input(), + ADMINIVE_FIELD_LABEL(T, track_point_color, "航迹点颜色").creatable().editable().color_input(), + ADMINIVE_FIELD_LABEL(T, base_station_color, "基站颜色").creatable().editable().color_input(), + ADMINIVE_FIELD_LABEL(T, aircraft_pixel_size, "飞机像素尺寸").creatable().editable().number_input(), + ADMINIVE_FIELD_LABEL(T, base_station_pixel_size, "基站像素尺寸").creatable().editable().number_input(), + ADMINIVE_FIELD_LABEL(T, track_point_pixel_size, "航迹点像素尺寸").creatable().editable().number_input(), + ADMINIVE_FIELD_LABEL(T, aircraft_scale, "飞机缩放").creatable().editable().number_input(), + ADMINIVE_FIELD_LABEL(T, base_station_scale, "基站缩放").creatable().editable().number_input(), + ADMINIVE_FIELD_LABEL(T, show_icao, "显示 ICAO").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, show_call_sign, "显示呼号").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, show_fly_status, "显示飞行状态").creatable().editable().boolean_input()); + } +}; +template <> +struct Type_Descriptor { + static auto get() { + using T = Data_Source_Map_Display_Config; + return object("data_source_map_display_config", "地图显示配置", + ADMINIVE_FIELD_LABEL(T, map2d, "2D 地图").creatable().editable(), + ADMINIVE_FIELD_LABEL(T, map3d, "3D 地图").creatable().editable()); + } +}; +template <> +struct Type_Descriptor { + static auto get() { + using T = Data_Source_Data; + return object("data_source_config", "数据源公共配置", + ADMINIVE_FIELD_LABEL(T, base_station_has_valid_position, "基站位置有效").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, map_display, "地图显示").creatable().editable(), + ADMINIVE_FIELD_LABEL(T, lat, "纬度").creatable().editable().number_input(), + ADMINIVE_FIELD_LABEL(T, lon, "经度").creatable().editable().number_input(), + ADMINIVE_FIELD_LABEL(T, alt, "高度").creatable().editable().number_input(), + ADMINIVE_FIELD_LABEL(T, ignore_msg_time, "忽略消息时间戳").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, update_form_gps, "从 GPS 更新位置").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, keep_mode, "保留模式").creatable().editable().boolean_input()); + } +}; +} +class Data_Source : public std::enable_shared_from_this, public Data_Source_Handler { public: + adminive::Managed_Value settings; std::shared_ptr that(); virtual asio::awaitable read_coro() { co_return ""; @@ -122,7 +167,7 @@ public: bool registered() const; virtual Psc::JSON get_custom_state_json() = 0; Psc::JSON get_state(); - std::string last_char; // 用于处理奇数字节 + std::string last_char; Data_Source(); asio::awaitable loop_coro() final; std::shared_ptr last_prase_msg = nullptr; @@ -141,11 +186,15 @@ public: return ret; } virtual Psc::JSON to_json() { - return With_Loop_Coro::to_base_json() += Data_Source_Data::to_base_json(); + return With_Loop_Coro::to_base_json() += settings.snapshot().to_base_json(); } virtual void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) { With_Loop_Coro::from_base_json(that_json, not_exist_use_default_value); - Data_Source_Data::from_base_json(that_json, not_exist_use_default_value); + auto value = settings.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + settings.write([&](auto& target) { + target = value; + }); } std::string buffer; std::atomic_bool ask_sleep = false; @@ -156,8 +205,20 @@ public: std::uint16_t port{}; PSC_USE_JSON }; -class TCP_Client_Data_Source : public Data_Source, public TCP_Client_Data_Source_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = TCP_Client_Data_Source_Data; + return object("tcp_client_data_source", "TCP 客户端配置", + ADMINIVE_FIELD_LABEL(T, ip, "IP 地址").creatable().editable().text_input(), + ADMINIVE_FIELD_LABEL(T, port, "端口").creatable().editable().number_input()); + } +}; +} +class TCP_Client_Data_Source : public Data_Source { public: + adminive::Managed_Value specific; asio::awaitable handle_in_loop_coro() override { co_await cli.tick_coro(); co_return; @@ -172,9 +233,10 @@ public: type = "TCP_Client_Data_Source"; } asio::awaitable _open() override { + const auto value = specific.snapshot(); Psc::asio_socket::Sockaddr_In addr; - addr.ip = ip; - addr.port = port; + addr.ip = value.ip; + addr.port = value.port; cli.set_dest_address(addr); cli.create(); co_await cli.connect_coro(); @@ -185,11 +247,15 @@ public: co_return; } Psc::JSON to_json() override { - return Data_Source::to_json() += TCP_Client_Data_Source_Data::to_base_json(); + return Data_Source::to_json() += specific.snapshot().to_base_json(); } void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) override { Data_Source::from_json(that_json, not_exist_use_default_value); - TCP_Client_Data_Source_Data::from_base_json(that_json, not_exist_use_default_value); + auto value = specific.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + specific.write([&](auto& target) { + target = value; + }); } asio::awaitable read_coro() override { co_return co_await cli.read_coro(); @@ -201,31 +267,38 @@ public: Baud_Rate_Type baud_rate{}; PSC_USE_JSON }; -class Serial_Data_Source : public Data_Source, public Serial_Data_Source_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Serial_Data_Source_Data; + return object("serial_data_source", "串口数据源配置", + ADMINIVE_FIELD_LABEL(T, port_name, "串口名称").creatable().editable().text_input(), + ADMINIVE_FIELD_LABEL(T, baud_rate, "波特率").creatable().editable().select_input()); + } +}; +} +class Serial_Data_Source : public Data_Source { public: + adminive::Managed_Value specific; Psc::JSON get_custom_state_json() override { return Psc::JSON::object(); } Serial_Data_Source() { - this->type = "Serial_Data_Source"; + type = "Serial_Data_Source"; } asio::awaitable _open() override { + const auto value = specific.snapshot(); serial = std::make_unique(); - serial->set_serial_name(port_name); - serial->set_baud_rate(baud_rate); + serial->set_serial_name(value.port_name); + serial->set_baud_rate(value.baud_rate); serial->set_parity(Psc::serial::Parity::NoParity); serial->set_data_bits(Psc::serial::DataBits::Data8); serial->set_stop_bits(Psc::serial::StopBits::OneStop); serial->set_flow_control(Psc::serial::FlowControl::HardwareControl); serial->set_buffer_byte_size(10 * 1024); - bool ok = serial->open(); - if (!ok) { - std::cerr << "createSerial " + port_name + ":" + std::to_string(baud_rate) + " 打开串口失败!\n"; - } - else { - // std::cerr << "createSerial " + serial_name + ":" + - // std::to_string(baud_rate) + " 打开串口成功!\n"; - } + if (!serial->open()) + std::cerr << "createSerial " + value.port_name + ":" + std::to_string(value.baud_rate) + " 打开串口失败!\n"; co_return; } asio::awaitable _close() override { @@ -234,29 +307,30 @@ public: co_return; } asio::awaitable handle_in_loop_coro() override { - if (serial) { + if (serial) co_await serial->tick_coro(); - } co_return; } asio::awaitable read_coro() override { - if (!serial) { + if (!serial) co_return ""; - } co_return co_await serial->read_coro(); } Psc::JSON to_json() override { - return Data_Source::to_json() += Serial_Data_Source_Data::to_base_json(); + return Data_Source::to_json() += specific.snapshot().to_base_json(); } void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) override { Data_Source::from_json(that_json, not_exist_use_default_value); - Serial_Data_Source_Data::from_base_json(that_json, not_exist_use_default_value); + auto value = specific.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + specific.write([&](auto& target) { + target = value; + }); } std::unique_ptr serial{}; ~Serial_Data_Source() override { - if (serial) { + if (serial) serial->close(); - } } }; enum class File_Data_Type { @@ -278,8 +352,21 @@ public: Play_Mode play_mode = Play_Mode::one; PSC_USE_JSON }; -class File_Data_Source : public Data_Source, public File_Data_Source_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = File_Data_Source_Data; + return object("file_data_source", "文件数据源配置", + ADMINIVE_FIELD_LABEL(T, file_path, "文件路径").creatable().editable().text_input(), + ADMINIVE_FIELD_LABEL(T, data_type, "数据格式").creatable().editable().select_input(), + ADMINIVE_FIELD_LABEL(T, play_mode, "播放模式").creatable().editable().select_input()); + } +}; +} +class File_Data_Source : public Data_Source { public: + adminive::Managed_Value specific; bool have_report_play_back_all_success = false; std::string state = "null"; Psc::JSON get_custom_state_json() override { @@ -288,7 +375,7 @@ public: return VAR_JSON_5(cur_virtual_time, playback_speed_rate, state, index, part_infos.size()); } File_Data_Source() { - this->type = "File_Data_Source"; + type = "File_Data_Source"; } ~File_Data_Source() override = default; void before_handle_msg(std::shared_ptr& msg) override; @@ -305,17 +392,21 @@ public: std::shared_ptr pre_cache = nullptr; std::deque> cache_list; std::string get_true_file_path() const { - return Psc::get_abs_path(file_path); + return Psc::get_abs_path(specific.member<&File_Data_Source_Data::file_path>().snapshot()); } asio::awaitable _close() override; asio::awaitable _open() override; std::vector readBinaryFileAsString(std::string_view filepath, size_t part_size); void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) override { Data_Source::from_json(that_json, not_exist_use_default_value); - File_Data_Source_Data::from_base_json(that_json, not_exist_use_default_value); + auto value = specific.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + specific.write([&](auto& target) { + target = value; + }); } Psc::JSON to_json() override { - return Data_Source::to_json() += File_Data_Source_Data::to_base_json(); + return Data_Source::to_json() += specific.snapshot().to_base_json(); } void origin_data_transform_mode_data(std::string& data) override; void handle_mode_s(std::shared_ptr& msg) override; @@ -333,8 +424,22 @@ public: std::size_t buffer_size{}; PSC_USE_JSON }; -class Dll_Data_Source : public Data_Source, public Dll_Data_Source_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Dll_Data_Source_Data; + return object("dll_data_source", "DLL 数据源配置", + ADMINIVE_FIELD_LABEL(T, library_path, "库路径").creatable().editable().text_input(), + ADMINIVE_FIELD_LABEL(T, function_name, "函数名").creatable().editable().text_input(), + ADMINIVE_FIELD_LABEL(T, data_type, "数据格式").creatable().editable().select_input(), + ADMINIVE_FIELD_LABEL(T, buffer_size, "读取缓冲区大小").creatable().editable().number_input()); + } +}; +} +class Dll_Data_Source : public Data_Source { public: + adminive::Managed_Value specific; Psc::JSON get_custom_state_json() override { auto ret = VAR_JSON_1(state); ret.append({"read_vaild_len", vs.to_string()}); @@ -342,21 +447,24 @@ public: } Psc::Value_Statistics vs; Dll_Data_Source() { - this->type = "Dll_Data_Source"; + type = "Dll_Data_Source"; } ~Dll_Data_Source() override = default; std::vector buffer; void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) override { Data_Source::from_json(that_json, not_exist_use_default_value); - Dll_Data_Source_Data::from_base_json(that_json, not_exist_use_default_value); - buffer.resize(buffer_size); + auto value = specific.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + specific.write([&](auto& target) { + target = value; + }); + buffer.resize(value.buffer_size); } Psc::JSON to_json() override { - return Data_Source::to_json() += Dll_Data_Source_Data::to_base_json(); + return Data_Source::to_json() += specific.snapshot().to_base_json(); } void* lib{}; using Func_Type = size_t (*)(char* buf, std::size_t max_len); - // using Func_Type = std::uint16_t (*)(char *buf, std::uint16_t max_len); Func_Type read_func_ptr{}; using Call_Back = void (*)(char* buf, std::size_t len); using set_Call_back = void (*)(Call_Back); @@ -366,8 +474,9 @@ public: co_return; } asio::awaitable _open() override { + const auto value = specific.snapshot(); { - auto r = Psc::try_load_library(Psc::get_abs_path(library_path)); + auto r = Psc::try_load_library(Psc::get_abs_path(value.library_path)); if (!r) { state = r.error().message(); co_return; @@ -375,10 +484,10 @@ public: lib = r.value(); } { - auto r = Psc::try_load_function(lib, function_name); + auto r = Psc::try_load_function(lib, value.function_name); if (!r) { state = r.error().message(); - std::cout << LOG_POS << " [" << function_name << "] 函数指针加载失败!" << std::endl; + std::cout << LOG_POS << " [" << value.function_name << "] 函数指针加载失败!" << std::endl; co_return; } read_func_ptr = (Func_Type)r.value(); @@ -395,17 +504,34 @@ public: File_Data_Type data_type = File_Data_Type::BIN_Blank_Text; PSC_USE_JSON }; -class Shared_Memory_Data_Source : public Data_Source, public Shared_Memory_Data_Source_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Shared_Memory_Data_Source_Data; + return object("shared_memory_data_source", "共享内存数据源配置", + ADMINIVE_FIELD_LABEL(T, shared_memory_name, "共享内存名称").creatable().editable().text_input(), + ADMINIVE_FIELD_LABEL(T, shared_memory_size, "共享内存大小").creatable().editable().number_input(), + ADMINIVE_FIELD_LABEL(T, data_type, "数据格式").creatable().editable().select_input()); + } +}; +} +class Shared_Memory_Data_Source : public Data_Source { public: + adminive::Managed_Value specific; Psc::JSON get_custom_state_json() override { return Psc::JSON::object(); } void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) override { Data_Source::from_json(that_json, not_exist_use_default_value); - Shared_Memory_Data_Source_Data::from_base_json(that_json, not_exist_use_default_value); + auto value = specific.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + specific.write([&](auto& target) { + target = value; + }); } Psc::JSON to_json() override { - return Data_Source::to_json() += Shared_Memory_Data_Source_Data::to_base_json(); + return Data_Source::to_json() += specific.snapshot().to_base_json(); } asio::awaitable _open() override; asio::awaitable _close() override; @@ -429,6 +555,7 @@ inline std::shared_ptr create_data_source_from_type(std::string_vie std::cout << "未知 Data_Source type类型!" << std::endl; throw std::invalid_argument("unknown Data_Source type: " + std::string(t)); } + ret->type = std::string(t); return ret; } struct Data_Source_Config { @@ -441,14 +568,14 @@ struct Data_Source_Config { map.push_back(ds); } } - Psc::JSON list() { + Psc::JSON list() const { auto connect_json = Psc::JSON::array(); for (auto& it : map.list()) { connect_json.children.emplace_back(it->to_json()); } return connect_json; } - Psc::JSON to_json() { + Psc::JSON to_json() const { Psc::JSON ret = Psc::JSON::object(); ret.append({"list", list()}); return ret; diff --git a/module/Local_Server/Data_Source/Data_Source_Handler.cpp b/module/Local_Server/Data_Source/Data_Source_Handler.cpp index 4ac4559..8a977af 100644 --- a/module/Local_Server/Data_Source/Data_Source_Handler.cpp +++ b/module/Local_Server/Data_Source/Data_Source_Handler.cpp @@ -145,7 +145,7 @@ size_t Data_Source_Handler::process_mode_acs_data(std::string_view origin_data) } }; auto f = [this, source, handle_packet](std::string& packet) { - auto& wait = Global::instance()->mode_acs.wait_process_msg; + const auto wait = Global::instance()->mode_acs.settings.member<&Mode_ACS_Config_Data::wait_process_msg>().snapshot(); if (wait) { handle_packet(packet); } @@ -168,29 +168,29 @@ void Data_Source_Handler::handle_mode_s(std::shared_ptr& mode_s mem2hex(t, true, " "), mem2hex(mode_s_msg->packet, true, " "), t, mode_s_msg->packet); Psc::fail_fast(data); } - auto& cfg = Global::instance()->mode_acs; - bool time_space_filter = cfg.time_space_filter.load(); - bool speed_filter = cfg.speed_filter.load(); - bool use_system_time = source->ignore_msg_time.load(); + const auto cfg = Global::instance()->mode_acs.settings.snapshot(); + const auto source_cfg = source->settings.snapshot(); + bool time_space_filter = cfg.time_space_filter; + bool speed_filter = cfg.speed_filter; + bool use_system_time = source_cfg.ignore_msg_time; auto base_station_pos = source->base_station.get_pos(); - if (!base_station_pos && source->base_station_has_valid_position.load()) { - base_station_pos = SSR::Position_3D{source->lat, source->lon, source->alt}; - } - auto range_filter = cfg.aircraft_change_list_adsb_range_filter.load(); - auto range_factor = cfg.aircraft_change_list_adsb_range_factor.load(); - SSR::ADS_B_T::Constraint air_constraint{cfg.max_speed_m_s, cfg.air_pos_timeout, SSR::cpr_cb, time_space_filter, - speed_filter, use_system_time, base_station_pos, source->alt, - range_filter, range_factor}; - SSR::ADS_B_T::Constraint surface_constraint{ - cfg.max_speed_m_s, cfg.surface_pos_timeout, SSR::cpr_cb, time_space_filter, speed_filter, - use_system_time, base_station_pos, source->alt, range_filter, range_factor}; + if (!base_station_pos && source_cfg.base_station_has_valid_position) + base_station_pos = SSR::Position_3D{source_cfg.lat, source_cfg.lon, source_cfg.alt}; + auto range_filter = cfg.aircraft_change_list_adsb_range_filter; + auto range_factor = cfg.aircraft_change_list_adsb_range_factor; + SSR::ADS_B_T::Constraint air_constraint{cfg.max_speed_m_s, cfg.air_pos_timeout, SSR::cpr_cb, time_space_filter, + speed_filter, use_system_time, base_station_pos, source_cfg.alt, + range_filter, range_factor}; + SSR::ADS_B_T::Constraint surface_constraint{cfg.max_speed_m_s, cfg.surface_pos_timeout, SSR::cpr_cb, time_space_filter, + speed_filter, use_system_time, base_station_pos, source_cfg.alt, + range_filter, range_factor}; SSR::parse_mode_s_bin(source.get(), mode_s_msg, base_station_pos, air_constraint, surface_constraint); auto base = source->get_aircraft(mode_s_msg->icao); if (base) { auto derived = std::dynamic_pointer_cast(base); derived->refresh_external_database_info(); } - if (Global::instance()->mlat.merge) { + if (Global::instance()->mlat.settings.member<&MLAT_Config_Data::merge>().snapshot()) { auto& mh = Global::instance()->mlat_handler; mh.push(mode_s_msg); auto tt = mh.get_all(); @@ -216,10 +216,13 @@ void Data_Source_Handler::handle_mode_s(std::shared_ptr& mode_s if (o_pos.has_value() && o_alt.has_value()) { have_pos = true; auto pos = o_pos.value(); - source->alt = o_alt.value(); + const auto altitude = o_alt.value(); + source->settings.member<&Data_Source_Data::alt>().write([altitude](double& value) { + value = altitude; + }); double x, y, z; - SSR::CPR::WGS84_LBH_to_XYZ(pos.lon, pos.lat, source->alt, x, y, z); - oss << "\tpos:[" << pos.lon << "," << pos.lat << "," << source->alt << "]" << std::endl; + SSR::CPR::WGS84_LBH_to_XYZ(pos.lon, pos.lat, altitude, x, y, z); + oss << "\tpos:[" << pos.lon << "," << pos.lat << "," << altitude << "]" << std::endl; oss << "\tXYZ:[" << x << "," << y << "," << z << "]" << std::endl; } } @@ -279,90 +282,85 @@ void Data_Source_Handler::handle_HULC(std::string_view packet) { } } void Data_Source_Handler::refresh_data_feed_key_list() { - // std::cout << key << " refresh_data_feed_key_list" << std::endl; - std::vector tmp; - for (auto& relation : Global::instance()->mode_acs.source_feed_relation_config.map.list()) { - if (!relation->enable) + auto* global = Global::instance(); + std::map assignment; + const auto sources = global->mode_acs.data_source_config.map.list(); + const auto feeds = global->mode_acs.data_feed_config.map.list(); + const auto relations = global->mode_acs.source_feed_relation_config.map.list(); + for (const auto& relation : relations) { + const auto config = relation->settings.snapshot(); + if (!config.enable || relation->type != "One_to_One_Relation") continue; - if (relation->type == "One_to_One_Relation") { - auto t = dynamic_cast(relation.get()); - // std::cout << VAR_STR_2(t->source_key, this->key) << " - // refresh_data_feed_key_list" << std::endl; - if (t->source_key == this->key) { - tmp.push_back(t->feed_key); - } + const auto source = global->mode_acs.data_source_config.map.get(config.source_key); + const auto feed = global->mode_acs.data_feed_config.map.get(config.feed_key); + if (source.has_value() && feed.has_value() && source.value()->enabled() && feed.value()->enabled() && !assignment.contains(config.feed_key)) + assignment.emplace(config.feed_key, config.source_key); + } + for (const auto& relation : relations) { + const auto config = relation->settings.snapshot(); + if (!config.enable || relation->type != "Name_One_To_Many_Relation") + continue; + const auto source = global->mode_acs.data_source_config.map.get(config.source_key); + if (!source.has_value() || !source.value()->enabled()) + continue; + const std::string& prefix = config.feed_name.empty() ? config.source_key : config.feed_name; + for (const auto& feed : feeds) { + if (!feed->enabled() || assignment.contains(feed->key) || !feed->key.starts_with(prefix)) + continue; + assignment.emplace(feed->key, config.source_key); } - else if (relation->type == "First_Source_To_All_Feed_Relation") { - auto t = dynamic_cast(relation.get()); - std::shared_ptr first = nullptr; - for (const auto& ds : Global::instance()->mode_acs.data_source_config.map.list()) { - if (ds->enable) { - first = ds; - break; - } - } - if (first->key == key) { - for (const auto& df : Global::instance()->mode_acs.data_feed_config.map.list()) { - if (df->enable) { - tmp.push_back(df->key); - } - } - } + } + bool use_default = false; + for (const auto& relation : relations) { + if (relation->type == "First_Source_To_All_Feed_Relation" && relation->settings.member<&Source_Feed_Relation_Data::enable>().snapshot()) { + use_default = true; + break; } - std::sort(tmp.begin(), tmp.end()); - // 去重 - auto last = std::unique(tmp.begin(), tmp.end()); - tmp.erase(last, tmp.end()); - { - std::vector tmp_info; - // 创建 tmp_info 向量,将 tmp 的内容转化为 Cached_Source_Info 对象 - for (const auto& key : tmp) { - tmp_info.push_back({key}); - } - { - // 进入临界区,锁住 mutex,确保线程安全 - std::lock_guard lock(cdf_mtx); - // 1. 删除 tmp 中没有的 cached_data_feed_key_list 元素 - auto it = cached_data_feed_key_list.begin(); - while (it != cached_data_feed_key_list.end()) { - if (std::find_if(tmp_info.begin(), tmp_info.end(), [&](const Cached_Source_Info& info) { - return info.key == it->key; - }) == tmp_info.end()) { - // 如果当前元素在 tmp 中找不到,删除它 - it = cached_data_feed_key_list.erase(it); - } - else { - ++it; - } - } - // 2. 创建 tmp 中有但 cached_data_feed_key_list 没有的元素 - for (const auto& tmp_item : tmp_info) { - auto found = std::find_if( - cached_data_feed_key_list.begin(), cached_data_feed_key_list.end(), - [&](const Cached_Source_Info& cached_item) { return cached_item.key == tmp_item.key; }); - if (found == cached_data_feed_key_list.end()) { - // 如果 tmp_item 不在 cached_data_feed_key_list 中,添加它 - cached_data_feed_key_list.push_back(tmp_item); - } - } + } + if (use_default) { + auto first = std::find_if(sources.begin(), sources.end(), [](const auto& source) { + return source->enabled(); + }); + if (first != sources.end()) { + for (const auto& feed : feeds) { + if (feed->enabled() && !assignment.contains(feed->key)) + assignment.emplace(feed->key, (*first)->key); } } } + std::vector next; + for (const auto& [feed_key, source_key] : assignment) { + if (source_key == key) + next.push_back({feed_key}); + } + std::lock_guard lock(cdf_mtx); + for (auto& item : next) { + auto old = std::find_if(cached_data_feed_key_list.begin(), cached_data_feed_key_list.end(), [&](const Cached_Source_Info& value) { + return value.key == item.key; + }); + if (old != cached_data_feed_key_list.end()) { + item.mode_s_cache_num = old->mode_s_cache_num; + item.mode_other_cache_num = old->mode_other_cache_num; + } + } + cached_data_feed_key_list = std::move(next); } + std::optional convert_to_send_format(Data_Source_Handler* ds, const std::shared_ptr& feed, const std::shared_ptr& msg) { auto& type = msg->type; auto fs = dynamic_cast(msg->source.get()); if (fs) { - if (fs->play_mode == Play_Mode::analysis) { + if (fs->specific.member<&File_Data_Source_Data::play_mode>().snapshot() == Play_Mode::analysis) { // std::cout << fs->key << " analysis i ==" << i << std::endl; } } // 出口输出的条件 - auto& output_format = feed->output_format.type; - auto& use_mode_ac = feed->output_format.use_mode_ac; - auto& use_status = feed->output_format.use_status; - auto& mode_s_output_type = feed->output_format.mode_s_output_type; + const auto feed_format = feed->output_format.snapshot(); + const auto output_format = feed_format.type; + const auto use_mode_ac = feed_format.use_mode_ac; + const auto use_status = feed_format.use_status; + const auto mode_s_output_type = feed_format.mode_s_output_type; // 计算出是否输出消息 std::optional send_msg = std::nullopt; if (type == SSR::Msg::HULC_Status) { @@ -416,7 +414,7 @@ std::optional convert_to_send_format(Data_Source_Handler* ds, const auto aircraft = ds->get_aircraft(s_msg->icao); if (output_format == Output_Data_Format::SBS) { bool sbs_out_put = false; - if (feed->output_format.sbs_only_pos) { + if (feed_format.sbs_only_pos) { auto s = ds->get_aircraft(s_msg->icao); if (s) { auto pos = s->pos(); @@ -490,8 +488,9 @@ void Data_Source_Handler::push_to_feed(const std::shared_ptr& msg) { refresh_data_feed_key_list(); need_refresh_data_feed_key_list = false; } - auto& other_size = Global::instance()->mode_acs.mode_other_max_num; - auto& s_size = Global::instance()->mode_acs.mode_other_max_num; + const auto mode_config = Global::instance()->mode_acs.settings.snapshot(); + const auto other_size = mode_config.mode_other_max_num; + const auto s_size = mode_config.mode_s_max_num; std::vector list; for (const auto& item : cached_data_feed_key_list) { auto& key = item.key; @@ -501,7 +500,7 @@ void Data_Source_Handler::push_to_feed(const std::shared_ptr& msg) { break; } std::shared_ptr& feed = opt_feed.value(); - if (!feed->enable) + if (!feed->enabled()) continue; if (msg->type == SSR::Mode_Msg::T::S7 || msg->type == SSR::Mode_Msg::T::S14) { // static Frequency_Limit fl; diff --git a/module/Local_Server/Data_Source/Database.cpp b/module/Local_Server/Data_Source/Database.cpp index beb751c..ec8cd01 100644 --- a/module/Local_Server/Data_Source/Database.cpp +++ b/module/Local_Server/Data_Source/Database.cpp @@ -160,7 +160,7 @@ Psc::JSON aircraft_stream_source_update_json( auto tracks = Psc::JSON::array(); ret.append({"key", data_source_key}); auto source = Global::instance()->source(data_source_key); - if (source == nullptr || !source->enable) { + if (source == nullptr || !source->enabled()) { ret.append({"change_list", Psc::JSON::array()}); ret.append({"removed_icaos", Psc::JSON::array()}); ret.append({"tracks", tracks}); @@ -290,7 +290,7 @@ void register_aircraft_stream_ws() { } } Aircraft::Aircraft(std::string_view icao) : Aircraft_Info(icao) { - auto size = Global::instance()->mode_acs.max_track_point_size.load(); + auto size = Global::instance()->mode_acs.settings.member<&Mode_ACS_Config_Data::max_track_point_size>().snapshot(); air_pos_track_list.init(size); surface_pos_track_list.init(size); } @@ -332,7 +332,7 @@ void DataBase::delete_timeout_aircraft() { std::cout << "未初始化的 timestamp " << LOG_POS << std::endl; } long long time = std::time(nullptr) - item->timestamp; - return time > Global::instance()->mode_acs.timeout_seconds; + return time > Global::instance()->mode_acs.settings.member<&Mode_ACS_Config_Data::timeout_seconds>().snapshot(); }); } @@ -361,14 +361,15 @@ DataBase::get_visible_aircraft_snapshot() { std::vector> ret; auto g = Global::instance(); auto min_position_points = - g->mode_acs.aircraft_change_list_min_position_points.load(); - auto range_filter = g->mode_acs.aircraft_change_list_adsb_range_filter.load(); - auto range_factor = g->mode_acs.aircraft_change_list_adsb_range_factor.load(); + g->mode_acs.settings.member<&Mode_ACS_Config_Data::aircraft_change_list_min_position_points>().snapshot(); + auto range_filter = g->mode_acs.settings.member<&Mode_ACS_Config_Data::aircraft_change_list_adsb_range_filter>().snapshot(); + auto range_factor = g->mode_acs.settings.member<&Mode_ACS_Config_Data::aircraft_change_list_adsb_range_factor>().snapshot(); auto source = g->source(get_key()); auto base_position = base_station.get_pos(); if (source) { - if (!base_position && source->base_station_has_valid_position.load()) { - base_position = SSR::Position_3D{source->lat, source->lon, source->alt}; + const auto source_config = source->settings.snapshot(); + if (!base_position && source_config.base_station_has_valid_position) { + base_position = SSR::Position_3D{source_config.lat, source_config.lon, source_config.alt}; } } for (auto &it : aircraft_map.values()) { @@ -540,12 +541,13 @@ void database_server(Global *g) { if (db) { ret = db->base_station.to_Json(); auto target_height = - g->mode_acs.adsb_theoretical_target_altitude_meters.load(); - auto range = Base_Station::theoretical_detection_range_meters(db->alt, + g->mode_acs.settings.member<&Mode_ACS_Config_Data::adsb_theoretical_target_altitude_meters>().snapshot(); + const auto source_config = db->settings.snapshot(); + auto range = Base_Station::theoretical_detection_range_meters(source_config.alt, target_height); - ret.append({"latitude", db->lat}); - ret.append({"longitude", db->lon}); - ret.append({"height", db->alt}); + ret.append({"latitude", source_config.lat}); + ret.append({"longitude", source_config.lon}); + ret.append({"height", source_config.alt}); ret.append({"adsb_theoretical_target_altitude_meters", target_height}); ret.append({"adsb_theoretical_detection_range_meters", range}); ret.append({"理论探测范围", range}); @@ -588,7 +590,7 @@ void database_server(Global *g) { std::shared_ptr ts = nullptr; auto ret = JSON::array(); for (const auto &li : list) { - if (li->enable) { + if (li->enabled()) { ret = li->get_aircraftlist(); break; } @@ -613,7 +615,7 @@ void database_server(Global *g) { return; } - auto limit_msg_num = g->mode_acs.default_min_aircraft_list_num.load(); + auto limit_msg_num = g->mode_acs.settings.member<&Mode_ACS_Config_Data::default_min_aircraft_list_num>().snapshot(); auto t = params.get("limit_msg_num"); if (t) { HTTP_REQUIRE_VALUE(requested_limit_msg_num, t->try_number_val()) diff --git a/module/Local_Server/External_Database/External_Database.cpp b/module/Local_Server/External_Database/External_Database.cpp index 68ab245..a5eb025 100644 --- a/module/Local_Server/External_Database/External_Database.cpp +++ b/module/Local_Server/External_Database/External_Database.cpp @@ -421,13 +421,13 @@ void External_Resources_Manager::server(Global* g) { CHECK_JSON_PARAM HTTP_REQUIRE_VALUE(name, params.try_get_string("name")) const auto result = co_await Ecap_Coro::to_drogon(refresh_external_database_coro(name, true)); - g->save(); + g->save(Config_Section::external_database); res->setBody(warp(external_resource_status_to_json(result)).to_json_string()); co_return; }); svr.Post_Coro(api + "refresh_external_databases", [this, g](HTTP_Param) -> drogon::Task<> { const auto result = co_await Ecap_Coro::to_drogon(refresh_external_databases_coro(true)); - g->save(); + g->save(Config_Section::external_database); res->setBody(warp(external_resource_status_list_to_json(result)).to_json_string()); co_return; }); @@ -436,7 +436,7 @@ void External_Resources_Manager::server(Global* g) { HTTP_REQUIRE_VALUE(name, params.try_get_string("name")) HTTP_REQUIRE_VALUE(source_file, params.try_get_string("source_file")) const auto result = co_await Ecap_Coro::to_drogon(import_external_database_coro(name, source_file)); - g->save(); + g->save(Config_Section::external_database); res->setBody(warp(external_resource_status_to_json(result)).to_json_string()); co_return; }); @@ -461,7 +461,7 @@ void External_Resources_Manager::server(Global* g) { } const auto source_file = std::filesystem::path(drogon::app().getUploadPath()) / saved_name; const auto result = co_await Ecap_Coro::to_drogon(import_external_database_coro(*name, source_file)); - g->save(); + g->save(Config_Section::external_database); res->setBody(warp(external_resource_status_to_json(result)).to_json_string()); co_return; }); @@ -469,7 +469,7 @@ void External_Resources_Manager::server(Global* g) { CHECK_JSON_PARAM HTTP_REQUIRE_VALUE(name, params.try_get_string("name")) const auto result = co_await Ecap_Coro::to_drogon(clear_external_database_table_coro(name)); - g->save(); + g->save(Config_Section::external_database); res->setBody(warp(external_resource_status_to_json(result)).to_json_string()); co_return; }); diff --git a/module/Local_Server/global_include.h b/module/Local_Server/global_include.h index 4add305..4f8c8ec 100644 --- a/module/Local_Server/global_include.h +++ b/module/Local_Server/global_include.h @@ -85,7 +85,7 @@ protected: template class Ordered_Map { public: - std::optional get(Key_Type key) { + std::optional get(Key_Type key) const { std::lock_guard g(mtx); auto it = _map.find(key); if (it != _map.end()) { @@ -136,7 +136,7 @@ public: _list.erase(_list.begin() + i); return true; } - std::vector list() { + std::vector list() const { std::lock_guard g(mtx); return this->_list; } @@ -144,12 +144,28 @@ public: // std::lock_guard g(mtx); // return this->_map; // } + bool set_order(const std::vector& keys) { + std::lock_guard g(mtx); + if (keys.size() != _list.size()) + return false; + std::vector reordered; + reordered.reserve(_list.size()); + for (const auto& key : keys) { + auto iterator = _map.find(key); + if (iterator == _map.end()) + return false; + reordered.push_back(iterator->second); + } + _list = std::move(reordered); + return true; + } void clear() { - list().clear(); + std::lock_guard g(mtx); + _list.clear(); _map.clear(); } protected: - std::mutex mtx; + mutable std::mutex mtx; std::vector _list; std::map _map; }; diff --git a/module/Local_Server/server/Adminive_Config.cpp b/module/Local_Server/server/Adminive_Config.cpp new file mode 100644 index 0000000..397faf1 --- /dev/null +++ b/module/Local_Server/server/Adminive_Config.cpp @@ -0,0 +1,940 @@ +#include "Adminive_Config.h" +#include "Global.h" +#include "adminive/adminive.hpp" +#include "adminive/adapters/drogon.hpp" +#include "adminive/adapters/magic_enum.hpp" +#include "adminive/adapters/nlohmann_json.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace ecap::adminive_config { +enum class Data_Source_Type { + Serial_Data_Source, + TCP_Client_Data_Source, + File_Data_Source, + Dll_Data_Source, + Shared_Memory_Data_Source +}; +enum class Data_Feed_Type { + Data_Feed_TCP_Server, + Data_Feed_UDP_Server, + Data_Feed_TCP_Client, + Data_Feed_UDP_Client +}; +enum class Source_Feed_Relation_Type { + One_to_One_Relation, + Name_One_To_Many_Relation, + First_Source_To_All_Feed_Relation +}; +struct Data_Source_Row { + std::string key; + Data_Source_Type type = Data_Source_Type::TCP_Client_Data_Source; + bool enable{}; + Data_Source_Data config; + std::string ip; + std::uint16_t port{}; + std::string port_name; + Baud_Rate_Type baud_rate{}; + std::string file_path; + File_Data_Type data_type = File_Data_Type::BIN_Blank_Text; + Play_Mode play_mode = Play_Mode::one; + std::string library_path; + std::string function_name; + std::size_t buffer_size{}; + std::string shared_memory_name; + std::uint64_t shared_memory_size{}; + std::string state; +}; +struct Data_Feed_Row { + std::string key; + Data_Feed_Type type = Data_Feed_Type::Data_Feed_TCP_Server; + bool enable{}; + Output_Format output_format; + Output_Data_Format format = Output_Data_Format::BIN; + std::uint16_t port{}; + std::string url; + std::size_t connect_user_buffer_size = 409600; + std::size_t connect_system_buffer_size = 409600; + std::string state; +}; +struct Source_Feed_Relation_Row { + std::string key; + Source_Feed_Relation_Type type = Source_Feed_Relation_Type::One_to_One_Relation; + bool enable{}; + std::string source_key; + std::string feed_key; + std::string feed_name; +}; +} +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = ecap::adminive_config::Data_Source_Row; + using E = ecap::adminive_config::Data_Source_Type; + auto type = ADMINIVE_FIELD_LABEL(T, type, "数据源类型").creatable().required().select_input(); + type = type.enum_label("串口数据源") + .enum_label("TCP 客户端数据源") + .enum_label("文件数据源") + .enum_label("DLL 数据源") + .enum_label("共享内存数据源"); + return object("data_source", "数据源", + ADMINIVE_FIELD_LABEL(T, key, "名称").creatable().required().text_input(), + type, + ADMINIVE_FIELD_LABEL(T, enable, "启用").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, config, "公共与地图配置").creatable().editable(), + ADMINIVE_FIELD_LABEL(T, ip, "IP 地址").creatable().editable().text_input().visible_on("${$self.type == 'TCP_Client_Data_Source'}"), + ADMINIVE_FIELD_LABEL(T, port, "端口").creatable().editable().number_input().visible_on("${$self.type == 'TCP_Client_Data_Source'}"), + ADMINIVE_FIELD_LABEL(T, port_name, "串口名称").creatable().editable().text_input().visible_on("${$self.type == 'Serial_Data_Source'}"), + ADMINIVE_FIELD_LABEL(T, baud_rate, "波特率").creatable().editable().select_input().visible_on("${$self.type == 'Serial_Data_Source'}"), + ADMINIVE_FIELD_LABEL(T, file_path, "文件路径").creatable().editable().text_input().visible_on("${$self.type == 'File_Data_Source'}"), + ADMINIVE_FIELD_LABEL(T, data_type, "数据格式").creatable().editable().select_input().visible_on("${$self.type == 'File_Data_Source' || $self.type == 'Dll_Data_Source' || $self.type == 'Shared_Memory_Data_Source'}"), + ADMINIVE_FIELD_LABEL(T, play_mode, "播放模式").creatable().editable().select_input().visible_on("${$self.type == 'File_Data_Source'}"), + ADMINIVE_FIELD_LABEL(T, library_path, "库路径").creatable().editable().text_input().visible_on("${$self.type == 'Dll_Data_Source'}"), + ADMINIVE_FIELD_LABEL(T, function_name, "函数名").creatable().editable().text_input().visible_on("${$self.type == 'Dll_Data_Source'}"), + ADMINIVE_FIELD_LABEL(T, buffer_size, "读取缓冲区大小").creatable().editable().number_input().visible_on("${$self.type == 'Dll_Data_Source'}"), + ADMINIVE_FIELD_LABEL(T, shared_memory_name, "共享内存名称").creatable().editable().text_input().visible_on("${$self.type == 'Shared_Memory_Data_Source'}"), + ADMINIVE_FIELD_LABEL(T, shared_memory_size, "共享内存大小").creatable().editable().number_input().visible_on("${$self.type == 'Shared_Memory_Data_Source'}"), + ADMINIVE_FIELD_LABEL(T, state, "运行状态").text_input()); + } +}; +template <> +struct Type_View_Descriptor { + static Table_View table() { + using T = ecap::adminive_config::Data_Source_Row; + return table_view(column<&T::key>("名称").search(), column<&T::type>("类型").filter(), column<&T::enable>("启用").filter(), column<&T::state>("状态")) + .titled("数据源") + .id_title("序号") + .create_title("添加数据源") + .edit_title("编辑") + .delete_title("删除") + .delete_confirmation("确定删除该数据源?存在关系依赖时后端会拒绝删除。") + .reorderable() + .create_layout(vertical(grid(2, use<&T::key>(), use<&T::type>(), use<&T::enable>()), use<&T::config>(), grid(2, use<&T::ip>(), use<&T::port>(), use<&T::port_name>(), use<&T::baud_rate>(), use<&T::file_path>(), use<&T::data_type>(), use<&T::play_mode>(), use<&T::library_path>(), use<&T::function_name>(), use<&T::buffer_size>(), use<&T::shared_memory_name>(), use<&T::shared_memory_size>()))) + .edit_layout(vertical(grid(2, use<&T::key>(), use<&T::type>(), use<&T::enable>(), use<&T::state>()), use<&T::config>(), grid(2, use<&T::ip>(), use<&T::port>(), use<&T::port_name>(), use<&T::baud_rate>(), use<&T::file_path>(), use<&T::data_type>(), use<&T::play_mode>(), use<&T::library_path>(), use<&T::function_name>(), use<&T::buffer_size>(), use<&T::shared_memory_name>(), use<&T::shared_memory_size>()))); + } +}; +template <> +struct Type_Descriptor { + static auto get() { + using T = ecap::adminive_config::Data_Feed_Row; + using E = ecap::adminive_config::Data_Feed_Type; + auto type = ADMINIVE_FIELD_LABEL(T, type, "数据馈送类型").creatable().required().select_input(); + type = type.enum_label("TCP 服务端") + .enum_label("UDP 服务端") + .enum_label("TCP 客户端") + .enum_label("UDP 客户端"); + return object("data_feed", "数据馈送", + ADMINIVE_FIELD_LABEL(T, key, "名称").creatable().required().text_input(), + type, + ADMINIVE_FIELD_LABEL(T, enable, "启用").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, output_format, "输出格式").creatable().editable(), + ADMINIVE_FIELD_LABEL(T, format, "数据格式").select_input(), + ADMINIVE_FIELD_LABEL(T, port, "端口").creatable().editable().number_input(), + ADMINIVE_FIELD_LABEL(T, url, "IP 地址").creatable().editable().text_input().visible_on("${$self.type == 'Data_Feed_TCP_Client' || $self.type == 'Data_Feed_UDP_Client'}"), + ADMINIVE_FIELD_LABEL(T, connect_user_buffer_size, "TCP 用户缓冲区").creatable().editable().number_input().visible_on("${$self.type == 'Data_Feed_TCP_Server'}"), + ADMINIVE_FIELD_LABEL(T, connect_system_buffer_size, "TCP 系统缓冲区").creatable().editable().number_input().visible_on("${$self.type == 'Data_Feed_TCP_Server'}"), + ADMINIVE_FIELD_LABEL(T, state, "运行状态").text_input()); + } +}; +template <> +struct Type_View_Descriptor { + static Table_View table() { + using T = ecap::adminive_config::Data_Feed_Row; + return table_view(column<&T::key>("名称").search(), column<&T::type>("类型").filter(), column<&T::format>("输出格式").filter(), column<&T::port>("端口").sort(), column<&T::enable>("启用").filter(), column<&T::state>("状态")) + .titled("数据馈送") + .id_title("序号") + .create_title("添加数据馈送") + .edit_title("编辑") + .delete_title("删除") + .delete_confirmation("确定删除该数据馈送?存在关系依赖时后端会拒绝删除。") + .reorderable() + .create_layout(vertical(grid(2, use<&T::key>(), use<&T::type>(), use<&T::enable>()), use<&T::output_format>(), grid(2, use<&T::url>(), use<&T::port>(), use<&T::connect_user_buffer_size>(), use<&T::connect_system_buffer_size>()))) + .edit_layout(vertical(grid(2, use<&T::key>(), use<&T::type>(), use<&T::enable>(), use<&T::state>()), use<&T::output_format>(), grid(2, use<&T::url>(), use<&T::port>(), use<&T::connect_user_buffer_size>(), use<&T::connect_system_buffer_size>()))); + } +}; +template <> +struct Type_Descriptor { + static auto get() { + using T = ecap::adminive_config::Source_Feed_Relation_Row; + using E = ecap::adminive_config::Source_Feed_Relation_Type; + auto type = ADMINIVE_FIELD_LABEL(T, type, "关系类型").creatable().required().select_input(); + type = type.enum_label("一对一") + .enum_label("按名称一对多") + .enum_label("默认:首个数据源到未匹配馈送"); + return object("source_feed_relation", "数据源-数据馈送关系", + ADMINIVE_FIELD_LABEL(T, key, "关系名称").creatable().required().text_input(), + type, + ADMINIVE_FIELD_LABEL(T, enable, "启用").creatable().editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, source_key, "数据源名称").creatable().editable().text_input().visible_on("${$self.type == 'One_to_One_Relation' || $self.type == 'Name_One_To_Many_Relation'}"), + ADMINIVE_FIELD_LABEL(T, feed_key, "数据馈送名称").creatable().editable().text_input().visible_on("${$self.type == 'One_to_One_Relation'}"), + ADMINIVE_FIELD_LABEL(T, feed_name, "数据馈送名称前缀").description("按名称扫描全部数据馈送;为空时使用数据源名称作为前缀。默认关系只接管前面规则没有匹配到的数据馈送。").creatable().editable().text_input().visible_on("${$self.type == 'Name_One_To_Many_Relation'}")); + } +}; +template <> +struct Type_View_Descriptor { + static Table_View table() { + using T = ecap::adminive_config::Source_Feed_Relation_Row; + return table_view(column<&T::key>("关系名称").search(), column<&T::type>("类型").filter(), column<&T::source_key>("数据源"), column<&T::feed_key>("数据馈送"), column<&T::feed_name>("名称前缀"), column<&T::enable>("启用").filter()) + .titled("数据源-数据馈送关系") + .id_title("序号") + .create_title("添加关系") + .edit_title("编辑") + .delete_title("删除") + .delete_confirmation("确定删除该关系?") + .reorderable() + .create_layout(vertical(grid(2, use<&T::key>(), use<&T::type>(), use<&T::enable>()), grid(2, use<&T::source_key>(), use<&T::feed_key>(), use<&T::feed_name>()))) + .edit_layout(vertical(grid(2, use<&T::key>(), use<&T::type>(), use<&T::enable>()), grid(2, use<&T::source_key>(), use<&T::feed_key>(), use<&T::feed_name>()))); + } +}; +template <> +struct Type_View_Descriptor { + static Form_View edit() { + using T = Mode_ACS_Config_Data; + return edit_form(vertical( + group("基础时序", grid(2, use<&T::timeout_seconds>(), use<&T::read_milliseconds>(), use<&T::air_pos_timeout>(), use<&T::surface_pos_timeout>())), + group("过滤参数", grid(2, use<&T::max_speed_m_s>(), use<&T::time_space_filter>(), use<&T::speed_filter>(), use<&T::aircraft_change_list_adsb_range_filter>(), use<&T::aircraft_change_list_adsb_range_factor>(), use<&T::adsb_theoretical_target_altitude_meters>())), + group("容量与上报", grid(2, use<&T::max_track_point_size>(), use<&T::mode_s_max_num>(), use<&T::mode_other_max_num>(), use<&T::report_data_feed_msg_mum>(), use<&T::default_min_aircraft_list_num>(), use<&T::aircraft_change_list_min_position_points>())), + group("运行开关", grid(2, use<&T::wait_process_msg>(), use<&T::ignore_df_11>(), use<&T::monitor_msg_live>())))).submit("保存 Mode ACS 配置"); + } +}; +template <> +struct Type_View_Descriptor { + static Form_View edit() { + using T = Cesium_Graphics_Config_Data; + return edit_form(vertical( + group("渲染质量", grid(2, use<&T::debug_show_frames_per_second>(), use<&T::target_frame_rate>(), use<&T::use_browser_recommended_resolution>(), use<&T::resolution_scale>(), use<&T::msaa_samples>(), use<&T::fxaa>(), use<&T::maximum_screen_space_error>())), + group("光照与阴影", grid(2, use<&T::shadows>(), use<&T::enable_lighting>(), use<&T::solar_lighting>(), use<&T::solar_light_intensity>())), + group("地形", grid(2, use<&T::terrain_enabled_in_3d>(), use<&T::terrain_exaggeration>(), use<&T::terrain_limit_maximum_level>(), use<&T::terrain_maximum_level>(), use<&T::terrain_cache_tiles>(), use<&T::terrain_occlusion_enabled>(), use<&T::occlusion_sample_spacing_meters>(), use<&T::occlusion_clearance_margin_meters>())), + group("辅助显示", grid(2, use<&T::surface_navigation_reference_visible>(), use<&T::view_axes_visible>(), use<&T::performance_panel_x>(), use<&T::performance_panel_y>(), use<&T::view_axes_panel_x>(), use<&T::view_axes_panel_y>(), use<&T::view_axes_panel_size>())))).submit("保存 Cesium 图形配置"); + } +}; +template <> +struct Type_View_Descriptor { + static Form_View edit() { + using T = Data_feed_Config_Data; + return edit_form(grid(2, use<&T::packet_byte_size>(), use<&T::empty_wait_milliseconds>(), use<&T::tcp_server_default_connect_user_buffer_size>(), use<&T::tcp_server_default_connect_system_buffer_size>())).submit("保存数据馈送全局配置"); + } +}; +template <> +struct Type_View_Descriptor { + static Form_View edit() { + using T = MLAT_Config_Data; + return edit_form(grid(2, use<&T::enable>(), use<&T::merge>(), use<&T::library_path>(), use<&T::function_name>())).submit("保存 MLAT 配置"); + } +}; +} +namespace ecap::adminive_config { +namespace { +using Json = nlohmann::json; +class Conflict_Error final : public std::runtime_error { +public: + using std::runtime_error::runtime_error; +}; +template +adminive::Resource_Transaction value_section_transaction(Config_Section section) { + adminive::Resource_Transaction transaction; + transaction.commit = [section](const Model& value, const adminive::Request_Context&) { + Config::save(section, value.to_base_json()); + }; + transaction.rollback = [section](const Model& value, const adminive::Request_Context&) { + Config::save(section, value.to_base_json()); + }; + return transaction; +} +adminive::Resource_Transaction mode_acs_transaction() { + adminive::Resource_Transaction transaction; + transaction.commit = [](const Mode_ACS_Config_Data& value, const adminive::Request_Context&) { + SSR::Monitor_Mode_ACS_Message_Num = value.monitor_msg_live; + Config::merge(Config_Section::mode_acs, value.to_base_json()); + }; + transaction.rollback = [](const Mode_ACS_Config_Data& value, const adminive::Request_Context&) { + SSR::Monitor_Mode_ACS_Message_Num = value.monitor_msg_live; + Config::merge(Config_Section::mode_acs, value.to_base_json()); + }; + return transaction; +} +adminive::Resource_Transaction data_feed_settings_transaction() { + adminive::Resource_Transaction transaction; + transaction.commit = [](const Data_feed_Config_Data& value, const adminive::Request_Context&) { + auto patch = Psc::JSON::object(); + patch.append({"data_feed", value.to_base_json()}); + Config::merge(Config_Section::mode_acs, patch); + }; + transaction.rollback = [](const Data_feed_Config_Data& value, const adminive::Request_Context&) { + auto patch = Psc::JSON::object(); + patch.append({"data_feed", value.to_base_json()}); + Config::merge(Config_Section::mode_acs, patch); + }; + return transaction; +} +void save_data_sources(Global* global) { + auto patch = Psc::JSON::object(); + patch.append({"data_source", global->mode_acs.data_source_config.to_json()}); + Config::merge(Config_Section::mode_acs, patch); +} +void save_data_feeds(Global* global) { + auto feed = Psc::JSON::object(); + feed.append({"list", global->mode_acs.data_feed_config.list()}); + auto patch = Psc::JSON::object(); + patch.append({"data_feed", feed}); + Config::merge(Config_Section::mode_acs, patch); +} +void save_source_feed_relations(Global* global) { + auto patch = Psc::JSON::object(); + patch.append({"source_feed_relation", global->mode_acs.source_feed_relation_config.to_json()}); + Config::merge(Config_Section::mode_acs, patch); +} +template +adminive::Http_Response business_response(Function&& function) noexcept { + try { + return std::forward(function)(); + } catch(const Conflict_Error& error) { + return adminive::make_http_error(409, error.what()); + } catch(const std::out_of_range& error) { + return adminive::make_http_error(404, error.what()); + } catch(const std::invalid_argument& error) { + return adminive::make_http_error(422, error.what()); + } catch(const std::exception& error) { + return adminive::make_http_error(500, error.what()); + } catch(...) { + return adminive::make_http_error(500, "unknown server error"); + } +} +template +void complete_business_request(std::function& callback, Function&& function) noexcept { + callback(adminive::make_drogon_response(business_response(std::forward(function)))); +} +adminive::Collection_Query read_collection_query(const drogon::HttpRequestPtr& request) { + adminive::Collection_Query query; + query.page = adminive::read_drogon_size_parameter(request, "page", 1); + query.per_page = adminive::read_drogon_size_parameter(request, "perPage", 20); + query.order_by = request->getParameter("orderBy"); + query.order_dir = request->getParameter("orderDir"); + for(const auto& [name, value] : request->getParameters()) { + if(name != "page" && name != "perPage" && name != "orderBy" && name != "orderDir") + query.fields.insert_or_assign(name, value); + } + return query; +} +std::vector read_order_ids(std::string_view body) { + const Json input = adminive::parse_json(body); + if(!input.contains("ids")) + throw std::invalid_argument("missing ids"); + const auto& value = input.at("ids"); + std::vector result; + if(value.is_array()) { + result.reserve(value.size()); + for(const auto& item : value) + result.push_back(item.is_string() ? std::stoull(item.get()) : item.get()); + return result; + } + const std::string text = value.get(); + std::size_t begin{}; + while(begin < text.size()) { + const auto end = text.find(',', begin); + const auto token = std::string_view(text).substr(begin, end == std::string::npos ? text.size() - begin : end - begin); + std::uint64_t id{}; + const auto [position, error] = std::from_chars(token.data(), token.data() + token.size(), id); + if(error != std::errc{} || position != token.data() + token.size()) + throw std::invalid_argument("invalid order id"); + result.push_back(id); + if(end == std::string::npos) + break; + begin = end + 1; + } + return result; +} +template +std::shared_ptr item_by_id(const std::vector>& list, std::uint64_t id) { + if(id == 0 || id > list.size()) + throw std::out_of_range("record not found"); + return list[id - 1]; +} +template +std::vector ordered_keys(const std::vector>& list, const std::vector& ids) { + if(ids.size() != list.size()) + throw std::invalid_argument("order must contain every record"); + std::vector used(list.size()); + std::vector keys; + keys.reserve(ids.size()); + for(const auto id : ids) { + if(id == 0 || id > list.size() || used[id - 1]) + throw std::invalid_argument("invalid order id"); + used[id - 1] = true; + keys.push_back(list[id - 1]->key); + } + return keys; +} +std::string source_type_name(Data_Source_Type value) { + return std::string(magic_enum::enum_name(value)); +} +std::string feed_type_name(Data_Feed_Type value) { + return std::string(magic_enum::enum_name(value)); +} +std::string relation_type_name(Source_Feed_Relation_Type value) { + return std::string(magic_enum::enum_name(value)); +} +Data_Source_Type source_type(std::string_view value) { + const auto result = magic_enum::enum_cast(value); + if(!result) + throw std::invalid_argument("未知数据源类型: " + std::string(value)); + return *result; +} +Data_Feed_Type feed_type(std::string_view value) { + const auto result = magic_enum::enum_cast(value); + if(!result) + throw std::invalid_argument("未知数据馈送类型: " + std::string(value)); + return *result; +} +Source_Feed_Relation_Type relation_type(std::string_view value) { + const auto result = magic_enum::enum_cast(value); + if(!result) + throw std::invalid_argument("未知关系类型: " + std::string(value)); + return *result; +} +Data_Source_Row source_row(const std::shared_ptr& source) { + Data_Source_Row row; + row.key = source->key; + row.type = source_type(source->type); + row.enable = source->enabled(); + row.config = source->settings.snapshot(); + row.state = Psc::to_string(source->state); + if(const auto source_value = dynamic_cast(source.get())) { + const auto value = source_value->specific.snapshot(); + row.ip = value.ip; + row.port = value.port; + } else if(const auto source_value = dynamic_cast(source.get())) { + const auto value = source_value->specific.snapshot(); + row.port_name = value.port_name; + row.baud_rate = value.baud_rate; + } else if(const auto source_value = dynamic_cast(source.get())) { + const auto value = source_value->specific.snapshot(); + row.file_path = value.file_path; + row.data_type = value.data_type; + row.play_mode = value.play_mode; + } else if(const auto source_value = dynamic_cast(source.get())) { + const auto value = source_value->specific.snapshot(); + row.library_path = value.library_path; + row.function_name = value.function_name; + row.data_type = value.data_type; + row.buffer_size = value.buffer_size; + } else if(const auto source_value = dynamic_cast(source.get())) { + const auto value = source_value->specific.snapshot(); + row.shared_memory_name = value.shared_memory_name; + row.shared_memory_size = value.shared_memory_size; + row.data_type = value.data_type; + } + return row; +} +void apply_source_row(const std::shared_ptr& source, const Data_Source_Row& row) { + source->set_enabled(row.enable); + source->settings.write([&](auto& value) { + value = row.config; + }); + if(auto source_value = dynamic_cast(source.get())) { + source_value->specific.write([&](auto& value) { + value.ip = row.ip; + value.port = row.port; + }); + } else if(auto source_value = dynamic_cast(source.get())) { + source_value->specific.write([&](auto& value) { + value.port_name = row.port_name; + value.baud_rate = row.baud_rate; + }); + } else if(auto source_value = dynamic_cast(source.get())) { + source_value->specific.write([&](auto& value) { + value.file_path = row.file_path; + value.data_type = row.data_type; + value.play_mode = row.play_mode; + }); + } else if(auto source_value = dynamic_cast(source.get())) { + source_value->specific.write([&](auto& value) { + value.library_path = row.library_path; + value.function_name = row.function_name; + value.data_type = row.data_type; + value.buffer_size = row.buffer_size; + }); + source_value->buffer.resize(row.buffer_size); + } else if(auto source_value = dynamic_cast(source.get())) { + source_value->specific.write([&](auto& value) { + value.shared_memory_name = row.shared_memory_name; + value.shared_memory_size = row.shared_memory_size; + value.data_type = row.data_type; + }); + } +} +Data_Feed_Row feed_row(const std::shared_ptr& feed) { + Data_Feed_Row row; + row.key = feed->key; + row.type = feed_type(feed->type); + row.enable = feed->enabled(); + row.output_format = feed->output_format.snapshot(); + row.format = row.output_format.type; + row.state = Psc::to_string(feed->state); + if(const auto feed_value = dynamic_cast(feed.get())) { + const auto value = feed_value->specific.snapshot(); + row.port = value.port; + row.connect_user_buffer_size = value.connect_user_buffer_size; + row.connect_system_buffer_size = value.connect_system_buffer_size; + } else if(const auto feed_value = dynamic_cast(feed.get())) { + row.port = feed_value->specific.member<&Data_Feed_UDP_Server_Data::port>().snapshot(); + } else if(const auto feed_value = dynamic_cast(feed.get())) { + const auto value = feed_value->specific.snapshot(); + row.url = value.url; + row.port = value.port; + } else if(const auto feed_value = dynamic_cast(feed.get())) { + const auto value = feed_value->specific.snapshot(); + row.url = value.url; + row.port = value.port; + } + return row; +} +void apply_feed_row(const std::shared_ptr& feed, const Data_Feed_Row& row) { + feed->set_enabled(row.enable); + feed->output_format.write([&](auto& value) { + value = row.output_format; + }); + if(auto feed_value = dynamic_cast(feed.get())) { + feed_value->specific.write([&](auto& value) { + value.port = row.port; + value.connect_user_buffer_size = row.connect_user_buffer_size; + value.connect_system_buffer_size = row.connect_system_buffer_size; + }); + } else if(auto feed_value = dynamic_cast(feed.get())) { + feed_value->specific.member<&Data_Feed_UDP_Server_Data::port>().write([&](auto& value) { + value = row.port; + }); + } else if(auto feed_value = dynamic_cast(feed.get())) { + feed_value->specific.write([&](auto& value) { + value.url = row.url; + value.port = row.port; + }); + } else if(auto feed_value = dynamic_cast(feed.get())) { + feed_value->specific.write([&](auto& value) { + value.url = row.url; + value.port = row.port; + }); + } +} +Source_Feed_Relation_Row relation_row(const std::shared_ptr& relation) { + Source_Feed_Relation_Row row; + const auto value = relation->settings.snapshot(); + row.key = relation->key; + row.type = relation_type(relation->type); + row.enable = value.enable; + row.source_key = value.source_key; + row.feed_key = value.feed_key; + row.feed_name = value.feed_name; + return row; +} +void apply_relation_row(const std::shared_ptr& relation, const Source_Feed_Relation_Row& row) { + relation->settings.write([&](auto& value) { + value.enable = row.enable; + value.source_key = row.source_key; + value.feed_key = row.feed_key; + value.feed_name = row.feed_name; + }); +} +template +adminive::Http_Response list_response(std::string path, std::vector rows, adminive::Collection_Query query) { + adminive::Collection_Service collection(std::move(path), std::move(rows)); + return collection.list_response(std::move(query)); +} +template +adminive::Http_Response item_response(std::string path, std::vector rows, std::uint64_t id) { + adminive::Collection_Service collection(std::move(path), std::move(rows)); + return collection.item_response(id); +} +std::vector source_rows(Global* global) { + std::vector rows; + for(const auto& source : global->mode_acs.data_source_config.map.list()) + rows.push_back(source_row(source)); + return rows; +} +std::vector feed_rows(Global* global) { + std::vector rows; + for(const auto& feed : global->mode_acs.data_feed_config.map.list()) + rows.push_back(feed_row(feed)); + return rows; +} +std::vector relation_rows(Global* global) { + std::vector rows; + for(const auto& relation : global->mode_acs.source_feed_relation_config.map.list()) + rows.push_back(relation_row(relation)); + return rows; +} +Json source_amis_schema() { + return adminive::to_amis_table_schema("/api/adminive/config/data_sources", adminive::describe_table_view()); +} +Json feed_amis_schema() { + return adminive::to_amis_table_schema("/api/adminive/config/data_feeds", adminive::describe_table_view()); +} +Json relation_amis_schema() { + return adminive::to_amis_table_schema("/api/adminive/config/source_feed_relations", adminive::describe_table_view()); +} +class Adminive_Config_Resources { +public: + explicit Adminive_Config_Resources(Global* global) : global_(global), + mode_acs_resource_(global->mode_acs.settings, mode_acs_path, mode_acs_transaction()), + data_feed_settings_resource_(global->mode_acs.data_feed_config.settings, data_feed_settings_path, data_feed_settings_transaction()), + mlat_resource_(global->mlat.settings, mlat_path, value_section_transaction(Config_Section::mlat)), + cesium_graphics_resource_(global->cesium_graphics_config.settings, cesium_graphics_path, value_section_transaction(Config_Section::cesium_graphics)) { + } + void bind() { + auto& app = drogon::app(); + mode_acs_resource_.bind(app); + data_feed_settings_resource_.bind(app); + mlat_resource_.bind(app); + cesium_graphics_resource_.bind(app); + bind_data_sources(app); + bind_data_feeds(app); + bind_relations(app); + app.registerHandler(manifest_path, [this](const drogon::HttpRequestPtr&, std::function&& callback) { + callback(adminive::make_drogon_response(adminive::make_http_success(manifest()))); + }, {drogon::Get}); + } +private: + void bind_data_sources(drogon::HttpAppFramework& app) { + const auto get = adminive::make_drogon_constraints(drogon::Get, {}); + const auto post = adminive::make_drogon_constraints(drogon::Post, {}); + const auto put = adminive::make_drogon_constraints(drogon::Put, {}); + const auto patch = adminive::make_drogon_constraints(drogon::Patch, {}); + const auto remove = adminive::make_drogon_constraints(drogon::Delete, {}); + app.registerHandler(data_sources_path + "/descriptor", [](const drogon::HttpRequestPtr&, std::function&& callback) { + callback(adminive::make_drogon_response(adminive::make_http_success(adminive::to_descriptor_json()))); + }, get); + app.registerHandler(data_sources_path + "/view", [](const drogon::HttpRequestPtr&, std::function&& callback) { + callback(adminive::make_drogon_response(adminive::make_http_success(adminive::to_view_json(adminive::describe_table_view())))); + }, get); + app.registerHandler(data_sources_path + "/amis", [](const drogon::HttpRequestPtr&, std::function&& callback) { + callback(adminive::make_drogon_response(adminive::make_http_success(source_amis_schema()))); + }, get); + app.registerHandler(data_sources_path, [this](const drogon::HttpRequestPtr& request, std::function&& callback) { + callback(adminive::make_drogon_response(list_response(data_sources_path, source_rows(global_), read_collection_query(request)))); + }, get); + app.registerHandler(data_sources_path + "/{1:id}", [this](const drogon::HttpRequestPtr&, std::function&& callback, std::uint64_t id) { + callback(adminive::make_drogon_response(item_response(data_sources_path, source_rows(global_), id))); + }, get); + app.registerHandler(data_sources_path, [this](const drogon::HttpRequestPtr& request, std::function&& callback) { + complete_business_request(callback, [this, body = std::string(request->getBody())] { + std::lock_guard lock(topology_mutex_); + Json input; + try { + input = adminive::parse_json(body); + } catch(const std::exception& error) { + return adminive::make_http_error(400, error.what()); + } + adminive::Json_Adapter::erase(input, "id"); + Data_Source_Row row; + const auto result = adminive::apply_frontend_create(row, input); + if(!result.success) + return adminive::make_http_error(422, result); + if(global_->mode_acs.data_source_config.map.get(row.key)) + return adminive::make_http_error(409, "数据源名称已存在: " + row.key); + auto source = create_data_source_from_type(source_type_name(row.type)); + source->key = row.key; + apply_source_row(source, row); + global_->mode_acs.data_source_config.map.push_back(source); + Source_Feed_Relation_Config::set_need_refresh(); + save_data_sources(global_); + return adminive::make_http_success(adminive::to_frontend_json(source_row(source)), "created"); + }); + }, post); + const auto update = [this](const drogon::HttpRequestPtr& request, std::function&& callback, std::uint64_t id) { + complete_business_request(callback, [this, id, body = std::string(request->getBody())] { + std::lock_guard lock(topology_mutex_); + const auto list = global_->mode_acs.data_source_config.map.list(); + auto source = item_by_id(list, id); + Json input; + try { + input = adminive::parse_json(body); + } catch(const std::exception& error) { + return adminive::make_http_error(400, error.what()); + } + adminive::Json_Adapter::erase(input, "id"); + auto row = source_row(source); + const auto result = adminive::apply_frontend_patch(row, input); + if(!result.success) + return adminive::make_http_error(422, result); + apply_source_row(source, row); + Source_Feed_Relation_Config::set_need_refresh(); + save_data_sources(global_); + return adminive::make_http_success(adminive::to_frontend_json(source_row(source)), result.message); + }); + }; + app.registerHandler(data_sources_path + "/{1:id}", update, put); + app.registerHandler(data_sources_path + "/{1:id}", update, patch); + app.registerHandler(data_sources_path + "/{1:id}", [this](const drogon::HttpRequestPtr&, std::function&& callback, std::uint64_t id) { + complete_business_request(callback, [this, id] { + std::lock_guard lock(topology_mutex_); + const auto list = global_->mode_acs.data_source_config.map.list(); + auto source = item_by_id(list, id); + if(global_->mode_acs.source_feed_relation_config.source_has_dependency(source->key)) + throw Conflict_Error("数据源存在数据馈送关系依赖,不能删除: " + source->key); + source->async_stop(); + if(!global_->mode_acs.data_source_config.map.remove(id - 1)) + throw std::out_of_range("record not found"); + Source_Feed_Relation_Config::set_need_refresh(); + save_data_sources(global_); + return adminive::make_http_success(adminive::json_object(), "deleted"); + }); + }, remove); + app.registerHandler(data_sources_path + "/order", [this](const drogon::HttpRequestPtr& request, std::function&& callback) { + complete_business_request(callback, [this, body = std::string(request->getBody())] { + std::lock_guard lock(topology_mutex_); + const auto list = global_->mode_acs.data_source_config.map.list(); + const auto keys = ordered_keys(list, read_order_ids(body)); + if(!global_->mode_acs.data_source_config.map.set_order(keys)) + throw std::invalid_argument("invalid order"); + Source_Feed_Relation_Config::set_need_refresh(); + save_data_sources(global_); + return adminive::make_http_success(adminive::json_object(), "reordered"); + }); + }, post); + } + void bind_data_feeds(drogon::HttpAppFramework& app) { + const auto get = adminive::make_drogon_constraints(drogon::Get, {}); + const auto post = adminive::make_drogon_constraints(drogon::Post, {}); + const auto put = adminive::make_drogon_constraints(drogon::Put, {}); + const auto patch = adminive::make_drogon_constraints(drogon::Patch, {}); + const auto remove = adminive::make_drogon_constraints(drogon::Delete, {}); + app.registerHandler(data_feeds_path + "/descriptor", [](const drogon::HttpRequestPtr&, std::function&& callback) { + callback(adminive::make_drogon_response(adminive::make_http_success(adminive::to_descriptor_json()))); + }, get); + app.registerHandler(data_feeds_path + "/view", [](const drogon::HttpRequestPtr&, std::function&& callback) { + callback(adminive::make_drogon_response(adminive::make_http_success(adminive::to_view_json(adminive::describe_table_view())))); + }, get); + app.registerHandler(data_feeds_path + "/amis", [](const drogon::HttpRequestPtr&, std::function&& callback) { + callback(adminive::make_drogon_response(adminive::make_http_success(feed_amis_schema()))); + }, get); + app.registerHandler(data_feeds_path, [this](const drogon::HttpRequestPtr& request, std::function&& callback) { + callback(adminive::make_drogon_response(list_response(data_feeds_path, feed_rows(global_), read_collection_query(request)))); + }, get); + app.registerHandler(data_feeds_path + "/{1:id}", [this](const drogon::HttpRequestPtr&, std::function&& callback, std::uint64_t id) { + callback(adminive::make_drogon_response(item_response(data_feeds_path, feed_rows(global_), id))); + }, get); + app.registerHandler(data_feeds_path, [this](const drogon::HttpRequestPtr& request, std::function&& callback) { + complete_business_request(callback, [this, body = std::string(request->getBody())] { + std::lock_guard lock(topology_mutex_); + Json input; + try { + input = adminive::parse_json(body); + } catch(const std::exception& error) { + return adminive::make_http_error(400, error.what()); + } + adminive::Json_Adapter::erase(input, "id"); + Data_Feed_Row row; + const auto result = adminive::apply_frontend_create(row, input); + if(!result.success) + return adminive::make_http_error(422, result); + if(global_->mode_acs.data_feed_config.map.get(row.key)) + return adminive::make_http_error(409, "数据馈送名称已存在: " + row.key); + auto feed = create_data_feed_from_type(feed_type_name(row.type)); + feed->key = row.key; + apply_feed_row(feed, row); + global_->mode_acs.data_feed_config.map.push_back(feed); + Source_Feed_Relation_Config::set_need_refresh(); + save_data_feeds(global_); + return adminive::make_http_success(adminive::to_frontend_json(feed_row(feed)), "created"); + }); + }, post); + const auto update = [this](const drogon::HttpRequestPtr& request, std::function&& callback, std::uint64_t id) { + complete_business_request(callback, [this, id, body = std::string(request->getBody())] { + std::lock_guard lock(topology_mutex_); + const auto list = global_->mode_acs.data_feed_config.map.list(); + auto feed = item_by_id(list, id); + Json input; + try { + input = adminive::parse_json(body); + } catch(const std::exception& error) { + return adminive::make_http_error(400, error.what()); + } + adminive::Json_Adapter::erase(input, "id"); + auto row = feed_row(feed); + const auto result = adminive::apply_frontend_patch(row, input); + if(!result.success) + return adminive::make_http_error(422, result); + apply_feed_row(feed, row); + Source_Feed_Relation_Config::set_need_refresh(); + save_data_feeds(global_); + return adminive::make_http_success(adminive::to_frontend_json(feed_row(feed)), result.message); + }); + }; + app.registerHandler(data_feeds_path + "/{1:id}", update, put); + app.registerHandler(data_feeds_path + "/{1:id}", update, patch); + app.registerHandler(data_feeds_path + "/{1:id}", [this](const drogon::HttpRequestPtr&, std::function&& callback, std::uint64_t id) { + complete_business_request(callback, [this, id] { + std::lock_guard lock(topology_mutex_); + const auto list = global_->mode_acs.data_feed_config.map.list(); + auto feed = item_by_id(list, id); + if(global_->mode_acs.source_feed_relation_config.feed_has_dependency(feed->key)) + throw Conflict_Error("数据馈送存在数据源关系依赖,不能删除: " + feed->key); + feed->async_stop(); + if(!global_->mode_acs.data_feed_config.map.remove(id - 1)) + throw std::out_of_range("record not found"); + Source_Feed_Relation_Config::set_need_refresh(); + save_data_feeds(global_); + return adminive::make_http_success(adminive::json_object(), "deleted"); + }); + }, remove); + app.registerHandler(data_feeds_path + "/order", [this](const drogon::HttpRequestPtr& request, std::function&& callback) { + complete_business_request(callback, [this, body = std::string(request->getBody())] { + std::lock_guard lock(topology_mutex_); + const auto list = global_->mode_acs.data_feed_config.map.list(); + const auto keys = ordered_keys(list, read_order_ids(body)); + if(!global_->mode_acs.data_feed_config.map.set_order(keys)) + throw std::invalid_argument("invalid order"); + Source_Feed_Relation_Config::set_need_refresh(); + save_data_feeds(global_); + return adminive::make_http_success(adminive::json_object(), "reordered"); + }); + }, post); + } + void bind_relations(drogon::HttpAppFramework& app) { + const auto get = adminive::make_drogon_constraints(drogon::Get, {}); + const auto post = adminive::make_drogon_constraints(drogon::Post, {}); + const auto put = adminive::make_drogon_constraints(drogon::Put, {}); + const auto patch = adminive::make_drogon_constraints(drogon::Patch, {}); + const auto remove = adminive::make_drogon_constraints(drogon::Delete, {}); + app.registerHandler(relations_path + "/descriptor", [](const drogon::HttpRequestPtr&, std::function&& callback) { + callback(adminive::make_drogon_response(adminive::make_http_success(adminive::to_descriptor_json()))); + }, get); + app.registerHandler(relations_path + "/view", [](const drogon::HttpRequestPtr&, std::function&& callback) { + callback(adminive::make_drogon_response(adminive::make_http_success(adminive::to_view_json(adminive::describe_table_view())))); + }, get); + app.registerHandler(relations_path + "/amis", [](const drogon::HttpRequestPtr&, std::function&& callback) { + callback(adminive::make_drogon_response(adminive::make_http_success(relation_amis_schema()))); + }, get); + app.registerHandler(relations_path, [this](const drogon::HttpRequestPtr& request, std::function&& callback) { + callback(adminive::make_drogon_response(list_response(relations_path, relation_rows(global_), read_collection_query(request)))); + }, get); + app.registerHandler(relations_path + "/{1:id}", [this](const drogon::HttpRequestPtr&, std::function&& callback, std::uint64_t id) { + callback(adminive::make_drogon_response(item_response(relations_path, relation_rows(global_), id))); + }, get); + app.registerHandler(relations_path, [this](const drogon::HttpRequestPtr& request, std::function&& callback) { + complete_business_request(callback, [this, body = std::string(request->getBody())] { + std::lock_guard lock(topology_mutex_); + Json input; + try { + input = adminive::parse_json(body); + } catch(const std::exception& error) { + return adminive::make_http_error(400, error.what()); + } + adminive::Json_Adapter::erase(input, "id"); + Source_Feed_Relation_Row row; + const auto result = adminive::apply_frontend_create(row, input); + if(!result.success) + return adminive::make_http_error(422, result); + auto relation = create_source_feed_relation_from_type(relation_type_name(row.type)); + relation->key = row.key; + apply_relation_row(relation, row); + global_->mode_acs.source_feed_relation_config.validate(*relation); + global_->mode_acs.source_feed_relation_config.map.push_back(relation); + Source_Feed_Relation_Config::set_need_refresh(); + save_source_feed_relations(global_); + return adminive::make_http_success(adminive::to_frontend_json(relation_row(relation)), "created"); + }); + }, post); + const auto update = [this](const drogon::HttpRequestPtr& request, std::function&& callback, std::uint64_t id) { + complete_business_request(callback, [this, id, body = std::string(request->getBody())] { + std::lock_guard lock(topology_mutex_); + const auto list = global_->mode_acs.source_feed_relation_config.map.list(); + auto relation = item_by_id(list, id); + Json input; + try { + input = adminive::parse_json(body); + } catch(const std::exception& error) { + return adminive::make_http_error(400, error.what()); + } + adminive::Json_Adapter::erase(input, "id"); + auto row = relation_row(relation); + const auto result = adminive::apply_frontend_patch(row, input); + if(!result.success) + return adminive::make_http_error(422, result); + auto candidate = create_source_feed_relation_from_type(relation->type); + candidate->key = relation->key; + apply_relation_row(candidate, row); + global_->mode_acs.source_feed_relation_config.validate(*candidate, relation->key); + apply_relation_row(relation, row); + Source_Feed_Relation_Config::set_need_refresh(); + save_source_feed_relations(global_); + return adminive::make_http_success(adminive::to_frontend_json(relation_row(relation)), result.message); + }); + }; + app.registerHandler(relations_path + "/{1:id}", update, put); + app.registerHandler(relations_path + "/{1:id}", update, patch); + app.registerHandler(relations_path + "/{1:id}", [this](const drogon::HttpRequestPtr&, std::function&& callback, std::uint64_t id) { + complete_business_request(callback, [this, id] { + std::lock_guard lock(topology_mutex_); + const auto list = global_->mode_acs.source_feed_relation_config.map.list(); + item_by_id(list, id); + if(!global_->mode_acs.source_feed_relation_config.map.remove(id - 1)) + throw std::out_of_range("record not found"); + Source_Feed_Relation_Config::set_need_refresh(); + save_source_feed_relations(global_); + return adminive::make_http_success(adminive::json_object(), "deleted"); + }); + }, remove); + app.registerHandler(relations_path + "/order", [this](const drogon::HttpRequestPtr& request, std::function&& callback) { + complete_business_request(callback, [this, body = std::string(request->getBody())] { + std::lock_guard lock(topology_mutex_); + const auto list = global_->mode_acs.source_feed_relation_config.map.list(); + const auto keys = ordered_keys(list, read_order_ids(body)); + if(!global_->mode_acs.source_feed_relation_config.map.set_order(keys)) + throw std::invalid_argument("invalid order"); + Source_Feed_Relation_Config::set_need_refresh(); + save_source_feed_relations(global_); + return adminive::make_http_success(adminive::json_object(), "reordered"); + }); + }, post); + } + Json manifest() const { + const auto view = adminive::composition_view("ecap_config", adminive::compose::frontend(adminive::compose::slot("mode_acs"), adminive::compose::slot("data_feed_settings"), adminive::compose::slot("data_sources"), adminive::compose::slot("data_feeds"), adminive::compose::slot("source_feed_relations"), adminive::compose::slot("mlat"), adminive::compose::slot("cesium_graphics"))).titled("ECAP 配置"); + const std::vector contracts = { + {"mode_acs", "amis_schema", mode_acs_path + "/descriptor", mode_acs_path + "/view", mode_acs_path + "/data", mode_acs_path + "/amis"}, + {"data_feed_settings", "amis_schema", data_feed_settings_path + "/descriptor", data_feed_settings_path + "/view", data_feed_settings_path + "/data", data_feed_settings_path + "/amis"}, + {"data_sources", "amis_schema", data_sources_path + "/descriptor", data_sources_path + "/view", data_sources_path, data_sources_path + "/amis"}, + {"data_feeds", "amis_schema", data_feeds_path + "/descriptor", data_feeds_path + "/view", data_feeds_path, data_feeds_path + "/amis"}, + {"source_feed_relations", "amis_schema", relations_path + "/descriptor", relations_path + "/view", relations_path, relations_path + "/amis"}, + {"mlat", "amis_schema", mlat_path + "/descriptor", mlat_path + "/view", mlat_path + "/data", mlat_path + "/amis"}, + {"cesium_graphics", "amis_schema", cesium_graphics_path + "/descriptor", cesium_graphics_path + "/view", cesium_graphics_path + "/data", cesium_graphics_path + "/amis"} + }; + const std::map schemas = { + {"mode_acs", mode_acs_resource_.amis_schema()}, + {"data_feed_settings", data_feed_settings_resource_.amis_schema()}, + {"data_sources", source_amis_schema()}, + {"data_feeds", feed_amis_schema()}, + {"source_feed_relations", relation_amis_schema()}, + {"mlat", mlat_resource_.amis_schema()}, + {"cesium_graphics", cesium_graphics_resource_.amis_schema()} + }; + return adminive::to_composition_manifest_json(view, contracts, schemas); + } + static inline const std::string mode_acs_path = "/api/adminive/config/mode_acs"; + static inline const std::string data_feed_settings_path = "/api/adminive/config/data_feed_settings"; + static inline const std::string data_sources_path = "/api/adminive/config/data_sources"; + static inline const std::string data_feeds_path = "/api/adminive/config/data_feeds"; + static inline const std::string relations_path = "/api/adminive/config/source_feed_relations"; + static inline const std::string mlat_path = "/api/adminive/config/mlat"; + static inline const std::string cesium_graphics_path = "/api/adminive/config/cesium_graphics"; + static inline const std::string manifest_path = "/api/adminive/config/manifest"; + Global* global_; + std::mutex topology_mutex_; + adminive::Drogon_Resource mode_acs_resource_; + adminive::Drogon_Resource data_feed_settings_resource_; + adminive::Drogon_Resource mlat_resource_; + adminive::Drogon_Resource cesium_graphics_resource_; +}; +} +void register_adminive_config(Global* global) { + static auto resources = std::make_unique(global); + resources->bind(); +} +} diff --git a/module/Local_Server/server/Adminive_Config.h b/module/Local_Server/server/Adminive_Config.h new file mode 100644 index 0000000..0bb096c --- /dev/null +++ b/module/Local_Server/server/Adminive_Config.h @@ -0,0 +1,5 @@ +#pragma once +class Global; +namespace ecap::adminive_config { +void register_adminive_config(Global* global); +} diff --git a/module/Local_Server/server/Config.cpp b/module/Local_Server/server/Config.cpp index 6d7d66a..1b17010 100644 --- a/module/Local_Server/server/Config.cpp +++ b/module/Local_Server/server/Config.cpp @@ -1,30 +1,136 @@ #include "Config.h" +#include "Config_Default.h" #include +#include +#include #include +#include +#include #include #include #include "Global.h" #include "Psc_Cpp_Core/Base/Check.h" #include "Psc_Cpp_Core/Net_Adapter/Net_Adapter.h" #undef interface -std::string default_config_path; -std::string get_config_path() { - // #ifdef WIN32 - // get_exe_dir() - // return "D:/ae/proj/projects/ECAP_Server/config/config.json"; - // #endif - static bool first = true; - if (!default_config_path.empty()) - return default_config_path; - std::string ret; - std::ostringstream oss; - std::string file_name = "config.json"; - ret = get_exe_dir() + "/" + file_name; - if (first) { - std::cout << oss.str() << std::endl; - first = false; +std::string default_config_directory; +namespace { +constexpr std::string_view config_section_names[] = {"version", "dsp", "drogon", "http_monitor", "mlat", "web_server", "map_resources", "map_view", "map_models", "device", "mode_acs", "log", "console", "external_database", "cesium_graphics"}; +constexpr std::string_view config_section_name(Config_Section section) { + return config_section_names[static_cast(section)]; +} +std::filesystem::path config_directory() { + if (!default_config_directory.empty()) { + return default_config_directory; } - return ret; + return std::filesystem::path(get_exe_dir()) / "config"; +} +std::filesystem::path config_section_path(Config_Section section) { + return config_directory() / (std::string(config_section_name(section)) + ".json"); +} +std::array(Config_Section::count)> config_section_mutexes; +void write_config_section_unlocked(Config_Section section, const Psc::JSON& value) { + const auto directory = config_directory(); + std::filesystem::create_directories(directory); + const auto path = config_section_path(section); + auto temporary = path; + temporary += ".tmp"; + std::ofstream output(temporary, std::ios::binary | std::ios::trunc); + if (!output) { + throw std::runtime_error("cannot open config section: " + temporary.string()); + } + const auto json = value.to_json_string(); + output.write(json.data(), static_cast(json.size())); + output.close(); + if (output.fail()) { + throw std::runtime_error("cannot write config section: " + temporary.string()); + } + std::error_code error; + std::filesystem::remove(path, error); + std::filesystem::rename(temporary, path); +} +void write_config_section(Config_Section section, const Psc::JSON& value) { + std::lock_guard lock(config_section_mutexes[static_cast(section)]); + write_config_section_unlocked(section, value); +} +Psc::JSON read_existing_config_section_unlocked(Config_Section section) { + const auto path = config_section_path(section); + std::ifstream input(path, std::ios::binary); + if (!input) { + throw std::runtime_error("cannot open config section: " + path.string()); + } + std::ostringstream buffer; + buffer << input.rdbuf(); + return Psc::parse_json(buffer.str()); +} +void merge_json_object(Psc::JSON& target, const Psc::JSON& value) { + for (const auto& child : value.children) { + auto iterator = std::find_if(target.children.begin(), target.children.end(), [&](const Psc::JSON& current) { + return current.key == child.key; + }); + if (iterator == target.children.end()) { + target.append(child); + continue; + } + if (iterator->valueType == Psc::Object && child.valueType == Psc::Object) + merge_json_object(*iterator, child); + else + *iterator = child; + } +} +Psc::JSON read_config_section(Config_Section section, const Psc::JSON& defaults) { + const auto path = config_section_path(section); + if (!std::filesystem::exists(path)) { + const auto name = config_section_name(section); + const auto value = defaults.get(name); + if (value == nullptr) { + throw std::runtime_error("default config section does not exist: " + std::string(name)); + } + write_config_section(section, *value); + return *value; + } + std::ifstream input(path, std::ios::binary); + if (!input) { + throw std::runtime_error("cannot open config section: " + path.string()); + } + std::ostringstream buffer; + buffer << input.rdbuf(); + return Psc::parse_json(buffer.str()); +} +Psc::JSON config_section_value(Global& global, Config_Section section) { + switch (section) { + case Config_Section::version: return Psc::JSON(global.version); + case Config_Section::dsp: return global.dsp_config.to_base_json(); + case Config_Section::drogon: return global.drogon_config; + case Config_Section::http_monitor: return global.http_monitor_config.to_base_json(); + case Config_Section::mlat: return global.mlat.to_base_json(); + case Config_Section::web_server: return global.web_server_config.to_base_json(); + case Config_Section::map_resources: return global.map_resources_config.to_base_json(); + case Config_Section::map_view: return global.map_view_config.to_base_json(); + case Config_Section::map_models: return global.map_model_config.to_base_json(); + case Config_Section::device: return global.device_config.to_base_json(); + case Config_Section::mode_acs: return global.mode_acs.to_base_json(); + case Config_Section::log: return global.log_config.to_base_json(); + case Config_Section::console: return global.console_config.to_base_json(); + case Config_Section::external_database: return global.external_resources_manager.to_base_json(); + case Config_Section::cesium_graphics: return global.cesium_graphics_config.to_base_json(); + case Config_Section::count: break; + } + std::terminate(); +} +} +std::string get_config_directory() { + return config_directory().string(); +} +Psc::JSON Config::load() { + std::filesystem::create_directories(config_directory()); + const auto defaults = Psc::parse_json(std::string_view(reinterpret_cast(ecap_default_config_json), sizeof(ecap_default_config_json))); + auto result = Psc::JSON::object(); + for (std::size_t index = 0; index < static_cast(Config_Section::count); ++index) { + const auto section = static_cast(index); + result.append({std::string(config_section_name(section)), read_config_section(section, defaults)}); + } + std::cout << get_current_date_string() << " 启动加载配置目录: 【" << config_directory().string() << "】" << std::endl; + return result; } static std::vector read_string_array(const Psc::JSON* json, std::string_view key) { auto field = json->get(key); @@ -359,27 +465,29 @@ Psc::JSON Map_View_Config::to_base_json() const { return ret; } void Cesium_Graphics_Config::from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) { - if (that_json == nullptr) { + if (that_json == nullptr) return; - } - if (that_json->valueType != Psc::Object) { + if (that_json->valueType != Psc::Object) throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "cesium_graphics"); - } - Cesium_Graphics_Config_Data::from_base_json(that_json, not_exist_use_default_value); - target_frame_rate = std::clamp(target_frame_rate, 1u, 120u); - resolution_scale = std::clamp(resolution_scale, 0.2, 1.5); - msaa_samples = std::clamp(msaa_samples, 1u, 8u); - maximum_screen_space_error = std::clamp(maximum_screen_space_error, 1u, 16u); - terrain_maximum_level = std::clamp(terrain_maximum_level, 0u, 24u); - terrain_cache_tiles = std::clamp(terrain_cache_tiles, 16u, 2048u); - performance_panel_x = std::max(0.0, performance_panel_x); - performance_panel_y = std::max(0.0, performance_panel_y); - view_axes_panel_y = std::max(0.0, view_axes_panel_y); - view_axes_panel_size = std::clamp(view_axes_panel_size, 48.0, 240.0); - occlusion_sample_spacing_meters = std::clamp(occlusion_sample_spacing_meters, 10.0, 5000.0); + auto value = settings.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + value.target_frame_rate = std::clamp(value.target_frame_rate, 1u, 120u); + value.resolution_scale = std::clamp(value.resolution_scale, 0.2, 1.5); + value.msaa_samples = std::clamp(value.msaa_samples, 1u, 8u); + value.maximum_screen_space_error = std::clamp(value.maximum_screen_space_error, 1u, 16u); + value.terrain_maximum_level = std::clamp(value.terrain_maximum_level, 0u, 24u); + value.terrain_cache_tiles = std::clamp(value.terrain_cache_tiles, 16u, 2048u); + value.performance_panel_x = std::max(0.0, value.performance_panel_x); + value.performance_panel_y = std::max(0.0, value.performance_panel_y); + value.view_axes_panel_y = std::max(0.0, value.view_axes_panel_y); + value.view_axes_panel_size = std::clamp(value.view_axes_panel_size, 48.0, 240.0); + value.occlusion_sample_spacing_meters = std::clamp(value.occlusion_sample_spacing_meters, 10.0, 5000.0); + settings.write([&](auto& target) { + target = value; + }); } Psc::JSON Cesium_Graphics_Config::to_base_json() const { - return Cesium_Graphics_Config_Data::to_base_json(); + return settings.snapshot().to_base_json(); } Map_Model_Item_Config::Map_Model_Item_Config(std::string url) : url(std::move(url)) { } @@ -431,34 +539,8 @@ Psc::JSON Map_Model_Config::to_base_json() const { return ret; } void Mode_ACS_Config::server(Global* g) { - auto& svr = g->svr; - auto& api = g->api; data_source_config.server(g); data_feed_config.server(g); - source_feed_relation_config.server(g); - svr.Post(api + "get_mode_s_basic_config", [this](HTTP_Param) { - CHECK_JSON_PARAM - res->setBody(warp(to_base_json()).to_json_string()); - }); - svr.Post(api + "set_mode_s_basic_config", [this](HTTP_Param) { - CHECK_JSON_PARAM - auto& j = o_params.value(); - init_base_json(&j, false); - if (auto min_points = j.try_get_number("aircraft_change_list_min_position_points")) { - aircraft_change_list_min_position_points = min_points.value(); - } - if (auto range_filter = j.try_get_bool("aircraft_change_list_adsb_range_filter")) { - aircraft_change_list_adsb_range_filter = range_filter.value(); - } - if (auto range_factor = j.try_get_number("aircraft_change_list_adsb_range_factor")) { - aircraft_change_list_adsb_range_factor = range_factor.value(); - } - if (auto target_altitude = j.try_get_number("adsb_theoretical_target_altitude_meters")) { - adsb_theoretical_target_altitude_meters = target_altitude.value(); - } - Global::save(); - res->setBody(warp(to_base_json()).to_json_string()); - }); } void Device_Config::server(Global* g) { auto& svr = g->svr; @@ -514,7 +596,7 @@ void Device_Config::server(Global* g) { *w.list.get("device").value() = device; *w.list.get("wifi").value() = wifi; } - Global::save(); + Global::save(Config_Section::device); if (use) { std::string interface; { @@ -571,9 +653,24 @@ void Device_Config::server(Global* g) { }); } void Config::save() { - std::ofstream config_file; - config_file.open(get_config_path()); - std::string json = Global::instance()->toJson().to_json_string(); - config_file.write(json.c_str(), static_cast(json.size())); - config_file.close(); + auto& global = *Global::instance(); + for (std::size_t index = 0; index < static_cast(Config_Section::count); ++index) { + const auto section = static_cast(index); + write_config_section(section, config_section_value(global, section)); + } +} +void Config::save(Config_Section section) { + auto& global = *Global::instance(); + write_config_section(section, config_section_value(global, section)); +} +void Config::save(Config_Section section, const Psc::JSON& value) { + write_config_section(section, value); +} +void Config::merge(Config_Section section, const Psc::JSON& value) { + std::lock_guard lock(config_section_mutexes[static_cast(section)]); + auto current = read_existing_config_section_unlocked(section); + if (current.valueType != Psc::Object || value.valueType != Psc::Object) + throw std::invalid_argument("config merge requires object"); + merge_json_object(current, value); + write_config_section_unlocked(section, current); } diff --git a/module/Local_Server/server/Config.h b/module/Local_Server/server/Config.h index 98e2eeb..1c134f7 100644 --- a/module/Local_Server/server/Config.h +++ b/module/Local_Server/server/Config.h @@ -9,9 +9,29 @@ #include "MLAT.h" #include "Psc_Cpp_Core/spdlog/export.h" #include "Source_Feed_Relation.h" +#include "adminive/json.hpp" +#include "adminive/managed.hpp" inline bool delete_log = false; -extern std::string default_config_path; -std::string get_config_path(); +extern std::string default_config_directory; +std::string get_config_directory(); +enum class Config_Section { + version, + dsp, + drogon, + http_monitor, + mlat, + web_server, + map_resources, + map_view, + map_models, + device, + mode_acs, + log, + console, + external_database, + cesium_graphics, + count +}; struct Log_Config { ~Log_Config() { if (delete_log) @@ -40,66 +60,85 @@ struct Http_Monitor_Config { std::size_t slow_scope_threshold_ms = 100; PSC_USE_JSON }; -struct Mode_ACS_Config_Base_Data { - Psc::Copyable_Atomic timeout_seconds{}; +struct Mode_ACS_Config_Data { + std::size_t timeout_seconds{}; SSR::CPR::D max_speed_m_s{}; SSR::CPR::D air_pos_timeout = 8; SSR::CPR::D surface_pos_timeout = 32; - Psc::Copyable_Atomic read_milliseconds{}; + std::size_t read_milliseconds{}; + bool wait_process_msg{}; + bool time_space_filter{}; + bool speed_filter{}; + std::size_t max_track_point_size{}; + std::size_t mode_s_max_num{}; + std::size_t mode_other_max_num{}; + std::size_t report_data_feed_msg_mum{}; + std::size_t default_min_aircraft_list_num{}; + std::size_t aircraft_change_list_min_position_points = 2; + bool aircraft_change_list_adsb_range_filter = true; + double aircraft_change_list_adsb_range_factor = 1.2; + double adsb_theoretical_target_altitude_meters = 10000.0; + bool ignore_df_11{}; + bool monitor_msg_live{}; 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{}; - Psc::Copyable_Atomic mode_s_max_num{}; - Psc::Copyable_Atomic mode_other_max_num{}; - Psc::Copyable_Atomic report_data_feed_msg_mum{}; - Psc::Copyable_Atomic default_min_aircraft_list_num{}; - Psc::Copyable_Atomic aircraft_change_list_min_position_points = 2; - Psc::Copyable_Atomic aircraft_change_list_adsb_range_filter = true; - Psc::Copyable_Atomic aircraft_change_list_adsb_range_factor = 1.2; - Psc::Copyable_Atomic adsb_theoretical_target_altitude_meters = 10000.0; - Psc::Copyable_Atomic ignore_df_11{}; - Psc::Copyable_Atomic monitor_msg_live{}; - PSC_USE_JSON +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Mode_ACS_Config_Data; + return object("mode_acs_config", "Mode ACS 基础配置", + ADMINIVE_FIELD_LABEL(T, timeout_seconds, "目标超时时间(秒)").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, max_speed_m_s, "飞机最大速度(m/s)").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, air_pos_timeout, "空中 CPR 超时(秒)").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, surface_pos_timeout, "地面 CPR 超时(秒)").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, read_milliseconds, "读取数据源延时(毫秒)").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, wait_process_msg, "等待处理消息").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, time_space_filter, "时空过滤").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, speed_filter, "速度过滤").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, max_track_point_size, "最大航迹点数量").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, mode_s_max_num, "Mode S 最大数量").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, mode_other_max_num, "其他 Mode 最大数量").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, report_data_feed_msg_mum, "数据馈送消息上报数量").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, default_min_aircraft_list_num, "默认最小飞机列表数量").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, aircraft_change_list_min_position_points, "飞机变化列表最少位置点").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, aircraft_change_list_adsb_range_filter, "基站理论范围过滤").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, aircraft_change_list_adsb_range_factor, "范围过滤系数").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, adsb_theoretical_target_altitude_meters, "理论目标高度(米)").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, ignore_df_11, "忽略 DF11").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, monitor_msg_live, "监控 Mode ACS 消息").editable().boolean_input()); + } }; -struct Mode_ACS_Config : Mode_ACS_Config_Base_Data, Mode_ACS_Config_Data { - DELETE_COPY(Mode_ACS_Config) +} +struct Mode_ACS_Config { + adminive::Managed_Value settings; Data_feed_Config data_feed_config; Data_Source_Config data_source_config; Source_Feed_Relation_Config source_feed_relation_config; - Psc::Copyable_Atomic Report_Data_Feed_Message_Num{}; + std::size_t Report_Data_Feed_Message_Num{}; Mode_ACS_Config() = default; ~Mode_ACS_Config() { if (delete_log) std::cout << " ~Mode_S_Config()" << std::endl; } Psc::JSON to_base_json() const { - return Mode_ACS_Config_Base_Data::to_base_json(); - } - void init_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) { - Mode_ACS_Config_Base_Data::from_base_json(that_json, not_exist_use_default_value); - } - void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) { - Mode_ACS_Config_Base_Data::from_base_json(that_json, not_exist_use_default_value); - Mode_ACS_Config_Data::from_base_json(that_json, not_exist_use_default_value); - data_feed_config.init(that_json->get("data_feed"), not_exist_use_default_value); - data_source_config.init(that_json->get("data_source"), not_exist_use_default_value); - source_feed_relation_config.from_json(that_json->get("source_feed_relation"), not_exist_use_default_value); - if (monitor_msg_live) { - SSR::Monitor_Mode_ACS_Message_Num = this->monitor_msg_live; - } - } - Psc::JSON to_base_json() { - Psc::JSON ret = Mode_ACS_Config_Base_Data::to_base_json(); - ret += Mode_ACS_Config_Data::to_base_json(); + auto ret = settings.snapshot().to_base_json(); ret.append(Psc::JSON("data_feed", data_feed_config.to_json())); ret.append(Psc::JSON("data_source", data_source_config.to_json())); ret.append(Psc::JSON("source_feed_relation", source_feed_relation_config.to_json())); return ret; } + void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) { + auto value = settings.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + settings.write([&](auto& target) { + target = value; + }); + data_feed_config.init(that_json->get("data_feed"), not_exist_use_default_value); + data_source_config.init(that_json->get("data_source"), not_exist_use_default_value); + source_feed_relation_config.from_json(that_json->get("source_feed_relation"), not_exist_use_default_value); + SSR::Monitor_Mode_ACS_Message_Num = value.monitor_msg_live; + } void server(Global* g); }; struct Web_Server_Config { @@ -232,7 +271,67 @@ struct Cesium_Graphics_Config_Data { double occlusion_clearance_margin_meters = 10.0; PSC_USE_JSON }; -struct Cesium_Graphics_Config : Cesium_Graphics_Config_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Cesium_Graphics_Config_Data; + auto descriptor = object("cesium_graphics_config", "Cesium 图形配置", + ADMINIVE_FIELD_LABEL(T, debug_show_frames_per_second, "显示帧率").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, target_frame_rate, "目标帧率").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, use_browser_recommended_resolution, "使用浏览器推荐分辨率").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, resolution_scale, "分辨率缩放").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, msaa_samples, "MSAA 采样数").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, fxaa, "启用 FXAA").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, shadows, "启用阴影").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, enable_lighting, "启用光照").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, solar_lighting, "启用太阳光照").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, solar_light_intensity, "太阳光强度").editable().required().number_input().visible_on("${$self.solar_lighting}"), + ADMINIVE_FIELD_LABEL(T, maximum_screen_space_error, "最大屏幕空间误差").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, terrain_enabled_in_3d, "3D 启用地形").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, terrain_exaggeration, "地形夸张系数").editable().required().number_input().visible_on("${$self.terrain_enabled_in_3d}"), + ADMINIVE_FIELD_LABEL(T, terrain_limit_maximum_level, "限制地形最大层级").editable().boolean_input().visible_on("${$self.terrain_enabled_in_3d}"), + ADMINIVE_FIELD_LABEL(T, terrain_maximum_level, "地形最大层级").editable().required().number_input().visible_on("${$self.terrain_enabled_in_3d && $self.terrain_limit_maximum_level}"), + ADMINIVE_FIELD_LABEL(T, terrain_cache_tiles, "地形缓存瓦片数").editable().required().number_input().visible_on("${$self.terrain_enabled_in_3d}"), + ADMINIVE_FIELD_LABEL(T, terrain_occlusion_enabled, "启用地形遮挡").editable().boolean_input().visible_on("${$self.terrain_enabled_in_3d}"), + ADMINIVE_FIELD_LABEL(T, surface_navigation_reference_visible, "显示地表导航参考").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, view_axes_visible, "显示视图坐标轴").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, performance_panel_x, "性能面板 X").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, performance_panel_y, "性能面板 Y").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, view_axes_panel_x, "坐标轴面板 X").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, view_axes_panel_y, "坐标轴面板 Y").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, view_axes_panel_size, "坐标轴面板尺寸").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, occlusion_sample_spacing_meters, "遮挡采样间距(米)").editable().required().number_input(), + ADMINIVE_FIELD_LABEL(T, occlusion_clearance_margin_meters, "遮挡净空余量(米)").editable().required().number_input()); + return descriptor.validator([](const T& value, Validation_Context& validation) { + if (value.target_frame_rate < 1 || value.target_frame_rate > 120) + validation.error("target_frame_rate", "必须在 1 到 120 之间"); + if (value.resolution_scale < 0.2 || value.resolution_scale > 1.5) + validation.error("resolution_scale", "必须在 0.2 到 1.5 之间"); + if (value.msaa_samples < 1 || value.msaa_samples > 8) + validation.error("msaa_samples", "必须在 1 到 8 之间"); + if (value.maximum_screen_space_error < 1 || value.maximum_screen_space_error > 16) + validation.error("maximum_screen_space_error", "必须在 1 到 16 之间"); + if (value.terrain_maximum_level > 24) + validation.error("terrain_maximum_level", "不能大于 24"); + if (value.terrain_cache_tiles < 16 || value.terrain_cache_tiles > 2048) + validation.error("terrain_cache_tiles", "必须在 16 到 2048 之间"); + if (value.performance_panel_x < 0) + validation.error("performance_panel_x", "不能小于 0"); + if (value.performance_panel_y < 0) + validation.error("performance_panel_y", "不能小于 0"); + if (value.view_axes_panel_y < 0) + validation.error("view_axes_panel_y", "不能小于 0"); + if (value.view_axes_panel_size < 48 || value.view_axes_panel_size > 240) + validation.error("view_axes_panel_size", "必须在 48 到 240 之间"); + if (value.occlusion_sample_spacing_meters < 10 || value.occlusion_sample_spacing_meters > 5000) + validation.error("occlusion_sample_spacing_meters", "必须在 10 到 5000 之间"); + }); + } +}; +} +struct Cesium_Graphics_Config { + adminive::Managed_Value settings; void from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value); [[nodiscard]] Psc::JSON to_base_json() const; }; @@ -322,22 +421,7 @@ public: Http_Monitor_Config http_monitor_config; std::string version; External_Resources_Manager external_resources_manager; - static Psc::JSON load() { - std::string config_path = get_config_path(); - std::ifstream iso(config_path); - if (!iso.is_open()) { - std::cerr << config_path << " can not open!" << std::endl; - Psc::fail_fast(); - } - else { - std::cout << get_current_date_string() << " 启动加载配置文件: 【" << config_path << "】" << std::endl; - } - std::stringstream ss; - std::stringstream buffer; - buffer << iso.rdbuf(); - std::string fileContents = buffer.str(); - return Psc::parse_json(fileContents); - } + static Psc::JSON load(); void from_base_json(Psc::JSON* that_json, bool not_exist_use_default_value) { Get_J(version) mlat.from_json(that_json->get("mlat"), not_exist_use_default_value); web_server_config.from_base_json(that_json->get("web_server"), not_exist_use_default_value); @@ -375,5 +459,8 @@ public: return ret; } static void save(); + static void save(Config_Section section); + static void save(Config_Section section, const Psc::JSON& value); + static void merge(Config_Section section, const Psc::JSON& value); }; #endif diff --git a/module/Local_Server/server/Config_Default.h.in b/module/Local_Server/server/Config_Default.h.in new file mode 100644 index 0000000..e9ea5a7 --- /dev/null +++ b/module/Local_Server/server/Config_Default.h.in @@ -0,0 +1,3 @@ +#pragma once +inline constexpr unsigned char ecap_default_config_json[] = { +@ecap_default_config_bytes@}; diff --git a/module/Local_Server/server/Global.h b/module/Local_Server/server/Global.h index df7ba1b..0314486 100644 --- a/module/Local_Server/server/Global.h +++ b/module/Local_Server/server/Global.h @@ -156,7 +156,7 @@ public: } std::shared_ptr total_source() { for (auto i : mode_acs.data_source_config.map.list()) { - if (i->enable) + if (i->enabled()) return i; } return mode_acs.data_source_config.map.list().at(0); diff --git a/module/Local_Server/server/MLAT.cpp b/module/Local_Server/server/MLAT.cpp index 0d22bc2..4c5ee5a 100644 --- a/module/Local_Server/server/MLAT.cpp +++ b/module/Local_Server/server/MLAT.cpp @@ -98,17 +98,21 @@ void read(MLAT_Data* buf, std::uint16_t* len) { MLAT_Config::MLAT_Config() { } void MLAT_Config::from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) { - MLAT_Config_Data::from_base_json(that_json, not_exist_use_default_value); - if (enable) { + auto value = settings.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + settings.write([&](auto& target) { + target = value; + }); + if (value.enable) { { - auto r = Psc::try_load_library(Psc::get_abs_path(library_path)); + auto r = Psc::try_load_library(Psc::get_abs_path(value.library_path)); if (!r) { Psc::fail_fast(); } lib = r.value(); } { - auto r = Psc::try_load_function(lib, function_name); + auto r = Psc::try_load_function(lib, value.function_name); if (!r) { Psc::fail_fast(); } diff --git a/module/Local_Server/server/MLAT.h b/module/Local_Server/server/MLAT.h index 7dd530d..7ced769 100644 --- a/module/Local_Server/server/MLAT.h +++ b/module/Local_Server/server/MLAT.h @@ -1,10 +1,10 @@ #ifndef MLAT_H #define MLAT_H -#include #include #include "../global_include.h" #include "Psc_Cpp_Core/Base/JSON.h" #include "Psc_Cpp_Core/system/export.h" +#include "adminive/managed.hpp" class Global; #pragma pack(push, 8) extern "C" { @@ -37,18 +37,32 @@ struct Base_MLAT_Data { extern std::multimap hex_mlat_map; class MLAT_Config_Data { public: - Psc::Copyable_Atomic enable{}; - Psc::Copyable_Atomic merge{}; + bool enable{}; + bool merge{}; std::string library_path; std::string function_name; PSC_USE_JSON }; -class MLAT_Config : public MLAT_Config_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = MLAT_Config_Data; + return object("mlat_config", "MLAT 配置", + ADMINIVE_FIELD_LABEL(T, enable, "启用 MLAT").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, merge, "合并 MLAT").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, library_path, "库路径").editable().text_input(), + ADMINIVE_FIELD_LABEL(T, function_name, "函数名").editable().text_input()); + } +}; +} +class MLAT_Config { public: + adminive::Managed_Value settings; ~MLAT_Config() = default; MLAT_Config(); [[nodiscard]] Psc::JSON to_base_json() const { - return MLAT_Config_Data::to_base_json(); + return settings.snapshot().to_base_json(); } void* lib{}; using Func_Type = void (*)(MLAT_Data* buf, std::uint16_t* len); diff --git a/module/Local_Server/server/Source_Feed_Relation.cpp b/module/Local_Server/server/Source_Feed_Relation.cpp index 962777c..758994c 100644 --- a/module/Local_Server/server/Source_Feed_Relation.cpp +++ b/module/Local_Server/server/Source_Feed_Relation.cpp @@ -1,70 +1,101 @@ #include "Source_Feed_Relation.h" #include "Config.h" #include "Global.h" -void Source_Feed_Relation_Config::set_need_refresh() { - for (const auto& li : Global::instance()->mode_acs.data_source_config.map.list()) { - li->need_refresh_data_feed_key_list = true; +void Source_Feed_Relation::from_json(const Psc::JSON* that_json) { + Get_J(key) Get_J(type) + auto value = settings.snapshot(); + value.from_base_json(that_json, true); + settings.write([&](auto& target) { + target = value; + }); +} +Psc::JSON Source_Feed_Relation::to_json() const { + auto ret = settings.snapshot().to_base_json(); + ret.append({"key", key}); + ret.append({"type", type}); + return ret; +} +void Source_Feed_Relation_Config::from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) { + static_cast(not_exist_use_default_value); + auto j_list = that_json->get("list"); + for (const Psc::JSON& item : j_list->children) { + auto relation = create_source_feed_relation_from_type(item.get_string("type")); + relation->from_json(&item); + validate(*relation); + map.push_back(relation); } } -void Source_Feed_Relation_Config::server(Global* g) { - auto& svr = g->svr; - auto& api = g->api; - std::string name = "source_feed_relation"; - svr.Post(api + "get_" + name + "_config", [this](HTTP_Param) { res->setBody(warp(to_json()).to_json_string()); }); - svr.Post(api + svr.search + name, [this, g](HTTP_Param) { - CHECK_JSON_PARAM - HTTP_REQUIRE_VALUE(key, params.try_get_string("key")) - HTTP_REQUIRE_VALUE(relation, map.get(key)) - res->setBody(warp(relation->to_json()).to_json_string()); - }); - svr.Post(api + svr.update + name, [this, g](HTTP_Param) { - CHECK_JSON_PARAM - std::cout << "Source_Feed_Relation:" << req->path() << that_json->to_json_string(); - HTTP_REQUIRE_VALUE(key, params.try_get_string("key")) - HTTP_REQUIRE_VALUE(ds, map.get(key)) - ds->from_json(¶ms); - // 直接关闭 - g->save(); - set_need_refresh(); - }); - svr.Post(api + svr.insert + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - std::cout << "Source_Feed_Relation:" << req->path() << that_json->to_json_string(); - HTTP_REQUIRE_VALUE(index, params.try_get_number("index")) - HTTP_REQUIRE_PTR(data, params.get("data")) - HTTP_REQUIRE_VALUE(t, data->try_get_string("type")) - std::cout << params.to_json_string() << std::endl; - auto ds = create_source_feed_relation_from_type(t); - ds->from_json(data); - HTTP_REQUIRE_TRUE(map.insert(index, ds), "index") - // std::cout << params.to_json_string() << std::endl; - res->setBody(warp(to_json()).to_json_string()); - g->save(); - set_need_refresh(); - }); - svr.Post(api + svr.remove + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - std::cout << "Source_Feed_Relation:" << req->path() << that_json->to_json_string(); - HTTP_REQUIRE_VALUE(index, params.try_get_number("index")) - HTTP_REQUIRE_TRUE(map.remove(index), "index") - g->save(); - res->setBody(warp(to_json()).to_json_string()); - set_need_refresh(); - }); - svr.Post(api + svr.rise + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - HTTP_REQUIRE_VALUE(index, params.try_get_number("index")) - HTTP_REQUIRE_TRUE(map.swap(index, index - 1), "index") - g->save(); - res->setBody(warp(to_json()).to_json_string()); - set_need_refresh(); - }); - svr.Post(api + svr.fall + name, [g, this](HTTP_Param) { - CHECK_JSON_PARAM - HTTP_REQUIRE_VALUE(index, params.try_get_number("index")) - HTTP_REQUIRE_TRUE(map.swap(index, index + 1), "index") - g->save(); - res->setBody(warp(to_json()).to_json_string()); - set_need_refresh(); - }); +Psc::JSON Source_Feed_Relation_Config::to_json() const { + Psc::JSON ret = Psc::JSON::object(); + Psc::JSON list = Psc::JSON::array(); + for (const auto& relation : map.list()) + list.append(relation->to_json()); + ret.append(Psc::JSON("list", list)); + return ret; +} +void Source_Feed_Relation_Config::set_need_refresh() { + for (const auto& source : Global::instance()->mode_acs.data_source_config.map.list()) + source->need_refresh_data_feed_key_list = true; +} +bool Source_Feed_Relation_Config::source_exists(std::string_view key) const { + return Global::instance()->mode_acs.data_source_config.map.get(std::string(key)).has_value(); +} +bool Source_Feed_Relation_Config::feed_exists(std::string_view key) const { + return Global::instance()->mode_acs.data_feed_config.map.get(std::string(key)).has_value(); +} +bool Source_Feed_Relation_Config::source_has_dependency(std::string_view key) const { + for (const auto& relation : map.list()) { + if (relation->type == "First_Source_To_All_Feed_Relation") + continue; + if (relation->settings.member<&Source_Feed_Relation_Data::source_key>().snapshot() == key) + return true; + } + return false; +} +bool Source_Feed_Relation_Config::feed_has_dependency(std::string_view key) const { + for (const auto& relation : map.list()) { + const auto value = relation->settings.snapshot(); + if (relation->type == "One_to_One_Relation" && value.feed_key == key) + return true; + if (relation->type == "Name_One_To_Many_Relation") { + const auto& prefix = value.feed_name.empty() ? value.source_key : value.feed_name; + if (key.starts_with(prefix)) + return true; + } + } + return false; +} +void Source_Feed_Relation_Config::validate(const Source_Feed_Relation& relation, std::string_view current_key) const { + if (relation.key.empty()) + throw std::invalid_argument("关系名称不能为空"); + auto existing = map.get(relation.key); + if (existing.has_value() && relation.key != current_key) + throw std::invalid_argument("关系名称已存在: " + relation.key); + const auto value = relation.settings.snapshot(); + if (relation.type == "One_to_One_Relation") { + if (!source_exists(value.source_key)) + throw std::invalid_argument("数据源不存在: " + value.source_key); + if (!feed_exists(value.feed_key)) + throw std::invalid_argument("数据馈送不存在: " + value.feed_key); + for (const auto& item : map.list()) { + if (item->key == current_key || item->type != "One_to_One_Relation") + continue; + if (item->settings.member<&Source_Feed_Relation_Data::feed_key>().snapshot() == value.feed_key) + throw std::invalid_argument("数据馈送已存在一对一关系: " + value.feed_key); + } + return; + } + if (relation.type == "Name_One_To_Many_Relation") { + if (!source_exists(value.source_key)) + throw std::invalid_argument("数据源不存在: " + value.source_key); + return; + } + if (relation.type == "First_Source_To_All_Feed_Relation") { + for (const auto& item : map.list()) { + if (item->key != current_key && item->type == "First_Source_To_All_Feed_Relation") + throw std::invalid_argument("默认关系只能存在一个"); + } + return; + } + throw std::invalid_argument("未知关系类型: " + relation.type); } diff --git a/module/Local_Server/server/Source_Feed_Relation.h b/module/Local_Server/server/Source_Feed_Relation.h index e226617..7d7f445 100644 --- a/module/Local_Server/server/Source_Feed_Relation.h +++ b/module/Local_Server/server/Source_Feed_Relation.h @@ -1,78 +1,52 @@ #ifndef SOURCE_FEED_RELATION_H #define SOURCE_FEED_RELATION_H +#include #include #include "../global_include.h" -#include "Psc_Cpp_Core/Serial/Serial.h" +#include "adminive/managed.hpp" +struct Source_Feed_Relation_Data { + bool enable{}; + std::string source_key; + std::string feed_key; + std::string feed_name; + PSC_USE_JSON +}; +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = Source_Feed_Relation_Data; + return object("source_feed_relation_data", "数据源-数据馈送关系", + ADMINIVE_FIELD_LABEL(T, enable, "启用").editable().boolean_input(), + ADMINIVE_FIELD_LABEL(T, source_key, "数据源名称").editable().text_input(), + ADMINIVE_FIELD_LABEL(T, feed_key, "数据馈送名称").editable().text_input(), + ADMINIVE_FIELD_LABEL(T, feed_name, "数据馈送名称前缀").editable().text_input()); + } +}; +} struct Source_Feed_Relation { std::string key; std::string type; - std::atomic enable{}; - virtual void from_json(const Psc::JSON* that_json) { - Get_J(key); - Get_J(enable); - Get_J(type); - } - [[nodiscard]] virtual Psc::JSON to_json() const { - Psc::JSON ret = Psc::JSON::object(); - Ret_J(key) Ret_J(enable) Ret_J(type) return ret; - } - virtual ~Source_Feed_Relation() = default; + adminive::Managed_Value settings; + void from_json(const Psc::JSON* that_json); + [[nodiscard]] Psc::JSON to_json() const; }; -struct One_to_One_Relation : Source_Feed_Relation { - std::string source_key; - std::string feed_key; - void from_json(const Psc::JSON* that_json) override { - Source_Feed_Relation::from_json(that_json); - Get_J(source_key) Get_J(feed_key) - } - [[nodiscard]] Psc::JSON to_json() const override { - Psc::JSON ret = Source_Feed_Relation::to_json(); - Ret_J(source_key) Ret_J(feed_key) return ret; - } - ~One_to_One_Relation() override = default; -}; -struct First_Source_To_All_Feed_Relation : Source_Feed_Relation { - void from_json(const Psc::JSON* that_json) override { - Source_Feed_Relation::from_json(that_json); - } - [[nodiscard]] Psc::JSON to_json() const override { - Psc::JSON ret = Source_Feed_Relation::to_json(); - return ret; - } - ~First_Source_To_All_Feed_Relation() override = default; -}; -inline std::shared_ptr create_source_feed_relation_from_type(std::string_view t) { - std::shared_ptr ret{}; - if (t == "One_to_One_Relation") - ret = std::make_shared(); - else if (t == "First_Source_To_All_Feed_Relation") - ret = std::make_shared(); - else { - std::cout << "未知的 Source_Feed_Relation type 类型! t:[" << t << "]" << std::endl; - throw std::invalid_argument("unknown Source_Feed_Relation type: " + std::string(t)); - } - return ret; +inline std::shared_ptr create_source_feed_relation_from_type(std::string_view type) { + if (type != "One_to_One_Relation" && type != "Name_One_To_Many_Relation" && type != "First_Source_To_All_Feed_Relation") + throw std::invalid_argument("unknown Source_Feed_Relation type: " + std::string(type)); + auto result = std::make_shared(); + result->type = type; + return result; } struct Source_Feed_Relation_Config { - void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) { - auto j_list = that_json->get("list"); - for (const Psc::JSON& li : j_list->children) { - auto p = create_source_feed_relation_from_type(li.get_string("type")); - p->from_json(&li); - map.push_back(p); - } - } - [[nodiscard]] Psc::JSON to_json() { - Psc::JSON ret = Psc::JSON::object(); - Psc::JSON j_list = Psc::JSON::array(); - for (auto& p : this->map.list()) { - j_list.append(p->to_json()); - } - ret.append(Psc::JSON("list", j_list)); - return ret; - } + void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value); + [[nodiscard]] Psc::JSON to_json() const; Ordered_Map> map; - static void set_need_refresh(); // 通知每一个data_source 映射关系需要更新 - void server(Global* g); + static void set_need_refresh(); + [[nodiscard]] bool source_exists(std::string_view key) const; + [[nodiscard]] bool feed_exists(std::string_view key) const; + [[nodiscard]] bool source_has_dependency(std::string_view key) const; + [[nodiscard]] bool feed_has_dependency(std::string_view key) const; + void validate(const Source_Feed_Relation& relation, std::string_view current_key = {}) const; }; #endif diff --git a/module/Local_Server/server/With_Loop_Coro.cpp b/module/Local_Server/server/With_Loop_Coro.cpp index 13b56b9..d5e7b4a 100644 --- a/module/Local_Server/server/With_Loop_Coro.cpp +++ b/module/Local_Server/server/With_Loop_Coro.cpp @@ -60,13 +60,13 @@ asio::awaitable With_Loop_Coro::tick() { } } if (state == State::Start) { - if (enable) { + if (enabled()) { set_state(State::Before_Request_Start_Loop); } } else if (state == State::Before_Request_Start_Loop) { co_await this->_open(); - loop_running = enable; + loop_running = enabled(); auto future = asio::co_spawn(Coro::instance()->io, run_loop_coro(), asio::use_future); loop_task = std::make_shared>(std::move(future)); //loop_task = Coro::instance()->spawn(run_loop_coro()); @@ -84,7 +84,7 @@ asio::awaitable With_Loop_Coro::tick() { } } else if (state == State::Loop_Running) { - if (!enable) { + if (!enabled()) { set_state(State::Before_Request_Stop_Loop, std::format("{} 检测到 enable变化 异步退出开始!", name)); } } diff --git a/module/Local_Server/server/With_Loop_Coro.h b/module/Local_Server/server/With_Loop_Coro.h index 813a033..e042868 100644 --- a/module/Local_Server/server/With_Loop_Coro.h +++ b/module/Local_Server/server/With_Loop_Coro.h @@ -14,21 +14,53 @@ #include #include "Psc_Cpp_Core/Base/JSON.h" #include "Psc_Cpp_Core/Statistics/Frequency_Limit.h" -class With_Loop_Coro_Data { -public: - std::string type; - std::string key; - Psc::Copyable_Atomic enable{}; +#include "adminive/managed.hpp" +struct With_Loop_Coro_Config { + bool enable{}; PSC_USE_JSON }; -class With_Loop_Coro : public With_Loop_Coro_Data { +namespace adminive { +template <> +struct Type_Descriptor { + static auto get() { + using T = With_Loop_Coro_Config; + return object("with_loop_coro_config", "运行配置", ADMINIVE_FIELD_LABEL(T, enable, "启用").editable().boolean_input()); + } +}; +} +class With_Loop_Coro { public: virtual ~With_Loop_Coro(); + std::string type; + std::string key; + adminive::Managed_Value config; std::shared_ptr> loop_task; virtual asio::awaitable loop_coro() = 0; void async_stop(); void sync_wait(); [[nodiscard]] bool running() const; + [[nodiscard]] bool enabled() const { + return config.member<&With_Loop_Coro_Config::enable>().snapshot(); + } + void set_enabled(bool value) { + config.member<&With_Loop_Coro_Config::enable>().write([value](bool& target) { + target = value; + }); + } + [[nodiscard]] Psc::JSON to_base_json() const { + auto ret = config.snapshot().to_base_json(); + ret.append({"type", type}); + ret.append({"key", key}); + return ret; + } + void from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value) { + Get_J(type) Get_J(key) + auto value = config.snapshot(); + value.from_base_json(that_json, not_exist_use_default_value); + config.write([&](auto& target) { + target = value; + }); + } asio::awaitable tick(); virtual asio::awaitable _open() = 0; virtual asio::awaitable _close() = 0; diff --git a/module/Local_Server/server/io_coro.cpp b/module/Local_Server/server/io_coro.cpp index c9e1b4c..0cbb969 100644 --- a/module/Local_Server/server/io_coro.cpp +++ b/module/Local_Server/server/io_coro.cpp @@ -146,7 +146,6 @@ asio::awaitable Data_Feed::loop_coro() { 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()); @@ -161,8 +160,9 @@ asio::awaitable Data_Feed::loop_coro() { 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(); + const auto mode_config = mode_acs.settings.snapshot(); + auto num = mode_config.report_data_feed_msg_mum; + auto monitor_msg_live = mode_config.monitor_msg_live; if (monitor_msg_live && num != 0 && all.size() > num && too_many_msg_limit.test()) { std::ostringstream oss; oss << "feed_key:" << feed->key << " "; @@ -187,7 +187,7 @@ asio::awaitable Data_Feed::loop_coro() { break; } for (auto& msg : messages) { - if (!feed->enable) { + if (!feed->enabled()) { break; } if (data_feed_debug) { @@ -217,7 +217,7 @@ asio::awaitable Data_Source::loop_coro() { 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()) { + if (!enabled() || !running() || !registered()) { break; } if (data_source_debug) { @@ -227,7 +227,7 @@ asio::awaitable Data_Source::loop_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; + const auto wait = Global::instance()->mode_acs.settings.member<&Mode_ACS_Config_Data::wait_process_msg>().snapshot(); if (wait) { // 调度到别的线程 co_await resume_on(co->process_data->get_executor()); diff --git a/module/Local_Server/server/server.cpp b/module/Local_Server/server/server.cpp index 740bc6b..42ef1d3 100644 --- a/module/Local_Server/server/server.cpp +++ b/module/Local_Server/server/server.cpp @@ -1,4 +1,5 @@ +#include "Adminive_Config.h" #include #include #include @@ -110,7 +111,7 @@ 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"}, {}, {}}, + {"", {"adsb_cfg.ini", "ecap_server", "lib9002config.json"}, {"lib9002", "wwwroot"}, {}, {}}, {"lib9002", {"lib9002.so", "testserver"}, {"env"}, {}, {}}}; return value; }(); @@ -160,6 +161,7 @@ void Global::init_web_server() { {drogon::Get}); }; init_tiles(); + ecap::adminive_config::register_adminive_config(this); drogon::app().registerHandlerViaRegex( "/", [return_file](const drogon::HttpRequestPtr& req, std::function&& callback) { @@ -380,7 +382,7 @@ void Global::init_web_server() { // 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) + if (!ds->enabled()) continue; arr.append({ds->key, ds->statistic_json()}); } diff --git a/module/Local_Server/server/tiles.cpp b/module/Local_Server/server/tiles.cpp index eaeb812..29ee3ef 100644 --- a/module/Local_Server/server/tiles.cpp +++ b/module/Local_Server/server/tiles.cpp @@ -492,7 +492,7 @@ void register_map_resources_handler() { return; } global->map_resources_config.current_imagery_key = key.value(); - Global::save(); + Global::save(Config_Section::map_resources); } response->setStatusCode(drogon::k200OK); response->setContentTypeCode(drogon::CT_APPLICATION_JSON); @@ -519,7 +519,7 @@ void register_map_graphics_handler() { } try { global->cesium_graphics_config.from_base_json(&parsed.value(), false); - Global::save(); + Global::save(Config_Section::cesium_graphics); } catch (const std::exception& error) { response->setStatusCode(drogon::k400BadRequest); @@ -634,7 +634,7 @@ void handle_map_model_upload(const drogon::HttpRequestPtr& req, Global* global) } auto aircraft_model_key = parser.getOptionalParameter("aircraft_model_key").value_or(""); save_uploaded_model_file(parser.getFiles().front(), *model_type, aircraft_model_key, global); - Global::save(); + Global::save(Config_Section::map_models); } void handle_map_model_config_update(const drogon::HttpRequestPtr& req, Global* global) { auto parsed = Psc::try_parse_json(req->body().data()); @@ -642,7 +642,7 @@ void handle_map_model_config_update(const drogon::HttpRequestPtr& req, Global* g throw std::invalid_argument("invalid map_models"); } global->map_model_config.from_base_json(&parsed.value(), false); - Global::save(); + Global::save(Config_Section::map_models); } void register_map_models_handler() { drogon::app().registerHandlerViaRegex( @@ -709,7 +709,8 @@ void register_map_view_handler() { } global->map_resources_config.current_imagery_key = key.value(); global->map_view_config = next_view; - Global::save(); + Global::save(Config_Section::map_resources); + Global::save(Config_Section::map_view); } catch (const std::exception& error) { response->setStatusCode(drogon::k400BadRequest); diff --git a/module/Local_Server_main.cpp b/module/Local_Server_main.cpp index 9bdb33e..d9733a7 100644 --- a/module/Local_Server_main.cpp +++ b/module/Local_Server_main.cpp @@ -11,7 +11,7 @@ #include "Local_Server/State_Report/State_Report.h" #include "Local_Server/server/Global.h" #include "Local_Server/server/io_coro.h" -// ./server ./config.json +// ./server ./config #ifdef __linux__ #include #endif @@ -53,11 +53,11 @@ struct Catch_Memory { int psc_main(int argc, char* argv[]) { std::cout << "wyc_main" << std::endl; if (argc >= 2) { - std::string config_path = argv[1]; - if (config_path.at(0) == '@') { - config_path.replace(0, 1, get_exe_dir()); + std::string config_directory = argv[1]; + if (config_directory.at(0) == '@') { + config_directory.replace(0, 1, get_exe_dir()); } - ::default_config_path = config_path; + ::default_config_directory = config_directory; } // std::cout << get_stack_trace() << std::endl; init_signal_config({SIGINT, SIGTERM}, @@ -134,9 +134,9 @@ int psc_main(int argc, char* argv[]) { } }); // manager.test_and_start_thread("io_coro 协程线程", io_coro); - static int t = Global::instance()->mode_acs.read_milliseconds; + static int t = static_cast(Global::instance()->mode_acs.settings.member<&Mode_ACS_Config_Data::read_milliseconds>().snapshot()); g->dsp_config.init_env(); - bool enable = g->mlat.enable; + bool enable = g->mlat.settings.member<&MLAT_Config_Data::enable>().snapshot(); auto coro = Coro::instance(); coro->start(); // Catch_Memory cm; @@ -148,7 +148,7 @@ int psc_main(int argc, char* argv[]) { g->mlat.refresh(); } #ifdef __linux__ - if (g->mode_acs.monitor_msg_live) + if (g->mode_acs.settings.member<&Mode_ACS_Config_Data::monitor_msg_live>().snapshot()) malloc_trim(0); #endif std::this_thread::sleep_for(std::chrono::seconds(3));