#ifndef CONFIG_H_H #define CONFIG_H_H #include #include #include #include "../DSP/export.h" #include "../Data_Feed/export.h" #include "../Data_Source/export.h" #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_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) std::cout << "~Log_Config()" << std::endl; } enum class Open_Mode { Append, Delete, Rename }; Open_Mode open_mode = Open_Mode::Rename; std::uint16_t piece_num{}; std::uint16_t size_MB{}; PSC_USE_JSON }; struct Console_Config { ~Console_Config() { if (delete_log) std::cout << "~Console_Config()" << std::endl; } bool record_playback; bool mode_s_console; bool post_request; bool mlat_server; PSC_USE_JSON }; struct Http_Monitor_Config { bool access_log = true; std::size_t slow_request_threshold_ms = 1000; std::size_t slow_scope_threshold_ms = 100; PSC_USE_JSON }; 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; 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 }; 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 { adminive::Managed_Value settings; Data_feed_Config data_feed_config; Data_Source_Config data_source_config; Source_Feed_Relation_Config source_feed_relation_config; 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 { auto ret = settings.read([](const auto& value) { return value.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) { settings.write([&](auto& value) { value.from_base_json(that_json, not_exist_use_default_value); SSR::Monitor_Mode_ACS_Message_Num = value.monitor_msg_live; }); 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); } void server(Global* g); }; struct Web_Server_Config { [[nodiscard]] std::string get_true_webapp() const { return Psc::get_abs_path(webapp); } PSC_USE_JSON std::string webapp; }; enum class Map_Tile_File_Format { directory, mbtiles, online }; enum class Map_Tile_Y_Axis { xyz, tms }; enum class Map_Tile_Projection { web_mercator }; enum class Map_Tile_Type { imagery, terrain }; enum class Map_Tile_Request_Method { GET }; enum class Map_Tile_Zoom_Mode { native, upscale, both }; struct Map_Tile_Config_Data { Map_Tile_File_Format file_format = Map_Tile_File_Format::directory; std::string path_layout; Map_Tile_Y_Axis y_axis = Map_Tile_Y_Axis::xyz; Map_Tile_Projection projection = Map_Tile_Projection::web_mercator; std::string extension; std::uint32_t tile_size{}; std::uint32_t minimum_level{}; std::uint32_t maximum_level{}; PSC_USE_JSON }; struct Map_Tile_Config : Map_Tile_Config_Data { std::vector directories; std::vector files; std::string encoding; [[nodiscard]] std::vector get_true_directories() const { std::vector ret; ret.reserve(directories.size()); for (const auto& path : directories) { ret.emplace_back(Psc::get_abs_path(path)); } return ret; } [[nodiscard]] std::vector get_true_files() const { std::vector ret; ret.reserve(files.size()); for (const auto& path : files) { ret.emplace_back(Psc::get_abs_path(path)); } return ret; } void from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value); [[nodiscard]] Psc::JSON to_base_json() const; [[nodiscard]] Psc::JSON to_public_json(std::string_view url) const; }; struct Map_Tile_Source_Config_Data { std::string key; std::string name; Map_Tile_Type tile_type = Map_Tile_Type::imagery; Map_Tile_Request_Method request_method = Map_Tile_Request_Method::GET; PSC_USE_JSON }; struct Map_Tile_Source_Config : Map_Tile_Source_Config_Data { std::string url; Map_Tile_Config resource; [[nodiscard]] bool local() const { return resource.file_format != Map_Tile_File_Format::online; } void from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value); [[nodiscard]] Psc::JSON to_base_json() const; [[nodiscard]] Psc::JSON to_public_json(std::string_view local_url) const; }; struct Map_Resources_Config { std::string current_imagery_key = "imagery"; std::vector tile_sources; void from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value); [[nodiscard]] Psc::JSON to_base_json() const; [[nodiscard]] Psc::JSON to_public_json() const; [[nodiscard]] bool is_imagery_key(std::string_view key) const; [[nodiscard]] bool is_terrain_key(std::string_view key) const; }; namespace adminive { template <> struct Type_Descriptor { static auto get() { using T = Map_Resources_Config; return object("map_resources_config", "地图资源配置", ADMINIVE_FIELD_LABEL(T, current_imagery_key, "当前影像源").read_write(), ADMINIVE_FIELD_LABEL(T, tile_sources, "地图瓦片源").read_write()); } }; } struct Map_Camera_Config { double longitude = 121.27; double latitude = 37.41; double height = 300000.0; double heading = 0.0; double pitch = -90.0; double roll = 0.0; PSC_USE_JSON }; namespace adminive { template <> struct Type_Descriptor { static auto get() { using T = Map_Camera_Config; return object("map_camera_config", "地图相机", ADMINIVE_FIELD_LABEL(T, longitude, "经度").editable().number_input(), ADMINIVE_FIELD_LABEL(T, latitude, "纬度").editable().number_input(), ADMINIVE_FIELD_LABEL(T, height, "高度").editable().number_input(), ADMINIVE_FIELD_LABEL(T, heading, "航向角").editable().number_input(), ADMINIVE_FIELD_LABEL(T, pitch, "俯仰角").editable().number_input(), ADMINIVE_FIELD_LABEL(T, roll, "横滚角").editable().number_input()); } }; } struct Map_Tile_View_Config { std::string current_imagery_key = "imagery"; std::string current_terrain_key = "terrain"; Map_Tile_Zoom_Mode tile_zoom_mode = Map_Tile_Zoom_Mode::native; std::uint32_t tile_display_maximum_level = 19; void from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value); [[nodiscard]] Psc::JSON to_base_json() const; }; namespace adminive { template <> struct Type_Descriptor { static auto get() { using T = Map_Tile_View_Config; return object("map_tile_view_config", "地图瓦片视图", ADMINIVE_FIELD_LABEL(T, current_imagery_key, "影像源").editable().text_input(), ADMINIVE_FIELD_LABEL(T, current_terrain_key, "地形源").editable().text_input(), ADMINIVE_FIELD_LABEL(T, tile_zoom_mode, "缩放模式").editable().select_input(), ADMINIVE_FIELD_LABEL(T, tile_display_maximum_level, "显示最大层级").editable().number_input()); } }; } struct Map_View_Config { std::string scene_mode = "3d"; Map_Tile_View_Config map2d; Map_Tile_View_Config map3d; Map_Camera_Config camera; double base_station_fly_to_height_offset_meters = 8000.0; void from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value); [[nodiscard]] Psc::JSON to_base_json() const; [[nodiscard]] static bool is_scene_mode(std::string_view mode); }; namespace adminive { template <> struct Type_Descriptor { static auto get() { using T = Map_View_Config; return object("map_view_config", "地图视图配置", ADMINIVE_FIELD_LABEL(T, scene_mode, "场景模式").editable().required().text_input(), ADMINIVE_FIELD_LABEL(T, map2d, "2D 地图").editable(), ADMINIVE_FIELD_LABEL(T, map3d, "3D 地图").editable(), ADMINIVE_FIELD_LABEL(T, camera, "相机").editable(), ADMINIVE_FIELD_LABEL(T, base_station_fly_to_height_offset_meters, "基站飞行高度偏移(米)").editable().number_input()); } }; } struct Cesium_Graphics_Config_Data { bool debug_show_frames_per_second = true; std::uint32_t target_frame_rate = 30; bool use_browser_recommended_resolution = true; double resolution_scale = 0.7; std::uint32_t msaa_samples = 1; bool fxaa = false; bool shadows = false; bool enable_lighting = false; bool solar_lighting = false; double solar_light_intensity = 1.5; std::uint32_t maximum_screen_space_error = 4; bool terrain_enabled_in_3d = false; double terrain_exaggeration = 1.0; bool terrain_limit_maximum_level = true; std::uint32_t terrain_maximum_level = 15; std::uint32_t terrain_cache_tiles = 128; bool terrain_occlusion_enabled = false; bool surface_navigation_reference_visible = false; bool view_axes_visible = false; double performance_panel_x = 12.0; double performance_panel_y = 92.0; double view_axes_panel_x = -1.0; double view_axes_panel_y = 76.0; double view_axes_panel_size = 84.0; double occlusion_sample_spacing_meters = 250.0; double occlusion_clearance_margin_meters = 10.0; PSC_USE_JSON }; 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; }; struct Map_Model_Item_Config { std::string url; double built_in_size = 50.0; double heading_offset_degrees = 0.0; double pitch_offset_degrees = 0.0; double roll_offset_degrees = 0.0; Map_Model_Item_Config() = default; explicit Map_Model_Item_Config(std::string url); Map_Model_Item_Config(std::string url, double built_in_size, double heading, double pitch, double roll); void from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value); [[nodiscard]] Psc::JSON to_base_json() const; }; namespace adminive { template <> struct Type_Descriptor { static auto get() { using T = Map_Model_Item_Config; return object("map_model_item_config", "Cesium 模型", ADMINIVE_FIELD_LABEL(T, url, "模型地址"), ADMINIVE_FIELD_LABEL(T, built_in_size, "模型原始尺寸").editable().number_input(), ADMINIVE_FIELD_LABEL(T, heading_offset_degrees, "航向偏移(度)").editable().number_input(), ADMINIVE_FIELD_LABEL(T, pitch_offset_degrees, "俯仰偏移(度)").editable().number_input(), ADMINIVE_FIELD_LABEL(T, roll_offset_degrees, "横滚偏移(度)").editable().number_input()); } }; } struct Map_Model_Config { Map_Model_Item_Config aircraft_model{"/ui/model/aircraft.glb", 50.0, -90.0, 0.0, 0.0}; std::map aircraft_models; Map_Model_Item_Config base_station_model{"/ui/model/base-station.glb", 16.635522, 0.0, 0.0, 0.0}; Map_Model_Item_Config device_model{"/ui/model/device.glb", 80.0, 0.0, 0.0, 0.0}; Map_Model_Config(); void from_base_json(const Psc::JSON* that_json, bool not_exist_use_default_value); [[nodiscard]] Psc::JSON to_base_json() const; }; namespace adminive { template <> struct Type_Descriptor { static auto get() { using T = Map_Model_Config; return object("map_model_config", "Cesium 模型配置", ADMINIVE_FIELD_LABEL(T, aircraft_model, "默认飞机模型").read_write(), ADMINIVE_FIELD_LABEL(T, aircraft_models, "机型模型").read_write(), ADMINIVE_FIELD_LABEL(T, base_station_model, "基站模型").read_write(), ADMINIVE_FIELD_LABEL(T, device_model, "设备模型").read_write()); } }; } struct Net_Config { std::string key{}; std::string ip{}; std::uint16_t port{}; std::string netmask{}; std::string gateway{}; PSC_USE_JSON }; struct Device_Config { Ordered_Map list; ~Device_Config() { if (delete_log) std::cout << "~Device_Config()" << std::endl; for (auto it : list.list()) { delete it; } list.clear(); } Psc::JSON to_base_json() { Psc::JSON ret = Psc::JSON::object(); Psc::JSON net = Psc::JSON::array(); for (auto& li : list.list()) { net.append(li->to_base_json()); } ret.append({"net", net}); return ret; } Device_Config() = default; void from_json(const Psc::JSON* that_json, bool not_exist_use_default_value) { for (auto& li : that_json->get("net")->children) { auto cur = new Net_Config(); cur->from_base_json(&li, not_exist_use_default_value); list.push_back(cur); } } Psc::JSON to_net_json() { Psc::JSON ret = Psc::JSON::object(); Psc::JSON net = Psc::JSON::array(); for (auto& li : list.list()) { net.append(li->to_base_json()); } ret.append({"net", net}); return ret; } void server(Global* g); }; class Config { public: Config() = default; DELETE_COPY(Config) MLAT_Config mlat; Device_Config device_config; Web_Server_Config web_server_config; adminive::Managed_Value map_resources_config; adminive::Managed_Value map_view_config; Cesium_Graphics_Config cesium_graphics_config; adminive::Managed_Value map_model_config; Mode_ACS_Config mode_acs; Log_Config log_config; Console_Config console_config{}; DSP_Config dsp_config; Psc::JSON drogon_config; Http_Monitor_Config http_monitor_config; std::string version; External_Resources_Manager external_resources_manager; 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); map_resources_config.write([&](auto& value) { value.from_base_json(that_json->get("map_resources"), not_exist_use_default_value); }); map_view_config.write([&](auto& value) { value.from_base_json(that_json->get("map_view"), not_exist_use_default_value); }); cesium_graphics_config.from_base_json(that_json->get("cesium_graphics"), not_exist_use_default_value); map_model_config.write([&](auto& value) { value.from_base_json(that_json->get("map_models"), not_exist_use_default_value); }); device_config.from_json(that_json->get("device"), not_exist_use_default_value); mode_acs.from_json(that_json->get("mode_acs"), not_exist_use_default_value); console_config.from_base_json(that_json->get("console"), not_exist_use_default_value); dsp_config.from_json(that_json->get("dsp"), not_exist_use_default_value); drogon_config = *that_json->get("drogon"); http_monitor_config.from_base_json(that_json->get("http_monitor"), not_exist_use_default_value); external_resources_manager.from_json(that_json->get("external_database"), not_exist_use_default_value); } Psc::JSON toJson() { auto ret = Psc::JSON::object({ {"version", version}, {"dsp", dsp_config.to_base_json()}, {"drogon", drogon_config}, {"http_monitor", http_monitor_config.to_base_json()}, {"mlat", mlat.to_base_json()}, {"web_server", web_server_config.to_base_json()}, {"map_resources", map_resources_config.read([](const auto& value) { return value.to_base_json(); })}, {"map_view", map_view_config.read([](const auto& value) { return value.to_base_json(); })}, {"map_models", map_model_config.read([](const auto& value) { return value.to_base_json(); })}, {"device", device_config.to_base_json()}, {"mode_acs", mode_acs.to_base_json()}, //{"ais", ais.to_json()}, {"log", log_config.to_base_json()}, {"console", console_config.to_base_json()}, {"external_database", external_resources_manager.to_base_json()}, {"cesium_graphics", cesium_graphics_config.to_base_json()}, }); 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