403 lines
14 KiB
C++
403 lines
14 KiB
C++
#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);
|
|
surface_pos_track_list.init(size);
|
|
}
|
|
|
|
void Aircraft::refresh_external_database_info() {
|
|
auto &manager = Global::instance()->external_resources_manager;
|
|
if (!external_database_info_loaded) {
|
|
external_database_info = manager.query_aircraft_external_databases(icao);
|
|
external_database_info_loaded = true;
|
|
}
|
|
|
|
auto callsign = flight();
|
|
if (!callsign)
|
|
callsign = bds20_call_sign();
|
|
if (last_external_database_callsign == callsign) {
|
|
return;
|
|
}
|
|
|
|
callsign_external_database_info =
|
|
manager.query_callsign_external_databases(callsign);
|
|
last_external_database_callsign = std::move(callsign);
|
|
}
|
|
|
|
std::shared_ptr<SSR::Aircraft_Info>
|
|
DataBase::get_aircraft(std::string_view icao) {
|
|
return aircraft_map.get(std::string(icao));
|
|
}
|
|
|
|
std::shared_ptr<SSR::Aircraft_Info>
|
|
DataBase::create_aircraft(std::string_view icao) {
|
|
auto ret = aircraft_map.create(std::string(icao));
|
|
ret->refresh_external_database_info();
|
|
return ret;
|
|
}
|
|
|
|
void DataBase::delete_timeout_aircraft() {
|
|
aircraft_map.remove_cond([](const std::shared_ptr<SSR::Aircraft_Info> &item) {
|
|
if (item->timestamp == 0) {
|
|
std::cout << "未初始化的 timestamp " << LOG_POS << std::endl;
|
|
}
|
|
long long time = std::time(nullptr) - item->timestamp;
|
|
return time > Global::instance()->mode_acs.timeout_seconds;
|
|
});
|
|
}
|
|
|
|
JSON DataBase::get_aircraftlist() {
|
|
JSON json = JSON::array();
|
|
for (auto &it : aircraft_map.values()) {
|
|
auto o = Aircraft_List_JSON_Service_O::to_That(this, it.get());
|
|
json.children.push_back(o.toJson());
|
|
}
|
|
return json;
|
|
}
|
|
|
|
Psc::JSON DataBase::get_aircraftlist(int limit_msg_num) {
|
|
JSON json = JSON::array();
|
|
for (auto &it : aircraft_map.values()) {
|
|
if (it->times < limit_msg_num)
|
|
continue;
|
|
auto o = Aircraft_List_JSON_Service_O::to_That(this, it.get());
|
|
json.children.push_back(o.toJson());
|
|
}
|
|
return json;
|
|
}
|
|
|
|
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()) {
|
|
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;
|
|
}
|
|
|
|
JSON DataBase::get_aircraft_list_after(time_t timestamp) {
|
|
JSON json = JSON::array();
|
|
auto g = Global::instance();
|
|
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 && vto.time_stamp > timestamp) {
|
|
json.children.push_back(vto.to_json());
|
|
}
|
|
}
|
|
return json;
|
|
}
|
|
|
|
#ifdef Cache_Some_Mode_S
|
|
Psc::JSON DataBase::get_limit_mode_s_msg_info() {
|
|
|
|
JSON ret = JSON::object();
|
|
JSON list = JSON::array();
|
|
std::map<int, size_t> map;
|
|
std::map<int, size_t> num_test;
|
|
for (auto &air : aircraft_map.values()) {
|
|
if (air->total_num > Cache_Some_Mode_S_Num)
|
|
continue;
|
|
Psc::JSON air_json = Psc::JSON::object();
|
|
{
|
|
Psc::JSON ms = Psc::JSON::array();
|
|
int n = air->cache.size();
|
|
for (auto msg : air->cache) {
|
|
Psc::JSON cur = Psc::JSON::object(
|
|
{{"hex", msg->msg_hex}, {"df", to_string(msg->df)}});
|
|
if (msg->df == Downlink_Format::Extended_Squitter_17) {
|
|
|
|
auto tc = type_code(msg->msg_bin);
|
|
cur.children.push_back({"tc", tc});
|
|
map[tc]++;
|
|
num_test[air->total_num] = n;
|
|
}
|
|
ms.append(cur);
|
|
}
|
|
air_json.children.push_back({"icao", air->icao});
|
|
air_json.children.push_back({"total_num", air->total_num});
|
|
air_json.children.push_back({"ms", ms});
|
|
}
|
|
list.children.push_back(air_json);
|
|
}
|
|
ret.children.push_back({"key", key});
|
|
for (auto pair : map) {
|
|
ret.children.push_back({std::to_string(pair.first), pair.second});
|
|
}
|
|
Psc::JSON t = Psc::JSON::object();
|
|
for (auto pair : num_test) {
|
|
t.children.push_back({std::to_string(pair.first), pair.second});
|
|
}
|
|
ret.children.push_back({"数量统计", t});
|
|
ret.children.push_back({"list", list});
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
size_t DataBase::get_aircraft_num() { return aircraft_map.size(); }
|
|
|
|
#define _init_db \
|
|
HTTP_REQUIRE_VALUE(data_source_key, \
|
|
params.try_get_string("data_source_key")) \
|
|
auto db = Global::instance()->source(data_source_key); \
|
|
if (db == nullptr) { \
|
|
res->setStatusCode(drogon::k503ServiceUnavailable); \
|
|
JSON ret; \
|
|
res->setBody(ret.to_json_string()); \
|
|
return; \
|
|
}
|
|
|
|
void database_server(Global *g) {
|
|
auto &svr = g->svr;
|
|
auto &api = g->api;
|
|
// g->load_aircraft_csv(g->mode_acs.aircraft_csv_path);
|
|
g->external_resources_manager.load_sqlite_db(
|
|
Psc::get_abs_path(g->external_resources_manager.ecap_sqlite_path));
|
|
|
|
#ifdef Cache_Some_Mode_S
|
|
svr.Post(api + "get_cache_some_mode_s_info", [](HTTP_Param) {
|
|
CHECK_JSON_PARAM
|
|
_init_db auto g = Global::instance();
|
|
auto ret = JSON::object();
|
|
if (db) {
|
|
ret = db->get_limit_mode_s_msg_info();
|
|
}
|
|
res->setBody(warp(ret).to_json_string());
|
|
});
|
|
#endif
|
|
|
|
svr.Post(api + "get_base_station_location", [](HTTP_Param) {
|
|
CHECK_JSON_PARAM
|
|
_init_db auto g = Global::instance();
|
|
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());
|
|
});
|
|
svr.Post(api + "get_aircraft_base_info", [](HTTP_Param) {
|
|
CHECK_JSON_PARAM
|
|
_init_db HTTP_REQUIRE_VALUE(icao,
|
|
params.try_get_string("icao")) auto aircraft =
|
|
db->get_aircraft(icao);
|
|
JSON ret;
|
|
if (aircraft != nullptr) {
|
|
auto vto = Flight_VTO::to_VTO(aircraft.get());
|
|
ret = vto.to_base_info_json();
|
|
}
|
|
res->setBody(ret.to_json_string());
|
|
});
|
|
svr.Post(api + "get_aircraft_detail_info", [](HTTP_Param) {
|
|
CHECK_JSON_PARAM
|
|
_init_db HTTP_REQUIRE_VALUE(icao,
|
|
params.try_get_string("icao")) auto aircraft =
|
|
db->get_aircraft(icao);
|
|
JSON ret;
|
|
if (aircraft != nullptr) {
|
|
ret = aircraft->toJson();
|
|
}
|
|
res->setBody(ret.to_json_string());
|
|
});
|
|
svr.Post(api + "get_aircraft_track_list", [](HTTP_Param) {
|
|
CHECK_JSON_PARAM
|
|
_init_db HTTP_REQUIRE_VALUE(icao, params.try_get_string("icao"))
|
|
HTTP_REQUIRE_VALUE(last_size, params.try_get_number<std::size_t>(
|
|
"last_size")) auto aircraft =
|
|
db->get_aircraft(icao);
|
|
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));
|
|
}
|
|
res->setBody(ret.to_json_string());
|
|
});
|
|
svr.Post(api + "get_aircraft_list_after", [](HTTP_Param) {
|
|
CHECK_JSON_PARAM
|
|
_init_db HTTP_REQUIRE_VALUE(
|
|
timestamp, params.try_get_number<std::uint32_t>("timestamp")) auto ret =
|
|
db->get_aircraft_list_after(timestamp);
|
|
res->setBody(ret.to_json_string());
|
|
});
|
|
|
|
svr.Post(api + "aircraft_change_list", [](HTTP_Param) {
|
|
CHECK_JSON_PARAM
|
|
_init_db JSON ret = JSON::array();
|
|
if (db) {
|
|
ret = db->get_all_aircraft_json();
|
|
} else {
|
|
}
|
|
res->setBody(warp(ret).to_json_string());
|
|
});
|
|
|
|
svr.Get(R"(/aircraftlist.json)", [](HTTP_Param) {
|
|
auto list = Global::instance()->mode_acs.data_source_config.map.list();
|
|
std::shared_ptr<Data_Source> ts = nullptr;
|
|
auto ret = JSON::array();
|
|
for (const auto &li : list) {
|
|
if (li->enable) {
|
|
ret = li->get_aircraftlist();
|
|
break;
|
|
}
|
|
}
|
|
res->setBody(ret.to_json_string("", "\n", ""));
|
|
});
|
|
|
|
// aircraftlist.json 的带数据源版本
|
|
svr.Post(api + "aircraft_list", [](HTTP_Param) {
|
|
CHECK_JSON_PARAM
|
|
auto g = Global::instance();
|
|
HTTP_REQUIRE_VALUE(data_source_key,
|
|
params.try_get_string("data_source_key"))
|
|
std::shared_ptr<Data_Source> db;
|
|
{
|
|
db = g->source(data_source_key);
|
|
}
|
|
if (db == nullptr) {
|
|
res->setStatusCode(drogon::k503ServiceUnavailable);
|
|
JSON ret;
|
|
res->setBody(ret.to_json_string());
|
|
return;
|
|
}
|
|
|
|
auto limit_msg_num = g->mode_acs.default_min_aircraft_list_num.load();
|
|
auto t = params.get("limit_msg_num");
|
|
if (t) {
|
|
HTTP_REQUIRE_VALUE(requested_limit_msg_num, t->try_number_val<int>())
|
|
limit_msg_num = requested_limit_msg_num;
|
|
}
|
|
JSON ret = JSON::array();
|
|
{
|
|
Scope_Timer timer("aircraft_list.db_query",
|
|
g->http_monitor_config.slow_scope_threshold_ms);
|
|
ret = db->get_aircraftlist(limit_msg_num);
|
|
}
|
|
std::string body;
|
|
{
|
|
Scope_Timer timer("aircraft_list.json_serialization",
|
|
g->http_monitor_config.slow_scope_threshold_ms);
|
|
body = warp(ret).to_json_string();
|
|
}
|
|
res->setBody(body);
|
|
});
|
|
}
|
|
|
|
static std::string output;
|
|
|
|
static void log_i(std::string_view label, std::string_view value,
|
|
std::string_view unit = "") {
|
|
std::ostringstream oss;
|
|
// Format the label and value
|
|
oss << std::left << std::setw(30) << label << ": " << value;
|
|
// Add unit if not empty
|
|
if (!unit.empty()) {
|
|
oss << " " << unit << "\n";
|
|
} else {
|
|
oss << "\n";
|
|
}
|
|
// Append formatted string to output
|
|
output += oss.str();
|
|
}
|
|
|
|
static void log_i(std::string_view label, double value,
|
|
std::string_view unit = "") {
|
|
log_i(label, std::to_string(value), unit);
|
|
}
|