3D优化
This commit is contained in:
@@ -1,5 +1,60 @@
|
||||
#include "../Aircraft/Flight_VTO.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
namespace {
|
||||
double to_radians(double degrees) {
|
||||
return degrees * 3.14159265358979323846 / 180.0;
|
||||
}
|
||||
double track_heading_from_points(const SSR::Position_Info& first,
|
||||
const SSR::Position_Info& second) {
|
||||
auto lat1 = to_radians(first.lat);
|
||||
auto lat2 = to_radians(second.lat);
|
||||
auto dlon = to_radians(second.lon - first.lon);
|
||||
auto y = std::sin(dlon) * std::cos(lat2);
|
||||
auto x = std::cos(lat1) * std::sin(lat2) -
|
||||
std::sin(lat1) * std::cos(lat2) * std::cos(dlon);
|
||||
auto angle = std::atan2(y, x) * 180.0 / 3.14159265358979323846;
|
||||
return angle < 0 ? angle + 360.0 : angle;
|
||||
}
|
||||
double ground_distance_meters(const SSR::Position_Info& first,
|
||||
const SSR::Position_Info& second) {
|
||||
auto dlat = to_radians(second.lat - first.lat);
|
||||
auto dlon = to_radians(second.lon - first.lon);
|
||||
auto lat1 = to_radians(first.lat);
|
||||
auto lat2 = to_radians(second.lat);
|
||||
auto a = std::sin(dlat / 2) * std::sin(dlat / 2) +
|
||||
std::cos(lat1) * std::cos(lat2) * std::sin(dlon / 2) *
|
||||
std::sin(dlon / 2);
|
||||
auto safe_a = std::min(1.0, std::max(0.0, a));
|
||||
return 6371000.0 * 2 *
|
||||
std::atan2(std::sqrt(safe_a), std::sqrt(1 - safe_a));
|
||||
}
|
||||
double track_pitch_from_points(const SSR::Position_Info& first,
|
||||
const SSR::Position_Info& second) {
|
||||
auto distance = ground_distance_meters(first, second);
|
||||
return std::atan2(second.alt - first.alt, distance) * 180.0 /
|
||||
3.14159265358979323846;
|
||||
}
|
||||
Flight_Track_Orientation track_orientation_from_points(const SSR::Position_Info& first,
|
||||
const SSR::Position_Info& second) {
|
||||
return {track_heading_from_points(first, second),
|
||||
track_pitch_from_points(first, second), 0.0};
|
||||
}
|
||||
}
|
||||
Psc::JSON Flight_Track_Orientation::to_json() const {
|
||||
auto ret = Psc::JSON::object();
|
||||
ret.append({"heading", heading});
|
||||
ret.append({"pitch", pitch});
|
||||
ret.append({"roll", roll});
|
||||
return ret;
|
||||
}
|
||||
Psc::JSON Flight_VTO::to_json() {
|
||||
return to_json(true);
|
||||
}
|
||||
Psc::JSON Flight_VTO::to_base_info_json() {
|
||||
return to_json(false);
|
||||
}
|
||||
Psc::JSON Flight_VTO::to_json(bool include_runtime_fields) {
|
||||
auto ret = Psc::JSON::object();
|
||||
ADD_Json(icao);
|
||||
ADD_Json(mlat);
|
||||
@@ -13,11 +68,32 @@ Psc::JSON Flight_VTO::to_json() {
|
||||
ret.children.emplace_back("longitude", nullptr);
|
||||
}
|
||||
ADD_Json_RET(track);
|
||||
if (include_runtime_fields) {
|
||||
if (track_orientation.has_value()) {
|
||||
ret.append({"track_orientation", track_orientation.value().to_json()});
|
||||
}
|
||||
else {
|
||||
ret.append({"track_orientation", nullptr});
|
||||
}
|
||||
}
|
||||
ADD_Json_RET(speed);
|
||||
ADD_Json_RET(altitude);
|
||||
ADD_Json_RET(vert_speed);
|
||||
ADD_Json_RET(call_sign);
|
||||
ADD_Json_RET(squawk);
|
||||
if (include_runtime_fields) {
|
||||
if (vortex_type.has_value()) {
|
||||
auto value = vortex_type.value();
|
||||
ret.append({"vortex_type", static_cast<int>(value)});
|
||||
ret.append({"vortex_type_key", std::string(SSR::Vortex_Type_to_key(value))});
|
||||
ret.append({"vortex_type_label", std::string(SSR::Vortex_Type_to_label(value))});
|
||||
}
|
||||
else {
|
||||
ret.append({"vortex_type", nullptr});
|
||||
ret.append({"vortex_type_key", nullptr});
|
||||
ret.append({"vortex_type_label", nullptr});
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
std::string Flight_VTO::time_to_string(time_t time_stamp) {
|
||||
@@ -34,6 +110,12 @@ Flight_VTO Flight_VTO::to_VTO(SSR::Aircraft_Info* info) {
|
||||
vto.speed = info->speed();
|
||||
// 这个地 是相对于地的意思
|
||||
vto.track = info->surface_magnetic_heading();
|
||||
auto last_two = info->air_pos_track_list.last_two();
|
||||
if (last_two.has_value()) {
|
||||
auto orientation = track_orientation_from_points(last_two->first, last_two->second);
|
||||
vto.track = orientation.heading;
|
||||
vto.track_orientation = orientation;
|
||||
}
|
||||
// vto.pos = info->pos();
|
||||
auto opt = info->air_pos_track_list.last();
|
||||
vto.pos = opt;
|
||||
@@ -50,5 +132,6 @@ Flight_VTO Flight_VTO::to_VTO(SSR::Aircraft_Info* info) {
|
||||
vto.vert_speed = info->vertical_rate();
|
||||
vto.call_sign = info->bds20_call_sign();
|
||||
vto.squawk = info->squawk();
|
||||
vto.vortex_type = info->vortex_type();
|
||||
return vto;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
#pragma once
|
||||
#include "global.h"
|
||||
struct Flight_Track_Orientation {
|
||||
double heading = 0.0; // 由最后两个空中位置点计算出的水平航迹角,单位:度
|
||||
double pitch = 0.0; // 由最后两个空中位置点计算出的三维俯仰角,单位:度
|
||||
double roll = 0.0; // 当前 ADS-B 位置轨迹没有横滚数据,保留给 Cesium 姿态结构
|
||||
Psc::JSON to_json() const;
|
||||
};
|
||||
struct Flight_VTO {
|
||||
std::string icao; // 飞机身份标识(四字节十六进制代码)
|
||||
time_t time_stamp{}; // 时间(原始时间数据,可能需要翻译显示)
|
||||
bool mlat;
|
||||
RET<SSR::CPR::Position> pos; // 纬度(飞机的当前位置纬度)
|
||||
RET<double> track; // 航迹(航迹方向,单位:度)
|
||||
RET<Flight_Track_Orientation> track_orientation; // Cesium 使用的航迹姿态,单位:度
|
||||
RET<double> speed; // 航速(单位:米/秒)
|
||||
RET<double> vert_speed; // 航速(单位:米/秒)
|
||||
RET<double> altitude; // 米
|
||||
RET<std::string> call_sign;
|
||||
RET<std::string> squawk;
|
||||
RET<SSR::Vortex_Type> vortex_type;
|
||||
Psc::JSON to_json();
|
||||
Psc::JSON to_base_info_json();
|
||||
Psc::JSON to_json(bool include_runtime_fields);
|
||||
static std::string time_to_string(time_t time_stamp);
|
||||
static Flight_VTO to_VTO(SSR::Aircraft_Info* info);
|
||||
};
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
#include "BaseStation.h"
|
||||
|
||||
namespace SSR {
|
||||
struct HULC_Status_Message;
|
||||
}
|
||||
|
||||
std::optional<SSR::CPR::Position> BaseStation::get_pos() {
|
||||
double BaseStation::theoretical_detection_range_meters(double base_height_meters,
|
||||
double target_height_meters) {
|
||||
return static_cast<double>(SSR::CPR::radio_line_of_sight_range_meters(base_height_meters, target_height_meters));
|
||||
}
|
||||
std::optional<SSR::Position_3D> BaseStation::get_pos() {
|
||||
std::lock_guard g(mtx);
|
||||
if (!hulc.GPS_has_valid_fix()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return SSR::CPR::Position{hulc.get_latitude(), hulc.get_longitude()};
|
||||
return SSR::Position_3D{hulc.get_latitude(), hulc.get_longitude(), static_cast<SSR::CPR::D>(hulc.Alt)};
|
||||
}
|
||||
double BaseStation::get_height() {
|
||||
std::lock_guard g(mtx);
|
||||
return hulc.Alt;
|
||||
}
|
||||
bool BaseStation::has_valid_position() {
|
||||
std::lock_guard g(mtx);
|
||||
@@ -33,9 +39,6 @@ Psc::JSON BaseStation::to_Json() {
|
||||
ret.append({"状态标志", hulc.Flags});
|
||||
ret.append({"内部使用", hulc.I_U_});
|
||||
ret.append({"时间", Psc::utc_2_local_time(hulc.xTime)});
|
||||
ret.append({"latitude", hulc.get_latitude()});
|
||||
ret.append({"longitude", hulc.get_longitude()});
|
||||
ret.append({"height", hulc.Alt});
|
||||
ret.append({"卫星数量", hulc.Sat});
|
||||
ret.append({"HDOP", hulc.get_HDOP()});
|
||||
ret.append({"GPS设备检测到", hulc.GPS_device_detected()});
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
#pragma once
|
||||
#include "global.h"
|
||||
|
||||
|
||||
|
||||
class BaseStation {
|
||||
public:
|
||||
std::string to_string();
|
||||
Psc::JSON to_Json();
|
||||
std::optional<SSR::CPR::Position> get_pos();
|
||||
bool has_valid_position();
|
||||
void set_msg(const SSR::HULC_Status_Message& msg);
|
||||
std::function<void (const SSR::HULC_Status_Message&)> handle_when_updated = nullptr;
|
||||
std::string to_string();
|
||||
Psc::JSON to_Json();
|
||||
std::optional<SSR::Position_3D> get_pos();
|
||||
double get_height();
|
||||
bool has_valid_position();
|
||||
void set_msg(const SSR::HULC_Status_Message& msg);
|
||||
static double theoretical_detection_range_meters(double base_height_meters, double target_height_meters);
|
||||
std::function<void(const SSR::HULC_Status_Message&)> handle_when_updated = nullptr;
|
||||
protected:
|
||||
std::mutex mtx;
|
||||
SSR::HULC_Status_Message hulc{};
|
||||
std::mutex mtx;
|
||||
SSR::HULC_Status_Message hulc{};
|
||||
};
|
||||
class Global;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -513,6 +513,7 @@ void Data_Source_Config::server(Global* g) {
|
||||
ds->from_json(¶ms);
|
||||
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
|
||||
|
||||
@@ -50,23 +50,73 @@ protected:
|
||||
std::mutex mtx;
|
||||
std::vector<bool> test_v;
|
||||
};
|
||||
class Data_Source_Map_Display_Data {
|
||||
public:
|
||||
Psc::Copyable_Atomic<bool> base_station_show = true;
|
||||
Psc::Copyable_Atomic<bool> aircraft_show = true;
|
||||
Psc::Copyable_Atomic<bool> constant_screen_size = true;
|
||||
std::string color = "#1677ff";
|
||||
std::string track_point_color = "#ffff00";
|
||||
std::string base_station_color = "#1677ff";
|
||||
std::uint32_t aircraft_pixel_size = 50;
|
||||
std::uint32_t base_station_pixel_size = 100;
|
||||
double aircraft_scale = 1.0;
|
||||
double base_station_scale = 1.0;
|
||||
Psc::Copyable_Atomic<bool> show_icao = true;
|
||||
Psc::Copyable_Atomic<bool> show_call_sign = false;
|
||||
Psc::Copyable_Atomic<bool> show_fly_status = false;
|
||||
PSC_USE_JSON
|
||||
};
|
||||
class Data_Source_Map_Display_Config {
|
||||
public:
|
||||
Data_Source_Map_Display_Data map2d;
|
||||
Data_Source_Map_Display_Data map3d;
|
||||
[[nodiscard]] Psc::JSON to_base_json() const {
|
||||
auto ret = Psc::JSON::object();
|
||||
ret.append({"map2d", map2d.to_base_json()});
|
||||
ret.append({"map3d", map3d.to_base_json()});
|
||||
return ret;
|
||||
}
|
||||
void from_base_json(const Psc::JSON* that_json) {
|
||||
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"));
|
||||
map3d.from_base_json(that_json->get("map3d"));
|
||||
}
|
||||
};
|
||||
class Data_Source_Data {
|
||||
public:
|
||||
Psc::Copyable_Atomic<bool> base_station_show{};
|
||||
Psc::Copyable_Atomic<bool> base_station_has_valid_position{};
|
||||
Psc::Copyable_Atomic<bool> aircraft_show{};
|
||||
std::string color = "#1677ff";
|
||||
int aircraft_pixel_size{};
|
||||
Data_Source_Map_Display_Config map_display;
|
||||
double lat{};
|
||||
double lon{};
|
||||
double alt{};
|
||||
Psc::Copyable_Atomic<bool> ignore_msg_time = false; // 忽略消息时间戳 如果启用 解码位置将不在判断消息自带时间戳
|
||||
Psc::Copyable_Atomic<bool> update_form_gps{};
|
||||
Psc::Copyable_Atomic<bool> show_icao = true;
|
||||
Psc::Copyable_Atomic<bool> show_call_sign = false;
|
||||
Psc::Copyable_Atomic<bool> show_fly_status = false;
|
||||
Psc::Copyable_Atomic<bool> keep_mode = true;
|
||||
PSC_USE_JSON
|
||||
[[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()});
|
||||
Ret_J(lat)
|
||||
Ret_J(lon)
|
||||
Ret_J(alt)
|
||||
Ret_J(ignore_msg_time)
|
||||
Ret_J(update_form_gps)
|
||||
Ret_J(keep_mode)
|
||||
return ret;
|
||||
}
|
||||
void from_base_json(const Psc::JSON* that_json) {
|
||||
Get_J(base_station_has_valid_position)
|
||||
map_display.from_base_json(that_json->get("map_display"));
|
||||
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<Data_Source>,
|
||||
public Data_Source_Handler, public Data_Source_Data {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "Data_Source_Handler.h"
|
||||
#include <string_view>
|
||||
#include "Data_Source.h"
|
||||
#include "Local_Server/server/Global.h"
|
||||
#include <string_view>
|
||||
#include "Local_Server/server/io_coro.h"
|
||||
using namespace Psc;
|
||||
std::shared_ptr<Data_Source> ds(Data_Source_Handler* dsh) {
|
||||
@@ -13,18 +13,16 @@ std::shared_ptr<SSR::Msg> Data_Source_Handler::create_msg(std::string_view packe
|
||||
std::cout << "first:" << mem2hex(std::string(packet)) << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
std::string error_len = source->key + " " + LOG_POS_SIMPLE +
|
||||
std::string(" ") + "mode_s_error_length";
|
||||
if (packet.size() < 2) return nullptr;
|
||||
std::string error_len = source->key + " " + LOG_POS_SIMPLE + std::string(" ") + "mode_s_error_length";
|
||||
if (packet.size() < 2)
|
||||
return nullptr;
|
||||
auto mt = packet[1];
|
||||
if (mt == SSR::Msg::AC) {
|
||||
if (packet.size() != SSR::Msg::AC_len) {
|
||||
auto packet_hex = mem2hex(std::string(packet));
|
||||
SSR::mode_s_logger->c_debug(
|
||||
error_len, {},
|
||||
std::to_string(mt) + " size:" + std::to_string(packet.size()) +
|
||||
" hex:" + packet_hex +
|
||||
" should:" + std::to_string(SSR::Msg::AC_len));
|
||||
SSR::mode_s_logger->c_debug(error_len, {},
|
||||
std::to_string(mt) + " size:" + std::to_string(packet.size()) +
|
||||
" hex:" + packet_hex + " should:" + std::to_string(SSR::Msg::AC_len));
|
||||
mode_ac_statistic.add_length_error();
|
||||
return nullptr;
|
||||
}
|
||||
@@ -34,13 +32,10 @@ std::shared_ptr<SSR::Msg> Data_Source_Handler::create_msg(std::string_view packe
|
||||
if (mt == SSR::Msg::S7) {
|
||||
if (packet.size() != SSR::Mode_S_Msg::S7_len) {
|
||||
auto packet_hex = mem2hex(std::string(packet));
|
||||
SSR::mode_s_logger->c_debug(
|
||||
error_len, {},
|
||||
std::to_string(mt) + " size:" + std::to_string(packet.size()) +
|
||||
" hex:" + packet_hex +
|
||||
" should:" + std::to_string(SSR::Msg::S7_len));
|
||||
mode_s_statistic.add_length_error(
|
||||
"[length error] Msg::S7 handle_mode_s_source");
|
||||
SSR::mode_s_logger->c_debug(error_len, {},
|
||||
std::to_string(mt) + " size:" + std::to_string(packet.size()) +
|
||||
" hex:" + packet_hex + " should:" + std::to_string(SSR::Msg::S7_len));
|
||||
mode_s_statistic.add_length_error("[length error] Msg::S7 handle_mode_s_source");
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_shared<SSR::Mode_S_Msg>(source, packet);
|
||||
@@ -48,13 +43,10 @@ std::shared_ptr<SSR::Msg> Data_Source_Handler::create_msg(std::string_view packe
|
||||
if (mt == SSR::Msg::S14) {
|
||||
if (packet.size() != SSR::Msg::S14_len) {
|
||||
auto packet_hex = mem2hex(std::string(packet));
|
||||
SSR::mode_s_logger->c_debug(
|
||||
error_len, {},
|
||||
std::to_string(mt) + " size:" + std::to_string(packet.size()) +
|
||||
" hex:" + packet_hex +
|
||||
" should:" + std::to_string(SSR::Msg::S14_len));
|
||||
mode_s_statistic.add_length_error(
|
||||
"[length error] Msg::S14 handle_mode_s_source");
|
||||
SSR::mode_s_logger->c_debug(error_len, {},
|
||||
std::to_string(mt) + " size:" + std::to_string(packet.size()) +
|
||||
" hex:" + packet_hex + " should:" + std::to_string(SSR::Msg::S14_len));
|
||||
mode_s_statistic.add_length_error("[length error] Msg::S14 handle_mode_s_source");
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_shared<SSR::Mode_S_Msg>(source, packet);
|
||||
@@ -63,13 +55,10 @@ std::shared_ptr<SSR::Msg> Data_Source_Handler::create_msg(std::string_view packe
|
||||
return nullptr; // 不知道如何解析跳过
|
||||
if (packet.size() != SSR::Msg::Radarcape_status_len) {
|
||||
auto packet_hex = mem2hex(std::string(packet));
|
||||
SSR::mode_s_logger->c_debug(
|
||||
error_len, {},
|
||||
std::to_string(mt) + " size:" + std::to_string(packet.size()) +
|
||||
" hex:" + packet_hex +
|
||||
" should:" + std::to_string(SSR::Msg::Radarcape_status_len));
|
||||
mode_s_statistic.add_length_error(
|
||||
"[length error] Msg::Radarcape_status_len handle_mode_s_source");
|
||||
SSR::mode_s_logger->c_debug(error_len, {},
|
||||
std::to_string(mt) + " size:" + std::to_string(packet.size()) + " hex:" +
|
||||
packet_hex + " should:" + std::to_string(SSR::Msg::Radarcape_status_len));
|
||||
mode_s_statistic.add_length_error("[length error] Msg::Radarcape_status_len handle_mode_s_source");
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_shared<SSR::Msg>(source, packet);
|
||||
@@ -79,10 +68,9 @@ std::shared_ptr<SSR::Msg> Data_Source_Handler::create_msg(std::string_view packe
|
||||
auto packet_hex = mem2hex(std::string(packet));
|
||||
// 找不到协议 size:23 hex:1A34195F0000001500B502FF06F423CE50000090000000
|
||||
// should:5
|
||||
SSR::mode_s_logger->c_debug(
|
||||
error_len, {},
|
||||
Psc::to_string(source->type) + " HULC size:" +
|
||||
std::to_string(packet.size()) + " hex:" + packet_hex);
|
||||
SSR::mode_s_logger->c_debug(error_len, {},
|
||||
Psc::to_string(source->type) + " HULC size:" + std::to_string(packet.size()) +
|
||||
" hex:" + packet_hex);
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_shared<SSR::Msg>(source, packet);
|
||||
@@ -101,8 +89,8 @@ size_t Data_Source_Handler::process_mode_acs_data(std::string_view origin_data)
|
||||
}
|
||||
origin_data_transform_mode_data(mode_data);
|
||||
if (data_source_debug) {
|
||||
std::cout << " after origin_data_transform_mode_data " << VAR_STR_2(name, mode_data.size()) <<
|
||||
key << std::endl;
|
||||
std::cout << " after origin_data_transform_mode_data " << VAR_STR_2(name, mode_data.size()) << key
|
||||
<< std::endl;
|
||||
}
|
||||
static Value_Growth_Multi_T mt;
|
||||
auto t = key;
|
||||
@@ -124,17 +112,16 @@ size_t Data_Source_Handler::process_mode_acs_data(std::string_view origin_data)
|
||||
static bool mode_s_console = Global::instance()->console_config.mode_s_console;
|
||||
static bool record_playback = Global::instance()->console_config.record_playback;
|
||||
if (record_playback) {
|
||||
pure_log(get_exe_dir() + "/playback/" + source->key + "_playback.dat",
|
||||
mem2hex(mode_data, true, " ") + "\n");
|
||||
pure_log(get_exe_dir() + "/playback/" + source->key + "_playback.dat", mem2hex(mode_data, true, " ") + "\n");
|
||||
}
|
||||
if (mode_s_console) {
|
||||
std::cout << source->key + " read:[mode_s_serial]:" << mem2hex(mode_data)
|
||||
<< std::endl;
|
||||
std::cout << source->key + " read:[mode_s_serial]:" << mem2hex(mode_data) << std::endl;
|
||||
}
|
||||
auto handle_packet = [this, source, &ret](std::string& packet) {
|
||||
ret++;
|
||||
auto msg = create_msg(packet);
|
||||
if (!msg) return;
|
||||
if (!msg)
|
||||
return;
|
||||
source->push_to_feed(msg);
|
||||
auto mt = msg->type;
|
||||
bool mode_s = mt == SSR::Msg::S7 || mt == SSR::Msg::S14;
|
||||
@@ -144,8 +131,7 @@ size_t Data_Source_Handler::process_mode_acs_data(std::string_view origin_data)
|
||||
else if (mt == SSR::Msg::Radarcape_status) {
|
||||
auto radarcape_msg = SSR::create_Radarcape_STATUS_Message(packet);
|
||||
std::cout << radarcape_msg.toJson().to_json_string() << std::endl;
|
||||
SSR::mode_s_logger->debug("Radarcape_status/radarcape", {},
|
||||
radarcape_msg.toJson().to_json_string());
|
||||
SSR::mode_s_logger->debug("Radarcape_status/radarcape", {}, radarcape_msg.toJson().to_json_string());
|
||||
}
|
||||
else if (mode_s) {
|
||||
// 拓展点
|
||||
@@ -160,9 +146,7 @@ size_t Data_Source_Handler::process_mode_acs_data(std::string_view origin_data)
|
||||
else {
|
||||
auto co = Coro::instance();
|
||||
auto executor = co->process_data->get_executor();
|
||||
asio::post(executor, [handle_packet, packet = std::move(packet)]() mutable {
|
||||
handle_packet(packet);
|
||||
});
|
||||
asio::post(executor, [handle_packet, packet = std::move(packet)]() mutable { handle_packet(packet); });
|
||||
}
|
||||
};
|
||||
SSR::Binary_Format_handle_buffer(source->buffer, mode_data, f);
|
||||
@@ -170,8 +154,8 @@ size_t Data_Source_Handler::process_mode_acs_data(std::string_view origin_data)
|
||||
}
|
||||
void Data_Source_Handler::handle_mode_s(std::shared_ptr<SSR::Mode_S_Msg> mode_s_msg) {
|
||||
auto source = ds(this);
|
||||
std::string t = mode_s_msg->mlat_timestamp.to_memory() +
|
||||
mode_s_msg->signal_level + Psc::hex2mem(mode_s_msg->msg_hex);
|
||||
std::string t =
|
||||
mode_s_msg->mlat_timestamp.to_memory() + mode_s_msg->signal_level + Psc::hex2mem(mode_s_msg->msg_hex);
|
||||
auto p = mode_s_msg->packet.substr(2);
|
||||
if (t != p) {
|
||||
std::cout << mem2hex(t, true, " ") << std::endl;
|
||||
@@ -183,12 +167,26 @@ void Data_Source_Handler::handle_mode_s(std::shared_ptr<SSR::Mode_S_Msg> mode_s_
|
||||
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();
|
||||
|
||||
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};
|
||||
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};
|
||||
SSR::parse_mode_s_bin(source.get(), mode_s_msg,
|
||||
source->base_station.get_pos(), air_constraint,
|
||||
surface_constraint);
|
||||
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};
|
||||
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<Aircraft>(base);
|
||||
@@ -199,8 +197,7 @@ void Data_Source_Handler::handle_mode_s(std::shared_ptr<SSR::Mode_S_Msg> mode_s_
|
||||
mh.push(mode_s_msg);
|
||||
auto tt = mh.get_all();
|
||||
if (!tt.empty()) {
|
||||
pure_log("@/logs/mlat.log",
|
||||
"receive[" + std::to_string(tt.size()) + "]:\n");
|
||||
pure_log("@/logs/mlat.log", "receive[" + std::to_string(tt.size()) + "]:\n");
|
||||
for (Mlat_MSG& t : tt) {
|
||||
std::ostringstream oss;
|
||||
oss << std::fixed << std::setprecision(10);
|
||||
@@ -223,12 +220,9 @@ void Data_Source_Handler::handle_mode_s(std::shared_ptr<SSR::Mode_S_Msg> mode_s_
|
||||
auto pos = o_pos.value();
|
||||
source->alt = o_alt.value();
|
||||
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;
|
||||
oss << "\tXYZ:[" << x << "," << y << "," << z << "]"
|
||||
<< std::endl;
|
||||
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;
|
||||
oss << "\tXYZ:[" << x << "," << y << "," << z << "]" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -262,23 +256,22 @@ void Data_Source_Handler::handle_HULC(std::string_view packet) {
|
||||
auto msg = mem2hex(std::string(packet));
|
||||
if (len != packet.size() - 4) {
|
||||
SSR::mode_s_logger->debug("HULC/error_length", {},
|
||||
"需要: " + std::to_string(len) +
|
||||
" 当前: " + std::to_string(packet.size()) +
|
||||
"需要: " + std::to_string(len) + " 当前: " + std::to_string(packet.size()) +
|
||||
" hex: " + mem2hex(std::string(packet)));
|
||||
return;
|
||||
}
|
||||
if (id == 1) {
|
||||
// 状态消息
|
||||
auto status_msg = SSR::create_HULC_Status_Message(packet);
|
||||
bool gps_ok = status_msg.GPS_device_detected() && status_msg.GPS_valid() &&
|
||||
status_msg.GPS_has_valid_fix();
|
||||
if (gps_ok) {}
|
||||
bool gps_ok = status_msg.GPS_device_detected() && status_msg.GPS_valid() && status_msg.GPS_has_valid_fix();
|
||||
if (gps_ok) {
|
||||
}
|
||||
auto g = Global::instance();
|
||||
base_station.set_msg(status_msg);
|
||||
Log_Type type({}, {{"msg", std::string(msg)}});
|
||||
SSR::mode_s_logger->debug("HULC/status", type,
|
||||
status_msg.toJson().to_json_string());
|
||||
if (gps_ok) {}
|
||||
SSR::mode_s_logger->debug("HULC/status", type, status_msg.toJson().to_json_string());
|
||||
if (gps_ok) {
|
||||
}
|
||||
}
|
||||
else if (id == 24) {
|
||||
SSR::mode_s_logger->debug("HULC/reply", {}, msg);
|
||||
@@ -290,9 +283,9 @@ 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<std::string> tmp;
|
||||
for (auto& relation :
|
||||
Global::instance()->mode_acs.source_feed_relation_config.map.list()) {
|
||||
if (!relation->enable) continue;
|
||||
for (auto& relation : Global::instance()->mode_acs.source_feed_relation_config.map.list()) {
|
||||
if (!relation->enable)
|
||||
continue;
|
||||
if (relation->type == "One_to_One_Relation") {
|
||||
auto t = dynamic_cast<One_to_One_Relation*>(relation.get());
|
||||
// std::cout << VAR_STR_2(t->source_key, this->key) << "
|
||||
@@ -302,19 +295,16 @@ void Data_Source_Handler::refresh_data_feed_key_list() {
|
||||
}
|
||||
}
|
||||
else if (relation->type == "First_Source_To_All_Feed_Relation") {
|
||||
auto t =
|
||||
dynamic_cast<First_Source_To_All_Feed_Relation*>(relation.get());
|
||||
auto t = dynamic_cast<First_Source_To_All_Feed_Relation*>(relation.get());
|
||||
std::shared_ptr<Data_Source> first = nullptr;
|
||||
for (const auto& ds :
|
||||
Global::instance()->mode_acs.data_source_config.map.list()) {
|
||||
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()) {
|
||||
for (const auto& df : Global::instance()->mode_acs.data_feed_config.map.list()) {
|
||||
if (df->enable) {
|
||||
tmp.push_back(df->key);
|
||||
}
|
||||
@@ -337,10 +327,9 @@ void Data_Source_Handler::refresh_data_feed_key_list() {
|
||||
// 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()) {
|
||||
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);
|
||||
}
|
||||
@@ -350,11 +339,9 @@ void Data_Source_Handler::refresh_data_feed_key_list() {
|
||||
}
|
||||
// 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;
|
||||
});
|
||||
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);
|
||||
@@ -364,8 +351,7 @@ void Data_Source_Handler::refresh_data_feed_key_list() {
|
||||
}
|
||||
}
|
||||
}
|
||||
std::optional<std::string> convert_to_send_format(Data_Source_Handler* ds,
|
||||
const std::shared_ptr<Data_Feed>& feed,
|
||||
std::optional<std::string> convert_to_send_format(Data_Source_Handler* ds, const std::shared_ptr<Data_Feed>& feed,
|
||||
const std::shared_ptr<SSR::Msg>& msg) {
|
||||
auto& type = msg->type;
|
||||
auto fs = dynamic_cast<File_Data_Source*>(msg->source.get());
|
||||
@@ -382,22 +368,22 @@ std::optional<std::string> convert_to_send_format(Data_Source_Handler* ds,
|
||||
// 计算出是否输出消息
|
||||
std::optional<std::string> send_msg = std::nullopt;
|
||||
if (type == SSR::Msg::HULC_Status) {
|
||||
if (!use_status) return std::nullopt;
|
||||
if (!use_status)
|
||||
return std::nullopt;
|
||||
auto data = static_cast<SSR::Msg*>(msg.get());
|
||||
if (output_format == Output_Data_Format::BIN ||
|
||||
output_format == Output_Data_Format::BIN_ID) {
|
||||
if (output_format == Output_Data_Format::BIN || output_format == Output_Data_Format::BIN_ID) {
|
||||
send_msg = SSR::packet_to_escape_format(data->packet);
|
||||
}
|
||||
}
|
||||
else if (type == SSR::Msg::Radarcape_status) {
|
||||
auto data = static_cast<SSR::Msg*>(msg.get());
|
||||
if (output_format == Output_Data_Format::BIN ||
|
||||
output_format == Output_Data_Format::BIN_ID) {
|
||||
if (output_format == Output_Data_Format::BIN || output_format == Output_Data_Format::BIN_ID) {
|
||||
send_msg = SSR::packet_to_escape_format(data->packet);
|
||||
}
|
||||
}
|
||||
else if (type == SSR::Msg::AC) {
|
||||
if (!use_mode_ac) return std::nullopt;
|
||||
if (!use_mode_ac)
|
||||
return std::nullopt;
|
||||
auto data = static_cast<SSR::Mode_AC_Msg*>(msg.get());
|
||||
std::string& msg_hex = data->msg_hex;
|
||||
char signal_level = data->signal_level;
|
||||
@@ -448,10 +434,12 @@ std::optional<std::string> convert_to_send_format(Data_Source_Handler* ds,
|
||||
sbs_out_put = true;
|
||||
}
|
||||
}
|
||||
if (!sbs_out_put) return std::nullopt;
|
||||
if (!sbs_out_put)
|
||||
return std::nullopt;
|
||||
send_msg = "";
|
||||
SSR::SBS_MSG msg_base;
|
||||
if (!aircraft) return std::nullopt;
|
||||
if (!aircraft)
|
||||
return std::nullopt;
|
||||
msg_base.set(aircraft.get());
|
||||
for (int msg_t = 1; msg_t <= 8; ++msg_t) {
|
||||
send_msg->append(msg_base.to_msg(msg_t) + "\n");
|
||||
@@ -463,11 +451,11 @@ std::optional<std::string> convert_to_send_format(Data_Source_Handler* ds,
|
||||
send_msg->append(msg_base.to_STA() + "\n");
|
||||
return send_msg;
|
||||
}
|
||||
bool DF_11_17_18 =
|
||||
df == SSR::Downlink_Format::All_Call_Reply_11 ||
|
||||
bool DF_11_17_18 = df == SSR::Downlink_Format::All_Call_Reply_11 ||
|
||||
df == SSR::Downlink_Format::Extended_Squitter_17 ||
|
||||
df == SSR::Downlink_Format::Extended_Squitter_Non_Transponder_18;
|
||||
if (mode_s_output_type == Mode_S_Output_Type::DF_11_17_18 && !DF_11_17_18) return std::nullopt;
|
||||
if (mode_s_output_type == Mode_S_Output_Type::DF_11_17_18 && !DF_11_17_18)
|
||||
return std::nullopt;
|
||||
if (mode_s_output_type == Mode_S_Output_Type::NO_POS_Mode_S) {
|
||||
if (aircraft && aircraft->pos().has_value()) {
|
||||
return std::nullopt;
|
||||
@@ -493,8 +481,7 @@ std::optional<std::string> convert_to_send_format(Data_Source_Handler* ds,
|
||||
send_msg = create_MLAT_AVR_format(msg_hex, &mlat_timestamp);
|
||||
}
|
||||
else {
|
||||
std::cerr << "Unknown output format " << VAR_STR_1(output_format)
|
||||
<< std::endl;
|
||||
std::cerr << "Unknown output format " << VAR_STR_1(output_format) << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
@@ -516,9 +503,9 @@ void Data_Source_Handler::push_to_feed(const std::shared_ptr<SSR::Msg>& msg) {
|
||||
break;
|
||||
}
|
||||
std::shared_ptr<Data_Feed>& feed = opt_feed.value();
|
||||
if (!feed->enable) continue;
|
||||
if (msg->type == SSR::Mode_Msg::T::S7 ||
|
||||
msg->type == SSR::Mode_Msg::T::S14) {
|
||||
if (!feed->enable)
|
||||
continue;
|
||||
if (msg->type == SSR::Mode_Msg::T::S7 || msg->type == SSR::Mode_Msg::T::S14) {
|
||||
// static Frequency_Limit fl;
|
||||
// if (!fl.test()) {
|
||||
// std::ostringstream oss;
|
||||
|
||||
@@ -1,12 +1,56 @@
|
||||
#include "Database.h"
|
||||
#include "Data_Source.h"
|
||||
#include "../server/Global.h"
|
||||
#include "../server/Performance_Monitor.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
|
||||
namespace {
|
||||
double to_radians(double degrees) {
|
||||
return degrees * 3.14159265358979323846 / 180.0;
|
||||
}
|
||||
double to_degrees(double radians) {
|
||||
return radians * 180.0 / 3.14159265358979323846;
|
||||
}
|
||||
bool aircraft_in_base_station_radio_range(const std::optional<SSR::Position_3D> &base_position,
|
||||
const SSR::Position_Info &position,
|
||||
double factor) {
|
||||
if (!base_position) {
|
||||
return true;
|
||||
}
|
||||
return SSR::CPR::in_radio_line_of_sight_range(*base_position, base_position->alt, position, position.alt, factor);
|
||||
}
|
||||
double track_heading_from_points(const SSR::Position_Info &first,
|
||||
const SSR::Position_Info &second) {
|
||||
auto lat1 = to_radians(first.lat);
|
||||
auto lat2 = to_radians(second.lat);
|
||||
auto dlon = to_radians(second.lon - first.lon);
|
||||
auto y = std::sin(dlon) * std::cos(lat2);
|
||||
auto x = std::cos(lat1) * std::sin(lat2) -
|
||||
std::sin(lat1) * std::cos(lat2) * std::cos(dlon);
|
||||
auto angle = to_degrees(std::atan2(y, x));
|
||||
return angle < 0 ? angle + 360.0 : angle;
|
||||
}
|
||||
double track_pitch_from_points(const SSR::Position_Info &first,
|
||||
const SSR::Position_Info &second) {
|
||||
auto distance = SSR::CPR::haversine(first, second);
|
||||
return std::atan2(second.alt - first.alt, distance) * 180.0 /
|
||||
3.14159265358979323846;
|
||||
}
|
||||
Psc::JSON track_orientation_json_from_points(const SSR::Position_Info &first,
|
||||
const SSR::Position_Info &second) {
|
||||
auto ret = Psc::JSON::object();
|
||||
ret.append({"heading", track_heading_from_points(first, second)});
|
||||
ret.append({"pitch", track_pitch_from_points(first, second)});
|
||||
ret.append({"roll", 0.0});
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
Aircraft::Aircraft(std::string_view icao) : Aircraft_Info(icao) {
|
||||
auto size = Global::instance()->mode_acs.max_track_point_size.load();
|
||||
air_pos_track_list.init(size);
|
||||
@@ -77,14 +121,34 @@ Psc::JSON DataBase::get_aircraftlist(int limit_msg_num) {
|
||||
JSON DataBase::get_all_aircraft_json() {
|
||||
JSON json = JSON::array();
|
||||
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();
|
||||
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};
|
||||
}
|
||||
}
|
||||
size_t num = 0;
|
||||
for (auto &it : aircraft_map.values()) {
|
||||
auto vto = Flight_VTO::to_VTO(it.get());
|
||||
bool have_pos = !it.get()->air_pos_track_list.empty();
|
||||
if (have_pos) {
|
||||
json.children.push_back(vto.to_json());
|
||||
num++;
|
||||
if (it->air_pos_track_list.size() < min_position_points) {
|
||||
continue;
|
||||
}
|
||||
auto last_position = it->air_pos_track_list.last();
|
||||
if (!last_position) {
|
||||
continue;
|
||||
}
|
||||
if (range_filter &&
|
||||
!aircraft_in_base_station_radio_range(base_position, *last_position,
|
||||
range_factor)) {
|
||||
continue;
|
||||
}
|
||||
auto vto = Flight_VTO::to_VTO(it.get());
|
||||
json.children.push_back(vto.to_json());
|
||||
num++;
|
||||
}
|
||||
have_pos_aircraft_num = num;
|
||||
return json;
|
||||
@@ -187,6 +251,16 @@ void database_server(Global *g) {
|
||||
auto ret = JSON::object();
|
||||
if (db) {
|
||||
ret = db->base_station.to_Json();
|
||||
auto target_height =
|
||||
g->mode_acs.adsb_theoretical_target_altitude_meters.load();
|
||||
auto range = BaseStation::theoretical_detection_range_meters(db->alt,
|
||||
target_height);
|
||||
ret.append({"latitude", db->lat});
|
||||
ret.append({"longitude", db->lon});
|
||||
ret.append({"height", db->alt});
|
||||
ret.append({"adsb_theoretical_target_altitude_meters", target_height});
|
||||
ret.append({"adsb_theoretical_detection_range_meters", range});
|
||||
ret.append({"理论探测范围", range});
|
||||
}
|
||||
res->setBody(warp(ret).to_json_string());
|
||||
});
|
||||
@@ -198,7 +272,7 @@ void database_server(Global *g) {
|
||||
JSON ret;
|
||||
if (aircraft != nullptr) {
|
||||
auto vto = Flight_VTO::to_VTO(aircraft.get());
|
||||
ret = vto.to_json();
|
||||
ret = vto.to_base_info_json();
|
||||
}
|
||||
res->setBody(ret.to_json_string());
|
||||
});
|
||||
@@ -222,6 +296,14 @@ void database_server(Global *g) {
|
||||
JSON ret;
|
||||
if (aircraft != nullptr) {
|
||||
ret = aircraft->air_pos_track_list.get_last_array_json(last_size);
|
||||
auto last_two = aircraft->air_pos_track_list.last_two();
|
||||
if (last_two.has_value()) {
|
||||
ret.children.insert(
|
||||
ret.children.begin(),
|
||||
JSON("track_orientation",
|
||||
track_orientation_json_from_points(last_two->first,
|
||||
last_two->second)));
|
||||
}
|
||||
ret.children.insert(ret.children.begin(), JSON("ds", data_source_key));
|
||||
ret.children.insert(ret.children.begin(), JSON("icao", icao));
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#ifndef PSC_GLOBAL_INCLUDE_H
|
||||
#define PSC_GLOBAL_INCLUDE_H
|
||||
#include "Core/Statistics/Statistics.h"
|
||||
#include "Core/socket/ByteOrder.h"
|
||||
#include "SSR/export.h"
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
@@ -11,10 +8,13 @@
|
||||
#include <queue>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string_view>
|
||||
#include "Core/Statistics/Statistics.h"
|
||||
#include "Core/socket/ByteOrder.h"
|
||||
#include "SSR/export.h"
|
||||
class Global;
|
||||
class Invalid_Http_Param final : public std::runtime_error {
|
||||
public:
|
||||
@@ -23,28 +23,28 @@ public:
|
||||
[[noreturn]] inline void throw_invalid_http_param(const char* detail) {
|
||||
throw Invalid_Http_Param(detail);
|
||||
}
|
||||
#define CHECK_JSON_PARAM \
|
||||
auto o_params = Psc::try_parse_json(req->body().data()); \
|
||||
if (!o_params.has_value()) { \
|
||||
throw_invalid_http_param("request body is not valid JSON"); \
|
||||
} \
|
||||
auto ¶ms = o_params.value(); \
|
||||
auto that_json = ¶ms;
|
||||
#define HTTP_REQUIRE_VALUE(NAME, EXPRESSION) \
|
||||
auto o_http_##NAME = (EXPRESSION); \
|
||||
if (!o_http_##NAME.has_value()) { \
|
||||
throw_invalid_http_param(#NAME); \
|
||||
} \
|
||||
auto NAME = std::move(o_http_##NAME).value();
|
||||
#define HTTP_REQUIRE_PTR(NAME, EXPRESSION) \
|
||||
auto NAME = (EXPRESSION); \
|
||||
if ((NAME) == nullptr) { \
|
||||
throw_invalid_http_param(#NAME); \
|
||||
}
|
||||
#define HTTP_REQUIRE_TRUE(EXPRESSION, DETAIL) \
|
||||
if (!(EXPRESSION)) { \
|
||||
throw_invalid_http_param(DETAIL); \
|
||||
}
|
||||
#define CHECK_JSON_PARAM \
|
||||
auto o_params = Psc::try_parse_json(req->body().data()); \
|
||||
if (!o_params.has_value()) { \
|
||||
throw_invalid_http_param("request body is not valid JSON"); \
|
||||
} \
|
||||
auto& params = o_params.value(); \
|
||||
auto that_json = ¶ms;
|
||||
#define HTTP_REQUIRE_VALUE(NAME, EXPRESSION) \
|
||||
auto o_http_##NAME = (EXPRESSION); \
|
||||
if (!o_http_##NAME.has_value()) { \
|
||||
throw_invalid_http_param(#NAME); \
|
||||
} \
|
||||
auto NAME = std::move(o_http_##NAME).value();
|
||||
#define HTTP_REQUIRE_PTR(NAME, EXPRESSION) \
|
||||
auto NAME = (EXPRESSION); \
|
||||
if ((NAME) == nullptr) { \
|
||||
throw_invalid_http_param(#NAME); \
|
||||
}
|
||||
#define HTTP_REQUIRE_TRUE(EXPRESSION, DETAIL) \
|
||||
if (!(EXPRESSION)) { \
|
||||
throw_invalid_http_param(DETAIL); \
|
||||
}
|
||||
template <typename Value_Type>
|
||||
class Ordered_List {
|
||||
public:
|
||||
@@ -218,9 +218,7 @@ public:
|
||||
// Ret_J(speed);
|
||||
std::string t = "null";
|
||||
if (total_num != 0) {
|
||||
t = std::to_string(static_cast<double>(crc_error_num) /
|
||||
static_cast<double>(total_num)) +
|
||||
"%";
|
||||
t = std::to_string(static_cast<double>(crc_error_num) / static_cast<double>(total_num)) + "%";
|
||||
}
|
||||
ret.append({"crc误码率", t});
|
||||
for (auto& cur : statistic_map) {
|
||||
@@ -301,12 +299,8 @@ public:
|
||||
if (iter != statistic.result_num.end()) {
|
||||
num = iter->second;
|
||||
}
|
||||
double rate = static_cast<double>(num) /
|
||||
static_cast<double>(statistic.total_num) * 100.0;
|
||||
type_result.append({
|
||||
Psc::to_string(result_type),
|
||||
std::format("{} {:.2f}%", num, rate)
|
||||
});
|
||||
double rate = static_cast<double>(num) / static_cast<double>(statistic.total_num) * 100.0;
|
||||
type_result.append({Psc::to_string(result_type), std::format("{} {:.2f}%", num, rate)});
|
||||
}
|
||||
result.append({Psc::to_string(cpr_type), type_result});
|
||||
}
|
||||
@@ -318,11 +312,8 @@ protected:
|
||||
std::map<SSR::CPR_Ret_Type, size_t> result_num;
|
||||
};
|
||||
inline static constexpr std::array statistic_types{
|
||||
SSR::CPR_Ret_Type::Speed_Error,
|
||||
SSR::CPR_Ret_Type::Parse_OK,
|
||||
SSR::CPR_Ret_Type::Inter_Error,
|
||||
SSR::CPR_Ret_Type::Time_Space_Too_Long
|
||||
};
|
||||
SSR::CPR_Ret_Type::Speed_Error, SSR::CPR_Ret_Type::Parse_OK, SSR::CPR_Ret_Type::Inter_Error,
|
||||
SSR::CPR_Ret_Type::Time_Space_Too_Long, SSR::CPR_Ret_Type::Out_of_Maximum_Detection_Range};
|
||||
std::map<SSR::CPR_Type, Statistic_Data> statistic_map;
|
||||
};
|
||||
class Mode_S_Statistic_Data {
|
||||
@@ -336,21 +327,14 @@ public:
|
||||
Ret_J(total_num);
|
||||
std::string t = "null";
|
||||
if (total_num != 0) {
|
||||
t = std::format(
|
||||
"{}%",
|
||||
static_cast<double>(crc_error_num) /
|
||||
static_cast<double>(total_num)
|
||||
);
|
||||
t = std::format("{}%", static_cast<double>(crc_error_num) / static_cast<double>(total_num));
|
||||
}
|
||||
Ret_J(crc_error_num);
|
||||
ret.append({"crc_error_rate", t});
|
||||
Psc::JSON df_sub_type = Psc::JSON::object();
|
||||
for (auto& cur : DF_Statistic_Data_map) {
|
||||
df_sub_type.append({
|
||||
Psc::to_string(cur.first) + " [" +
|
||||
std::to_string(static_cast<int>(cur.first)) + "]",
|
||||
cur.second.to_json()
|
||||
});
|
||||
df_sub_type.append({Psc::to_string(cur.first) + " [" + std::to_string(static_cast<int>(cur.first)) + "]",
|
||||
cur.second.to_json()});
|
||||
}
|
||||
ret.append({"DF子类型", df_sub_type});
|
||||
ret.append({"CPR统计信息", cpr_statistic_data.to_json()});
|
||||
@@ -362,9 +346,7 @@ public:
|
||||
void add_cpr_error(SSR::CPR_Type cpr_type, SSR::CPR_Ret_Type result_type) {
|
||||
cpr_statistic_data.add(cpr_type, result_type);
|
||||
}
|
||||
DF_Statistic_Data* get_create_df_statistic_data(
|
||||
const SSR::Downlink_Format& key
|
||||
) {
|
||||
DF_Statistic_Data* get_create_df_statistic_data(const SSR::Downlink_Format& key) {
|
||||
DF_Statistic_Data* t;
|
||||
auto iter = DF_Statistic_Data_map.find(key);
|
||||
if (iter == DF_Statistic_Data_map.end()) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#undef interface
|
||||
|
||||
std::string default_config_path;
|
||||
@@ -49,6 +50,59 @@ static Psc::JSON write_string_array(std::string_view key, const std::vector<std:
|
||||
}
|
||||
return Psc::JSON(key, array);
|
||||
}
|
||||
static const std::pair<std::string_view, std::string_view> kDefaultAircraftModelUrls[] = {
|
||||
{"no_category_information", "/ui/model/aircraft-no-category-information.glb"},
|
||||
{"surface_emergency_vehicle", "/ui/model/aircraft-surface-emergency-vehicle.glb"},
|
||||
{"surface_service_vehicle", "/ui/model/aircraft-surface-service-vehicle.glb"},
|
||||
{"ground_obstruction_4", "/ui/model/aircraft-ground-obstruction-4.glb"},
|
||||
{"ground_obstruction_5", "/ui/model/aircraft-ground-obstruction-5.glb"},
|
||||
{"ground_obstruction_6", "/ui/model/aircraft-ground-obstruction-6.glb"},
|
||||
{"ground_obstruction_7", "/ui/model/aircraft-ground-obstruction-7.glb"},
|
||||
{"glider", "/ui/model/aircraft-glider.glb"},
|
||||
{"lighter_than_air", "/ui/model/aircraft-lighter-than-air.glb"},
|
||||
{"parachutist", "/ui/model/aircraft-parachutist.glb"},
|
||||
{"ultralight_hangglider_paraglider", "/ui/model/aircraft-ultralight-hangglider-paraglider.glb"},
|
||||
{"reserved_3_5", "/ui/model/aircraft-reserved-3-5.glb"},
|
||||
{"unmanned_aerial_vehicle", "/ui/model/aircraft-unmanned-aerial-vehicle.glb"},
|
||||
{"space_transatmospheric_vehicle", "/ui/model/aircraft-space-transatmospheric-vehicle.glb"},
|
||||
{"light_aircraft", "/ui/model/aircraft-light-aircraft.glb"},
|
||||
{"medium_1_aircraft", "/ui/model/aircraft-medium-1-aircraft.glb"},
|
||||
{"medium_2_aircraft", "/ui/model/aircraft-medium-2-aircraft.glb"},
|
||||
{"high_vortex_aircraft", "/ui/model/aircraft-high-vortex-aircraft.glb"},
|
||||
{"heavy_aircraft", "/ui/model/aircraft-heavy-aircraft.glb"},
|
||||
{"high_performance_aircraft", "/ui/model/aircraft-high-performance-aircraft.glb"},
|
||||
{"rotorcraft", "/ui/model/aircraft-rotorcraft.glb"}
|
||||
};
|
||||
static std::map<std::string, Map_Model_Item_Config> make_default_aircraft_models() {
|
||||
std::map<std::string, Map_Model_Item_Config> ret;
|
||||
for (const auto& item : kDefaultAircraftModelUrls) {
|
||||
ret.emplace(std::string(item.first), Map_Model_Item_Config{std::string(item.second), 50.0, -90.0, 0.0, 0.0});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static std::map<std::string, Map_Model_Item_Config> read_optional_model_object_field(const Psc::JSON* json, std::string_view key, std::map<std::string, Map_Model_Item_Config> fallback) {
|
||||
auto field = json->get(key);
|
||||
if (field == nullptr) {
|
||||
return fallback;
|
||||
}
|
||||
if (field->valueType != Psc::Object) {
|
||||
throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), std::string(key));
|
||||
}
|
||||
for (const auto& child : field->children) {
|
||||
if (child.valueType != Psc::Object) {
|
||||
throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), std::string(key));
|
||||
}
|
||||
fallback[child.key].from_base_json(&child);
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
static Psc::JSON write_model_object(std::string_view key, const std::map<std::string, Map_Model_Item_Config>& values) {
|
||||
auto object = Psc::JSON::object();
|
||||
for (const auto& item : values) {
|
||||
object.append({item.first, item.second.to_base_json()});
|
||||
}
|
||||
return Psc::JSON(key, object);
|
||||
}
|
||||
static std::string read_string_field(const Psc::JSON* json, std::string_view key) {
|
||||
auto field = json->get(key);
|
||||
if (field == nullptr || field->valueType != Psc::String) {
|
||||
@@ -242,6 +296,31 @@ Psc::JSON Map_Camera_Config::to_base_json() const {
|
||||
bool Map_View_Config::is_scene_mode(std::string_view mode) {
|
||||
return mode == "2d" || mode == "2.5d" || mode == "3d";
|
||||
}
|
||||
bool Map_Tile_View_Config::is_tile_zoom_mode(std::string_view mode) {
|
||||
return mode == "native" || mode == "upscale" || mode == "both";
|
||||
}
|
||||
void Map_Tile_View_Config::from_base_json(const Psc::JSON* that_json) {
|
||||
if (that_json == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (that_json->valueType != Psc::Object) {
|
||||
throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "map_tile_view");
|
||||
}
|
||||
current_imagery_key = read_optional_string_field(that_json, "current_imagery_key", current_imagery_key);
|
||||
auto next_tile_zoom_mode = read_optional_string_field(that_json, "tile_zoom_mode", tile_zoom_mode);
|
||||
if (!is_tile_zoom_mode(next_tile_zoom_mode)) {
|
||||
throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "tile_zoom_mode");
|
||||
}
|
||||
tile_zoom_mode = next_tile_zoom_mode;
|
||||
tile_display_maximum_level = std::clamp(read_optional_uint32_field(that_json, "tile_display_maximum_level", tile_display_maximum_level), 0u, 24u);
|
||||
}
|
||||
Psc::JSON Map_Tile_View_Config::to_base_json() const {
|
||||
auto ret = Psc::JSON::object();
|
||||
ret.append({"current_imagery_key", current_imagery_key});
|
||||
ret.append({"tile_zoom_mode", tile_zoom_mode});
|
||||
ret.append({"tile_display_maximum_level", tile_display_maximum_level});
|
||||
return ret;
|
||||
}
|
||||
void Map_View_Config::from_base_json(const Psc::JSON* that_json) {
|
||||
if (that_json == nullptr) {
|
||||
return;
|
||||
@@ -254,11 +333,15 @@ void Map_View_Config::from_base_json(const Psc::JSON* that_json) {
|
||||
throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "scene_mode");
|
||||
}
|
||||
scene_mode = next_scene_mode;
|
||||
map2d.from_base_json(that_json->get("map2d"));
|
||||
map3d.from_base_json(that_json->get("map3d"));
|
||||
camera.from_base_json(that_json->get("camera"));
|
||||
}
|
||||
Psc::JSON Map_View_Config::to_base_json() const {
|
||||
auto ret = Psc::JSON::object();
|
||||
ret.append({"scene_mode", scene_mode});
|
||||
ret.append({"map2d", map2d.to_base_json()});
|
||||
ret.append({"map3d", map3d.to_base_json()});
|
||||
ret.append({"camera", camera.to_base_json()});
|
||||
return ret;
|
||||
}
|
||||
@@ -279,7 +362,7 @@ void Cesium_Graphics_Config::from_base_json(const Psc::JSON* that_json) {
|
||||
enable_lighting = read_optional_bool_field(that_json, "enable_lighting", enable_lighting);
|
||||
maximum_screen_space_error = std::clamp(read_optional_uint32_field(that_json, "maximum_screen_space_error", maximum_screen_space_error), 1u, 16u);
|
||||
terrain_enabled_in_3d = read_optional_bool_field(that_json, "terrain_enabled_in_3d", terrain_enabled_in_3d);
|
||||
terrain_exaggeration = std::clamp(read_optional_double_field(that_json, "terrain_exaggeration", terrain_exaggeration), 0.0, 5.0);
|
||||
terrain_exaggeration = read_optional_double_field(that_json, "terrain_exaggeration", terrain_exaggeration);
|
||||
terrain_limit_maximum_level = read_optional_bool_field(that_json, "terrain_limit_maximum_level", terrain_limit_maximum_level);
|
||||
terrain_maximum_level = std::clamp(read_optional_uint32_field(that_json, "terrain_maximum_level", terrain_maximum_level), 0u, 24u);
|
||||
terrain_cache_tiles = std::clamp(read_optional_uint32_field(that_json, "terrain_cache_tiles", terrain_cache_tiles), 16u, 2048u);
|
||||
@@ -302,6 +385,49 @@ Psc::JSON Cesium_Graphics_Config::to_base_json() const {
|
||||
ret.append({"terrain_cache_tiles", terrain_cache_tiles});
|
||||
return ret;
|
||||
}
|
||||
Map_Model_Item_Config::Map_Model_Item_Config(std::string url)
|
||||
: url(std::move(url)) {}
|
||||
Map_Model_Item_Config::Map_Model_Item_Config(std::string url, double built_in_size, double heading, double pitch, double roll)
|
||||
: url(std::move(url)), built_in_size(built_in_size), heading_offset_degrees(heading), pitch_offset_degrees(pitch), roll_offset_degrees(roll) {}
|
||||
void Map_Model_Item_Config::from_base_json(const Psc::JSON* that_json) {
|
||||
if (that_json == nullptr || that_json->valueType != Psc::Object) {
|
||||
throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "map_model_item");
|
||||
}
|
||||
url = read_optional_string_field(that_json, "url", url);
|
||||
built_in_size = read_optional_double_field(that_json, "built_in_size", built_in_size);
|
||||
heading_offset_degrees = read_optional_double_field(that_json, "heading_offset_degrees", heading_offset_degrees);
|
||||
pitch_offset_degrees = read_optional_double_field(that_json, "pitch_offset_degrees", pitch_offset_degrees);
|
||||
roll_offset_degrees = read_optional_double_field(that_json, "roll_offset_degrees", roll_offset_degrees);
|
||||
}
|
||||
Psc::JSON Map_Model_Item_Config::to_base_json() const {
|
||||
auto ret = Psc::JSON::object();
|
||||
ret.append({"url", url});
|
||||
ret.append({"built_in_size", built_in_size});
|
||||
ret.append({"heading_offset_degrees", heading_offset_degrees});
|
||||
ret.append({"pitch_offset_degrees", pitch_offset_degrees});
|
||||
ret.append({"roll_offset_degrees", roll_offset_degrees});
|
||||
return ret;
|
||||
}
|
||||
Map_Model_Config::Map_Model_Config()
|
||||
: aircraft_models(make_default_aircraft_models()) {}
|
||||
void Map_Model_Config::from_base_json(const Psc::JSON* that_json) {
|
||||
if (that_json == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (that_json->valueType != Psc::Object) {
|
||||
throw Psc::json_assign_error(std::make_error_code(std::errc::invalid_argument), "map_models");
|
||||
}
|
||||
aircraft_model.from_base_json(that_json->get("aircraft_model"));
|
||||
aircraft_models = read_optional_model_object_field(that_json, "aircraft_models", make_default_aircraft_models());
|
||||
base_station_model.from_base_json(that_json->get("base_station_model"));
|
||||
}
|
||||
Psc::JSON Map_Model_Config::to_base_json() const {
|
||||
auto ret = Psc::JSON::object();
|
||||
ret.append({"aircraft_model", aircraft_model.to_base_json()});
|
||||
ret.append(write_model_object("aircraft_models", aircraft_models));
|
||||
ret.append({"base_station_model", base_station_model.to_base_json()});
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Mode_ACS_Config::server(Global *g) {
|
||||
auto &svr = g->svr;
|
||||
@@ -318,6 +444,19 @@ void Mode_ACS_Config::server(Global *g) {
|
||||
CHECK_JSON_PARAM
|
||||
auto &j = o_params.value();
|
||||
init_base_json(&j);
|
||||
if (auto min_points = j.try_get_number<std::size_t>("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<double>("aircraft_change_list_adsb_range_factor")) {
|
||||
aircraft_change_list_adsb_range_factor = range_factor.value();
|
||||
}
|
||||
if (auto target_altitude = j.try_get_number<double>("adsb_theoretical_target_altitude_meters")) {
|
||||
adsb_theoretical_target_altitude_meters = target_altitude.value();
|
||||
}
|
||||
Global::save();
|
||||
res->setBody(warp(to_base_json()).to_json_string());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "MLAT.h"
|
||||
#include "Source_Feed_Relation.h"
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
inline bool delete_log = false;
|
||||
extern std::string default_config_path;
|
||||
@@ -56,6 +57,10 @@ struct Mode_ACS_Config_Data {
|
||||
Psc::Copyable_Atomic<std::size_t> mode_other_max_num{};
|
||||
Psc::Copyable_Atomic<std::size_t> report_data_feed_msg_mum{};
|
||||
Psc::Copyable_Atomic<std::size_t> default_min_aircraft_list_num{};
|
||||
Psc::Copyable_Atomic<std::size_t> aircraft_change_list_min_position_points = 2;
|
||||
Psc::Copyable_Atomic<bool> aircraft_change_list_adsb_range_filter = true;
|
||||
Psc::Copyable_Atomic<double> aircraft_change_list_adsb_range_factor = 1.2;
|
||||
Psc::Copyable_Atomic<double> adsb_theoretical_target_altitude_meters = 10000.0;
|
||||
Psc::Copyable_Atomic<bool> ignore_df_11{};
|
||||
Psc::Copyable_Atomic<bool> monitor_msg_live{};
|
||||
PSC_USE_JSON
|
||||
@@ -148,8 +153,18 @@ struct Map_Camera_Config {
|
||||
void from_base_json(const Psc::JSON* that_json);
|
||||
[[nodiscard]] Psc::JSON to_base_json() const;
|
||||
};
|
||||
struct Map_Tile_View_Config {
|
||||
std::string current_imagery_key = "imagery";
|
||||
std::string tile_zoom_mode = "native";
|
||||
std::uint32_t tile_display_maximum_level = 19;
|
||||
void from_base_json(const Psc::JSON* that_json);
|
||||
[[nodiscard]] Psc::JSON to_base_json() const;
|
||||
[[nodiscard]] static bool is_tile_zoom_mode(std::string_view mode);
|
||||
};
|
||||
struct Map_View_Config {
|
||||
std::string scene_mode = "3d";
|
||||
Map_Tile_View_Config map2d;
|
||||
Map_Tile_View_Config map3d;
|
||||
Map_Camera_Config camera;
|
||||
void from_base_json(const Psc::JSON* that_json);
|
||||
[[nodiscard]] Psc::JSON to_base_json() const;
|
||||
@@ -173,6 +188,26 @@ struct Cesium_Graphics_Config {
|
||||
void from_base_json(const Psc::JSON* that_json);
|
||||
[[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);
|
||||
[[nodiscard]] Psc::JSON to_base_json() const;
|
||||
};
|
||||
struct Map_Model_Config {
|
||||
Map_Model_Item_Config aircraft_model{"/ui/model/aircraft.glb", 50.0, -90.0, 0.0, 0.0};
|
||||
std::map<std::string, Map_Model_Item_Config> aircraft_models;
|
||||
Map_Model_Item_Config base_station_model{"/ui/model/base-station.glb", 100.0, 0.0, 0.0, 0.0};
|
||||
Map_Model_Config();
|
||||
void from_base_json(const Psc::JSON* that_json);
|
||||
[[nodiscard]] Psc::JSON to_base_json() const;
|
||||
};
|
||||
struct Net_Config {
|
||||
std::string key{};
|
||||
std::string ip{};
|
||||
@@ -229,6 +264,7 @@ public:
|
||||
Map_Resources_Config map_resources_config;
|
||||
Map_View_Config map_view_config;
|
||||
Cesium_Graphics_Config cesium_graphics_config;
|
||||
Map_Model_Config map_model_config;
|
||||
Mode_ACS_Config mode_acs;
|
||||
Log_Config log_config;
|
||||
Console_Config console_config{};
|
||||
@@ -261,6 +297,7 @@ public:
|
||||
map_resources_config.from_base_json(that_json->get("map_resources"));
|
||||
map_view_config.from_base_json(that_json->get("map_view"));
|
||||
cesium_graphics_config.from_base_json(that_json->get("cesium_graphics"));
|
||||
map_model_config.from_base_json(that_json->get("map_models"));
|
||||
device_config.init(that_json->get("device"));
|
||||
mode_acs.init(that_json->get("mode_acs"));
|
||||
console_config.from_base_json(that_json->get("console"));
|
||||
@@ -279,6 +316,7 @@ public:
|
||||
{"web_server", web_server_config.to_base_json()},
|
||||
{"map_resources", map_resources_config.to_base_json()},
|
||||
{"map_view", map_view_config.to_base_json()},
|
||||
{"map_models", map_model_config.to_base_json()},
|
||||
{"device", device_config.to_base_json()},
|
||||
{"mode_acs", mode_acs.to_base_json()},
|
||||
//{"ais", ais.to_json()},
|
||||
|
||||
@@ -148,6 +148,12 @@ void Global::init_web_server() {
|
||||
httplib::detail::read_file(path, content);
|
||||
auto content_type = httplib::detail::find_content_type(
|
||||
path, {}, "application/octet-stream");
|
||||
if (path.ends_with(".glb")) {
|
||||
content_type = "model/gltf-binary";
|
||||
}
|
||||
else if (path.ends_with(".gltf")) {
|
||||
content_type = "model/gltf+json";
|
||||
}
|
||||
res->setContentTypeString(content_type);
|
||||
res->setBody(content);
|
||||
// res.set_file_content(path);
|
||||
|
||||
@@ -399,6 +399,135 @@ Psc::JSON make_map_view_response(Global* global) {
|
||||
ret.append({"current_imagery_key", global->map_resources_config.current_imagery_key});
|
||||
return ret;
|
||||
}
|
||||
bool is_multipart_request(const drogon::HttpRequestPtr& req) {
|
||||
auto content_type = req->getHeader("content-type");
|
||||
return content_type.find("multipart/form-data") != std::string::npos;
|
||||
}
|
||||
std::string normalize_upload_extension(std::string extension) {
|
||||
std::ranges::transform(extension, extension.begin(), [](unsigned char ch) {
|
||||
return static_cast<char>(std::tolower(ch));
|
||||
});
|
||||
if (!extension.empty() && extension.front() != '.') {
|
||||
extension.insert(extension.begin(), '.');
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
std::filesystem::path web_model_directory(Global* global) {
|
||||
return std::filesystem::path(global->web_server_config.get_true_webapp()) / "model";
|
||||
}
|
||||
std::filesystem::path source_model_directory(Global* global) {
|
||||
return std::filesystem::path(global->web_server_config.get_true_webapp()).parent_path() / "model";
|
||||
}
|
||||
bool is_safe_aircraft_model_key(std::string_view key) {
|
||||
return !key.empty() && std::ranges::all_of(key, [](unsigned char ch) {
|
||||
return std::isalnum(ch) != 0 || ch == '_';
|
||||
});
|
||||
}
|
||||
std::string model_file_name(std::string_view model_type, std::string_view aircraft_model_key) {
|
||||
if (model_type == "aircraft") {
|
||||
return "aircraft-custom.glb";
|
||||
}
|
||||
if (model_type == "aircraft_type") {
|
||||
if (!is_safe_aircraft_model_key(aircraft_model_key)) {
|
||||
throw std::invalid_argument("invalid aircraft_model_key");
|
||||
}
|
||||
auto normalized_key = std::string(aircraft_model_key);
|
||||
std::ranges::replace(normalized_key, '_', '-');
|
||||
return "aircraft-" + normalized_key + ".glb";
|
||||
}
|
||||
if (model_type == "base_station") {
|
||||
return "base-station-custom.glb";
|
||||
}
|
||||
throw std::invalid_argument("invalid model_type");
|
||||
}
|
||||
void save_uploaded_model_file(const drogon::HttpFile& upload,
|
||||
std::string_view model_type,
|
||||
std::string_view aircraft_model_key,
|
||||
Global* global) {
|
||||
auto extension = normalize_upload_extension(std::string(upload.getFileExtension()));
|
||||
if (extension != ".glb") {
|
||||
throw std::invalid_argument("model file must be .glb");
|
||||
}
|
||||
if (model_type == "aircraft_type" && !global->map_model_config.aircraft_models.contains(std::string(aircraft_model_key))) {
|
||||
throw std::invalid_argument("unknown aircraft_model_key");
|
||||
}
|
||||
auto upload_dir = std::filesystem::path(drogon::app().getUploadPath());
|
||||
std::filesystem::create_directories(upload_dir);
|
||||
auto temp_name = std::string(model_type) + "_" + std::string(aircraft_model_key) + "_model_upload.tmp";
|
||||
if (upload.saveAs(temp_name) != 0) {
|
||||
throw std::runtime_error("cannot save uploaded model");
|
||||
}
|
||||
auto temp_path = upload_dir / temp_name;
|
||||
auto file_name = model_file_name(model_type, aircraft_model_key);
|
||||
auto web_dir = web_model_directory(global);
|
||||
auto source_dir = source_model_directory(global);
|
||||
std::filesystem::create_directories(web_dir);
|
||||
std::filesystem::create_directories(source_dir);
|
||||
std::filesystem::copy_file(temp_path, web_dir / file_name, std::filesystem::copy_options::overwrite_existing);
|
||||
std::filesystem::copy_file(temp_path, source_dir / file_name, std::filesystem::copy_options::overwrite_existing);
|
||||
std::filesystem::remove(temp_path);
|
||||
auto url = "/ui/model/" + file_name;
|
||||
if (model_type == "aircraft") {
|
||||
global->map_model_config.aircraft_model.url = url;
|
||||
}
|
||||
else if (model_type == "aircraft_type") {
|
||||
global->map_model_config.aircraft_models[std::string(aircraft_model_key)].url = url;
|
||||
}
|
||||
else {
|
||||
global->map_model_config.base_station_model.url = url;
|
||||
}
|
||||
}
|
||||
void handle_map_model_upload(const drogon::HttpRequestPtr& req, Global* global) {
|
||||
drogon::MultiPartParser parser;
|
||||
if (parser.parse(req) != 0) {
|
||||
throw std::invalid_argument("invalid multipart model upload");
|
||||
}
|
||||
auto model_type = parser.getOptionalParameter<std::string>("model_type");
|
||||
if (!model_type || parser.getFiles().size() != 1) {
|
||||
throw std::invalid_argument("model_type or file");
|
||||
}
|
||||
auto aircraft_model_key = parser.getOptionalParameter<std::string>("aircraft_model_key").value_or("");
|
||||
save_uploaded_model_file(parser.getFiles().front(), *model_type, aircraft_model_key, global);
|
||||
Global::save();
|
||||
}
|
||||
void handle_map_model_config_update(const drogon::HttpRequestPtr& req, Global* global) {
|
||||
auto parsed = Psc::try_parse_json(req->body().data());
|
||||
if (!parsed.has_value()) {
|
||||
throw std::invalid_argument("invalid map_models");
|
||||
}
|
||||
global->map_model_config.from_base_json(&parsed.value());
|
||||
Global::save();
|
||||
}
|
||||
void register_map_models_handler() {
|
||||
drogon::app().registerHandlerViaRegex(
|
||||
R"(^/map/models$)",
|
||||
[](const drogon::HttpRequestPtr& req,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& callback) {
|
||||
auto global = Global::instance();
|
||||
auto response = drogon::HttpResponse::newHttpResponse();
|
||||
try {
|
||||
if (req->method() == drogon::Post) {
|
||||
if (is_multipart_request(req)) {
|
||||
handle_map_model_upload(req, global);
|
||||
}
|
||||
else {
|
||||
handle_map_model_config_update(req, global);
|
||||
}
|
||||
}
|
||||
response->setStatusCode(drogon::k200OK);
|
||||
response->setContentTypeCode(drogon::CT_APPLICATION_JSON);
|
||||
response->setBody(global->map_model_config.to_base_json().to_json_string());
|
||||
}
|
||||
catch (const std::exception& error) {
|
||||
response->setStatusCode(drogon::k400BadRequest);
|
||||
response->setContentTypeCode(drogon::CT_TEXT_PLAIN);
|
||||
response->setBody(Psc::platform_2_utf8(error.what()));
|
||||
}
|
||||
response->addHeader("Cache-Control", "no-store");
|
||||
callback(response);
|
||||
},
|
||||
{drogon::Get, drogon::Post});
|
||||
}
|
||||
void register_map_view_handler() {
|
||||
drogon::app().registerHandlerViaRegex(
|
||||
R"(^/map/view$)",
|
||||
@@ -426,6 +555,10 @@ void register_map_view_handler() {
|
||||
try {
|
||||
auto next_view = global->map_view_config;
|
||||
next_view.from_base_json(&parsed.value());
|
||||
if (!global->map_resources_config.is_imagery_key(next_view.map2d.current_imagery_key) ||
|
||||
!global->map_resources_config.is_imagery_key(next_view.map3d.current_imagery_key)) {
|
||||
throw std::invalid_argument("invalid current_imagery_key");
|
||||
}
|
||||
global->map_resources_config.current_imagery_key = key.value();
|
||||
global->map_view_config = next_view;
|
||||
Global::save();
|
||||
@@ -453,6 +586,7 @@ void Global::init_tiles() {
|
||||
auto terrain = std::make_shared<Tile_Source>(map_resources_config.terrain);
|
||||
register_map_resources_handler();
|
||||
register_map_graphics_handler();
|
||||
register_map_models_handler();
|
||||
register_map_view_handler();
|
||||
register_tile_handler(R"(^/map/imagery/.*$)", "/map/imagery/", imagery);
|
||||
register_tile_handler(R"(^/tiles/.*$)", "/tiles/", google_imagery);
|
||||
|
||||
Reference in New Issue
Block a user