418 lines
15 KiB
C++
418 lines
15 KiB
C++
#ifndef PSC_GLOBAL_INCLUDE_H
|
|
#define PSC_GLOBAL_INCLUDE_H
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <queue>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <thread>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include "Psc_Cpp_Core/Statistics/Statistics.h"
|
|
#include "Psc_Cpp_Core/socket/ByteOrder.h"
|
|
#include "SSR/export.h"
|
|
class Global;
|
|
class Invalid_Http_Param final : public std::runtime_error {
|
|
public:
|
|
using std::runtime_error::runtime_error;
|
|
};
|
|
[[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& 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:
|
|
void swap(size_t i, size_t j) {
|
|
std::lock_guard g(mtx);
|
|
if (i >= _list.size() || j >= _list.size())
|
|
return;
|
|
std::swap(_list[i], _list[j]);
|
|
}
|
|
void push_back(Value_Type value) {
|
|
std::lock_guard g(mtx);
|
|
_list.push_back(value); // 添加默认值
|
|
}
|
|
void insert(int i, Value_Type value) {
|
|
std::lock_guard g(mtx);
|
|
_list.insert(_list.begin() + i, value);
|
|
}
|
|
Value_Type get(size_t i) {
|
|
return _list[i];
|
|
}
|
|
void remove(size_t i) {
|
|
std::lock_guard g(mtx);
|
|
if (i >= _list.size())
|
|
return;
|
|
_list.erase(_list.begin() + i);
|
|
}
|
|
std::vector<Value_Type> list() {
|
|
std::lock_guard g(mtx);
|
|
return this->_list;
|
|
}
|
|
void clear() {
|
|
list().clear();
|
|
}
|
|
protected:
|
|
std::mutex mtx;
|
|
std::vector<Value_Type> _list;
|
|
};
|
|
template <typename Key_Type, typename Value_Type>
|
|
class Ordered_Map {
|
|
public:
|
|
std::optional<Value_Type> get(Key_Type key) const {
|
|
std::lock_guard g(mtx);
|
|
auto it = _map.find(key);
|
|
if (it != _map.end()) {
|
|
return it->second;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
bool swap(size_t i, size_t j) {
|
|
std::lock_guard g(mtx);
|
|
if (i >= _list.size() || j >= _list.size())
|
|
return false;
|
|
std::swap(_list[i], _list[j]);
|
|
// 更新 map 中的 value,假设你可以从 Value 中获取 key
|
|
Key_Type key_i = _list[i]->key;
|
|
Key_Type key_j = _list[j]->key;
|
|
_map[key_i] = _list[i];
|
|
_map[key_j] = _list[j];
|
|
return true;
|
|
}
|
|
void push_back(Value_Type value) {
|
|
std::lock_guard g(mtx);
|
|
_list.push_back(value); // 添加默认值
|
|
_map[value->key] = value; // 插入到 map
|
|
}
|
|
bool insert(int i, Value_Type value) {
|
|
std::lock_guard g(mtx);
|
|
if (i < 0 || static_cast<size_t>(i) > _list.size())
|
|
return false;
|
|
_list.insert(_list.begin() + i, value);
|
|
_map[value->key] = value;
|
|
return true;
|
|
}
|
|
Value_Type get(size_t i) {
|
|
return _list[i];
|
|
}
|
|
std::optional<Value_Type> try_get(size_t i) {
|
|
std::lock_guard g(mtx);
|
|
if (i >= _list.size())
|
|
return std::nullopt;
|
|
return _list[i];
|
|
}
|
|
bool remove(size_t i) {
|
|
std::lock_guard g(mtx);
|
|
if (i >= _list.size())
|
|
return false;
|
|
Key_Type& key = _list[i]->key;
|
|
_map.erase(key);
|
|
_list.erase(_list.begin() + i);
|
|
return true;
|
|
}
|
|
std::vector<Value_Type> list() const {
|
|
std::lock_guard g(mtx);
|
|
return this->_list;
|
|
}
|
|
// std::map<Key_Type, Value_Type> map(){
|
|
// std::lock_guard g(mtx);
|
|
// return this->_map;
|
|
// }
|
|
bool set_order(const std::vector<Key_Type>& keys) {
|
|
std::lock_guard g(mtx);
|
|
if (keys.size() != _list.size())
|
|
return false;
|
|
std::vector<Value_Type> reordered;
|
|
reordered.reserve(_list.size());
|
|
for (const auto& key : keys) {
|
|
auto iterator = _map.find(key);
|
|
if (iterator == _map.end())
|
|
return false;
|
|
reordered.push_back(iterator->second);
|
|
}
|
|
_list = std::move(reordered);
|
|
return true;
|
|
}
|
|
void clear() {
|
|
std::lock_guard g(mtx);
|
|
_list.clear();
|
|
_map.clear();
|
|
}
|
|
protected:
|
|
mutable std::mutex mtx;
|
|
std::vector<Value_Type> _list;
|
|
std::map<Key_Type, Value_Type> _map;
|
|
};
|
|
class Mode_AC_Statistic_Data {
|
|
public:
|
|
Psc::JSON to_json() {
|
|
std::lock_guard g(mtx);
|
|
Psc::JSON ret = Psc::JSON::object();
|
|
const auto messages_per_second = utc == std::time(nullptr) ? pre_second_num : 0;
|
|
ret.append({"messages_per_second", messages_per_second});
|
|
ret.append({"messages_total", total_num});
|
|
ret.append({"length_errors_total", length_error_num.load()});
|
|
return ret;
|
|
}
|
|
void add() {
|
|
std::lock_guard g(mtx);
|
|
add_inter();
|
|
}
|
|
void add_length_error() {
|
|
std::lock_guard g(mtx);
|
|
add_inter();
|
|
length_error_num++;
|
|
}
|
|
protected:
|
|
std::atomic<size_t> length_error_num{};
|
|
void add_inter() {
|
|
total_num++;
|
|
auto cur_time = std::time(nullptr);
|
|
if (cur_time != utc) {
|
|
utc = cur_time;
|
|
pre_second_num = 0;
|
|
}
|
|
pre_second_num++;
|
|
}
|
|
std::mutex mtx;
|
|
size_t pre_second_num{};
|
|
size_t total_num{};
|
|
time_t utc{};
|
|
};
|
|
class Statistic_Data {
|
|
public:
|
|
time_t utc{};
|
|
size_t pre_second_num{};
|
|
size_t total_num{};
|
|
[[nodiscard]] Psc::JSON to_json() const {
|
|
Psc::JSON ret = Psc::JSON::object();
|
|
const auto messages_per_second = utc == std::time(nullptr) ? pre_second_num : 0;
|
|
ret.append({"messages_per_second", messages_per_second});
|
|
ret.append({"messages_total", total_num});
|
|
return ret;
|
|
}
|
|
Statistic_Data() = default;
|
|
};
|
|
class DF_Statistic_Data {
|
|
public:
|
|
time_t utc{};
|
|
size_t pre_second_num{};
|
|
size_t total_num{};
|
|
size_t crc_error_num{};
|
|
Psc::JSON to_json() {
|
|
std::lock_guard lock(mtx);
|
|
return to_json_locked();
|
|
}
|
|
[[nodiscard]] std::tuple<size_t, size_t, size_t> counters() const {
|
|
std::lock_guard lock(mtx);
|
|
return {utc == std::time(nullptr) ? pre_second_num : 0, total_num, crc_error_num};
|
|
}
|
|
void add() {
|
|
std::lock_guard lock(mtx);
|
|
add_inter();
|
|
}
|
|
void add_crc_error() {
|
|
std::lock_guard lock(mtx);
|
|
crc_error_num++;
|
|
}
|
|
void add_sub_part(std::string_view key) {
|
|
std::lock_guard lock(mtx);
|
|
add_sub_part_inter(key);
|
|
}
|
|
protected:
|
|
Psc::JSON to_json_locked() const {
|
|
Psc::JSON ret = Psc::JSON::object();
|
|
const auto messages_per_second = utc == std::time(nullptr) ? pre_second_num : 0;
|
|
const auto crc_error_percent = total_num == 0 ? 0.0 : static_cast<double>(crc_error_num) * 100.0 / static_cast<double>(total_num);
|
|
ret.append({"messages_per_second", messages_per_second});
|
|
ret.append({"messages_total", total_num});
|
|
ret.append({"crc_errors_total", crc_error_num});
|
|
ret.append({"crc_error_percent", crc_error_percent});
|
|
Psc::JSON subtypes = Psc::JSON::object();
|
|
for (const auto& cur : statistic_map) {
|
|
subtypes.append({cur.first, cur.second.to_json()});
|
|
}
|
|
ret.append({"subtypes", subtypes});
|
|
return ret;
|
|
}
|
|
void add_inter() {
|
|
total_num++;
|
|
auto cur_time = std::time(nullptr);
|
|
if (cur_time != utc) {
|
|
utc = cur_time;
|
|
pre_second_num = 0;
|
|
}
|
|
pre_second_num++;
|
|
}
|
|
void add_sub_part_inter(std::string_view key) {
|
|
auto t = get_create_statistic_data(key);
|
|
t->total_num++;
|
|
auto cur_time = std::time(nullptr);
|
|
if (cur_time != t->utc) {
|
|
t->utc = cur_time;
|
|
t->pre_second_num = 0;
|
|
}
|
|
t->pre_second_num++;
|
|
}
|
|
Statistic_Data* get_create_statistic_data(std::string_view key) {
|
|
Statistic_Data* t;
|
|
auto key_string = std::string(key);
|
|
auto iter = statistic_map.find(key_string);
|
|
if (iter == statistic_map.end()) {
|
|
Statistic_Data cur{};
|
|
statistic_map.insert(std::make_pair(key_string, cur));
|
|
}
|
|
t = &statistic_map[key_string];
|
|
return t;
|
|
}
|
|
std::map<std::string, Statistic_Data> statistic_map;
|
|
mutable std::mutex mtx;
|
|
};
|
|
class CPR_Percentage_Statistic_Data {
|
|
public:
|
|
void add(SSR::CPR_Type cpr_type, SSR::CPR_Ret_Type result_type) {
|
|
std::lock_guard lock(mtx);
|
|
switch (result_type) {
|
|
case SSR::CPR_Ret_Type::Speed_Error:
|
|
case SSR::CPR_Ret_Type::Parse_OK:
|
|
case SSR::CPR_Ret_Type::Inter_Error:
|
|
case SSR::CPR_Ret_Type::Time_Space_Too_Long:
|
|
break;
|
|
case SSR::CPR_Ret_Type::Lack_Other_Msg:
|
|
case SSR::CPR_Ret_Type::Lack_Old_Pos:
|
|
case SSR::CPR_Ret_Type::Base:
|
|
return;
|
|
}
|
|
auto& statistic = statistic_map[cpr_type];
|
|
++statistic.total_num;
|
|
++statistic.result_num[result_type];
|
|
}
|
|
Psc::JSON to_json() const {
|
|
std::lock_guard lock(mtx);
|
|
Psc::JSON result = Psc::JSON::object();
|
|
for (const auto& [cpr_type, statistic] : statistic_map) {
|
|
Psc::JSON type_result = Psc::JSON::object();
|
|
type_result.append({"total", statistic.total_num});
|
|
for (auto result_type : statistic_types) {
|
|
size_t num = 0;
|
|
auto iter = statistic.result_num.find(result_type);
|
|
if (iter != statistic.result_num.end()) {
|
|
num = iter->second;
|
|
}
|
|
double rate = statistic.total_num == 0 ? 0.0 : static_cast<double>(num) / static_cast<double>(statistic.total_num) * 100.0;
|
|
Psc::JSON value = Psc::JSON::object();
|
|
value.append({"count", num});
|
|
value.append({"percent", rate});
|
|
type_result.append({Psc::to_string(result_type), value});
|
|
}
|
|
result.append({Psc::to_string(cpr_type), type_result});
|
|
}
|
|
return result;
|
|
}
|
|
protected:
|
|
struct Statistic_Data {
|
|
size_t total_num{};
|
|
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::Out_of_Maximum_Detection_Range};
|
|
std::map<SSR::CPR_Type, Statistic_Data> statistic_map;
|
|
mutable std::mutex mtx;
|
|
};
|
|
class Mode_S_Statistic_Data {
|
|
public:
|
|
Psc::JSON to_json() {
|
|
std::lock_guard lock(mtx);
|
|
size_t total_pre_second_num = 0;
|
|
size_t total_num = 0;
|
|
size_t crc_error_num = 0;
|
|
for (auto& [_, statistic] : DF_Statistic_Data_map) {
|
|
const auto [pre_second, total, crc_error] = statistic.counters();
|
|
total_pre_second_num += pre_second;
|
|
total_num += total;
|
|
crc_error_num += crc_error;
|
|
}
|
|
Psc::JSON ret = Psc::JSON::object();
|
|
const auto crc_error_percent = total_num == 0 ? 0.0 : static_cast<double>(crc_error_num) * 100.0 / static_cast<double>(total_num);
|
|
ret.append({"messages_per_second", total_pre_second_num});
|
|
ret.append({"messages_total", total_num});
|
|
ret.append({"length_errors_total", length_error_num.load()});
|
|
ret.append({"crc_errors_total", crc_error_num});
|
|
ret.append({"crc_error_percent", crc_error_percent});
|
|
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()});
|
|
}
|
|
ret.append({"downlink_formats", df_sub_type});
|
|
ret.append({"cpr", cpr_statistic_data.to_json()});
|
|
return ret;
|
|
}
|
|
void add_length_error(std::string_view key) {
|
|
++length_error_num;
|
|
}
|
|
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) {
|
|
std::lock_guard lock(mtx);
|
|
return &DF_Statistic_Data_map.try_emplace(key).first->second;
|
|
}
|
|
size_t get_total_pre_second_num() {
|
|
std::lock_guard lock(mtx);
|
|
size_t ret = 0;
|
|
for (auto& cur : DF_Statistic_Data_map)
|
|
ret += std::get<0>(cur.second.counters());
|
|
return ret;
|
|
}
|
|
size_t get_total_num() {
|
|
std::lock_guard lock(mtx);
|
|
size_t ret = 0;
|
|
for (auto& cur : DF_Statistic_Data_map)
|
|
ret += std::get<1>(cur.second.counters());
|
|
return ret;
|
|
}
|
|
size_t get_crc_error_num() {
|
|
std::lock_guard lock(mtx);
|
|
size_t ret = 0;
|
|
for (auto& cur : DF_Statistic_Data_map)
|
|
ret += std::get<2>(cur.second.counters());
|
|
return ret;
|
|
}
|
|
protected:
|
|
std::atomic<size_t> length_error_num{};
|
|
std::map<SSR::Downlink_Format, DF_Statistic_Data> DF_Statistic_Data_map;
|
|
CPR_Percentage_Statistic_Data cpr_statistic_data;
|
|
std::mutex mtx;
|
|
};
|
|
template <typename T>
|
|
class Immutable : public T {};
|
|
#endif
|