修改了 CPP 文件的格式化,使用 K&R 代码风格
This commit is contained in:
parent
81fcd1f438
commit
90b372f659
14
.clang-format
Normal file
14
.clang-format
Normal file
@ -0,0 +1,14 @@
|
||||
# 精简版 K&R 风格配置
|
||||
BasedOnStyle: LLVM
|
||||
BreakBeforeBraces: Linux
|
||||
IndentWidth: 4
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
|
||||
# 显式禁用所有大括号换行
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterControlStatement: Never
|
||||
21
.cursorrules
21
.cursorrules
@ -1,14 +1,5 @@
|
||||
请遵循:
|
||||
- 最小化修改代码,不要修改任何与解决当前问题无关的代码(包括注释),一定不能删除任何其他的代码
|
||||
- 使用 C++20 标准
|
||||
- 使用 CMake 3.14 及以上版本
|
||||
- 使用 nlohmann_json 3.11.3 版本
|
||||
- 使用 FetchContent 管理第三方库
|
||||
- 使用 Google Test 进行单元测试
|
||||
- 使用 Google Mock 进行单元测试
|
||||
- 使用 Google Benchmark 进行性能测试
|
||||
- 在头文件中定义函数,在源文件中实现函数
|
||||
- 在头文件中定义类,在源文件中实现类
|
||||
- 使用源代码中的格式化方式和注释风格
|
||||
- 日志的字符串格式采用拼接方式,不要使用{}占位符
|
||||
|
||||
@ -23,7 +14,6 @@
|
||||
|
||||
2. 列表格式
|
||||
- 无序列表统一使用 -
|
||||
- 有序列表使用 1. 2. 3.
|
||||
- 列表项前后空一行
|
||||
- 列表嵌套使用两个空格缩进
|
||||
|
||||
@ -44,18 +34,11 @@
|
||||
- 格式统一一致
|
||||
|
||||
请遵循如下 C++ 规范:
|
||||
- 使用 C++20 标准
|
||||
- 使用 C++17 标准
|
||||
- 使用 CMake 3.14 及以上版本
|
||||
- 使用 nlohmann_json 3.11.3 版本
|
||||
- 使用 FetchContent 管理第三方库
|
||||
- 使用 Google Test 进行单元测试
|
||||
- 使用 Google Mock 进行单元测试
|
||||
- 使用 Google Benchmark 进行性能测试
|
||||
- 在头文件中定义函数,在源文件中实现函数
|
||||
|
||||
修改代码原则:
|
||||
- 先询问代码的用途
|
||||
- 理解代码的功能和作用
|
||||
- 确认是否可以修改或删除
|
||||
- 如果不确定就直接问我
|
||||
- 不要随便修改原有代码中的中文注释
|
||||
- 在头文件中定义函数,在源文件中实现函数
|
||||
@ -81,6 +81,7 @@ endif()
|
||||
# 源文件列表
|
||||
set(LIB_SOURCES
|
||||
src/collector/DataCollector.cpp
|
||||
src/collector/DataSourceConfig.cpp
|
||||
src/core/System.cpp
|
||||
src/detector/CollisionDetector.cpp
|
||||
src/detector/SafetyZone.cpp
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
std::string getSignalStateString(SignalState state) {
|
||||
switch (state) {
|
||||
case SignalState::RED:
|
||||
return "RED";
|
||||
case SignalState::YELLOW:
|
||||
return "YELLOW";
|
||||
case SignalState::GREEN:
|
||||
return "GREEN";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
@ -2,23 +2,23 @@
|
||||
#include "network/HTTPDataSource.h"
|
||||
#include "utils/Logger.h"
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
DataCollector::DataCollector(const AirportBounds& bounds)
|
||||
: last_position_fetch_(std::chrono::steady_clock::now())
|
||||
, last_unmanned_fetch_(std::chrono::steady_clock::now())
|
||||
, last_traffic_light_fetch_(std::chrono::steady_clock::now())
|
||||
, last_warning_time_(std::chrono::steady_clock::now())
|
||||
, airportBounds_(bounds) {
|
||||
}
|
||||
DataCollector::DataCollector(const AirportBounds& bounds)
|
||||
: last_position_fetch_(std::chrono::steady_clock::now()),
|
||||
last_unmanned_fetch_(std::chrono::steady_clock::now()),
|
||||
last_traffic_light_fetch_(std::chrono::steady_clock::now()),
|
||||
last_warning_time_(std::chrono::steady_clock::now()),
|
||||
airportBounds_(bounds) {}
|
||||
|
||||
DataCollector::~DataCollector() {
|
||||
stop();
|
||||
}
|
||||
|
||||
bool DataCollector::initialize(const DataSourceConfig& dataSourceConfig, const WarnConfig& warnConfig) {
|
||||
bool DataCollector::initialize(const DataSourceConfig& dataSourceConfig,
|
||||
const WarnConfig& warnConfig) {
|
||||
if (!dataSource_) {
|
||||
dataSourceConfig_ = dataSourceConfig;
|
||||
dataSource_ = std::make_shared<HTTPDataSource>(dataSourceConfig_);
|
||||
@ -35,24 +35,24 @@ void DataCollector::start() {
|
||||
if (running_) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!dataSource_) {
|
||||
Logger::error("No data source available");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
running_ = true;
|
||||
last_position_fetch_ = std::chrono::steady_clock::now();
|
||||
last_unmanned_fetch_ = std::chrono::steady_clock::now();
|
||||
last_traffic_light_fetch_ = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
// 尝试连接,但即使失败也继续运行
|
||||
if (!dataSource_->connect()) {
|
||||
Logger::error("Failed to connect to data source");
|
||||
// 立即检查超时,这样如果一开始就连不上,会马上发出告警
|
||||
checkTimeout();
|
||||
}
|
||||
|
||||
|
||||
// 启动三个独立的采集线程
|
||||
positionThread_ = std::thread(&DataCollector::positionLoop, this);
|
||||
unmannedThread_ = std::thread(&DataCollector::unmannedLoop, this);
|
||||
@ -63,9 +63,9 @@ void DataCollector::stop() {
|
||||
if (!running_) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
running_ = false;
|
||||
|
||||
|
||||
// 等待所有线程结束
|
||||
if (positionThread_.joinable()) {
|
||||
positionThread_.join();
|
||||
@ -76,32 +76,37 @@ void DataCollector::stop() {
|
||||
if (trafficLightThread_.joinable()) {
|
||||
trafficLightThread_.join();
|
||||
}
|
||||
|
||||
|
||||
if (dataSource_) {
|
||||
dataSource_->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void DataCollector::positionLoop() {
|
||||
Logger::debug("位置数据采集循环启动,刷新间隔: ", dataSourceConfig_.position.refresh_interval_ms, "ms");
|
||||
Logger::debug("位置数据采集循环启动,刷新间隔: ",
|
||||
dataSourceConfig_.position.refresh_interval_ms, "ms");
|
||||
auto next_collection_time = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
while (running_) {
|
||||
bool success = fetchPositionData();
|
||||
if (!success) {
|
||||
Logger::warning("位置数据采集失败");
|
||||
checkTimeout();
|
||||
}
|
||||
|
||||
|
||||
// 计算下一次采集时间
|
||||
next_collection_time += std::chrono::milliseconds(dataSourceConfig_.position.refresh_interval_ms);
|
||||
next_collection_time += std::chrono::milliseconds(
|
||||
dataSourceConfig_.position.refresh_interval_ms);
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
if (next_collection_time > now) {
|
||||
auto wait_time = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
next_collection_time - now).count();
|
||||
auto wait_time =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
next_collection_time - now)
|
||||
.count();
|
||||
if (wait_time > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(wait_time));
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::milliseconds(wait_time));
|
||||
}
|
||||
} else {
|
||||
// 如果已经超过了下一次采集时间,立即重置
|
||||
@ -111,25 +116,30 @@ void DataCollector::positionLoop() {
|
||||
}
|
||||
|
||||
void DataCollector::unmannedLoop() {
|
||||
Logger::debug("无人车状态采集循环启动,刷新间隔: ", dataSourceConfig_.vehicle.refresh_interval_ms, "ms");
|
||||
Logger::debug("无人车状态采集循环启动,刷新间隔: ",
|
||||
dataSourceConfig_.vehicle.refresh_interval_ms, "ms");
|
||||
auto next_collection_time = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
while (running_) {
|
||||
bool success = fetchUnmannedVehicleData();
|
||||
if (!success) {
|
||||
Logger::warning("无人车状态采集失败");
|
||||
checkTimeout();
|
||||
}
|
||||
|
||||
|
||||
// 计算下一次采集时间
|
||||
next_collection_time += std::chrono::milliseconds(dataSourceConfig_.vehicle.refresh_interval_ms);
|
||||
next_collection_time += std::chrono::milliseconds(
|
||||
dataSourceConfig_.vehicle.refresh_interval_ms);
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
if (next_collection_time > now) {
|
||||
auto wait_time = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
next_collection_time - now).count();
|
||||
auto wait_time =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
next_collection_time - now)
|
||||
.count();
|
||||
if (wait_time > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(wait_time));
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::milliseconds(wait_time));
|
||||
}
|
||||
} else {
|
||||
// 如果已经超过了下一次采集时间,立即重置
|
||||
@ -139,25 +149,30 @@ void DataCollector::unmannedLoop() {
|
||||
}
|
||||
|
||||
void DataCollector::trafficLightLoop() {
|
||||
Logger::debug("红绿灯数据采集循环启动,刷新间隔: ", dataSourceConfig_.traffic_light.refresh_interval_ms, "ms");
|
||||
Logger::debug("红绿灯数据采集循环启动,刷新间隔: ",
|
||||
dataSourceConfig_.traffic_light.refresh_interval_ms, "ms");
|
||||
auto next_collection_time = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
while (running_) {
|
||||
bool success = fetchTrafficLightData();
|
||||
if (!success) {
|
||||
Logger::warning("红绿灯数据采集失败");
|
||||
checkTimeout();
|
||||
}
|
||||
|
||||
|
||||
// 计算下一次采集时间
|
||||
next_collection_time += std::chrono::milliseconds(dataSourceConfig_.traffic_light.refresh_interval_ms);
|
||||
next_collection_time += std::chrono::milliseconds(
|
||||
dataSourceConfig_.traffic_light.refresh_interval_ms);
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
if (next_collection_time > now) {
|
||||
auto wait_time = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
next_collection_time - now).count();
|
||||
auto wait_time =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
next_collection_time - now)
|
||||
.count();
|
||||
if (wait_time > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(wait_time));
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::milliseconds(wait_time));
|
||||
}
|
||||
} else {
|
||||
// 如果已经超过了下一次采集时间,立即重置
|
||||
@ -168,24 +183,30 @@ void DataCollector::trafficLightLoop() {
|
||||
|
||||
void DataCollector::checkTimeout() {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
// 检查位置数据超时
|
||||
auto position_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_position_fetch_).count();
|
||||
auto position_elapsed =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_position_fetch_)
|
||||
.count();
|
||||
if (position_elapsed > dataSourceConfig_.position.timeout_ms) {
|
||||
sendTimeoutWarning("position", position_elapsed);
|
||||
}
|
||||
|
||||
|
||||
// 检查无人车数据超时
|
||||
auto unmanned_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_unmanned_fetch_).count();
|
||||
auto unmanned_elapsed =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_unmanned_fetch_)
|
||||
.count();
|
||||
if (unmanned_elapsed > dataSourceConfig_.vehicle.timeout_ms) {
|
||||
sendTimeoutWarning("unmanned", unmanned_elapsed);
|
||||
}
|
||||
|
||||
|
||||
// 检查红绿灯数据超时
|
||||
auto traffic_light_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_traffic_light_fetch_).count();
|
||||
auto traffic_light_elapsed =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_traffic_light_fetch_)
|
||||
.count();
|
||||
if (traffic_light_elapsed > dataSourceConfig_.traffic_light.timeout_ms) {
|
||||
sendTimeoutWarning("traffic_light", traffic_light_elapsed);
|
||||
}
|
||||
@ -193,38 +214,46 @@ void DataCollector::checkTimeout() {
|
||||
|
||||
void DataCollector::resetTimeout(const std::string& source) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
if (source == "position") {
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_position_fetch_).count();
|
||||
now - last_position_fetch_)
|
||||
.count();
|
||||
if (elapsed > dataSourceConfig_.position.timeout_ms) {
|
||||
Logger::info("Position data source connection restored after ", elapsed, "ms timeout");
|
||||
Logger::info("Position data source connection restored after ",
|
||||
elapsed, "ms timeout");
|
||||
}
|
||||
last_position_fetch_ = now;
|
||||
}
|
||||
else if (source == "unmanned") {
|
||||
} else if (source == "unmanned") {
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_unmanned_fetch_).count();
|
||||
now - last_unmanned_fetch_)
|
||||
.count();
|
||||
if (elapsed > dataSourceConfig_.vehicle.timeout_ms) {
|
||||
Logger::info("Unmanned vehicle data source connection restored after ", elapsed, "ms timeout");
|
||||
Logger::info(
|
||||
"Unmanned vehicle data source connection restored after ",
|
||||
elapsed, "ms timeout");
|
||||
}
|
||||
last_unmanned_fetch_ = now;
|
||||
}
|
||||
else if (source == "traffic_light") {
|
||||
} else if (source == "traffic_light") {
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_traffic_light_fetch_).count();
|
||||
now - last_traffic_light_fetch_)
|
||||
.count();
|
||||
if (elapsed > dataSourceConfig_.traffic_light.timeout_ms) {
|
||||
Logger::info("Traffic light data source connection restored after ", elapsed, "ms timeout");
|
||||
Logger::info("Traffic light data source connection restored after ",
|
||||
elapsed, "ms timeout");
|
||||
}
|
||||
last_traffic_light_fetch_ = now;
|
||||
}
|
||||
}
|
||||
|
||||
void DataCollector::sendTimeoutWarning(const std::string& source, int64_t elapsed_ms) {
|
||||
void DataCollector::sendTimeoutWarning(const std::string& source,
|
||||
int64_t elapsed_ms) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto warning_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_warning_time_).count();
|
||||
|
||||
auto warning_elapsed =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_warning_time_)
|
||||
.count();
|
||||
|
||||
if (warning_elapsed >= warnConfig_.warning_interval_ms) {
|
||||
if (!system_) {
|
||||
Logger::debug("System not set, skipping timeout warning");
|
||||
@ -234,71 +263,68 @@ void DataCollector::sendTimeoutWarning(const std::string& source, int64_t elapse
|
||||
network::TimeoutWarningMessage msg;
|
||||
msg.source = source;
|
||||
msg.elapsed_ms = elapsed_ms;
|
||||
|
||||
|
||||
// 根据数据源类型设置超时阈值
|
||||
int64_t timeout_ms = 0;
|
||||
if (source == "position") {
|
||||
timeout_ms = dataSourceConfig_.position.timeout_ms;
|
||||
}
|
||||
else if (source == "unmanned") {
|
||||
} else if (source == "unmanned") {
|
||||
timeout_ms = dataSourceConfig_.vehicle.timeout_ms;
|
||||
}
|
||||
else if (source == "traffic_light") {
|
||||
} else if (source == "traffic_light") {
|
||||
timeout_ms = dataSourceConfig_.traffic_light.timeout_ms;
|
||||
}
|
||||
|
||||
|
||||
msg.is_read_timeout = (elapsed_ms > timeout_ms);
|
||||
|
||||
|
||||
system_->broadcastTimeoutWarning(msg);
|
||||
last_warning_time_ = now;
|
||||
}
|
||||
}
|
||||
|
||||
void DataCollector::filterPositionData(std::vector<Aircraft>& aircraft, std::vector<Vehicle>& vehicles) {
|
||||
void DataCollector::filterPositionData(std::vector<Aircraft>& aircraft,
|
||||
std::vector<Vehicle>& vehicles) {
|
||||
size_t originalAircraftCount = aircraft.size();
|
||||
size_t originalVehicleCount = vehicles.size();
|
||||
|
||||
// 过滤航空器数据
|
||||
auto aircraftIt = std::remove_if(aircraft.begin(), aircraft.end(),
|
||||
[this](const Aircraft& a) {
|
||||
|
||||
// 过滤掉不在机场区域内的航空器数据
|
||||
auto aircraftIt = std::remove_if(
|
||||
aircraft.begin(), aircraft.end(), [this](const Aircraft& a) {
|
||||
return !airportBounds_.isPointInBounds(a.position);
|
||||
});
|
||||
aircraft.erase(aircraftIt, aircraft.end());
|
||||
|
||||
// 过滤车辆数据
|
||||
auto vehicleIt = std::remove_if(vehicles.begin(), vehicles.end(),
|
||||
[this](const Vehicle& v) {
|
||||
|
||||
// 过滤掉不在机场区域内的车辆数据
|
||||
auto vehicleIt = std::remove_if(
|
||||
vehicles.begin(), vehicles.end(), [this](const Vehicle& v) {
|
||||
return !airportBounds_.isPointInBounds(v.position);
|
||||
});
|
||||
vehicles.erase(vehicleIt, vehicles.end());
|
||||
|
||||
|
||||
// 记录过滤结果
|
||||
size_t filteredAircraftCount = originalAircraftCount - aircraft.size();
|
||||
size_t filteredVehicleCount = originalVehicleCount - vehicles.size();
|
||||
|
||||
|
||||
if (filteredAircraftCount > 0 || filteredVehicleCount > 0) {
|
||||
Logger::info(
|
||||
"数据过滤结果: 过滤掉 ", filteredAircraftCount, " 架航空器, ",
|
||||
filteredVehicleCount, " 辆车辆"
|
||||
);
|
||||
Logger::info("数据过滤结果: 过滤掉 ", filteredAircraftCount,
|
||||
" 架航空器, ", filteredVehicleCount, " 辆车辆");
|
||||
}
|
||||
}
|
||||
|
||||
bool DataCollector::fetchPositionData() {
|
||||
std::vector<Aircraft> newAircraft;
|
||||
std::vector<Vehicle> newVehicles;
|
||||
|
||||
|
||||
if (!dataSource_) {
|
||||
Logger::error("No data source available");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = dataSource_->fetchPositionAircraftData(newAircraft) &&
|
||||
dataSource_->fetchPositionVehicleData(newVehicles);
|
||||
|
||||
|
||||
bool success = dataSource_->fetchPositionAircraftData(newAircraft) &&
|
||||
dataSource_->fetchPositionVehicleData(newVehicles);
|
||||
|
||||
if (success) {
|
||||
std::lock_guard<std::mutex> lock(cacheMutex_);
|
||||
|
||||
|
||||
// 更新运动信息
|
||||
for (auto& a : newAircraft) {
|
||||
auto it = lastAircraftPositions_.find(a.id);
|
||||
@ -312,7 +338,7 @@ bool DataCollector::fetchPositionData() {
|
||||
// 更新时间戳
|
||||
lastAircraftTimestamp_ = a.timestamp;
|
||||
}
|
||||
|
||||
|
||||
// 更新运动信息
|
||||
for (auto& v : newVehicles) {
|
||||
auto it = lastVehiclePositions_.find(v.id);
|
||||
@ -326,30 +352,32 @@ bool DataCollector::fetchPositionData() {
|
||||
// 更新时间戳
|
||||
lastVehicleTimestamp_ = v.timestamp;
|
||||
}
|
||||
|
||||
|
||||
// 转换坐标系
|
||||
for (auto& a : newAircraft) {
|
||||
// 1. 先转换为世界坐标
|
||||
a.position = CoordinateConverter::instance().toLocalXY(a.geo.latitude, a.geo.longitude);
|
||||
a.position = CoordinateConverter::instance().toLocalXY(
|
||||
a.geo.latitude, a.geo.longitude);
|
||||
// 2. 再转换为机场坐标
|
||||
a.position = airportBounds_.toAirportCoordinate(a.position);
|
||||
}
|
||||
|
||||
|
||||
for (auto& v : newVehicles) {
|
||||
// 1. 先转换为世界坐标
|
||||
v.position = CoordinateConverter::instance().toLocalXY(v.geo.latitude, v.geo.longitude);
|
||||
v.position = CoordinateConverter::instance().toLocalXY(
|
||||
v.geo.latitude, v.geo.longitude);
|
||||
// 2. 再转换为机场坐标
|
||||
v.position = airportBounds_.toAirportCoordinate(v.position);
|
||||
}
|
||||
|
||||
|
||||
// 过滤数据
|
||||
filterPositionData(newAircraft, newVehicles);
|
||||
|
||||
|
||||
aircraftCache_ = std::move(newAircraft);
|
||||
vehicleCache_ = std::move(newVehicles);
|
||||
resetTimeout("position");
|
||||
}
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@ -362,15 +390,17 @@ bool DataCollector::fetchUnmannedVehicleData() {
|
||||
bool any_success = false;
|
||||
bool any_failure = false;
|
||||
auto& vehicles = ControllableVehicles::getInstance().getVehicles();
|
||||
|
||||
|
||||
for (const auto& vehicle : vehicles) {
|
||||
if (vehicle.type == "UNMANNED") {
|
||||
std::string status;
|
||||
if (dataSource_->fetchUnmannedVehicleStatus(vehicle.vehicleNo, status)) {
|
||||
if (dataSource_->fetchUnmannedVehicleStatus(vehicle.vehicleNo,
|
||||
status)) {
|
||||
any_success = true;
|
||||
} else {
|
||||
any_failure = true;
|
||||
Logger::error("获取无人车状态失败, vehicleNo: " + vehicle.vehicleNo);
|
||||
Logger::error("获取无人车状态失败, vehicleNo: " +
|
||||
vehicle.vehicleNo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -380,21 +410,21 @@ bool DataCollector::fetchUnmannedVehicleData() {
|
||||
// 如果有部分成功,就返回 true
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 只有全部失败才返回 false
|
||||
return !any_failure;
|
||||
}
|
||||
|
||||
bool DataCollector::fetchTrafficLightData() {
|
||||
std::vector<TrafficLightSignal> newSignals;
|
||||
|
||||
|
||||
if (!dataSource_) {
|
||||
Logger::error("No data source available");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool success = dataSource_->fetchTrafficLightSignals(newSignals);
|
||||
|
||||
|
||||
if (success) {
|
||||
std::lock_guard<std::mutex> lock(cacheMutex_);
|
||||
trafficLightCache_ = std::move(newSignals);
|
||||
@ -404,7 +434,7 @@ bool DataCollector::fetchTrafficLightData() {
|
||||
}
|
||||
resetTimeout("traffic_light");
|
||||
}
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@ -413,7 +443,8 @@ std::vector<TrafficLightSignal> DataCollector::getTrafficLightSignals() {
|
||||
return trafficLightCache_;
|
||||
}
|
||||
|
||||
bool DataCollector::fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals) {
|
||||
bool DataCollector::fetchTrafficLightSignals(
|
||||
std::vector<TrafficLightSignal>& signals) {
|
||||
if (!dataSource_) {
|
||||
return false;
|
||||
}
|
||||
@ -430,10 +461,11 @@ std::vector<Vehicle> DataCollector::getVehicleData() {
|
||||
return vehicleCache_;
|
||||
}
|
||||
|
||||
bool DataCollector::fetchUnmannedVehicleStatus(const std::string& vehicle_id, std::string& status) {
|
||||
bool DataCollector::fetchUnmannedVehicleStatus(const std::string& vehicle_id,
|
||||
std::string& status) {
|
||||
if (!dataSource_) {
|
||||
Logger::error("No data source available");
|
||||
return false;
|
||||
}
|
||||
return dataSource_->fetchUnmannedVehicleStatus(vehicle_id, status);
|
||||
}
|
||||
}
|
||||
@ -8,40 +8,65 @@ DataSourceConfig DataSourceConfig::fromSystemConfig(const SystemConfig& config)
|
||||
// 转换位置数据源配置
|
||||
dataSourceConfig.position.host = config.data_source.position.host;
|
||||
dataSourceConfig.position.port = config.data_source.position.port;
|
||||
dataSourceConfig.position.aircraft_path = config.data_source.position.aircraft_path;
|
||||
dataSourceConfig.position.vehicle_path = config.data_source.position.vehicle_path;
|
||||
dataSourceConfig.position.refresh_interval_ms = config.data_source.position.refresh_interval_ms;
|
||||
dataSourceConfig.position.timeout_ms = config.data_source.position.timeout_ms;
|
||||
dataSourceConfig.position.read_timeout_ms = config.data_source.position.read_timeout_ms;
|
||||
dataSourceConfig.position.auth.username = config.data_source.position.auth.username;
|
||||
dataSourceConfig.position.auth.password = config.data_source.position.auth.password;
|
||||
dataSourceConfig.position.auth.auth_path = config.data_source.position.auth.auth_path;
|
||||
dataSourceConfig.position.auth.auth_required = config.data_source.position.auth.auth_required;
|
||||
dataSourceConfig.position.aircraft_path =
|
||||
config.data_source.position.aircraft_path;
|
||||
dataSourceConfig.position.vehicle_path =
|
||||
config.data_source.position.vehicle_path;
|
||||
dataSourceConfig.position.refresh_interval_ms =
|
||||
config.data_source.position.refresh_interval_ms;
|
||||
dataSourceConfig.position.timeout_ms =
|
||||
config.data_source.position.timeout_ms;
|
||||
dataSourceConfig.position.read_timeout_ms =
|
||||
config.data_source.position.read_timeout_ms;
|
||||
dataSourceConfig.position.auth.username =
|
||||
config.data_source.position.auth.username;
|
||||
dataSourceConfig.position.auth.password =
|
||||
config.data_source.position.auth.password;
|
||||
dataSourceConfig.position.auth.auth_path =
|
||||
config.data_source.position.auth.auth_path;
|
||||
dataSourceConfig.position.auth.auth_required =
|
||||
config.data_source.position.auth.auth_required;
|
||||
|
||||
// 转换无人车数据源配置
|
||||
dataSourceConfig.vehicle.host = config.data_source.vehicle.host;
|
||||
dataSourceConfig.vehicle.port = config.data_source.vehicle.port;
|
||||
dataSourceConfig.vehicle.status_path = config.data_source.vehicle.status_path;
|
||||
dataSourceConfig.vehicle.command_path = config.data_source.vehicle.command_path;
|
||||
dataSourceConfig.vehicle.refresh_interval_ms = config.data_source.vehicle.refresh_interval_ms;
|
||||
dataSourceConfig.vehicle.status_path =
|
||||
config.data_source.vehicle.status_path;
|
||||
dataSourceConfig.vehicle.command_path =
|
||||
config.data_source.vehicle.command_path;
|
||||
dataSourceConfig.vehicle.refresh_interval_ms =
|
||||
config.data_source.vehicle.refresh_interval_ms;
|
||||
dataSourceConfig.vehicle.timeout_ms = config.data_source.vehicle.timeout_ms;
|
||||
dataSourceConfig.vehicle.read_timeout_ms = config.data_source.vehicle.read_timeout_ms;
|
||||
dataSourceConfig.vehicle.auth.username = config.data_source.vehicle.auth.username;
|
||||
dataSourceConfig.vehicle.auth.password = config.data_source.vehicle.auth.password;
|
||||
dataSourceConfig.vehicle.auth.auth_path = config.data_source.vehicle.auth.auth_path;
|
||||
dataSourceConfig.vehicle.auth.auth_required = config.data_source.vehicle.auth.auth_required;
|
||||
dataSourceConfig.vehicle.read_timeout_ms =
|
||||
config.data_source.vehicle.read_timeout_ms;
|
||||
dataSourceConfig.vehicle.auth.username =
|
||||
config.data_source.vehicle.auth.username;
|
||||
dataSourceConfig.vehicle.auth.password =
|
||||
config.data_source.vehicle.auth.password;
|
||||
dataSourceConfig.vehicle.auth.auth_path =
|
||||
config.data_source.vehicle.auth.auth_path;
|
||||
dataSourceConfig.vehicle.auth.auth_required =
|
||||
config.data_source.vehicle.auth.auth_required;
|
||||
|
||||
// 转换红绿灯数据源配置
|
||||
dataSourceConfig.traffic_light.host = config.data_source.traffic_light.host;
|
||||
dataSourceConfig.traffic_light.port = config.data_source.traffic_light.port;
|
||||
dataSourceConfig.traffic_light.signal_path = config.data_source.traffic_light.signal_path;
|
||||
dataSourceConfig.traffic_light.refresh_interval_ms = config.data_source.traffic_light.refresh_interval_ms;
|
||||
dataSourceConfig.traffic_light.timeout_ms = config.data_source.traffic_light.timeout_ms;
|
||||
dataSourceConfig.traffic_light.read_timeout_ms = config.data_source.traffic_light.read_timeout_ms;
|
||||
dataSourceConfig.traffic_light.auth.username = config.data_source.traffic_light.auth.username;
|
||||
dataSourceConfig.traffic_light.auth.password = config.data_source.traffic_light.auth.password;
|
||||
dataSourceConfig.traffic_light.auth.auth_path = config.data_source.traffic_light.auth.auth_path;
|
||||
dataSourceConfig.traffic_light.auth.auth_required = config.data_source.traffic_light.auth.auth_required;
|
||||
dataSourceConfig.traffic_light.signal_path =
|
||||
config.data_source.traffic_light.signal_path;
|
||||
dataSourceConfig.traffic_light.refresh_interval_ms =
|
||||
config.data_source.traffic_light.refresh_interval_ms;
|
||||
dataSourceConfig.traffic_light.timeout_ms =
|
||||
config.data_source.traffic_light.timeout_ms;
|
||||
dataSourceConfig.traffic_light.read_timeout_ms =
|
||||
config.data_source.traffic_light.read_timeout_ms;
|
||||
dataSourceConfig.traffic_light.auth.username =
|
||||
config.data_source.traffic_light.auth.username;
|
||||
dataSourceConfig.traffic_light.auth.password =
|
||||
config.data_source.traffic_light.auth.password;
|
||||
dataSourceConfig.traffic_light.auth.auth_path =
|
||||
config.data_source.traffic_light.auth.auth_path;
|
||||
dataSourceConfig.traffic_light.auth.auth_required =
|
||||
config.data_source.traffic_light.auth.auth_required;
|
||||
|
||||
return dataSourceConfig;
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
#include "AirportBounds.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include "utils/Logger.h"
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
@ -18,7 +18,8 @@ void AirportBounds::loadConfig(const std::string& configFile) {
|
||||
// 检查文件是否存在
|
||||
if (!std::filesystem::exists(configFile)) {
|
||||
Logger::error("配置文件不存在: ", configFile);
|
||||
Logger::error("当前工作目录: ", std::filesystem::current_path().string());
|
||||
Logger::error("当前工作目录: ",
|
||||
std::filesystem::current_path().string());
|
||||
throw std::runtime_error("配置文件不存在: " + configFile);
|
||||
}
|
||||
|
||||
@ -40,9 +41,10 @@ void AirportBounds::loadConfig(const std::string& configFile) {
|
||||
auto& airport = j["airport"];
|
||||
if (airport.contains("rotation_angle")) {
|
||||
// 将角度转换为弧度
|
||||
rotationAngle_ = airport["rotation_angle"].get<double>() * M_PI / 180.0;
|
||||
rotationAngle_ =
|
||||
airport["rotation_angle"].get<double>() * M_PI / 180.0;
|
||||
}
|
||||
|
||||
|
||||
if (airport.contains("reference_point")) {
|
||||
referencePoint_.x = airport["reference_point"]["x"].get<double>();
|
||||
referencePoint_.y = airport["reference_point"]["y"].get<double>();
|
||||
@ -50,12 +52,10 @@ void AirportBounds::loadConfig(const std::string& configFile) {
|
||||
|
||||
// 加载机场边界
|
||||
auto& airportBounds = j["airport"]["bounds"];
|
||||
airportBounds_ = {
|
||||
airportBounds["x"].get<double>(),
|
||||
airportBounds["y"].get<double>(),
|
||||
airportBounds["width"].get<double>(),
|
||||
airportBounds["height"].get<double>()
|
||||
};
|
||||
airportBounds_ = { airportBounds["x"].get<double>(),
|
||||
airportBounds["y"].get<double>(),
|
||||
airportBounds["width"].get<double>(),
|
||||
airportBounds["height"].get<double>() };
|
||||
|
||||
// 检查 areas 字段
|
||||
if (!j.contains("areas")) {
|
||||
@ -65,12 +65,18 @@ void AirportBounds::loadConfig(const std::string& configFile) {
|
||||
// 加载各区域配置
|
||||
for (const auto& [key, value] : j["areas"].items()) {
|
||||
AreaType type;
|
||||
if (key == "runway") type = AreaType::RUNWAY;
|
||||
else if (key == "taxiway") type = AreaType::TAXIWAY;
|
||||
else if (key == "gate") type = AreaType::GATE;
|
||||
else if (key == "service") type = AreaType::SERVICE;
|
||||
else if (key == "test_zone") type = AreaType::TEST_ZONE;
|
||||
else continue;
|
||||
if (key == "runway")
|
||||
type = AreaType::RUNWAY;
|
||||
else if (key == "taxiway")
|
||||
type = AreaType::TAXIWAY;
|
||||
else if (key == "gate")
|
||||
type = AreaType::GATE;
|
||||
else if (key == "service")
|
||||
type = AreaType::SERVICE;
|
||||
else if (key == "test_zone")
|
||||
type = AreaType::TEST_ZONE;
|
||||
else
|
||||
continue;
|
||||
|
||||
// 检查必要的字段
|
||||
if (!value.contains("bounds") || !value.contains("config")) {
|
||||
@ -80,11 +86,8 @@ void AirportBounds::loadConfig(const std::string& configFile) {
|
||||
// 加载区域边界
|
||||
auto& bounds = value["bounds"];
|
||||
areaBounds_[type] = {
|
||||
bounds["x"].get<double>(),
|
||||
bounds["y"].get<double>(),
|
||||
bounds["width"].get<double>(),
|
||||
bounds["height"].get<double>()
|
||||
};
|
||||
bounds["x"].get<double>(), bounds["y"].get<double>(),
|
||||
bounds["width"].get<double>(), bounds["height"].get<double>() };
|
||||
|
||||
// 加载区域配置
|
||||
auto& config = value["config"];
|
||||
@ -95,34 +98,26 @@ void AirportBounds::loadConfig(const std::string& configFile) {
|
||||
RadiusConfig collision_config = {
|
||||
collision_radius["aircraft"].get<double>(),
|
||||
collision_radius["special"].get<double>(),
|
||||
collision_radius["unmanned"].get<double>()
|
||||
};
|
||||
collision_radius["unmanned"].get<double>() };
|
||||
|
||||
RadiusConfig warning_config = {
|
||||
warning_zone_radius["aircraft"].get<double>(),
|
||||
warning_zone_radius["special"].get<double>(),
|
||||
warning_zone_radius["unmanned"].get<double>()
|
||||
};
|
||||
warning_zone_radius["unmanned"].get<double>() };
|
||||
|
||||
RadiusConfig alert_config = {
|
||||
alert_zone_radius["aircraft"].get<double>(),
|
||||
alert_zone_radius["special"].get<double>(),
|
||||
alert_zone_radius["unmanned"].get<double>()
|
||||
};
|
||||
alert_zone_radius["unmanned"].get<double>() };
|
||||
|
||||
areaConfigs_[type] = {
|
||||
collision_config,
|
||||
config["height_threshold"].get<double>(),
|
||||
warning_config,
|
||||
alert_config
|
||||
};
|
||||
areaConfigs_[type] = { collision_config,
|
||||
config["height_threshold"].get<double>(),
|
||||
warning_config, alert_config };
|
||||
}
|
||||
}
|
||||
catch (const json::exception& e) {
|
||||
} catch (const json::exception& e) {
|
||||
Logger::error("JSON解析错误: ", e.what());
|
||||
throw;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("加载配置文件失败: ", e.what());
|
||||
throw;
|
||||
}
|
||||
@ -132,29 +127,27 @@ Vector2D AirportBounds::toAirportCoordinate(const Vector2D& point) const {
|
||||
// 1. 计算相对于参考点的偏移
|
||||
double dx = point.x - referencePoint_.x;
|
||||
double dy = point.y - referencePoint_.y;
|
||||
|
||||
|
||||
// 2. 应用旋转变换
|
||||
double cos_angle = std::cos(rotationAngle_);
|
||||
double sin_angle = std::sin(rotationAngle_);
|
||||
|
||||
|
||||
// 3. 返回机场坐标系中的坐标(逆时针旋转)
|
||||
Vector2D result{
|
||||
dx * cos_angle - dy * sin_angle,
|
||||
dx * sin_angle + dy * cos_angle
|
||||
};
|
||||
|
||||
Vector2D result{ dx * cos_angle - dy * sin_angle,
|
||||
dx * sin_angle + dy * cos_angle };
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
AreaType AirportBounds::getAreaType(const Vector2D& position) const {
|
||||
|
||||
|
||||
// 按优先级检查位置所在区域
|
||||
for (const auto& [type, bounds] : areaBounds_) {
|
||||
if (bounds.contains(position)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 默认返回测试区
|
||||
return AreaType::TEST_ZONE;
|
||||
}
|
||||
@ -173,13 +166,14 @@ bool AirportBounds::isPointInBounds(const Vector2D& position) const {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AirportBounds::isPointInArea(const Vector2D& position, AreaType areaType) const {
|
||||
bool AirportBounds::isPointInArea(const Vector2D& position,
|
||||
AreaType areaType) const {
|
||||
// 获取区域定义
|
||||
auto it = areaBounds_.find(areaType);
|
||||
if (it == areaBounds_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 在机场坐标系中判断是否在区域内
|
||||
return it->second.contains(position);
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
IntersectionConfig IntersectionConfig::load(const std::string& configFile) {
|
||||
IntersectionConfig config;
|
||||
|
||||
|
||||
std::ifstream file(configFile);
|
||||
if (!file.is_open()) {
|
||||
throw std::runtime_error("Failed to open intersection config file: " + configFile);
|
||||
@ -22,7 +22,7 @@ IntersectionConfig IntersectionConfig::load(const std::string& configFile) {
|
||||
info.id = item["id"].get<std::string>();
|
||||
info.name = item["name"].get<std::string>();
|
||||
info.trafficLightId = item["trafficLightId"].get<std::string>();
|
||||
|
||||
|
||||
// 加载位置信息,检查是否为 null
|
||||
if (item["position"]["longitude"].is_null() ||
|
||||
item["position"]["latitude"].is_null() ||
|
||||
@ -32,7 +32,7 @@ IntersectionConfig IntersectionConfig::load(const std::string& configFile) {
|
||||
info.position.longitude = item["position"]["longitude"].get<double>();
|
||||
info.position.latitude = item["position"]["latitude"].get<double>();
|
||||
info.position.altitude = item["position"]["altitude"].get<double>();
|
||||
|
||||
|
||||
// 加载路口宽度和安全区配置,检查是否为 null
|
||||
if (item["width"].is_null() ||
|
||||
item["safetyZone"]["aircraftRadius"].is_null() ||
|
||||
@ -44,20 +44,20 @@ IntersectionConfig IntersectionConfig::load(const std::string& configFile) {
|
||||
info.safetyZone.vehicleRadius = item["safetyZone"]["vehicleRadius"].get<double>();
|
||||
|
||||
config.intersections_.push_back(info);
|
||||
|
||||
Logger::debug("Loaded intersection: id=", info.id,
|
||||
", name=", info.name,
|
||||
", trafficLightId=", info.trafficLightId,
|
||||
", width=", info.width,
|
||||
", aircraftRadius=", info.safetyZone.aircraftRadius,
|
||||
", vehicleRadius=", info.safetyZone.vehicleRadius);
|
||||
|
||||
Logger::debug("Loaded intersection: id=", info.id,
|
||||
", name=", info.name,
|
||||
", trafficLightId=", info.trafficLightId,
|
||||
", width=", info.width,
|
||||
", aircraftRadius=", info.safetyZone.aircraftRadius,
|
||||
", vehicleRadius=", info.safetyZone.vehicleRadius);
|
||||
}
|
||||
|
||||
Logger::info("Loaded ", config.intersections_.size(), " intersections");
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Failed to parse intersection config: " + std::string(e.what()));
|
||||
}
|
||||
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@ -75,4 +75,4 @@ const Intersection* IntersectionConfig::findById(const std::string& intersection
|
||||
return info.id == intersectionId;
|
||||
});
|
||||
return iter != intersections_.end() ? &(*iter) : nullptr;
|
||||
}
|
||||
}
|
||||
@ -8,14 +8,14 @@ void SystemConfig::load(const std::string& filename) {
|
||||
if (!file.is_open()) {
|
||||
throw std::runtime_error("Failed to open config file: " + filename);
|
||||
}
|
||||
|
||||
|
||||
nlohmann::json j;
|
||||
file >> j;
|
||||
nlohmann::from_json(j, *this);
|
||||
|
||||
|
||||
Logger::info("Loaded system configuration from ", filename);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
throw std::runtime_error("Failed to load config: " + std::string(e.what()));
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error("Failed to load config: " +
|
||||
std::string(e.what()));
|
||||
}
|
||||
}
|
||||
@ -9,12 +9,12 @@
|
||||
|
||||
System* System::instance_ = nullptr;
|
||||
|
||||
System::System()
|
||||
System::System()
|
||||
: controllableVehicles_(ControllableVehicles::getInstance()) {
|
||||
instance_ = this;
|
||||
std::signal(SIGINT, signalHandler);
|
||||
std::signal(SIGTERM, signalHandler);
|
||||
|
||||
|
||||
// 使用单例模式获取配置实例
|
||||
auto& config = SystemConfig::instance();
|
||||
}
|
||||
@ -40,8 +40,7 @@ bool System::initialize() {
|
||||
// 初始化全局坐标转换器
|
||||
CoordinateConverter::instance().setReferencePoint(
|
||||
system_config.airport.reference_point.latitude,
|
||||
system_config.airport.reference_point.longitude
|
||||
);
|
||||
system_config.airport.reference_point.longitude);
|
||||
|
||||
// 加载路口配置
|
||||
intersection_config_ = IntersectionConfig::load("config/intersections.json");
|
||||
@ -49,33 +48,29 @@ bool System::initialize() {
|
||||
|
||||
// 初始化 WebSocket 服务器
|
||||
ws_server_ = std::make_unique<network::WebSocketServer>(system_config.websocket.port);
|
||||
ws_thread_ = std::thread([this]() {
|
||||
ws_server_->start();
|
||||
});
|
||||
|
||||
ws_thread_ = std::thread([this]() { ws_server_->start(); });
|
||||
|
||||
// 加载机场区域配置
|
||||
airportBounds_ = std::make_unique<AirportBounds>("config/airport_bounds.json");
|
||||
|
||||
|
||||
// 初始化冲突检测器
|
||||
collisionDetector_ = std::make_unique<CollisionDetector>(*airportBounds_, controllableVehicles_);
|
||||
|
||||
// 初始化红绿灯检测器
|
||||
trafficLightDetector_ = std::make_unique<TrafficLightDetector>(intersection_config_, controllableVehicles_, *this);
|
||||
|
||||
|
||||
// 初始化安全区
|
||||
initializeSafetyZones();
|
||||
|
||||
// 加载告警配置
|
||||
WarnConfig warnConfig{
|
||||
system_config.warning.warning_interval_ms,
|
||||
system_config.warning.log_interval_ms
|
||||
};
|
||||
system_config.warning.log_interval_ms };
|
||||
|
||||
// 创建数据采集器并初始化
|
||||
dataCollector_ = std::make_unique<DataCollector>(*airportBounds_);
|
||||
return dataCollector_->initialize(system_config.data_source, warnConfig);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("Failed to initialize system: ", e.what());
|
||||
return false;
|
||||
}
|
||||
@ -84,30 +79,29 @@ bool System::initialize() {
|
||||
void System::initializeSafetyZones() {
|
||||
// 清空现有的安全区
|
||||
safetyZones_.clear();
|
||||
|
||||
|
||||
// 获取所有路口配置
|
||||
const auto& intersections = intersection_config_.getIntersections();
|
||||
|
||||
|
||||
// 为每个路口创建安全区
|
||||
for (const auto& intersection : intersections) {
|
||||
// 获取路口中心点的地理坐标
|
||||
Vector2D center = CoordinateConverter::instance().toLocalXY(
|
||||
intersection.position.latitude,
|
||||
intersection.position.longitude
|
||||
);
|
||||
|
||||
intersection.position.longitude);
|
||||
|
||||
// 创建安全区
|
||||
auto safetyZone = std::make_unique<SafetyZone>(
|
||||
center,
|
||||
intersection.safetyZone.aircraftRadius, // 飞机安全区半径
|
||||
intersection.safetyZone.vehicleRadius // 特勤车安全区半径
|
||||
intersection.safetyZone.aircraftRadius, // 飞机安全区半径
|
||||
intersection.safetyZone.vehicleRadius // 特勤车安全区半径
|
||||
);
|
||||
|
||||
|
||||
Logger::debug("创建路口安全区: id=", intersection.id,
|
||||
", center=(", center.x, ",", center.y, ")",
|
||||
", aircraftRadius=", intersection.safetyZone.aircraftRadius,
|
||||
", vehicleRadius=", intersection.safetyZone.vehicleRadius);
|
||||
|
||||
", center=(", center.x, ",", center.y, ")",
|
||||
", aircraftRadius=", intersection.safetyZone.aircraftRadius,
|
||||
", vehicleRadius=", intersection.safetyZone.vehicleRadius);
|
||||
|
||||
// 保存安全区
|
||||
safetyZones_[intersection.id] = std::move(safetyZone);
|
||||
}
|
||||
@ -117,7 +111,7 @@ void System::start() {
|
||||
if (running_) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
running_ = true;
|
||||
dataCollector_->start();
|
||||
processThread_ = std::thread(&System::processLoop, this);
|
||||
@ -128,27 +122,27 @@ void System::stop() {
|
||||
if (!running_) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
running_ = false;
|
||||
|
||||
|
||||
// 停止 WebSocket 服务器
|
||||
if (ws_server_) {
|
||||
ws_server_->stop();
|
||||
}
|
||||
|
||||
|
||||
// 等待线程结束
|
||||
if (ws_thread_.joinable()) {
|
||||
ws_thread_.join();
|
||||
}
|
||||
|
||||
|
||||
if (processThread_.joinable()) {
|
||||
processThread_.join();
|
||||
}
|
||||
|
||||
|
||||
if (dataCollector_) {
|
||||
dataCollector_->stop();
|
||||
}
|
||||
|
||||
|
||||
Logger::info("System stopped");
|
||||
}
|
||||
|
||||
@ -156,32 +150,32 @@ void System::processLoop() {
|
||||
while (running_) {
|
||||
try {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
// 获取最新数据和时间戳
|
||||
std::tie(latest_aircraft_, latest_vehicles_, latest_traffic_lights_) = dataCollector_->getLatestData();
|
||||
auto [aircraft_ts, vehicle_ts, traffic_light_ts] = dataCollector_->getLastUpdateTimestamps();
|
||||
|
||||
|
||||
// 检查数据更新
|
||||
bool has_new_data = false;
|
||||
|
||||
|
||||
if (aircraft_ts > last_aircraft_timestamp) {
|
||||
has_new_data = true;
|
||||
last_aircraft_timestamp = aircraft_ts;
|
||||
Logger::debug("航空器数据已更新,新时间戳: ", aircraft_ts);
|
||||
}
|
||||
|
||||
|
||||
if (vehicle_ts > last_vehicle_timestamp) {
|
||||
has_new_data = true;
|
||||
last_vehicle_timestamp = vehicle_ts;
|
||||
Logger::debug("车辆数据已更新,新时间戳: ", vehicle_ts);
|
||||
}
|
||||
|
||||
|
||||
if (traffic_light_ts > last_traffic_light_timestamp) {
|
||||
has_new_data = true;
|
||||
last_traffic_light_timestamp = traffic_light_ts;
|
||||
Logger::debug("红绿灯数据已更新,新时间戳: ", traffic_light_ts);
|
||||
}
|
||||
|
||||
|
||||
if (has_new_data) {
|
||||
// 使用成员变量的引用构建 objects 向量
|
||||
std::vector<MovingObject*> objects;
|
||||
@ -198,45 +192,46 @@ void System::processLoop() {
|
||||
}
|
||||
|
||||
// 创建当前周期的风险管理器
|
||||
std::unordered_map<std::string, RiskLevel> vehicleMaxRiskLevels; // 统一记录所有车辆的最高风险等级
|
||||
std::vector<CollisionRisk> detectedRisks; // 统一收集所有检测到的风险
|
||||
|
||||
std::unordered_map<std::string, RiskLevel> vehicleMaxRiskLevels; // 统一记录所有车辆的最高风险等级
|
||||
std::vector<CollisionRisk> detectedRisks; // 统一收集所有检测到的风险
|
||||
|
||||
// 检查安全区更新
|
||||
auto safety_zone_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_safety_zone_update).count();
|
||||
|
||||
now - last_safety_zone_update)
|
||||
.count();
|
||||
|
||||
if (safety_zone_elapsed >= SystemConfig::instance().collision_detection.update_interval_ms) {
|
||||
updateSafetyZoneStates(objects);
|
||||
Logger::debug("开始检查安全区冲突...");
|
||||
|
||||
|
||||
// 记录安全区风险检测前的风险数量
|
||||
size_t beforeSafetyZone = detectedRisks.size();
|
||||
checkUnmannedVehicleSafetyZones(latest_vehicles_, objects, vehicleMaxRiskLevels, detectedRisks);
|
||||
Logger::debug("安全区检测新增风险数量: ", detectedRisks.size() - beforeSafetyZone);
|
||||
|
||||
|
||||
last_safety_zone_update = now;
|
||||
}
|
||||
|
||||
|
||||
// 检查和处理常规碰撞风险
|
||||
collisionDetector_->updateTraffic(latest_aircraft_, latest_vehicles_);
|
||||
auto collisionRisks = collisionDetector_->detectCollisions();
|
||||
|
||||
|
||||
Logger::debug("碰撞检测器检测到风险数量: ", collisionRisks.size());
|
||||
for (const auto& risk : collisionRisks) {
|
||||
Logger::debug("碰撞风险: id1=", risk.id1,
|
||||
", id2=", risk.id2,
|
||||
", level=", static_cast<int>(risk.level),
|
||||
", distance=", risk.distance);
|
||||
", id2=", risk.id2,
|
||||
", level=", static_cast<int>(risk.level),
|
||||
", distance=", risk.distance);
|
||||
}
|
||||
|
||||
|
||||
// 合并风险
|
||||
size_t beforeMerge = detectedRisks.size();
|
||||
detectedRisks.insert(detectedRisks.end(), collisionRisks.begin(), collisionRisks.end());
|
||||
Logger::debug("合并后新增风险数量: ", detectedRisks.size() - beforeMerge);
|
||||
|
||||
|
||||
// 统一处理所有风险
|
||||
processCollisions(detectedRisks, vehicleMaxRiskLevels);
|
||||
|
||||
|
||||
// 广播位置更新
|
||||
for (const auto& ac : latest_aircraft_) {
|
||||
broadcastPositionUpdate(ac);
|
||||
@ -244,30 +239,29 @@ void System::processLoop() {
|
||||
for (const auto& veh : latest_vehicles_) {
|
||||
broadcastPositionUpdate(veh);
|
||||
}
|
||||
|
||||
|
||||
// 处理红绿灯信号
|
||||
for (const auto& signal : latest_traffic_lights_) {
|
||||
broadcastTrafficLightStatus(signal);
|
||||
trafficLightDetector_->processSignal(signal, latest_vehicles_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 等待下一次处理
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(
|
||||
SystemConfig::instance().collision_detection.update_interval_ms));
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("处理循环发生错误: ", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void System::checkUnmannedVehicleSafetyZones(
|
||||
const std::vector<Vehicle>& vehicles,
|
||||
const std::vector<Vehicle>& vehicles,
|
||||
const std::vector<MovingObject*>& objects,
|
||||
std::unordered_map<std::string, RiskLevel>& vehicleMaxRiskLevels,
|
||||
std::vector<CollisionRisk>& detectedRisks) {
|
||||
|
||||
|
||||
// 遍历所有无人车
|
||||
for (const auto& vehicle : vehicles) {
|
||||
// 遍历所有路口安全区
|
||||
@ -275,13 +269,13 @@ void System::checkUnmannedVehicleSafetyZones(
|
||||
if (zone->getState() == SafetyZoneState::INACTIVE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 计算无人车到安全区中心的距离
|
||||
double dx = vehicle.position.x - zone->getCenter().x;
|
||||
double dy = vehicle.position.y - zone->getCenter().y;
|
||||
double distance = std::sqrt(dx * dx + dy * dy);
|
||||
double zoneRadius = zone->getCurrentRadius();
|
||||
|
||||
|
||||
RiskLevel riskLevel = RiskLevel::NONE;
|
||||
if (distance <= zoneRadius) {
|
||||
riskLevel = RiskLevel::CRITICAL;
|
||||
@ -296,8 +290,8 @@ void System::checkUnmannedVehicleSafetyZones(
|
||||
if (obj->id == vehicle.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((obj->isAircraft() || obj->isSpecialVehicle()) &&
|
||||
|
||||
if ((obj->isAircraft() || obj->isSpecialVehicle()) &&
|
||||
zone->isObjectInZone(*obj)) {
|
||||
// 创建风险对象
|
||||
CollisionRisk risk;
|
||||
@ -307,27 +301,27 @@ void System::checkUnmannedVehicleSafetyZones(
|
||||
risk.distance = distance;
|
||||
risk.minDistance = distance;
|
||||
risk.relativeSpeed = std::sqrt(
|
||||
std::pow(obj->speed * std::cos(obj->heading) -
|
||||
vehicle.speed * std::cos(vehicle.heading), 2) +
|
||||
std::pow(obj->speed * std::sin(obj->heading) -
|
||||
vehicle.speed * std::sin(vehicle.heading), 2)
|
||||
);
|
||||
std::pow(obj->speed * std::cos(obj->heading) -
|
||||
vehicle.speed * std::cos(vehicle.heading),
|
||||
2) +
|
||||
std::pow(obj->speed * std::sin(obj->heading) -
|
||||
vehicle.speed * std::sin(vehicle.heading),
|
||||
2));
|
||||
risk.relativeMotion = {
|
||||
obj->position.x - vehicle.position.x,
|
||||
obj->position.y - vehicle.position.y
|
||||
};
|
||||
|
||||
obj->position.y - vehicle.position.y };
|
||||
|
||||
detectedRisks.push_back(risk);
|
||||
|
||||
|
||||
// 更新风险级别
|
||||
auto& currentRisk = vehicleMaxRiskLevels[vehicle.id];
|
||||
currentRisk = std::max(currentRisk, riskLevel);
|
||||
|
||||
|
||||
Logger::debug("安全区风险: id1=", risk.id1,
|
||||
", id2=", risk.id2,
|
||||
", level=", static_cast<int>(risk.level),
|
||||
", distance=", risk.distance);
|
||||
|
||||
", id2=", risk.id2,
|
||||
", level=", static_cast<int>(risk.level),
|
||||
", distance=", risk.distance);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -339,37 +333,36 @@ void System::checkUnmannedVehicleSafetyZones(
|
||||
void System::processCollisions(
|
||||
const std::vector<CollisionRisk>& detectedRisks,
|
||||
std::unordered_map<std::string, RiskLevel>& vehicleMaxRiskLevels) {
|
||||
|
||||
|
||||
// 记录当前有风险的可控车辆
|
||||
std::unordered_set<std::string> currentVehiclesWithRisk;
|
||||
|
||||
|
||||
// 修改日志输出,显示更多信息
|
||||
Logger::debug("开始处理碰撞风险,风险数量: ", detectedRisks.size());
|
||||
for (const auto& risk : detectedRisks) {
|
||||
Logger::debug("待处理风险: id1=", risk.id1,
|
||||
", id2=", risk.id2,
|
||||
", level=", static_cast<int>(risk.level),
|
||||
", distance=", risk.distance,
|
||||
", minDistance=", risk.minDistance);
|
||||
", id2=", risk.id2,
|
||||
", level=", static_cast<int>(risk.level),
|
||||
", distance=", risk.distance,
|
||||
", minDistance=", risk.minDistance);
|
||||
}
|
||||
|
||||
|
||||
// 处理当前的碰撞风险
|
||||
for (const auto& risk : detectedRisks) {
|
||||
// 处理 id1 和 id2 中的可控车辆
|
||||
bool id1_controllable = controllableVehicles_.isControllable(risk.id1);
|
||||
bool id2_controllable = controllableVehicles_.isControllable(risk.id2);
|
||||
|
||||
|
||||
auto processVehicle = [&](const std::string& vehicleId, const std::string& otherId) {
|
||||
// 发送指令
|
||||
VehicleCommand cmd;
|
||||
cmd.vehicleId = vehicleId;
|
||||
cmd.type = risk.level == RiskLevel::CRITICAL ? CommandType::ALERT : CommandType::WARNING;
|
||||
cmd.reason = otherId.substr(0, 2) == "AC" ?
|
||||
CommandReason::AIRCRAFT_CROSSING :
|
||||
CommandReason::SPECIAL_VEHICLE;
|
||||
cmd.reason = otherId.substr(0, 2) == "AC" ? CommandReason::AIRCRAFT_CROSSING : CommandReason::SPECIAL_VEHICLE;
|
||||
cmd.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
|
||||
// 设置目标位置
|
||||
const MovingObject* target = findVehicle(otherId);
|
||||
if (target) {
|
||||
@ -380,26 +373,26 @@ void System::processCollisions(
|
||||
cmd.relativeMotionY = risk.relativeMotion.y;
|
||||
cmd.minDistance = risk.minDistance;
|
||||
}
|
||||
|
||||
|
||||
Logger::debug("准备发送指令: 车辆=", vehicleId,
|
||||
", 类型=", cmd.type == CommandType::ALERT ? "ALERT" : "WARNING",
|
||||
", 原因=", cmd.reason == CommandReason::AIRCRAFT_CROSSING ? "AIRCRAFT_CROSSING" : "SPECIAL_VEHICLE",
|
||||
", 距离=", risk.distance,
|
||||
", 风险等级=", static_cast<int>(risk.level));
|
||||
|
||||
", 类型=", cmd.type == CommandType::ALERT ? "ALERT" : "WARNING",
|
||||
", 原因=", cmd.reason == CommandReason::AIRCRAFT_CROSSING ? "AIRCRAFT_CROSSING" : "SPECIAL_VEHICLE",
|
||||
", 距离=", risk.distance,
|
||||
", 风险等级=", static_cast<int>(risk.level));
|
||||
|
||||
broadcastVehicleCommand(cmd);
|
||||
controllableVehicles_.sendCommand(vehicleId, cmd);
|
||||
|
||||
|
||||
// 更新风险记录
|
||||
currentVehiclesWithRisk.insert(vehicleId);
|
||||
vehicleMaxRiskLevels[vehicleId] = risk.level;
|
||||
|
||||
|
||||
Logger::info("碰撞风险: 车辆=", vehicleId,
|
||||
", 目标=", otherId,
|
||||
", 距离=", risk.distance, "m",
|
||||
", 最小距离=", risk.minDistance, "m",
|
||||
", 相对速度=", risk.relativeSpeed, "m/s");
|
||||
};
|
||||
", 目标=", otherId,
|
||||
", 距离=", risk.distance, "m",
|
||||
", 最小距离=", risk.minDistance, "m",
|
||||
", 相对速度=", risk.relativeSpeed, "m/s");
|
||||
};
|
||||
|
||||
// 为每个可控车辆处理风险
|
||||
if (id1_controllable) {
|
||||
@ -408,11 +401,11 @@ void System::processCollisions(
|
||||
if (id2_controllable) {
|
||||
processVehicle(risk.id2, risk.id1);
|
||||
}
|
||||
|
||||
|
||||
// 广播碰撞预警消息
|
||||
broadcastCollisionWarning(risk);
|
||||
}
|
||||
|
||||
|
||||
// 处理恢复指令
|
||||
for (const auto& vehicleId : lastVehiclesWithRisk_) {
|
||||
if (currentVehiclesWithRisk.find(vehicleId) == currentVehiclesWithRisk.end()) {
|
||||
@ -423,15 +416,16 @@ void System::processCollisions(
|
||||
cmd.type = CommandType::RESUME;
|
||||
cmd.reason = CommandReason::RESUME_TRAFFIC;
|
||||
cmd.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
|
||||
broadcastVehicleCommand(cmd);
|
||||
controllableVehicles_.sendCommand(vehicleId, cmd);
|
||||
Logger::info("发送恢复指令到车辆: ", vehicleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 更新风险车辆列表
|
||||
lastVehiclesWithRisk_ = std::move(currentVehiclesWithRisk);
|
||||
}
|
||||
@ -440,7 +434,7 @@ void System::broadcastPositionUpdate(const MovingObject& obj) {
|
||||
if (!ws_server_) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
network::PositionUpdateMessage msg;
|
||||
msg.objectId = obj.id;
|
||||
msg.objectType = (typeid(obj) == typeid(Aircraft)) ? "aircraft" : "vehicle";
|
||||
@ -449,67 +443,64 @@ void System::broadcastPositionUpdate(const MovingObject& obj) {
|
||||
msg.latitude = obj.geo.latitude;
|
||||
msg.heading = obj.heading;
|
||||
msg.speed = obj.speed;
|
||||
|
||||
|
||||
nlohmann::json j = {
|
||||
{"type", "position_update"},
|
||||
{"object_id", msg.objectId},
|
||||
{"object_type", msg.objectType},
|
||||
{"timestamp", msg.timestamp},
|
||||
{"position", {
|
||||
{"longitude", msg.longitude},
|
||||
{"latitude", msg.latitude}
|
||||
}},
|
||||
{"position", {{"longitude", msg.longitude}, {"latitude", msg.latitude}}},
|
||||
{"heading", msg.heading},
|
||||
{"speed", msg.speed}
|
||||
};
|
||||
|
||||
{"speed", msg.speed} };
|
||||
|
||||
ws_server_->broadcast(j.dump());
|
||||
Logger::debug("广播位置更新: id=", msg.objectId,
|
||||
" type=", msg.objectType,
|
||||
" pos=(", msg.longitude, ",", msg.latitude, ")",
|
||||
" heading=", msg.heading,
|
||||
" speed=", msg.speed,
|
||||
" timestamp=", msg.timestamp);
|
||||
" type=", msg.objectType,
|
||||
" pos=(", msg.longitude, ",", msg.latitude, ")",
|
||||
" heading=", msg.heading,
|
||||
" speed=", msg.speed,
|
||||
" timestamp=", msg.timestamp);
|
||||
}
|
||||
|
||||
void System::broadcastTrafficLightStatus(const TrafficLightSignal& signal) {
|
||||
if (!ws_server_) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 查找路口信息
|
||||
const Intersection* intersection = intersection_config_.findByTrafficLightId(signal.trafficLightId);
|
||||
|
||||
|
||||
nlohmann::json j = {
|
||||
{"type", "traffic_light_status"},
|
||||
{"id", signal.trafficLightId},
|
||||
{"status", static_cast<int>(signal.status)},
|
||||
{"timestamp", signal.timestamp}
|
||||
};
|
||||
|
||||
{"timestamp", signal.timestamp} };
|
||||
|
||||
// 添加路口信息
|
||||
if (intersection) {
|
||||
j["intersection"] = intersection->id;
|
||||
j["position"] = {
|
||||
{"longitude", intersection->position.longitude},
|
||||
{"latitude", intersection->position.latitude}
|
||||
};
|
||||
{"latitude", intersection->position.latitude} };
|
||||
}
|
||||
|
||||
|
||||
ws_server_->broadcast(j.dump());
|
||||
Logger::debug("广播红绿灯状态: id=", signal.trafficLightId,
|
||||
" status=", static_cast<int>(signal.status),
|
||||
" intersection=", intersection ? intersection->id : "",
|
||||
" position=(", intersection ? intersection->position.longitude : 0.0, ",",
|
||||
intersection ? intersection->position.latitude : 0.0, ")",
|
||||
" timestamp=", signal.timestamp);
|
||||
Logger::debug("广播红绿灯状态: id=", signal.trafficLightId,
|
||||
" status=", static_cast<int>(signal.status),
|
||||
" intersection=", intersection ? intersection->id : "",
|
||||
" position=(", intersection ? intersection->position.longitude : 0.0, ",",
|
||||
intersection ? intersection->position.latitude : 0.0, ")",
|
||||
" timestamp=", signal.timestamp);
|
||||
}
|
||||
|
||||
std::string getRiskLevelString(RiskLevel level) {
|
||||
switch (level) {
|
||||
case RiskLevel::CRITICAL: return "critical";
|
||||
case RiskLevel::WARNING: return "warning";
|
||||
default: return "none";
|
||||
case RiskLevel::CRITICAL:
|
||||
return "critical";
|
||||
case RiskLevel::WARNING:
|
||||
return "warning";
|
||||
default:
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
|
||||
@ -517,7 +508,7 @@ void System::broadcastCollisionWarning(const CollisionRisk& risk) {
|
||||
if (!ws_server_) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 构造冲突预警消息
|
||||
nlohmann::json j = {
|
||||
{"type", "collision_warning"},
|
||||
@ -527,28 +518,28 @@ void System::broadcastCollisionWarning(const CollisionRisk& risk) {
|
||||
{"relativeSpeed", risk.relativeSpeed},
|
||||
{"warningLevel", getRiskLevelString(risk.level)},
|
||||
{"timestamp", std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count()}
|
||||
};
|
||||
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count()} };
|
||||
|
||||
// 广播冲突预警消息
|
||||
ws_server_->broadcast(j.dump());
|
||||
Logger::debug("广播冲突预警: id1=", risk.id1, " id2=", risk.id2,
|
||||
" distance=", risk.distance, "m",
|
||||
" relativeSpeed=", risk.relativeSpeed, "m/s",
|
||||
" level=", getRiskLevelString(risk.level));
|
||||
" distance=", risk.distance, "m",
|
||||
" relativeSpeed=", risk.relativeSpeed, "m/s",
|
||||
" level=", getRiskLevelString(risk.level));
|
||||
}
|
||||
|
||||
void System::broadcastTimeoutWarning(const network::TimeoutWarningMessage& warning) {
|
||||
if (ws_server_) {
|
||||
ws_server_->broadcast(warning.toJson().dump());
|
||||
|
||||
|
||||
// 根据超时时间记录不同级的日志
|
||||
if (warning.elapsed_ms > 30000) { // 30秒以上
|
||||
if (warning.elapsed_ms > 30000) { // 30秒以上
|
||||
Logger::error("Severe timeout: ", warning.source, " - ",
|
||||
warning.elapsed_ms, "ms without response");
|
||||
warning.elapsed_ms, "ms without response");
|
||||
} else {
|
||||
Logger::warning("Connection timeout: ", warning.source, " - ",
|
||||
warning.elapsed_ms, "ms without response");
|
||||
warning.elapsed_ms, "ms without response");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -557,34 +548,46 @@ void System::broadcastVehicleCommand(const VehicleCommand& cmd) {
|
||||
if (!ws_server_) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
nlohmann::json j = {
|
||||
{"type", "vehicle_command"},
|
||||
{"vehicleId", cmd.vehicleId},
|
||||
{"commandType", [&]() {
|
||||
switch (cmd.type) {
|
||||
case CommandType::SIGNAL: return "SIGNAL";
|
||||
case CommandType::ALERT: return "ALERT";
|
||||
case CommandType::WARNING: return "WARNING";
|
||||
case CommandType::RESUME: return "RESUME";
|
||||
case CommandType::PARKING: return "PARKING";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}()},
|
||||
switch (cmd.type) {
|
||||
case CommandType::SIGNAL:
|
||||
return "SIGNAL";
|
||||
case CommandType::ALERT:
|
||||
return "ALERT";
|
||||
case CommandType::WARNING:
|
||||
return "WARNING";
|
||||
case CommandType::RESUME:
|
||||
return "RESUME";
|
||||
case CommandType::PARKING:
|
||||
return "PARKING";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}()},
|
||||
{"reason", [&]() {
|
||||
switch (cmd.reason) {
|
||||
case CommandReason::TRAFFIC_LIGHT: return "TRAFFIC_LIGHT";
|
||||
case CommandReason::AIRCRAFT_CROSSING: return "AIRCRAFT_CROSSING";
|
||||
case CommandReason::SPECIAL_VEHICLE: return "SPECIAL_VEHICLE";
|
||||
case CommandReason::AIRCRAFT_PUSH: return "AIRCRAFT_PUSH";
|
||||
case CommandReason::RESUME_TRAFFIC: return "RESUME_TRAFFIC";
|
||||
case CommandReason::PARKING_SIDE: return "PARKING_SIDE";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}()},
|
||||
{"timestamp", cmd.timestamp}
|
||||
};
|
||||
|
||||
switch (cmd.reason) {
|
||||
case CommandReason::TRAFFIC_LIGHT:
|
||||
return "TRAFFIC_LIGHT";
|
||||
case CommandReason::AIRCRAFT_CROSSING:
|
||||
return "AIRCRAFT_CROSSING";
|
||||
case CommandReason::SPECIAL_VEHICLE:
|
||||
return "SPECIAL_VEHICLE";
|
||||
case CommandReason::AIRCRAFT_PUSH:
|
||||
return "AIRCRAFT_PUSH";
|
||||
case CommandReason::RESUME_TRAFFIC:
|
||||
return "RESUME_TRAFFIC";
|
||||
case CommandReason::PARKING_SIDE:
|
||||
return "PARKING_SIDE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}()},
|
||||
{"timestamp", cmd.timestamp} };
|
||||
|
||||
// 添加可选字段
|
||||
if (cmd.type == CommandType::SIGNAL) {
|
||||
j["signalState"] = cmd.signalState == SignalState::RED ? "RED" : "GREEN";
|
||||
@ -596,7 +599,7 @@ void System::broadcastVehicleCommand(const VehicleCommand& cmd) {
|
||||
j["targetLatitude"] = cmd.latitude;
|
||||
j["targetLongitude"] = cmd.longitude;
|
||||
}
|
||||
|
||||
|
||||
ws_server_->broadcast(j.dump());
|
||||
}
|
||||
|
||||
@ -608,21 +611,21 @@ const MovingObject* System::findVehicle(const std::string& vehicleId) const {
|
||||
// 获取数据引用(注意:DataCollector 内部会加锁)
|
||||
const auto& aircrafts = dataCollector_->getAircraftRef();
|
||||
const auto& vehicles = dataCollector_->getVehicleRef();
|
||||
|
||||
|
||||
// 在航空器中查找
|
||||
for (const auto& aircraft : aircrafts) {
|
||||
if (aircraft.flightNo == vehicleId) {
|
||||
return &aircraft;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 在车辆中查找
|
||||
for (const auto& vehicle : vehicles) {
|
||||
if (vehicle.vehicleNo == vehicleId) {
|
||||
return &vehicle;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -630,18 +633,17 @@ void System::updateSafetyZoneStates(const std::vector<MovingObject*>& objects) {
|
||||
// 重置所有安全区状态为未激活,同时重置类型
|
||||
for (auto& [id, zone] : safetyZones_) {
|
||||
zone->setState(SafetyZoneState::INACTIVE);
|
||||
zone->resetType(); // 重置安全区类型
|
||||
zone->resetType(); // 重置安全区类型
|
||||
}
|
||||
|
||||
|
||||
// 检查所有移动物体
|
||||
for (const auto& obj : objects) {
|
||||
Logger::debug("检查移动物体: id=", obj->id,
|
||||
", type=", obj->type == MovingObjectType::UNMANNED ? "无人车" :
|
||||
(obj->type == MovingObjectType::SPECIAL ? "特勤车" : "飞机"),
|
||||
", isAircraft=", obj->isAircraft(),
|
||||
", isSpecialVehicle=", obj->isSpecialVehicle(),
|
||||
", isUnmannedVehicle=", obj->isUnmannedVehicle());
|
||||
|
||||
Logger::debug("检查移动物体: id=", obj->id,
|
||||
", type=", obj->type == MovingObjectType::UNMANNED ? "无人车" : (obj->type == MovingObjectType::SPECIAL ? "特勤车" : "飞机"),
|
||||
", isAircraft=", obj->isAircraft(),
|
||||
", isSpecialVehicle=", obj->isSpecialVehicle(),
|
||||
", isUnmannedVehicle=", obj->isUnmannedVehicle());
|
||||
|
||||
// 只检查飞机和特勤车
|
||||
if (obj->isAircraft() || obj->isSpecialVehicle()) {
|
||||
checkSafetyZoneIntrusion(*obj);
|
||||
@ -650,32 +652,32 @@ void System::updateSafetyZoneStates(const std::vector<MovingObject*>& objects) {
|
||||
}
|
||||
|
||||
void System::checkSafetyZoneIntrusion(const MovingObject& obj) {
|
||||
Logger::debug("检查安全区入侵: id=", obj.id,
|
||||
", type=", obj.isAircraft() ? "飞机" : (obj.isSpecialVehicle() ? "特勤车" : "其他"),
|
||||
", position=(", obj.position.x, ",", obj.position.y, ")");
|
||||
|
||||
Logger::debug("检查安全区入侵: id=", obj.id,
|
||||
", type=", obj.isAircraft() ? "飞机" : (obj.isSpecialVehicle() ? "特勤车" : "其他"),
|
||||
", position=(", obj.position.x, ",", obj.position.y, ")");
|
||||
|
||||
// 检查每个安全区
|
||||
for (auto& [id, zone] : safetyZones_) {
|
||||
// 先检查是否在安全区内
|
||||
if (zone->isObjectInZone(obj)) {
|
||||
// 如果在区内,尝试激活安全区
|
||||
if (zone->tryActivate(obj)) {
|
||||
Logger::debug("目标 ", obj.id, " 进入路口 ", id, " 安全区, 类型: ",
|
||||
zone->getType() == SafetyZoneType::AIRCRAFT ? "飞机" : "特勤车",
|
||||
", 半径: ", zone->getCurrentRadius(),
|
||||
", 中心点=(", zone->getCenter().x, ",", zone->getCenter().y, ")");
|
||||
Logger::debug("目标 ", obj.id, " 进入路口 ", id, " 安全区, 类型: ",
|
||||
zone->getType() == SafetyZoneType::AIRCRAFT ? "飞机" : "特勤车",
|
||||
", 半径: ", zone->getCurrentRadius(),
|
||||
", 中心点=(", zone->getCenter().x, ",", zone->getCenter().y, ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool System::handleSafetyZoneRisk(const Vehicle& vehicle,
|
||||
const SafetyZone* zone,
|
||||
const std::vector<MovingObject*>& objects,
|
||||
double distance,
|
||||
const std::string& intersectionId,
|
||||
CommandType cmdType,
|
||||
const std::string& riskLevel) {
|
||||
bool System::handleSafetyZoneRisk(const Vehicle& vehicle,
|
||||
const SafetyZone* zone,
|
||||
const std::vector<MovingObject*>& objects,
|
||||
double distance,
|
||||
const std::string& intersectionId,
|
||||
CommandType cmdType,
|
||||
const std::string& riskLevel) {
|
||||
// 检查是否为无人车
|
||||
if (!controllableVehicles_.isControllable(vehicle.id)) {
|
||||
return false;
|
||||
@ -684,46 +686,44 @@ bool System::handleSafetyZoneRisk(const Vehicle& vehicle,
|
||||
VehicleCommand cmd;
|
||||
cmd.vehicleId = vehicle.id;
|
||||
cmd.type = cmdType;
|
||||
cmd.reason = zone->getType() == SafetyZoneType::AIRCRAFT ?
|
||||
CommandReason::AIRCRAFT_CROSSING :
|
||||
CommandReason::SPECIAL_VEHICLE;
|
||||
cmd.reason = zone->getType() == SafetyZoneType::AIRCRAFT ? CommandReason::AIRCRAFT_CROSSING : CommandReason::SPECIAL_VEHICLE;
|
||||
cmd.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
|
||||
// 查找触发安全区的目标物体
|
||||
const MovingObject* target = nullptr;
|
||||
for (const auto& obj : objects) {
|
||||
if ((obj->isAircraft() || obj->isSpecialVehicle()) &&
|
||||
if ((obj->isAircraft() || obj->isSpecialVehicle()) &&
|
||||
zone->isObjectInZone(*obj)) {
|
||||
target = obj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果找到目标物体,添加相关信息
|
||||
if (target) {
|
||||
cmd.latitude = target->geo.latitude;
|
||||
cmd.longitude = target->geo.longitude;
|
||||
|
||||
|
||||
// 计算相对距离
|
||||
double rel_dx = target->position.x - vehicle.position.x;
|
||||
double rel_dy = target->position.y - vehicle.position.y;
|
||||
|
||||
|
||||
// 计算相对速度
|
||||
cmd.relativeSpeed = std::sqrt(
|
||||
(target->position.x - vehicle.position.x) * (target->position.x - vehicle.position.x) +
|
||||
(target->position.y - vehicle.position.y) * (target->position.y - vehicle.position.y)
|
||||
);
|
||||
(target->position.y - vehicle.position.y) * (target->position.y - vehicle.position.y));
|
||||
cmd.relativeMotionX = rel_dx;
|
||||
cmd.relativeMotionY = rel_dy;
|
||||
cmd.minDistance = distance;
|
||||
|
||||
|
||||
Logger::debug("安全区冲突目标信息: id=", target->id,
|
||||
", 相对距离=(", rel_dx, ",", rel_dy, ")",
|
||||
", 相对速度=", cmd.relativeSpeed,
|
||||
", 最小距离=", distance);
|
||||
", 相对距离=(", rel_dx, ",", rel_dy, ")",
|
||||
", 相对速度=", cmd.relativeSpeed,
|
||||
", 最小距离=", distance);
|
||||
}
|
||||
|
||||
|
||||
broadcastVehicleCommand(cmd);
|
||||
controllableVehicles_.sendCommand(vehicle.id, cmd);
|
||||
|
||||
@ -733,10 +733,10 @@ bool System::handleSafetyZoneRisk(const Vehicle& vehicle,
|
||||
risk.level = cmdType == CommandType::ALERT ? RiskLevel::CRITICAL : RiskLevel::WARNING;
|
||||
risk.distance = distance;
|
||||
risk.relativeSpeed = cmd.relativeSpeed;
|
||||
risk.relativeMotion = {cmd.relativeMotionX, cmd.relativeMotionY};
|
||||
risk.relativeMotion = { cmd.relativeMotionX, cmd.relativeMotionY };
|
||||
risk.zoneType = WarningZoneType::WARNING;
|
||||
|
||||
broadcastCollisionWarning(risk);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -10,10 +10,10 @@ SafetyZone::SafetyZone(const Vector2D& center, double aircraftRadius, double spe
|
||||
, currentRadius_(0.0)
|
||||
, state_(SafetyZoneState::INACTIVE)
|
||||
, type_(SafetyZoneType::NONE) {
|
||||
|
||||
Logger::debug("创建安全区: center=(", center_.x, ",", center_.y,
|
||||
"), aircraftRadius=", aircraftRadius_,
|
||||
", specialVehicleRadius=", specialVehicleRadius_);
|
||||
|
||||
Logger::debug("创建安全区: center=(", center_.x, ",", center_.y,
|
||||
"), aircraftRadius=", aircraftRadius_,
|
||||
", specialVehicleRadius=", specialVehicleRadius_);
|
||||
}
|
||||
|
||||
bool SafetyZone::isInZone(const Vector2D& point) const {
|
||||
@ -21,12 +21,12 @@ bool SafetyZone::isInZone(const Vector2D& point) const {
|
||||
if (type_ == SafetyZoneType::NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 计算点到中心的距离
|
||||
double dx = point.x - center_.x;
|
||||
double dy = point.y - center_.y;
|
||||
double distance = std::sqrt(dx * dx + dy * dy);
|
||||
|
||||
|
||||
// 使用当前设置的安全区半径
|
||||
return distance <= currentRadius_;
|
||||
}
|
||||
@ -56,20 +56,20 @@ bool SafetyZone::isTypeMatched(const MovingObject& object) const {
|
||||
if (type_ == SafetyZoneType::NONE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 如果是无人车,返回 true
|
||||
if (object.isUnmannedVehicle()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 如果安全区类型已设置,检查是否匹配
|
||||
if ((type_ == SafetyZoneType::AIRCRAFT && !object.isAircraft()) ||
|
||||
(type_ == SafetyZoneType::VEHICLE && !object.isSpecialVehicle())) {
|
||||
Logger::debug("目标 ", object.id, " 类型与安全区类型不匹配,当前安全区类型: ",
|
||||
type_ == SafetyZoneType::AIRCRAFT ? "飞机" : "特勤车");
|
||||
Logger::debug("目标 ", object.id, " 类型与安全区类型不匹配,当前安全区类型: ",
|
||||
type_ == SafetyZoneType::AIRCRAFT ? "飞机" : "特勤车");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -78,20 +78,20 @@ bool SafetyZone::isObjectInZone(const MovingObject& object) const {
|
||||
double dx = object.position.x - center_.x;
|
||||
double dy = object.position.y - center_.y;
|
||||
double distance = std::sqrt(dx * dx + dy * dy);
|
||||
|
||||
|
||||
// 如果安全区类型未设置,使用对应的临时半径进行检查
|
||||
double checkRadius = (type_ == SafetyZoneType::NONE)
|
||||
double checkRadius = (type_ == SafetyZoneType::NONE)
|
||||
? (object.isAircraft() ? aircraftRadius_ : specialVehicleRadius_)
|
||||
: currentRadius_;
|
||||
|
||||
|
||||
Logger::debug("检查目标 ", object.id, " 是否在安全区内: ",
|
||||
"dx=", dx, ", dy=", dy, ", distance=", distance,
|
||||
", checkRadius=", checkRadius,
|
||||
", type=", type_ == SafetyZoneType::NONE ? "NONE" :
|
||||
(type_ == SafetyZoneType::AIRCRAFT ? "AIRCRAFT" : "VEHICLE"),
|
||||
", center=(", center_.x, ",", center_.y, ")",
|
||||
", object=(", object.position.x, ",", object.position.y, ")");
|
||||
|
||||
"dx=", dx, ", dy=", dy, ", distance=", distance,
|
||||
", checkRadius=", checkRadius,
|
||||
", type=", type_ == SafetyZoneType::NONE ? "NONE" :
|
||||
(type_ == SafetyZoneType::AIRCRAFT ? "AIRCRAFT" : "VEHICLE"),
|
||||
", center=(", center_.x, ",", center_.y, ")",
|
||||
", object=(", object.position.x, ",", object.position.y, ")");
|
||||
|
||||
return distance <= checkRadius;
|
||||
}
|
||||
|
||||
@ -101,25 +101,25 @@ bool SafetyZone::tryActivate(const MovingObject& object) {
|
||||
Logger::debug("目标 ", object.id, " 是无人车,不激活安全区");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 如果类型已设置且不匹配,不激活
|
||||
if (!isTypeMatched(object)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 检查是否在区内
|
||||
bool inZone = isObjectInZone(object);
|
||||
if (!inZone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 如果安全区类型未设置,设置新的类型和半径
|
||||
if (type_ == SafetyZoneType::NONE) {
|
||||
type_ = object.isAircraft() ? SafetyZoneType::AIRCRAFT : SafetyZoneType::VEHICLE;
|
||||
currentRadius_ = object.isAircraft() ? aircraftRadius_ : specialVehicleRadius_;
|
||||
Logger::debug("安全区设置为", object.isAircraft() ? "飞机" : "特勤车", "类型,半径: ", currentRadius_);
|
||||
}
|
||||
|
||||
|
||||
// 激活安全区
|
||||
state_ = SafetyZoneState::ACTIVE;
|
||||
return true;
|
||||
|
||||
@ -5,15 +5,14 @@
|
||||
#include <chrono>
|
||||
|
||||
TrafficLightDetector::TrafficLightDetector(const IntersectionConfig& intersectionConfig,
|
||||
ControllableVehicles& controllableVehicles,
|
||||
System& system)
|
||||
ControllableVehicles& controllableVehicles,
|
||||
System& system)
|
||||
: intersection_config_(intersectionConfig)
|
||||
, controllable_vehicles_(controllableVehicles)
|
||||
, system_(system) {
|
||||
}
|
||||
, system_(system) {}
|
||||
|
||||
void TrafficLightDetector::processSignal(const TrafficLightSignal& signal,
|
||||
const std::vector<Vehicle>& vehicles) {
|
||||
void TrafficLightDetector::processSignal(const TrafficLightSignal& signal,
|
||||
const std::vector<Vehicle>& vehicles) {
|
||||
// 根据红绿灯ID查找对应的路口
|
||||
const Intersection* intersection = intersection_config_.findByTrafficLightId(signal.trafficLightId);
|
||||
if (!intersection) {
|
||||
@ -29,18 +28,18 @@ void TrafficLightDetector::processSignal(const TrafficLightSignal& signal,
|
||||
if (controllable_vehicles_.isControllable(vehicle.vehicleNo)) {
|
||||
// 根据信号灯状态发送指令
|
||||
switch (signal.status) {
|
||||
case SignalStatus::RED:
|
||||
sendSignalCommand(vehicle.vehicleNo, SignalState::RED);
|
||||
break;
|
||||
case SignalStatus::GREEN:
|
||||
sendSignalCommand(vehicle.vehicleNo, SignalState::GREEN);
|
||||
break;
|
||||
case SignalStatus::YELLOW:
|
||||
sendSignalCommand(vehicle.vehicleNo, SignalState::YELLOW);
|
||||
break;
|
||||
default:
|
||||
Logger::warning("未知的信号灯状态");
|
||||
break;
|
||||
case SignalStatus::RED:
|
||||
sendSignalCommand(vehicle.vehicleNo, SignalState::RED);
|
||||
break;
|
||||
case SignalStatus::GREEN:
|
||||
sendSignalCommand(vehicle.vehicleNo, SignalState::GREEN);
|
||||
break;
|
||||
case SignalStatus::YELLOW:
|
||||
sendSignalCommand(vehicle.vehicleNo, SignalState::YELLOW);
|
||||
break;
|
||||
default:
|
||||
Logger::warning("未知的信号灯状态");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,7 +64,7 @@ void TrafficLightDetector::sendSignalCommand(const std::string& vehicleNo, Signa
|
||||
|
||||
controllable_vehicles_.sendCommand(vehicleNo, cmd);
|
||||
system_.broadcastVehicleCommand(cmd);
|
||||
Logger::debug("发送信号灯指令到车辆: ", vehicleNo,
|
||||
" 路口: ", cmd.intersectionId,
|
||||
" 状态: ", state == SignalState::RED ? "红灯" : state == SignalState::GREEN ? "绿灯" : "黄灯");
|
||||
Logger::debug("发送信号灯指令到车辆: ", vehicleNo,
|
||||
" 路口: ", cmd.intersectionId,
|
||||
" 状态: ", state == SignalState::RED ? "红灯" : state == SignalState::GREEN ? "绿灯" : "黄灯");
|
||||
}
|
||||
@ -3,8 +3,8 @@
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
std::string getSignalStateString(SignalState state) {
|
||||
switch (state) {
|
||||
std::string getSignalStateString(SignalState state) {
|
||||
switch (state) {
|
||||
case SignalState::RED:
|
||||
return "RED";
|
||||
case SignalState::YELLOW:
|
||||
@ -13,8 +13,8 @@ std::string getSignalStateString(SignalState state) {
|
||||
return "GREEN";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
HTTPClient::HTTPClient() {
|
||||
@ -116,14 +116,14 @@ bool HTTPClient::sendCommand(const std::string& ip, int port, const VehicleComma
|
||||
// 检查响应状态码
|
||||
long http_code = 0;
|
||||
curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &http_code);
|
||||
|
||||
|
||||
if (http_code == 200) {
|
||||
try {
|
||||
// 解析响应
|
||||
nlohmann::json response = nlohmann::json::parse(response_buffer_);
|
||||
int code = response["code"].get<int>();
|
||||
std::string msg = response["msg"].get<std::string>();
|
||||
|
||||
|
||||
if (code == 200) {
|
||||
Logger::debug("Command sent successfully: ", request_body);
|
||||
return true;
|
||||
|
||||
@ -59,37 +59,36 @@ bool HTTPDataSource::tryReconnect() {
|
||||
if (is_reconnecting_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
is_reconnecting_ = true;
|
||||
|
||||
|
||||
try {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_connect_attempt_).count();
|
||||
|
||||
|
||||
if (elapsed < config_.position.timeout_ms) {
|
||||
is_reconnecting_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
last_connect_attempt_ = now;
|
||||
|
||||
|
||||
for (int retry = 0; retry < MAX_RETRIES; ++retry) {
|
||||
if (connect()) {
|
||||
is_reconnecting_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (retry < MAX_RETRIES - 1) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(config_.position.timeout_ms/3));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(config_.position.timeout_ms / 3));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Logger::error("Failed to reconnect after ", MAX_RETRIES, " attempts");
|
||||
is_reconnecting_ = false;
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("Error during reconnection: ", e.what());
|
||||
is_reconnecting_ = false;
|
||||
return false;
|
||||
@ -100,11 +99,11 @@ bool HTTPDataSource::checkConnectionHealth() {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
now - last_health_check_).count();
|
||||
|
||||
|
||||
if (elapsed < HEALTH_CHECK_INTERVAL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
last_health_check_ = now;
|
||||
return curl_ != nullptr;
|
||||
}
|
||||
@ -113,17 +112,17 @@ bool HTTPDataSource::ensureConnected() {
|
||||
if (isAvailable() && checkConnectionHealth()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return tryReconnect();
|
||||
}
|
||||
|
||||
bool HTTPDataSource::sendRequest(const std::string& url, const AuthState* auth_state,
|
||||
std::string& response, HttpMethod method, const std::string& body) {
|
||||
std::string& response, HttpMethod method, const std::string& body) {
|
||||
if (!curl_) {
|
||||
Logger::error("CURL not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
curl_easy_reset(curl_);
|
||||
curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
@ -131,7 +130,7 @@ bool HTTPDataSource::sendRequest(const std::string& url, const AuthState* auth_s
|
||||
curl_easy_setopt(curl_, CURLOPT_TIMEOUT, config_.position.timeout_ms / 1000);
|
||||
curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT, config_.position.timeout_ms / 1000);
|
||||
curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L);
|
||||
|
||||
|
||||
if (method == HttpMethod::GET) {
|
||||
curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1L);
|
||||
curl_easy_setopt(curl_, CURLOPT_POST, 0L);
|
||||
@ -144,64 +143,64 @@ bool HTTPDataSource::sendRequest(const std::string& url, const AuthState* auth_s
|
||||
curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct curl_slist* headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Accept: application/json");
|
||||
headers = curl_slist_append(headers, "Cache-Control: no-cache");
|
||||
headers = curl_slist_append(headers, "Expect:");
|
||||
|
||||
|
||||
if (method == HttpMethod::POST) {
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
}
|
||||
|
||||
|
||||
if (auth_state && auth_state->is_authenticated && !auth_state->token.empty()) {
|
||||
std::string auth_header = "Authorization: " + auth_state->token;
|
||||
headers = curl_slist_append(headers, auth_header.c_str());
|
||||
}
|
||||
|
||||
|
||||
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
|
||||
response.clear();
|
||||
CURLcode res = curl_easy_perform(curl_);
|
||||
curl_slist_free_all(headers);
|
||||
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
Logger::error("请求失败: ", curl_easy_strerror(res));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
long http_code = 0;
|
||||
curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &http_code);
|
||||
|
||||
|
||||
if (http_code != 200) {
|
||||
Logger::error("HTTP 请求失败,状态码: ", http_code);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HTTPDataSource::fetchPositionAircraftData(std::vector<Aircraft>& aircraft) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
|
||||
if (!ensureConnected()) {
|
||||
Logger::error("连接失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ensureAuthenticated(config_.position.auth,
|
||||
config_.position.host,
|
||||
config_.position.port,
|
||||
position_auth_,
|
||||
DataSourceType::POSITION)) {
|
||||
|
||||
if (!ensureAuthenticated(config_.position.auth,
|
||||
config_.position.host,
|
||||
config_.position.port,
|
||||
position_auth_,
|
||||
DataSourceType::POSITION)) {
|
||||
Logger::error("认证失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string url = "http://" + config_.position.host + ":" +
|
||||
std::to_string(config_.position.port) +
|
||||
config_.position.aircraft_path;
|
||||
|
||||
|
||||
std::string url = "http://" + config_.position.host + ":" +
|
||||
std::to_string(config_.position.port) +
|
||||
config_.position.aircraft_path;
|
||||
|
||||
std::string response;
|
||||
if (!sendRequest(url, &position_auth_, response, HttpMethod::GET)) {
|
||||
Logger::error("请求失败");
|
||||
@ -213,23 +212,23 @@ bool HTTPDataSource::fetchPositionAircraftData(std::vector<Aircraft>& aircraft)
|
||||
|
||||
bool HTTPDataSource::fetchPositionVehicleData(std::vector<Vehicle>& vehicles) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
|
||||
if (!ensureConnected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ensureAuthenticated(config_.position.auth,
|
||||
config_.position.host,
|
||||
config_.position.port,
|
||||
position_auth_,
|
||||
DataSourceType::POSITION)) {
|
||||
|
||||
if (!ensureAuthenticated(config_.position.auth,
|
||||
config_.position.host,
|
||||
config_.position.port,
|
||||
position_auth_,
|
||||
DataSourceType::POSITION)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string url = "http://" + config_.position.host + ":" +
|
||||
std::to_string(config_.position.port) +
|
||||
config_.position.vehicle_path;
|
||||
|
||||
|
||||
std::string url = "http://" + config_.position.host + ":" +
|
||||
std::to_string(config_.position.port) +
|
||||
config_.position.vehicle_path;
|
||||
|
||||
std::string response;
|
||||
if (!sendRequest(url, &position_auth_, response, HttpMethod::GET)) {
|
||||
return false;
|
||||
@ -240,23 +239,23 @@ bool HTTPDataSource::fetchPositionVehicleData(std::vector<Vehicle>& vehicles) {
|
||||
|
||||
bool HTTPDataSource::fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
|
||||
if (!ensureConnected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ensureAuthenticated(config_.traffic_light.auth,
|
||||
config_.traffic_light.host,
|
||||
config_.traffic_light.port,
|
||||
traffic_light_auth_,
|
||||
DataSourceType::TRAFFIC_LIGHT)) {
|
||||
|
||||
if (!ensureAuthenticated(config_.traffic_light.auth,
|
||||
config_.traffic_light.host,
|
||||
config_.traffic_light.port,
|
||||
traffic_light_auth_,
|
||||
DataSourceType::TRAFFIC_LIGHT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string url = "http://" + config_.traffic_light.host + ":" +
|
||||
std::to_string(config_.traffic_light.port) +
|
||||
config_.traffic_light.signal_path;
|
||||
|
||||
|
||||
std::string url = "http://" + config_.traffic_light.host + ":" +
|
||||
std::to_string(config_.traffic_light.port) +
|
||||
config_.traffic_light.signal_path;
|
||||
|
||||
std::string response;
|
||||
if (!sendRequest(url, &traffic_light_auth_, response, HttpMethod::GET)) {
|
||||
return false;
|
||||
@ -267,39 +266,39 @@ bool HTTPDataSource::fetchTrafficLightSignals(std::vector<TrafficLightSignal>& s
|
||||
|
||||
std::string getSignalStateString(SignalState state) {
|
||||
switch (state) {
|
||||
case SignalState::RED:
|
||||
return "RED";
|
||||
case SignalState::YELLOW:
|
||||
return "YELLOW";
|
||||
case SignalState::GREEN:
|
||||
return "GREEN";
|
||||
default:
|
||||
return "GREEN"; // 默认为绿灯
|
||||
case SignalState::RED:
|
||||
return "RED";
|
||||
case SignalState::YELLOW:
|
||||
return "YELLOW";
|
||||
case SignalState::GREEN:
|
||||
return "GREEN";
|
||||
default:
|
||||
return "GREEN"; // 默认为绿灯
|
||||
}
|
||||
}
|
||||
|
||||
bool HTTPDataSource::sendUnmannedVehicleCommand(const std::string& vehicle_id, const VehicleCommand& command) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
|
||||
if (!ensureConnected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ensureAuthenticated(config_.vehicle.auth,
|
||||
config_.vehicle.host,
|
||||
config_.vehicle.port,
|
||||
unmanned_vehicle_auth_,
|
||||
DataSourceType::UNMANNED)) {
|
||||
|
||||
if (!ensureAuthenticated(config_.vehicle.auth,
|
||||
config_.vehicle.host,
|
||||
config_.vehicle.port,
|
||||
unmanned_vehicle_auth_,
|
||||
DataSourceType::UNMANNED)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string url = "http://" + config_.vehicle.host + ":" +
|
||||
std::to_string(config_.vehicle.port) +
|
||||
config_.vehicle.command_path;
|
||||
|
||||
|
||||
std::string url = "http://" + config_.vehicle.host + ":" +
|
||||
std::to_string(config_.vehicle.port) +
|
||||
config_.vehicle.command_path;
|
||||
|
||||
std::stringstream transId;
|
||||
transId << std::hex << std::chrono::system_clock::now().time_since_epoch().count();
|
||||
|
||||
|
||||
nlohmann::json request = {
|
||||
{"transId", transId.str()},
|
||||
{"timestamp", command.timestamp},
|
||||
@ -335,27 +334,26 @@ bool HTTPDataSource::sendUnmannedVehicleCommand(const std::string& vehicle_id, c
|
||||
request["relativeMotionY"] = command.relativeMotionY;
|
||||
request["minDistance"] = command.minDistance;
|
||||
}
|
||||
|
||||
|
||||
std::string response;
|
||||
if (!sendRequest(url, &unmanned_vehicle_auth_, response, HttpMethod::POST, request.dump())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
nlohmann::json j = nlohmann::json::parse(response);
|
||||
if (!j.contains("code") || !j.contains("msg")) {
|
||||
Logger::error("Invalid command response format");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (j["code"].get<int>() != 200) {
|
||||
Logger::error("Command failed: ", j["msg"].get<std::string>());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("Failed to parse command response: ", e.what());
|
||||
return false;
|
||||
}
|
||||
@ -363,73 +361,72 @@ bool HTTPDataSource::sendUnmannedVehicleCommand(const std::string& vehicle_id, c
|
||||
|
||||
bool HTTPDataSource::fetchUnmannedVehicleStatus(const std::string& vehicle_id, std::string& status) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
|
||||
if (!ensureConnected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ensureAuthenticated(config_.vehicle.auth,
|
||||
config_.vehicle.host,
|
||||
config_.vehicle.port,
|
||||
unmanned_vehicle_auth_,
|
||||
DataSourceType::UNMANNED)) {
|
||||
|
||||
if (!ensureAuthenticated(config_.vehicle.auth,
|
||||
config_.vehicle.host,
|
||||
config_.vehicle.port,
|
||||
unmanned_vehicle_auth_,
|
||||
DataSourceType::UNMANNED)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string url = "http://" + config_.vehicle.host + ":" +
|
||||
std::to_string(config_.vehicle.port) +
|
||||
config_.vehicle.status_path + "?vehicleId=" + vehicle_id;
|
||||
|
||||
|
||||
std::string url = "http://" + config_.vehicle.host + ":" +
|
||||
std::to_string(config_.vehicle.port) +
|
||||
config_.vehicle.status_path + "?vehicleId=" + vehicle_id;
|
||||
|
||||
std::string response;
|
||||
if (!sendRequest(url, &unmanned_vehicle_auth_, response, HttpMethod::GET)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
nlohmann::json j = nlohmann::json::parse(response);
|
||||
if (!j.contains("status") || !j.contains("data")) {
|
||||
Logger::error("Invalid vehicle status response format");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (j["status"].get<int>() != 200) {
|
||||
Logger::error("Failed to get vehicle status");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
status = j["data"].dump();
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("Failed to parse vehicle status response: ", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool HTTPDataSource::authenticatePosition(const AuthConfig& auth_config,
|
||||
bool HTTPDataSource::authenticatePosition(const AuthConfig& auth_config,
|
||||
const std::string& host, int port, AuthState& auth_state) {
|
||||
if (!auth_config.auth_required) {
|
||||
auth_state.is_authenticated = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::lock_guard<std::mutex> lock(auth_mutex_);
|
||||
|
||||
|
||||
if (!curl_) {
|
||||
Logger::error("CURL not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string url = "http://" + host + ":" + std::to_string(port) +
|
||||
auth_config.auth_path + "?username=" + auth_config.username +
|
||||
"&password=" + auth_config.password;
|
||||
|
||||
|
||||
std::string url = "http://" + host + ":" + std::to_string(port) +
|
||||
auth_config.auth_path + "?username=" + auth_config.username +
|
||||
"&password=" + auth_config.password;
|
||||
|
||||
std::string response;
|
||||
if (!sendRequest(url, nullptr, response, HttpMethod::POST, "")) {
|
||||
Logger::error("认证请求失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
nlohmann::json j = nlohmann::json::parse(response);
|
||||
if (!j.contains("status") || !j.contains("msg") || !j.contains("data")) {
|
||||
@ -449,46 +446,45 @@ bool HTTPDataSource::authenticatePosition(const AuthConfig& auth_config,
|
||||
auth_state.token = token;
|
||||
auth_state.is_authenticated = true;
|
||||
auth_state.last_auth_time = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("解析认证响应失败: ", e.what(), ", 响应内容: ", response);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool HTTPDataSource::authenticateUnmanned(const AuthConfig& auth_config,
|
||||
bool HTTPDataSource::authenticateUnmanned(const AuthConfig& auth_config,
|
||||
const std::string& host, int port, AuthState& auth_state) {
|
||||
auth_state.is_authenticated = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HTTPDataSource::authenticateTrafficLight(const AuthConfig& auth_config,
|
||||
bool HTTPDataSource::authenticateTrafficLight(const AuthConfig& auth_config,
|
||||
const std::string& host, int port, AuthState& auth_state) {
|
||||
auth_state.is_authenticated = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HTTPDataSource::ensureAuthenticated(const AuthConfig& auth_config, const std::string& host,
|
||||
int port, AuthState& auth_state, DataSourceType type) {
|
||||
int port, AuthState& auth_state, DataSourceType type) {
|
||||
if (!auth_config.auth_required) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (!auth_state.is_authenticated) {
|
||||
switch(type) {
|
||||
case DataSourceType::POSITION:
|
||||
return authenticatePosition(auth_config, host, port, auth_state);
|
||||
case DataSourceType::UNMANNED:
|
||||
return authenticateUnmanned(auth_config, host, port, auth_state);
|
||||
case DataSourceType::TRAFFIC_LIGHT:
|
||||
return authenticateTrafficLight(auth_config, host, port, auth_state);
|
||||
default:
|
||||
return false;
|
||||
switch (type) {
|
||||
case DataSourceType::POSITION:
|
||||
return authenticatePosition(auth_config, host, port, auth_state);
|
||||
case DataSourceType::UNMANNED:
|
||||
return authenticateUnmanned(auth_config, host, port, auth_state);
|
||||
case DataSourceType::TRAFFIC_LIGHT:
|
||||
return authenticateTrafficLight(auth_config, host, port, auth_state);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -499,12 +495,12 @@ bool HTTPDataSource::parsePositionAircraftResponse(const std::string& response,
|
||||
Logger::error("Invalid aircraft response format");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (j["status"].get<int>() != 200) {
|
||||
Logger::error("Failed to get aircraft data: ", j["msg"].get<std::string>());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const auto& data = j["data"];
|
||||
aircraft.clear();
|
||||
for (const auto& item : data) {
|
||||
@ -514,7 +510,7 @@ bool HTTPDataSource::parsePositionAircraftResponse(const std::string& response,
|
||||
ac.geo.longitude = item["longitude"].get<double>();
|
||||
ac.geo.latitude = item["latitude"].get<double>();
|
||||
ac.timestamp = item["time"].get<uint64_t>();
|
||||
|
||||
|
||||
if (item.contains("altitude")) {
|
||||
ac.altitude = item["altitude"].get<double>();
|
||||
}
|
||||
@ -524,8 +520,7 @@ bool HTTPDataSource::parsePositionAircraftResponse(const std::string& response,
|
||||
aircraft.push_back(ac);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("Failed to parse aircraft response: ", e.what());
|
||||
return false;
|
||||
}
|
||||
@ -538,12 +533,12 @@ bool HTTPDataSource::parsePositionVehicleResponse(const std::string& response, s
|
||||
Logger::error("Invalid vehicle response format");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (j["status"].get<int>() != 200) {
|
||||
Logger::error("Failed to get vehicle data: ", j["msg"].get<std::string>());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const auto& data = j["data"];
|
||||
vehicles.clear();
|
||||
for (const auto& item : data) {
|
||||
@ -553,7 +548,7 @@ bool HTTPDataSource::parsePositionVehicleResponse(const std::string& response, s
|
||||
vehicle.geo.longitude = item["longitude"].get<double>();
|
||||
vehicle.geo.latitude = item["latitude"].get<double>();
|
||||
vehicle.timestamp = item["time"].get<uint64_t>();
|
||||
|
||||
|
||||
if (item.contains("direction")) {
|
||||
vehicle.heading = item["direction"].get<double>();
|
||||
}
|
||||
@ -563,8 +558,7 @@ bool HTTPDataSource::parsePositionVehicleResponse(const std::string& response, s
|
||||
vehicles.push_back(vehicle);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("Failed to parse vehicle response: ", e.what());
|
||||
return false;
|
||||
}
|
||||
@ -577,12 +571,12 @@ bool HTTPDataSource::parseTrafficLightResponse(const std::string& response, std:
|
||||
Logger::error("Invalid traffic light response format");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (j["status"].get<int>() != 200) {
|
||||
Logger::error("Failed to get traffic light data: ", j["msg"].get<std::string>());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const auto& data = j["data"];
|
||||
signals.clear();
|
||||
for (const auto& item : data) {
|
||||
@ -591,12 +585,12 @@ bool HTTPDataSource::parseTrafficLightResponse(const std::string& response, std:
|
||||
signal.status = [&]() {
|
||||
int state = item["state"].get<int>();
|
||||
switch (state) {
|
||||
case 0: return SignalStatus::RED;
|
||||
case 1: return SignalStatus::GREEN;
|
||||
case 2: return SignalStatus::YELLOW;
|
||||
default: return SignalStatus::UNKNOWN;
|
||||
case 0: return SignalStatus::RED;
|
||||
case 1: return SignalStatus::GREEN;
|
||||
case 2: return SignalStatus::YELLOW;
|
||||
default: return SignalStatus::UNKNOWN;
|
||||
}
|
||||
}();
|
||||
}();
|
||||
signal.timestamp = item["timestamp"].get<uint64_t>();
|
||||
signal.intersectionId = item["intersection"].get<std::string>();
|
||||
signal.latitude = item["position"]["latitude"].get<double>();
|
||||
@ -604,8 +598,7 @@ bool HTTPDataSource::parseTrafficLightResponse(const std::string& response, std:
|
||||
signals.push_back(signal);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
} catch (const std::exception& e) {
|
||||
Logger::error("Failed to parse traffic light response: ", e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -7,136 +7,135 @@
|
||||
|
||||
namespace network {
|
||||
|
||||
WebSocketServer::WebSocketServer(uint16_t port)
|
||||
: acceptor_(ioc_, {boost::asio::ip::tcp::v4(), port}) {
|
||||
}
|
||||
WebSocketServer::WebSocketServer(uint16_t port)
|
||||
: acceptor_(ioc_, { boost::asio::ip::tcp::v4(), port }) {}
|
||||
|
||||
WebSocketServer::~WebSocketServer() {
|
||||
// 关闭所有连接
|
||||
std::lock_guard<std::mutex> lock(sessions_mutex_);
|
||||
for (auto& session : sessions_) {
|
||||
try {
|
||||
session.lock()->close(boost::beast::websocket::close_code::normal);
|
||||
} catch (...) {
|
||||
// 忽略关闭时的错误
|
||||
}
|
||||
}
|
||||
sessions_.clear();
|
||||
|
||||
// 停止 io_context
|
||||
ioc_.stop();
|
||||
}
|
||||
|
||||
void WebSocketServer::start() {
|
||||
Logger::info("WebSocket 服务器启动,监听端口: ", acceptor_.local_endpoint().port());
|
||||
handleAccept();
|
||||
ioc_.run();
|
||||
}
|
||||
|
||||
void WebSocketServer::broadcast(const std::string& message) {
|
||||
std::lock_guard<std::mutex> lock(sessions_mutex_);
|
||||
auto it = sessions_.begin();
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
while (it != sessions_.end()) {
|
||||
if (auto session = it->lock()) { // 获取 shared_ptr
|
||||
WebSocketServer::~WebSocketServer() {
|
||||
// 关闭所有连接
|
||||
std::lock_guard<std::mutex> lock(sessions_mutex_);
|
||||
for (auto& session : sessions_) {
|
||||
try {
|
||||
session->write(boost::asio::buffer(message));
|
||||
++successCount;
|
||||
++it;
|
||||
session.lock()->close(boost::beast::websocket::close_code::normal);
|
||||
} catch (...) {
|
||||
Logger::warning("广播消息到客户端失败");
|
||||
++failCount;
|
||||
it = sessions_.erase(it); // 移除失败的会话
|
||||
// 忽略关闭时的错误
|
||||
}
|
||||
} else {
|
||||
Logger::debug("移除失效的会话");
|
||||
it = sessions_.erase(it); // 移除已失效的会话
|
||||
}
|
||||
sessions_.clear();
|
||||
|
||||
// 停止 io_context
|
||||
ioc_.stop();
|
||||
}
|
||||
|
||||
Logger::debug("广播消息完成: ", successCount, " 个成功, ", failCount, " 个失败, 当前共 ", sessions_.size(), " 个连接");
|
||||
}
|
||||
|
||||
void WebSocketServer::handleAccept() {
|
||||
acceptor_.async_accept(
|
||||
[this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket) {
|
||||
if (!ec) {
|
||||
Logger::info("接受新的 WebSocket 连接: ", socket.remote_endpoint().address().to_string());
|
||||
|
||||
// 创建新的 WebSocket 会话
|
||||
auto ws = std::make_shared<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>>(std::move(socket));
|
||||
|
||||
// 异步完成 WebSocket 握手
|
||||
ws->async_accept(
|
||||
[this, ws](boost::system::error_code ec) {
|
||||
if (!ec) {
|
||||
// 握手成功,保存会话并开始读取消息
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sessions_mutex_);
|
||||
sessions_.push_back(ws); // 存储 weak_ptr
|
||||
Logger::info("WebSocket 握手成功,当前连接数: ", sessions_.size());
|
||||
}
|
||||
doRead(ws);
|
||||
} else {
|
||||
Logger::error("WebSocket 握手失败: ", ec.message());
|
||||
}
|
||||
});
|
||||
void WebSocketServer::start() {
|
||||
Logger::info("WebSocket 服务器启动,监听端口: ", acceptor_.local_endpoint().port());
|
||||
handleAccept();
|
||||
ioc_.run();
|
||||
}
|
||||
|
||||
void WebSocketServer::broadcast(const std::string& message) {
|
||||
std::lock_guard<std::mutex> lock(sessions_mutex_);
|
||||
auto it = sessions_.begin();
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
while (it != sessions_.end()) {
|
||||
if (auto session = it->lock()) { // 获取 shared_ptr
|
||||
try {
|
||||
session->write(boost::asio::buffer(message));
|
||||
++successCount;
|
||||
++it;
|
||||
} catch (...) {
|
||||
Logger::warning("广播消息到客户端失败");
|
||||
++failCount;
|
||||
it = sessions_.erase(it); // 移除失败的会话
|
||||
}
|
||||
} else {
|
||||
Logger::error("接受 WebSocket 连接失败: ", ec.message());
|
||||
Logger::debug("移除失效的会话");
|
||||
it = sessions_.erase(it); // 移除已失效的会话
|
||||
}
|
||||
|
||||
// 继续接受新的连接
|
||||
handleAccept();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketServer::doRead(std::shared_ptr<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>> ws) {
|
||||
// 为每个会话创建一个专用的缓冲区
|
||||
auto buffer = std::make_shared<boost::beast::multi_buffer>();
|
||||
|
||||
// 异步读取消息
|
||||
ws->async_read(
|
||||
*buffer,
|
||||
[this, ws, buffer = buffer](boost::system::error_code ec, std::size_t bytes_transferred) {
|
||||
if (!ec) {
|
||||
// 成功读取消息,继续读取下一条
|
||||
buffer->consume(buffer->size()); // 清空缓冲区
|
||||
doRead(ws);
|
||||
} else {
|
||||
// 发生错误或连接关闭,移除会话
|
||||
std::lock_guard<std::mutex> lock(sessions_mutex_);
|
||||
auto it = std::find_if(sessions_.begin(), sessions_.end(),
|
||||
[ws](const std::weak_ptr<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>>& weak) {
|
||||
return !weak.expired() && weak.lock() == ws;
|
||||
});
|
||||
if (it != sessions_.end()) {
|
||||
sessions_.erase(it);
|
||||
Logger::debug("广播消息完成: ", successCount, " 个成功, ", failCount, " 个失败, 当前共 ", sessions_.size(), " 个连接");
|
||||
}
|
||||
|
||||
void WebSocketServer::handleAccept() {
|
||||
acceptor_.async_accept(
|
||||
[this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket) {
|
||||
if (!ec) {
|
||||
Logger::info("接受新的 WebSocket 连接: ", socket.remote_endpoint().address().to_string());
|
||||
|
||||
// 创建新的 WebSocket 会话
|
||||
auto ws = std::make_shared<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>>(std::move(socket));
|
||||
|
||||
// 异步完成 WebSocket 握手
|
||||
ws->async_accept(
|
||||
[this, ws](boost::system::error_code ec) {
|
||||
if (!ec) {
|
||||
// 握手成功,保存会话并开始读取消息
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sessions_mutex_);
|
||||
sessions_.push_back(ws); // 存储 weak_ptr
|
||||
Logger::info("WebSocket 握手成功,当前连接数: ", sessions_.size());
|
||||
}
|
||||
doRead(ws);
|
||||
} else {
|
||||
Logger::error("WebSocket 握手失败: ", ec.message());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Logger::error("接受 WebSocket 连接失败: ", ec.message());
|
||||
}
|
||||
|
||||
// 继续接受新的连接
|
||||
handleAccept();
|
||||
});
|
||||
}
|
||||
|
||||
void WebSocketServer::doRead(std::shared_ptr<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>> ws) {
|
||||
// 为每个会话创建一个专用的缓冲区
|
||||
auto buffer = std::make_shared<boost::beast::multi_buffer>();
|
||||
|
||||
// 异步读取消息
|
||||
ws->async_read(
|
||||
*buffer,
|
||||
[this, ws, buffer = buffer](boost::system::error_code ec, std::size_t bytes_transferred) {
|
||||
if (!ec) {
|
||||
// 成功读取消息,继续读取下一条
|
||||
buffer->consume(buffer->size()); // 清空缓冲区
|
||||
doRead(ws);
|
||||
} else {
|
||||
// 发生错误或连接关闭,移除会话
|
||||
std::lock_guard<std::mutex> lock(sessions_mutex_);
|
||||
auto it = std::find_if(sessions_.begin(), sessions_.end(),
|
||||
[ws](const std::weak_ptr<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>>& weak) {
|
||||
return !weak.expired() && weak.lock() == ws;
|
||||
});
|
||||
if (it != sessions_.end()) {
|
||||
sessions_.erase(it);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void WebSocketServer::stop() {
|
||||
running_ = false;
|
||||
Logger::info("正在停止 WebSocket 服务器...");
|
||||
ioc_.stop(); // 停止 io_context
|
||||
|
||||
// 关闭所有连接
|
||||
std::lock_guard<std::mutex> lock(sessions_mutex_);
|
||||
for (auto& session : sessions_) {
|
||||
if (auto ws = session.lock()) {
|
||||
try {
|
||||
ws->close(boost::beast::websocket::close_code::normal);
|
||||
Logger::debug("关闭 WebSocket 连接");
|
||||
} catch (...) {
|
||||
Logger::warning("关闭 WebSocket 连接时发生错误");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void WebSocketServer::stop() {
|
||||
running_ = false;
|
||||
Logger::info("正在停止 WebSocket 服务器...");
|
||||
ioc_.stop(); // 停止 io_context
|
||||
|
||||
// 关闭所有连接
|
||||
std::lock_guard<std::mutex> lock(sessions_mutex_);
|
||||
for (auto& session : sessions_) {
|
||||
if (auto ws = session.lock()) {
|
||||
try {
|
||||
ws->close(boost::beast::websocket::close_code::normal);
|
||||
Logger::debug("关闭 WebSocket 连接");
|
||||
} catch (...) {
|
||||
Logger::warning("关闭 WebSocket 连接时发生错误");
|
||||
}
|
||||
}
|
||||
sessions_.clear();
|
||||
Logger::info("WebSocket 服务器已停止");
|
||||
}
|
||||
sessions_.clear();
|
||||
Logger::info("WebSocket 服务器已停止");
|
||||
}
|
||||
|
||||
} // namespace network
|
||||
@ -13,7 +13,7 @@ ControllableVehicles& ControllableVehicles::getInstance() {
|
||||
return *instance_;
|
||||
}
|
||||
|
||||
ControllableVehicles::ControllableVehicles(const std::string& configFile)
|
||||
ControllableVehicles::ControllableVehicles(const std::string& configFile)
|
||||
: http_client_(std::make_unique<HTTPClient>()) {
|
||||
if (!configFile.empty()) {
|
||||
loadConfig(configFile);
|
||||
@ -36,7 +36,7 @@ const ControllableVehicleConfig* ControllableVehicles::findVehicle(const std::st
|
||||
[&](const ControllableVehicleConfig& config) {
|
||||
return config.vehicleNo == vehicleNo;
|
||||
});
|
||||
|
||||
|
||||
return iter != vehicles_.end() ? &(*iter) : nullptr;
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ bool ControllableVehicles::sendCommand(const std::string& vehicleNo, const Vehic
|
||||
try {
|
||||
bool success = http_client_->sendCommand(vehicle->ip, vehicle->port, command);
|
||||
if (success) {
|
||||
Logger::info("Successfully sent command to vehicle ", vehicleNo, ": ",
|
||||
Logger::info("Successfully sent command to vehicle ", vehicleNo, ": ",
|
||||
command.type == CommandType::SIGNAL ? "SIGNAL" :
|
||||
command.type == CommandType::ALERT ? "ALERT" :
|
||||
command.type == CommandType::WARNING ? "WARNING" :
|
||||
|
||||
@ -29,15 +29,15 @@ protected:
|
||||
void SetUp() override {
|
||||
// 设置日志级别
|
||||
Logger::setLogLevel(LogLevel::DEBUG);
|
||||
|
||||
|
||||
// 创建测试实例
|
||||
bounds_ = std::make_unique<TestAirportBounds>();
|
||||
|
||||
|
||||
// 设置基本测试数据
|
||||
// 参考点设为原点 (0, 0)
|
||||
// 旋转角度设为 90 度,便于验证
|
||||
bounds_->setTestData(
|
||||
Vector2D{0, 0}, // 参考点
|
||||
Vector2D{ 0, 0 }, // 参考点
|
||||
90.0, // 旋转角度(度)
|
||||
Bounds(-2000, -2000, 4000, 4000) // 4km x 4km 的区域
|
||||
);
|
||||
@ -54,57 +54,57 @@ protected:
|
||||
// 测试坐标转换 - 参考点
|
||||
TEST_F(AirportBoundsTest, ReferencePointTransformation) {
|
||||
// 参考点应该转换为原点 (0,0)
|
||||
Vector2D result = bounds_->toAirportCoordinate(Vector2D{0, 0});
|
||||
EXPECT_TRUE(pointsAreClose(result, Vector2D{0, 0}))
|
||||
Vector2D result = bounds_->toAirportCoordinate(Vector2D{ 0, 0 });
|
||||
EXPECT_TRUE(pointsAreClose(result, Vector2D{ 0, 0 }))
|
||||
<< "参考点转换结果: (" << result.x << ", " << result.y << ")";
|
||||
}
|
||||
|
||||
// 测试坐标转换 - 基本位移(90度逆时针旋转)
|
||||
TEST_F(AirportBoundsTest, BasicTranslation) {
|
||||
// 向右移动 100 米,逆时针旋转 90 度后,应该在 y 轴正方向
|
||||
Vector2D result = bounds_->toAirportCoordinate(Vector2D{100, 0});
|
||||
Vector2D result = bounds_->toAirportCoordinate(Vector2D{ 100, 0 });
|
||||
EXPECT_NEAR(result.x, 0, 0.001);
|
||||
EXPECT_NEAR(result.y, 100, 0.001)
|
||||
<< "向右100米的点逆时针旋转90度后应该在 (0, 100),实际在: ("
|
||||
<< "向右100米的点逆时针旋转90度后应该在 (0, 100),实际在: ("
|
||||
<< result.x << ", " << result.y << ")";
|
||||
|
||||
|
||||
// 向上移动 100 米,逆时针旋转 90 度后,应该在 x 轴负方向
|
||||
result = bounds_->toAirportCoordinate(Vector2D{0, 100});
|
||||
result = bounds_->toAirportCoordinate(Vector2D{ 0, 100 });
|
||||
EXPECT_NEAR(result.x, -100, 0.001);
|
||||
EXPECT_NEAR(result.y, 0, 0.001)
|
||||
<< "向上100米的点逆时针旋转90度后应该在 (-100, 0),实际在: ("
|
||||
<< "向上100米的点逆时针旋转90度后应该在 (-100, 0),实际在: ("
|
||||
<< result.x << ", " << result.y << ")";
|
||||
}
|
||||
|
||||
// 测试坐标转换 - 复合变换(90度逆时针旋转)
|
||||
TEST_F(AirportBoundsTest, CompositeTransformation) {
|
||||
// 向右上方移动 (100, 100),逆时针旋转 90 度
|
||||
Vector2D result = bounds_->toAirportCoordinate(Vector2D{100, 100});
|
||||
Vector2D result = bounds_->toAirportCoordinate(Vector2D{ 100, 100 });
|
||||
EXPECT_NEAR(result.x, -100, 0.001);
|
||||
EXPECT_NEAR(result.y, 100, 0.001)
|
||||
<< "点(100,100)逆时针旋转90度后应该在 (-100, 100),实际在: ("
|
||||
<< "点(100,100)逆时针旋转90度后应该在 (-100, 100),实际在: ("
|
||||
<< result.x << ", " << result.y << ")";
|
||||
}
|
||||
|
||||
// 测试边界检查 - 边界内的点
|
||||
TEST_F(AirportBoundsTest, PointInBounds) {
|
||||
// 测试参考点(一定在边界内)
|
||||
EXPECT_TRUE(bounds_->isPointInBounds(Vector2D{0, 0}))
|
||||
EXPECT_TRUE(bounds_->isPointInBounds(Vector2D{ 0, 0 }))
|
||||
<< "参考点应该在边界内";
|
||||
|
||||
|
||||
// 测试边界内的点
|
||||
EXPECT_TRUE(bounds_->isPointInBounds(Vector2D{1000, 1000}))
|
||||
EXPECT_TRUE(bounds_->isPointInBounds(Vector2D{ 1000, 1000 }))
|
||||
<< "距离参考点1km的点应该在边界内";
|
||||
}
|
||||
|
||||
// 测试边界检查 - 边界外的点
|
||||
TEST_F(AirportBoundsTest, PointOutOfBounds) {
|
||||
// 测试边界外的点
|
||||
EXPECT_FALSE(bounds_->isPointInBounds(Vector2D{3000, 3000}))
|
||||
EXPECT_FALSE(bounds_->isPointInBounds(Vector2D{ 3000, 3000 }))
|
||||
<< "距离参考点3km的点应该在边界外";
|
||||
|
||||
|
||||
// 测试另一个边界外的点
|
||||
EXPECT_FALSE(bounds_->isPointInBounds(Vector2D{-2500, -2500}))
|
||||
EXPECT_FALSE(bounds_->isPointInBounds(Vector2D{ -2500, -2500 }))
|
||||
<< "距离参考点2.5km的点应该在边界外";
|
||||
}
|
||||
|
||||
@ -112,24 +112,24 @@ TEST_F(AirportBoundsTest, PointOutOfBounds) {
|
||||
TEST_F(AirportBoundsTest, DifferentRotations) {
|
||||
// 重新设置为45度逆时针旋转
|
||||
bounds_->setTestData(
|
||||
Vector2D{0, 0}, // 参考点
|
||||
Vector2D{ 0, 0 }, // 参考点
|
||||
45.0, // 旋转角度(度)
|
||||
Bounds(-2000, -2000, 4000, 4000)
|
||||
);
|
||||
|
||||
|
||||
// 向右移动 100 米,逆时针旋转 45 度
|
||||
Vector2D result = bounds_->toAirportCoordinate(Vector2D{100, 0});
|
||||
Vector2D result = bounds_->toAirportCoordinate(Vector2D{ 100, 0 });
|
||||
double sqrt2_2 = std::sqrt(2.0) / 2.0; // cos(45°) = sin(45°) = √2/2
|
||||
EXPECT_NEAR(result.x, 100 * sqrt2_2, 0.001);
|
||||
EXPECT_NEAR(result.y, 100 * sqrt2_2, 0.001)
|
||||
<< "向右100米的点逆时针旋转45度后应该在 (70.71, 70.71),实际在: ("
|
||||
<< "向右100米的点逆时针旋转45度后应该在 (70.71, 70.71),实际在: ("
|
||||
<< result.x << ", " << result.y << ")";
|
||||
|
||||
|
||||
// 向上移动 100 米,逆时针旋转 45 度
|
||||
result = bounds_->toAirportCoordinate(Vector2D{0, 100});
|
||||
result = bounds_->toAirportCoordinate(Vector2D{ 0, 100 });
|
||||
EXPECT_NEAR(result.x, -100 * sqrt2_2, 0.001);
|
||||
EXPECT_NEAR(result.y, 100 * sqrt2_2, 0.001)
|
||||
<< "向上100米的点逆时针旋转45度后应该在 (-70.71, 70.71),实际在: ("
|
||||
<< "向上100米的点逆时针旋转45度后应该在 (-70.71, 70.71),实际在: ("
|
||||
<< result.x << ", " << result.y << ")";
|
||||
}
|
||||
|
||||
@ -137,33 +137,33 @@ TEST_F(AirportBoundsTest, DifferentRotations) {
|
||||
TEST_F(AirportBoundsTest, RotationTransformation) {
|
||||
// 重新设置为30度逆时针旋转
|
||||
bounds_->setTestData(
|
||||
Vector2D{0, 0}, // 参考点
|
||||
Vector2D{ 0, 0 }, // 参考点
|
||||
30.0, // 旋转角度(度)
|
||||
Bounds(-2000, -2000, 4000, 4000)
|
||||
);
|
||||
|
||||
|
||||
// 在世界坐标系中向右移动1000米,逆时针旋转 30 度
|
||||
Vector2D result = bounds_->toAirportCoordinate(Vector2D{1000, 0});
|
||||
double cos30 = std::cos(M_PI/6); // cos(30°) ≈ 0.866025
|
||||
double sin30 = std::sin(M_PI/6); // sin(30°) ≈ 0.5
|
||||
|
||||
Vector2D result = bounds_->toAirportCoordinate(Vector2D{ 1000, 0 });
|
||||
double cos30 = std::cos(M_PI / 6); // cos(30°) ≈ 0.866025
|
||||
double sin30 = std::sin(M_PI / 6); // sin(30°) ≈ 0.5
|
||||
|
||||
// 在机场坐标系中的期望结果:
|
||||
// x = 1000 * cos(30°) ≈ 866.025
|
||||
// y = 1000 * sin(30°) ≈ 500
|
||||
EXPECT_NEAR(result.x, 1000 * cos30, 0.1);
|
||||
EXPECT_NEAR(result.y, 1000 * sin30, 0.1)
|
||||
<< "向右1000米的点逆时针旋转30度后应该在 (866.025, 500),实际在: ("
|
||||
<< "向右1000米的点逆时针旋转30度后应该在 (866.025, 500),实际在: ("
|
||||
<< result.x << ", " << result.y << ")";
|
||||
|
||||
|
||||
// 在世界坐标系中向上移动1000米,逆时针旋转 30 度
|
||||
result = bounds_->toAirportCoordinate(Vector2D{0, 1000});
|
||||
|
||||
result = bounds_->toAirportCoordinate(Vector2D{ 0, 1000 });
|
||||
|
||||
// 在机场坐标系中的期望结果:
|
||||
// x = -1000 * sin(30°) ≈ -500
|
||||
// y = 1000 * cos(30°) ≈ 866.025
|
||||
EXPECT_NEAR(result.x, -1000 * sin30, 0.1);
|
||||
EXPECT_NEAR(result.y, 1000 * cos30, 0.1)
|
||||
<< "向上1000米的点逆时针旋转30度后应该在 (-500, 866.025),实际在: ("
|
||||
<< "向上1000米的点逆时针旋转30度后应该在 (-500, 866.025),实际在: ("
|
||||
<< result.x << ", " << result.y << ")";
|
||||
}
|
||||
|
||||
@ -171,27 +171,27 @@ TEST_F(AirportBoundsTest, RotationTransformation) {
|
||||
TEST_F(AirportBoundsTest, AreaCheck) {
|
||||
// 重新设置为0度旋转,便于测试
|
||||
bounds_->setTestData(
|
||||
Vector2D{0, 0}, // 参考点
|
||||
Vector2D{ 0, 0 }, // 参考点
|
||||
0.0, // 旋转角度(度)
|
||||
Bounds(-2000, -2000, 4000, 4000)
|
||||
);
|
||||
|
||||
|
||||
// 设置测试区域
|
||||
bounds_->areaBounds_[AreaType::RUNWAY] = Bounds(-1000, -100, 2000, 200);
|
||||
bounds_->areaBounds_[AreaType::TAXIWAY] = Bounds(-800, -300, 1600, 600);
|
||||
|
||||
|
||||
// 测试跑道上的点
|
||||
Vector2D runwayPoint{500, 0}; // 跑道中心偏右500米
|
||||
Vector2D runwayPoint{ 500, 0 }; // 跑道中心偏右500米
|
||||
EXPECT_TRUE(bounds_->isPointInArea(runwayPoint, AreaType::RUNWAY))
|
||||
<< "跑道上的点应该在跑道区域内";
|
||||
|
||||
|
||||
// 测试滑行道上的点
|
||||
Vector2D taxiwayPoint{0, 200}; // 滑行道中心向上200米
|
||||
Vector2D taxiwayPoint{ 0, 200 }; // 滑行道中心向上200米
|
||||
EXPECT_TRUE(bounds_->isPointInArea(taxiwayPoint, AreaType::TAXIWAY))
|
||||
<< "滑行道上的点应该在滑行道区域内";
|
||||
|
||||
|
||||
// 测试区域外的点
|
||||
Vector2D outPoint{3000, 3000}; // 远离机场的点
|
||||
Vector2D outPoint{ 3000, 3000 }; // 远离机场的点
|
||||
EXPECT_FALSE(bounds_->isPointInArea(outPoint, AreaType::RUNWAY))
|
||||
<< "远离机场的点不应该在任何区域内";
|
||||
EXPECT_FALSE(bounds_->isPointInArea(outPoint, AreaType::TAXIWAY))
|
||||
@ -202,12 +202,12 @@ TEST_F(AirportBoundsTest, AreaCheck) {
|
||||
TEST_F(AirportBoundsTest, ConfigLoading) {
|
||||
// 使用项目中的配置文件
|
||||
AirportBounds bounds("config/airport_bounds.json");
|
||||
|
||||
|
||||
// 验证配置是否正确加载
|
||||
const auto& airportBounds = bounds.getAirportBounds();
|
||||
EXPECT_TRUE(airportBounds.width > 0 && airportBounds.height > 0)
|
||||
<< "机场边界的宽度和高度应该为正值";
|
||||
|
||||
|
||||
// 验证区域配置
|
||||
const auto& runwayBounds = bounds.getAreaBounds(AreaType::RUNWAY);
|
||||
EXPECT_TRUE(runwayBounds.width > 0 && runwayBounds.height > 0)
|
||||
@ -220,7 +220,7 @@ TEST_F(AirportBoundsTest, ConfigLoading) {
|
||||
TEST_F(AirportBoundsTest, ConfigErrorHandling) {
|
||||
// 测试文件不存在
|
||||
EXPECT_THROW(AirportBounds("nonexistent.json"), std::runtime_error);
|
||||
|
||||
|
||||
// 测试配置文件格式错误
|
||||
EXPECT_THROW(AirportBounds("tests/data/invalid_airport_bounds.json"), std::runtime_error);
|
||||
}
|
||||
@ -229,18 +229,18 @@ TEST_F(AirportBoundsTest, ConfigErrorHandling) {
|
||||
TEST_F(AirportBoundsTest, EdgeCases) {
|
||||
// 重新设置为0度旋转,便于测试
|
||||
bounds_->setTestData(
|
||||
Vector2D{0, 0}, // 参考点
|
||||
Vector2D{ 0, 0 }, // 参考点
|
||||
0.0, // 旋转角度(度)
|
||||
Bounds(-2000, -2000, 4000, 4000)
|
||||
);
|
||||
|
||||
|
||||
// 测试正好在边界上的点
|
||||
Vector2D edge{2000, 2000}; // 右上角边界点
|
||||
Vector2D edge{ 2000, 2000 }; // 右上角边界点
|
||||
EXPECT_TRUE(bounds_->isPointInBounds(edge))
|
||||
<< "边界上的点应该被认为在边界内";
|
||||
|
||||
|
||||
// 测试边界外的点
|
||||
Vector2D outside{2001, 2001}; // 刚好超出边界
|
||||
Vector2D outside{ 2001, 2001 }; // 刚好超出边界
|
||||
EXPECT_FALSE(bounds_->isPointInBounds(outside))
|
||||
<< "边界外的点应该被认为在边界外";
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user