将 3 类数据接口分开处理,增加一个地图页面工具

This commit is contained in:
Tian jianyong 2025-01-15 16:31:24 +08:00
parent 61b50c3bc0
commit 6281537f80
29 changed files with 2073 additions and 1006 deletions

View File

@ -24,17 +24,50 @@
]
},
"data_source": {
"host": "localhost",
"port": 8081,
"username": "dianxin",
"password": "dianxin@123",
"auth_path": "/login",
"aircraft_path": "/openApi/getCurrentFlightPositions",
"vehicle_path": "/openApi/getCurrentVehiclePositions",
"traffic_light_path": "/getTrafficLightSignals",
"refresh_interval_ms": 1000,
"timeout_ms": 5000,
"read_timeout_ms": 2000
"position": {
"host": "localhost",
"port": 8081,
"aircraft_path": "/openApi/getCurrentFlightPositions",
"vehicle_path": "/openApi/getCurrentVehiclePositions",
"refresh_interval_ms": 1000,
"auth": {
"username": "dianxin",
"password": "dianxin@123",
"auth_path": "/login",
"auth_required": true
},
"timeout_ms": 5000,
"read_timeout_ms": 2000
},
"unmanned_vehicle": {
"host": "localhost",
"port": 8081,
"status_path": "/openApi/getVehicleStatus",
"command_path": "/openApi/sendVehicleCommand",
"refresh_interval_ms": 1000,
"auth": {
"username": "dianxin",
"password": "dianxin@123",
"auth_path": "/login",
"auth_required": false
},
"timeout_ms": 5000,
"read_timeout_ms": 2000
},
"traffic_light": {
"host": "localhost",
"port": 8081,
"signal_path": "/openApi/getTrafficLightSignals",
"refresh_interval_ms": 1000,
"auth": {
"username": "dianxin",
"password": "dianxin@123",
"auth_path": "/login",
"auth_required": false
},
"timeout_ms": 5000,
"read_timeout_ms": 2000
}
},
"warning": {
"warning_interval_ms": 1000,
@ -60,7 +93,7 @@
}
},
"logging": {
"level": "info",
"level": "debug",
"file": "logs/system.log",
"max_size_mb": 10,
"max_files": 5,

47
docs/requirements.md Normal file
View File

@ -0,0 +1,47 @@
# 需求文档
## 1. 需求概述
## 2. 功能需求
### 2.1 航空器、车辆位置显示
### 2.2 冲突检测
### 2.3 红绿灯信号处理
### 2.4 数据采集
1. 航空器和车辆位置数据
1. 数据来源:
1. 航空器ADS-B
2. 车辆1.8G北斗
2. 数据格式:
参考 official_api.md
3. 采集频率:
1. 航空器3Hz
2. 车辆3Hz
2. 红绿灯信号
1. 数据来源:
1. 红绿灯5G
2. 数据格式:
参考 official_api.md
3. 采集频率:
1. 红绿灯1Hz
3. 无人车状态
1. 数据来源:
1. 无人车5G
2. 数据格式:
参考 official_api.md
3. 采集频率:
1. 无人车1Hz
### 2.5 数据处理
### 2.6 数据存储
## 3. 非功能需求
## 4. 约束
## 5. 其他

View File

@ -3,9 +3,13 @@
#include "utils/Logger.h"
#include <chrono>
#include <mutex>
#include <fstream>
#include <nlohmann/json.hpp>
DataCollector::DataCollector()
: lastSuccessfulFetch_(std::chrono::steady_clock::now()) // 初始化时间戳
: 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()) {
}
@ -18,7 +22,9 @@ bool DataCollector::initialize(const DataSourceConfig& dataSourceConfig, const W
dataSourceConfig_ = dataSourceConfig;
dataSource_ = std::make_shared<HTTPDataSource>(dataSourceConfig_);
warnConfig_ = warnConfig;
lastSuccessfulFetch_ = std::chrono::steady_clock::now(); // 重置时间戳
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();
}
return true;
@ -35,8 +41,9 @@ void DataCollector::start() {
}
running_ = true;
lastSuccessfulFetch_ = std::chrono::steady_clock::now(); // 记录启动时间
last_warning_time_ = lastSuccessfulFetch_; // 初始化告警时间
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()) {
@ -45,7 +52,10 @@ void DataCollector::start() {
checkTimeout();
}
collectorThread_ = std::thread(&DataCollector::collectLoop, this);
// 启动三个独立的采集线程
positionThread_ = std::thread(&DataCollector::positionLoop, this);
unmannedThread_ = std::thread(&DataCollector::unmannedLoop, this);
trafficLightThread_ = std::thread(&DataCollector::trafficLightLoop, this);
}
void DataCollector::stop() {
@ -54,8 +64,16 @@ void DataCollector::stop() {
}
running_ = false;
if (collectorThread_.joinable()) {
collectorThread_.join();
// 等待所有线程结束
if (positionThread_.joinable()) {
positionThread_.join();
}
if (unmannedThread_.joinable()) {
unmannedThread_.join();
}
if (trafficLightThread_.joinable()) {
trafficLightThread_.join();
}
if (dataSource_) {
@ -63,37 +81,85 @@ void DataCollector::stop() {
}
}
void DataCollector::collectLoop() {
Logger::debug("数据采集循环启动,刷新间隔: ", dataSourceConfig_.refresh_interval_ms, "ms");
void DataCollector::positionLoop() {
Logger::debug("位置数据采集循环启动,刷新间隔: ", dataSourceConfig_.position.refresh_interval_ms, "ms");
auto next_collection_time = std::chrono::steady_clock::now();
while (running_) {
loopCount_++;
Logger::debug("开始第 ", loopCount_.load(), " 次数据采集循环");
bool success = fetchData();
bool success = fetchPositionData();
if (!success) {
Logger::warning("", loopCount_.load(), "数据采集失败");
checkTimeout(); // 每次获取失败都检查超时
Logger::warning("位置数据采集失败");
checkTimeout();
}
// 即使成功获取数据,也定期检查超时状态
checkTimeout();
// 计算下一次采集时间
next_collection_time += std::chrono::milliseconds(dataSourceConfig_.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();
if (wait_time > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(wait_time));
}
} else {
// 如果已经超过了下一次采集时间,立即重置
next_collection_time = std::chrono::steady_clock::now();
}
}
}
void DataCollector::unmannedLoop() {
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);
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();
if (wait_time > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(wait_time));
}
} else {
// 如果已经超过了下一次采集时间,立即重置
next_collection_time = std::chrono::steady_clock::now();
}
}
}
void DataCollector::trafficLightLoop() {
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);
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();
if (wait_time > 0) {
Logger::debug("等待 ", wait_time, "ms 进行下一次采集");
std::this_thread::sleep_for(std::chrono::milliseconds(wait_time));
}
} else {
// 如果已经超过了下一次采集时间,立即重置
Logger::warning("数据采集延迟,重置采集时间");
next_collection_time = std::chrono::steady_clock::now();
}
}
@ -101,124 +167,158 @@ void DataCollector::collectLoop() {
void DataCollector::checkTimeout() {
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - lastSuccessfulFetch_).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();
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();
if (traffic_light_elapsed > dataSourceConfig_.traffic_light.timeout_ms) {
sendTimeoutWarning("traffic_light", traffic_light_elapsed);
}
}
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();
if (elapsed > dataSourceConfig_.position.timeout_ms) {
Logger::info("Position data source connection restored after ", elapsed, "ms timeout");
}
last_position_fetch_ = now;
}
else if (source == "unmanned") {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_unmanned_fetch_).count();
if (elapsed > dataSourceConfig_.vehicle.timeout_ms) {
Logger::info("Unmanned vehicle data source connection restored after ", elapsed, "ms timeout");
}
last_unmanned_fetch_ = now;
}
else if (source == "traffic_light") {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
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");
}
last_traffic_light_fetch_ = now;
}
}
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();
if (warning_elapsed >= warnConfig_.warning_interval_ms) {
// 如果超过了超时阈值,发送警告
if (elapsed > dataSourceConfig_.read_timeout_ms) {
sendTimeoutWarning(elapsed);
last_warning_time_ = now;
// 只在非测试环境中记录警告日志
if (system_) {
Logger::warning("Data source timeout: No response for ", elapsed, "ms");
} else {
Logger::debug("Data source timeout in test environment: ", elapsed, "ms");
}
if (!system_) {
Logger::debug("System not set, skipping timeout warning");
return;
}
}
}
void DataCollector::resetTimeout() {
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - lastSuccessfulFetch_).count();
network::TimeoutWarningMessage msg;
msg.source = source;
msg.elapsed_ms = elapsed_ms;
if (elapsed > dataSourceConfig_.timeout_ms) {
// 如果之前处于超时状态,记录恢复日志
Logger::info("Data source connection restored after ", elapsed, "ms timeout");
// 根据数据源类型设置超时阈值
int64_t timeout_ms = 0;
if (source == "position") {
timeout_ms = dataSourceConfig_.position.timeout_ms;
}
else if (source == "unmanned") {
timeout_ms = dataSourceConfig_.vehicle.timeout_ms;
}
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;
Logger::debug("Sent timeout warning: source=", msg.source,
" elapsed=", msg.elapsed_ms, "ms",
" is_read_timeout=", msg.is_read_timeout ? "true" : "false",
" timeout=", timeout_ms);
}
lastSuccessfulFetch_ = now;
}
void DataCollector::sendTimeoutWarning(int64_t elapsed_ms) {
if (!system_) {
Logger::debug("System not set, skipping timeout warning");
return;
}
network::TimeoutWarningMessage msg;
msg.source = "data_source";
msg.elapsed_ms = elapsed_ms;
// 如果超时时间超过了读取超时阈值,就是读取超时
msg.is_read_timeout = (elapsed_ms > dataSourceConfig_.read_timeout_ms);
system_->broadcastTimeoutWarning(msg);
Logger::debug("Sent timeout warning: source=", msg.source,
" elapsed=", msg.elapsed_ms, "ms",
" is_read_timeout=", msg.is_read_timeout ? "true" : "false",
" read_timeout=", dataSourceConfig_.read_timeout_ms,
" connect_timeout=", dataSourceConfig_.timeout_ms);
}
bool DataCollector::fetchData() {
bool success = true;
bool DataCollector::fetchPositionData() {
std::vector<Aircraft> newAircraft;
std::vector<Vehicle> newVehicles;
std::vector<TrafficLightSignal> newSignals;
Logger::debug("开始获取数据...");
if (dataSource_->fetchAircraftData(newAircraft)) {
std::lock_guard<std::mutex> lock(cacheMutex_);
// 更新时间戳为数据源中最新的时间戳
int64_t maxTimestamp = 0;
for (const auto& aircraft : newAircraft) {
if (aircraft.timestamp > maxTimestamp) {
maxTimestamp = aircraft.timestamp;
}
}
if (maxTimestamp > 0) {
lastAircraftTimestamp_.store(maxTimestamp);
Logger::debug("更新航空器时间戳为: ", lastAircraftTimestamp_.load());
}
aircraftCache_ = std::move(newAircraft);
Logger::debug("成功获取航空器数据: ", aircraftCache_.size(), "");
} else {
Logger::warning("获取航空器数据失败");
success = false;
if (!dataSource_) {
Logger::error("No data source available");
return false;
}
if (dataSource_->fetchVehicleData(newVehicles)) {
std::lock_guard<std::mutex> lock(cacheMutex_);
// 更新时间戳为数据源中最新的时间戳
int64_t maxTimestamp = 0;
for (const auto& vehicle : newVehicles) {
if (vehicle.timestamp > maxTimestamp) {
maxTimestamp = vehicle.timestamp;
}
}
if (maxTimestamp > 0) {
lastVehicleTimestamp_.store(maxTimestamp);
Logger::debug("更新车辆时间戳为: ", lastVehicleTimestamp_.load());
}
vehicleCache_ = std::move(newVehicles);
Logger::debug("成功获取车辆数据: ", vehicleCache_.size(), "");
} else {
Logger::warning("获取车辆数据失败");
success = false;
}
if (dataSource_->fetchTrafficLightSignals(newSignals)) {
std::lock_guard<std::mutex> lock(cacheMutex_);
trafficLightCache_ = std::move(newSignals);
Logger::debug("成功获取红绿灯数据: ", trafficLightCache_.size(), "");
} else {
Logger::warning("获取红绿灯数据失败");
success = false;
}
bool success = dataSource_->fetchPositionAircraftData(newAircraft) &&
dataSource_->fetchPositionVehicleData(newVehicles);
if (success) {
resetTimeout(); // 重置超时计时器
Logger::debug("数据获取完成");
} else {
Logger::warning("数据获取过程中存在错误");
std::lock_guard<std::mutex> lock(cacheMutex_);
aircraftCache_ = std::move(newAircraft);
vehicleCache_ = std::move(newVehicles);
resetTimeout("position");
}
return success;
}
bool DataCollector::fetchUnmannedVehicleData() {
if (!dataSource_) {
Logger::error("数据源未初始化");
return false;
}
bool success = false;
auto& vehicles = ControllableVehicles::getInstance().getVehicles();
for (const auto& vehicle : vehicles) {
if (vehicle.type == "UNMANNED") {
std::string status;
if (dataSource_->fetchUnmannedVehicleStatus(vehicle.vehicleNo, status)) {
success = true;
} else {
Logger::error("获取无人车状态失败, vehicleNo: " + vehicle.vehicleNo);
}
}
}
if (success) {
resetTimeout("unmanned");
}
return success;
}
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);
resetTimeout("traffic_light");
}
return success;
@ -259,7 +359,7 @@ void DataCollector::refresh() {
trafficLightCache_ = std::move(newSignals);
}
if (dataSource_->fetchAircraftData(newAircraft)) {
if (dataSource_->fetchPositionAircraftData(newAircraft)) {
std::lock_guard<std::mutex> lock(cacheMutex_);
// 更新运动信息
for (auto& a : newAircraft) {
@ -277,7 +377,7 @@ void DataCollector::refresh() {
aircraftCache_ = std::move(newAircraft);
}
if (dataSource_->fetchVehicleData(newVehicles)) {
if (dataSource_->fetchPositionVehicleData(newVehicles)) {
std::lock_guard<std::mutex> lock(cacheMutex_);
// 更新运动信息
for (auto& v : newVehicles) {
@ -299,4 +399,12 @@ void DataCollector::refresh() {
Logger::error("刷新数据失败: ", e.what());
throw;
}
}
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);
}

View File

@ -12,6 +12,7 @@
#include "types/BasicTypes.h"
#include "config/WarnConfig.h"
#include "types/TrafficLightTypes.h"
#include "vehicle/ControllableVehicles.h"
// 前向声明
class System;
@ -34,6 +35,9 @@ public:
std::vector<TrafficLightSignal> getTrafficLightSignals();
bool fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals);
// 无人车状态获取接口
bool fetchUnmannedVehicleStatus(const std::string& vehicle_id, std::string& status);
// 添加设置数据源的方法(用于测试)
void setDataSource(std::shared_ptr<DataSource> source) {
dataSource_ = source;
@ -55,7 +59,11 @@ private:
std::shared_ptr<System> system_;
DataSourceConfig dataSourceConfig_;
WarnConfig warnConfig_;
std::thread collectorThread_;
// 三个独立的采集线程
std::thread positionThread_; // 位置数据采集线程
std::thread unmannedThread_; // 无人车状态采集线程
std::thread trafficLightThread_; // 红绿灯数据采集线程
std::atomic<bool> running_{false};
std::atomic<uint64_t> loopCount_{0};
@ -74,16 +82,24 @@ private:
std::atomic<int64_t> lastVehicleTimestamp_{0};
// 超时检测相关
std::chrono::steady_clock::time_point lastSuccessfulFetch_;
std::chrono::steady_clock::time_point last_position_fetch_; // 位置数据最后获取时间
std::chrono::steady_clock::time_point last_unmanned_fetch_; // 无人车状态最后获取时间
std::chrono::steady_clock::time_point last_traffic_light_fetch_; // 红绿灯数据最后获取时间
std::chrono::steady_clock::time_point last_warning_time_;
std::chrono::steady_clock::time_point last_log_time_;
void checkTimeout();
void resetTimeout();
void sendTimeoutWarning(int64_t elapsed_ms);
void resetTimeout(const std::string& source); // 修改resetTimeout方法签名
void sendTimeoutWarning(const std::string& source, int64_t elapsed_ms); // 修改告警方法签名
void collectLoop();
bool fetchData();
// 三个独立的采集循环
void positionLoop(); // 位置数据采集循环
void unmannedLoop(); // 无人车状态采集循环
void trafficLightLoop(); // 红绿灯数据采集循环
bool fetchPositionData(); // 获取位置数据
bool fetchUnmannedVehicleData(); // 获取无人车状态数据
bool fetchTrafficLightData(); // 获取红绿灯数据
};
#endif // AIRPORT_COLLECTOR_DATA_COLLECTOR_H

View File

@ -3,20 +3,35 @@
#include "types/BasicTypes.h"
#include "types/TrafficLightTypes.h"
#include "types/VehicleCommand.h"
#include <optional>
#include <vector>
#include <string>
class DataSource {
public:
// 数据源类型
enum class DataSourceType {
POSITION, // 位置数据接口
UNMANNED, // 无人车接口
TRAFFIC_LIGHT // 红绿灯接口
};
virtual ~DataSource() = default;
virtual bool connect() = 0;
virtual void disconnect() = 0;
virtual bool isAvailable() const = 0;
// 获取数据的接口
virtual bool fetchAircraftData(std::vector<Aircraft>& aircraft) = 0;
virtual bool fetchVehicleData(std::vector<Vehicle>& vehicles) = 0;
// 位置数据接口
virtual bool fetchPositionAircraftData(std::vector<Aircraft>& aircraft) = 0;
virtual bool fetchPositionVehicleData(std::vector<Vehicle>& vehicles) = 0;
// 无人车接口
virtual bool sendUnmannedVehicleCommand(const std::string& vehicle_id, const VehicleCommand& command) = 0;
virtual bool fetchUnmannedVehicleStatus(const std::string& vehicle_id, std::string& status) = 0;
// 红绿灯接口
virtual bool fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals) = 0;
};

View File

@ -0,0 +1,47 @@
#include "DataSourceConfig.h"
#include "config/SystemConfig.h"
#include "utils/Logger.h"
DataSourceConfig DataSourceConfig::fromSystemConfig(const SystemConfig& config) {
DataSourceConfig dataSourceConfig;
// 转换位置数据源配置
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.vehicle.host = config.data_source.unmanned_vehicle.host;
dataSourceConfig.vehicle.port = config.data_source.unmanned_vehicle.port;
dataSourceConfig.vehicle.status_path = config.data_source.unmanned_vehicle.status_path;
dataSourceConfig.vehicle.command_path = config.data_source.unmanned_vehicle.command_path;
dataSourceConfig.vehicle.refresh_interval_ms = config.data_source.unmanned_vehicle.refresh_interval_ms;
dataSourceConfig.vehicle.timeout_ms = config.data_source.unmanned_vehicle.timeout_ms;
dataSourceConfig.vehicle.read_timeout_ms = config.data_source.unmanned_vehicle.read_timeout_ms;
dataSourceConfig.vehicle.auth.username = config.data_source.unmanned_vehicle.auth.username;
dataSourceConfig.vehicle.auth.password = config.data_source.unmanned_vehicle.auth.password;
dataSourceConfig.vehicle.auth.auth_path = config.data_source.unmanned_vehicle.auth.auth_path;
dataSourceConfig.vehicle.auth.auth_required = config.data_source.unmanned_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;
return dataSourceConfig;
}

View File

@ -2,19 +2,170 @@
#define AIRPORT_COLLECTOR_DATA_SOURCE_CONFIG_H
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
struct DataSourceConfig {
std::string host;
uint16_t port;
std::string aircraft_path;
std::string vehicle_path;
std::string traffic_light_path;
std::string auth_path; // 认证接口路径
std::string username; // 用户名
std::string password; // 密码
int refresh_interval_ms;
int timeout_ms; // 连接超时时间, 单位: 毫秒
int read_timeout_ms; // 读取超时时间, 单位: 毫秒, 小于连接超时时间
using json = nlohmann::json;
// 前向声明
class SystemConfig;
// 认证配置
struct AuthConfig {
std::string username; // 认证用户名
std::string password; // 认证密码
std::string auth_path; // 认证接口路径
bool auth_required = false; // 是否需要认证
};
// AuthConfig 的 JSON 序列化/反序列化函数
inline void to_json(json& j, const AuthConfig& a) {
j = json{
{"username", a.username},
{"password", a.password},
{"auth_path", a.auth_path},
{"auth_required", a.auth_required}
};
}
inline void from_json(const json& j, AuthConfig& a) {
j.at("username").get_to(a.username);
j.at("password").get_to(a.password);
j.at("auth_path").get_to(a.auth_path);
j.at("auth_required").get_to(a.auth_required);
}
// 位置数据源配置
struct PositionDataConfig {
std::string host; // 服务器地址
int port; // 服务器端口
std::string aircraft_path; // 航空器位置接口
std::string vehicle_path; // 车辆位置接口
int refresh_interval_ms; // 位置数据刷新间隔
int timeout_ms; // 连接超时时间
int read_timeout_ms; // 读取超时时间
AuthConfig auth; // 认证配置
};
// PositionDataConfig 的 JSON 序列化/反序列化函数
inline void to_json(json& j, const PositionDataConfig& p) {
j = json{
{"host", p.host},
{"port", p.port},
{"aircraft_path", p.aircraft_path},
{"vehicle_path", p.vehicle_path},
{"refresh_interval_ms", p.refresh_interval_ms},
{"timeout_ms", p.timeout_ms},
{"read_timeout_ms", p.read_timeout_ms},
{"auth", p.auth}
};
}
inline void from_json(const json& j, PositionDataConfig& p) {
j.at("host").get_to(p.host);
j.at("port").get_to(p.port);
j.at("aircraft_path").get_to(p.aircraft_path);
j.at("vehicle_path").get_to(p.vehicle_path);
j.at("refresh_interval_ms").get_to(p.refresh_interval_ms);
j.at("timeout_ms").get_to(p.timeout_ms);
j.at("read_timeout_ms").get_to(p.read_timeout_ms);
j.at("auth").get_to(p.auth);
}
// 无人车数据源配置
struct UnmannedVehicleConfig {
std::string host; // 服务器地址
int port; // 服务器端口
std::string status_path; // 无人车状态接口
std::string command_path; // 无人车指令接口
int refresh_interval_ms; // 状态数据刷新间隔
int timeout_ms; // 连接超时时间
int read_timeout_ms; // 读取超时时间
AuthConfig auth; // 认证配置
};
// UnmannedVehicleConfig 的 JSON 序列化/反序列化函数
inline void to_json(json& j, const UnmannedVehicleConfig& u) {
j = json{
{"host", u.host},
{"port", u.port},
{"status_path", u.status_path},
{"command_path", u.command_path},
{"refresh_interval_ms", u.refresh_interval_ms},
{"timeout_ms", u.timeout_ms},
{"read_timeout_ms", u.read_timeout_ms},
{"auth", u.auth}
};
}
inline void from_json(const json& j, UnmannedVehicleConfig& u) {
j.at("host").get_to(u.host);
j.at("port").get_to(u.port);
j.at("status_path").get_to(u.status_path);
j.at("command_path").get_to(u.command_path);
j.at("refresh_interval_ms").get_to(u.refresh_interval_ms);
j.at("timeout_ms").get_to(u.timeout_ms);
j.at("read_timeout_ms").get_to(u.read_timeout_ms);
j.at("auth").get_to(u.auth);
}
// 红绿灯数据源配置
struct TrafficLightConfig {
std::string host; // 服务器地址
int port; // 服务器端口
std::string signal_path; // 红绿灯信号接口
int refresh_interval_ms; // 信号数据刷新间隔
int timeout_ms; // 连接超时时间
int read_timeout_ms; // 读取超时时间
AuthConfig auth; // 认证配置
};
// TrafficLightConfig 的 JSON 序列化/反序列化函数
inline void to_json(json& j, const TrafficLightConfig& t) {
j = json{
{"host", t.host},
{"port", t.port},
{"signal_path", t.signal_path},
{"refresh_interval_ms", t.refresh_interval_ms},
{"timeout_ms", t.timeout_ms},
{"read_timeout_ms", t.read_timeout_ms},
{"auth", t.auth}
};
}
inline void from_json(const json& j, TrafficLightConfig& t) {
j.at("host").get_to(t.host);
j.at("port").get_to(t.port);
j.at("signal_path").get_to(t.signal_path);
j.at("refresh_interval_ms").get_to(t.refresh_interval_ms);
j.at("timeout_ms").get_to(t.timeout_ms);
j.at("read_timeout_ms").get_to(t.read_timeout_ms);
j.at("auth").get_to(t.auth);
}
// 数据源配置
struct DataSourceConfig {
PositionDataConfig position; // 位置数据源配置
UnmannedVehicleConfig vehicle; // 无人车数据源配置
TrafficLightConfig traffic_light; // 红绿灯数据源配置
// 从系统配置创建数据源配置的静态工厂方法
static DataSourceConfig fromSystemConfig(const SystemConfig& system_config);
};
// DataSourceConfig 的 JSON 序列化/反序列化函数
inline void to_json(json& j, const DataSourceConfig& d) {
j = json{
{"position", d.position},
{"unmanned_vehicle", d.vehicle},
{"traffic_light", d.traffic_light}
};
}
inline void from_json(const json& j, DataSourceConfig& d) {
j.at("position").get_to(d.position);
j.at("unmanned_vehicle").get_to(d.vehicle);
j.at("traffic_light").get_to(d.traffic_light);
}
#endif

View File

@ -3,6 +3,7 @@
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
#include "collector/DataSourceConfig.h"
using json = nlohmann::json;
@ -45,19 +46,7 @@ public:
std::vector<CoordinatePoint> coordinate_points;
} airport;
struct DataSource {
std::string host;
uint16_t port;
std::string aircraft_path;
std::string vehicle_path;
std::string traffic_light_path;
std::string auth_path; // 认证接口路径
std::string username; // 用户名
std::string password; // 密码
int refresh_interval_ms;
int timeout_ms;
int read_timeout_ms;
} data_source;
DataSourceConfig data_source; // 使用 DataSourceConfig 替换原有的 DataSource 结构
struct WebSocket {
struct PositionUpdate {
@ -206,36 +195,6 @@ inline void from_json(const json& j, SystemConfig::CollisionDetection& p) {
j.at("prediction").get_to(p.prediction);
}
inline void to_json(json& j, const SystemConfig::DataSource& p) {
j = json{
{"host", p.host},
{"port", p.port},
{"aircraft_path", p.aircraft_path},
{"vehicle_path", p.vehicle_path},
{"traffic_light_path", p.traffic_light_path},
{"auth_path", p.auth_path},
{"username", p.username},
{"password", p.password},
{"refresh_interval_ms", p.refresh_interval_ms},
{"timeout_ms", p.timeout_ms},
{"read_timeout_ms", p.read_timeout_ms}
};
}
inline void from_json(const json& j, SystemConfig::DataSource& p) {
j.at("host").get_to(p.host);
j.at("port").get_to(p.port);
j.at("aircraft_path").get_to(p.aircraft_path);
j.at("vehicle_path").get_to(p.vehicle_path);
j.at("traffic_light_path").get_to(p.traffic_light_path);
j.at("auth_path").get_to(p.auth_path);
j.at("username").get_to(p.username);
j.at("password").get_to(p.password);
j.at("refresh_interval_ms").get_to(p.refresh_interval_ms);
j.at("timeout_ms").get_to(p.timeout_ms);
j.at("read_timeout_ms").get_to(p.read_timeout_ms);
}
inline void to_json(json& j, const SystemConfig::Logging& p) {
j = json{
{"level", p.level},
@ -300,4 +259,5 @@ inline void from_json(const json& j, SystemConfig& config) {
j.at("logging").get_to(config.logging);
j.at("debug").get_to(config.debug);
j.at("warning").get_to(config.warning);
}
}

View File

@ -9,7 +9,8 @@
System* System::instance_ = nullptr;
System::System() {
System::System()
: controllableVehicles_(ControllableVehicles::getInstance()) {
instance_ = this;
std::signal(SIGINT, signalHandler);
std::signal(SIGTERM, signalHandler);
@ -48,35 +49,17 @@ bool System::initialize() {
// 加载机场区域配置
airportBounds_ = std::make_unique<AirportBounds>("config/airport_bounds.json");
// 加载可控车辆配置
controllableVehicles_ = std::make_unique<ControllableVehicles>("config/vehicle_control.json");
Logger::info("Loaded controllable vehicles configuration");
// 初始化冲突检测器
collisionDetector_ = std::make_unique<CollisionDetector>(*airportBounds_, *controllableVehicles_);
collisionDetector_ = std::make_unique<CollisionDetector>(*airportBounds_, controllableVehicles_);
// 初始化红绿灯检测器
trafficLightDetector_ = std::make_unique<TrafficLightDetector>(intersection_config_, *controllableVehicles_, *this);
trafficLightDetector_ = std::make_unique<TrafficLightDetector>(intersection_config_, controllableVehicles_, *this);
// 创建数据采集器
dataCollector_ = std::make_unique<DataCollector>();
// 数据采集器初始化并启动
DataSourceConfig dataSourceConfig{
system_config.data_source.host,
system_config.data_source.port,
system_config.data_source.aircraft_path,
system_config.data_source.vehicle_path,
system_config.data_source.traffic_light_path,
system_config.data_source.auth_path,
system_config.data_source.username,
system_config.data_source.password,
system_config.data_source.refresh_interval_ms,
system_config.data_source.timeout_ms,
system_config.data_source.read_timeout_ms
};
WarnConfig warnConfig{
system_config.warning.warning_interval_ms,
system_config.warning.log_interval_ms
@ -85,7 +68,8 @@ bool System::initialize() {
// 初始化安全区
initializeSafetyZones();
return dataCollector_->initialize(dataSourceConfig, warnConfig);
// 初始化数据采集器
return dataCollector_->initialize(system_config.data_source, warnConfig);
}
catch (const std::exception& e) {
Logger::error("Failed to initialize system: ", e.what());
@ -176,12 +160,18 @@ void System::processLoop() {
auto last_vehicle_update = std::chrono::steady_clock::now();
auto last_collision_update = std::chrono::steady_clock::now();
auto last_traffic_light_update = std::chrono::steady_clock::now();
auto last_safety_zone_update = std::chrono::steady_clock::now(); // 添加安全区更新时间
auto last_safety_zone_update = std::chrono::steady_clock::now();
// 添加三种数据的最后采集时间
auto last_position_collection = std::chrono::steady_clock::now();
auto last_vehicle_collection = std::chrono::steady_clock::now();
auto last_traffic_light_collection = std::chrono::steady_clock::now();
int64_t last_aircraft_timestamp = 0;
int64_t last_vehicle_timestamp = 0;
int64_t last_collision_timestamp = 0;
int64_t last_traffic_light_timestamp = 0;
int64_t last_safety_zone_timestamp = 0; // 添加全区时间戳
int64_t last_safety_zone_timestamp = 0;
Logger::debug("数据处理循环启动");
@ -189,12 +179,35 @@ void System::processLoop() {
try {
auto now = std::chrono::steady_clock::now();
const auto& system_config = SystemConfig::instance();
auto last_data_collection = now;
Logger::debug("开始获取数据...");
// 计算各数据源是否需要采集
bool need_position_collection = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_position_collection).count() >= system_config.data_source.position.refresh_interval_ms;
bool need_vehicle_collection = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_vehicle_collection).count() >= system_config.data_source.vehicle.refresh_interval_ms;
bool need_traffic_light_collection = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_traffic_light_collection).count() >= system_config.data_source.traffic_light.refresh_interval_ms;
// 根据需要刷新相应的数据
if (need_position_collection || need_vehicle_collection || need_traffic_light_collection) {
dataCollector_->refresh(); // 刷新数据
if (need_position_collection) {
last_position_collection = now;
}
if (need_vehicle_collection) {
last_vehicle_collection = now;
}
if (need_traffic_light_collection) {
last_traffic_light_collection = now;
}
}
// 获取最新数据
dataCollector_->refresh(); // 先刷新数据
auto aircraft = dataCollector_->getAircraftData();
auto vehicles = dataCollector_->getVehicleData();
auto traffic_lights = dataCollector_->getTrafficLightSignals();
@ -206,9 +219,7 @@ void System::processLoop() {
}
for (auto& veh : vehicles) {
objects.push_back(&veh);
//Logger::debug("车辆 ", veh.vehicleNo, " 类型: ", veh.type);
//Logger::debug("车辆 ", veh.vehicleNo, " 是否可控: ", veh.isControllable);
const auto* config = controllableVehicles_->findVehicle(veh.vehicleNo);
const auto* config = controllableVehicles_.findVehicle(veh.vehicleNo);
if (config) {
veh.type = config->type == "UNMANNED" ? MovingObjectType::UNMANNED : MovingObjectType::SPECIAL;
veh.isControllable = config->type == "UNMANNED";
@ -218,8 +229,11 @@ void System::processLoop() {
// 检查安全区更新
auto safety_zone_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_safety_zone_update).count();
Logger::debug("安全区检测时间间隔: elapsed=", safety_zone_elapsed, "ms, interval=", system_config.collision_detection.update_interval_ms, "ms");
if (safety_zone_elapsed >= system_config.collision_detection.update_interval_ms) {
// 只有在数据更新时才进行检测
Logger::debug("数据时间戳: aircraft=", last_aircraft_timestamp, ", vehicle=", last_vehicle_timestamp, ", last_safety=", last_safety_zone_timestamp);
if (last_aircraft_timestamp > last_safety_zone_timestamp ||
last_vehicle_timestamp > last_safety_zone_timestamp) {
// 更新安全区状态
@ -232,7 +246,7 @@ void System::processLoop() {
}
// 检查无人车与安全区的冲突
checkUnmannedVehicleSafetyZones(vehicles, objects);
//checkUnmannedVehicleSafetyZones(vehicles, objects);
// 检查航空器更新
auto aircraft_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
@ -355,9 +369,23 @@ void System::processLoop() {
last_traffic_light_update = now;
}
// 计算下一次数据采集前的等待时间
auto next_collection = last_data_collection + std::chrono::milliseconds(system_config.data_source.refresh_interval_ms);
auto wait_time = std::chrono::duration_cast<std::chrono::milliseconds>(next_collection - now).count();
// 计算下一次数据采集的等待时间
auto next_position_collection = last_position_collection +
std::chrono::milliseconds(system_config.data_source.position.refresh_interval_ms);
auto next_vehicle_collection = last_vehicle_collection +
std::chrono::milliseconds(system_config.data_source.vehicle.refresh_interval_ms);
auto next_traffic_light_collection = last_traffic_light_collection +
std::chrono::milliseconds(system_config.data_source.traffic_light.refresh_interval_ms);
// 取最小等待时间
auto next_collection = std::min({next_position_collection,
next_vehicle_collection,
next_traffic_light_collection});
auto wait_time = std::chrono::duration_cast<std::chrono::milliseconds>(
next_collection - now).count();
if (wait_time > 0) {
Logger::debug("等待 ", wait_time, "ms 进行下一次采集");
@ -380,8 +408,8 @@ void System::processCollisions(const std::vector<CollisionRisk>& risks) {
// 处理当前的碰撞风险
for (const auto& risk : risks) {
// 处理 id1 和 id2 中的可控车辆
bool id1_controllable = controllableVehicles_->isControllable(risk.id1);
bool id2_controllable = controllableVehicles_->isControllable(risk.id2);
bool id1_controllable = controllableVehicles_.isControllable(risk.id1);
bool id2_controllable = controllableVehicles_.isControllable(risk.id2);
// 如果两个都不是可控车辆,跳过
if (!id1_controllable && !id2_controllable) {
@ -414,7 +442,7 @@ void System::processCollisions(const std::vector<CollisionRisk>& risks) {
}
broadcastVehicleCommand(cmd);
controllableVehicles_->sendCommand(vehicleId, cmd);
controllableVehicles_.sendCommand(vehicleId, cmd);
Logger::warning("发送告警指令到车辆: ", vehicleId,
" 当前距离: ", risk.distance, "m",
" 预测最小距离: ", risk.minDistance, "m",
@ -445,7 +473,7 @@ void System::processCollisions(const std::vector<CollisionRisk>& risks) {
}
broadcastVehicleCommand(cmd);
controllableVehicles_->sendCommand(vehicleId, cmd);
controllableVehicles_.sendCommand(vehicleId, cmd);
Logger::info("发送预警指令到车辆: ", vehicleId,
" 当前距离: ", risk.distance, "m",
" 预测最小距离: ", risk.minDistance, "m",
@ -492,7 +520,7 @@ void System::processCollisions(const std::vector<CollisionRisk>& risks) {
std::chrono::system_clock::now().time_since_epoch()).count();
broadcastVehicleCommand(cmd);
controllableVehicles_->sendCommand(vehicleId, cmd);
controllableVehicles_.sendCommand(vehicleId, cmd);
Logger::info("发送恢复指令到车辆: ", vehicleId);
} else {
Logger::debug("车辆 ", vehicleId, " 仍然有风险,不发送恢复指令");
@ -730,28 +758,21 @@ void System::updateSafetyZoneStates(const std::vector<MovingObject*>& objects) {
}
void System::checkSafetyZoneIntrusion(const MovingObject& obj) {
// 创建坐标转换器
CoordinateConverter converter;
converter.setReferencePoint(
SystemConfig::instance().airport.reference_point.latitude,
SystemConfig::instance().airport.reference_point.longitude
);
// 转换目标位置到本地坐标系
Vector2D position = converter.toLocalXY(obj.geo.latitude, obj.geo.longitude);
Logger::debug("检查安全区入侵: id=", obj.id,
", type=", obj.isAircraft() ? "飞机" : (obj.isSpecialVehicle() ? "特勤车" : "其他"),
", position=(", position.x, ",", position.y, ")");
", position=(", obj.position.x, ",", obj.position.y, ")");
// 检查每个安全区
for (auto& [id, zone] : safetyZones_) {
// 检查是否在安全区内,同时会尝试设置安全区类型
// 先检查是否在安全区内
if (zone->isObjectInZone(obj)) {
zone->setState(SafetyZoneState::ACTIVE);
Logger::debug("目标 ", obj.id, " 进入路口 ", id, " 安全区, 类型: ",
zone->getType() == SafetyZoneType::AIRCRAFT ? "飞机" : "特勤车",
", 半径: ", zone->getCurrentRadius(),
", 中心点: (", zone->getCenter().x, ",", zone->getCenter().y, ")");
// 如果在区内,尝试激活安全区
if (zone->tryActivate(obj)) {
Logger::debug("目标 ", obj.id, " 进入路口 ", id, " 安全区, 类型: ",
zone->getType() == SafetyZoneType::AIRCRAFT ? "飞机" : "特勤车",
", 半径: ", zone->getCurrentRadius(),
", 中心点=(", zone->getCenter().x, ",", zone->getCenter().y, ")");
}
}
}
}
@ -765,8 +786,10 @@ void System::checkUnmannedVehicleSafetyZones(const std::vector<Vehicle>& vehicle
// 遍历所有路口安全区
for (const auto& [intersectionId, zone] : safetyZones_) {
Logger::debug("检查路口 ", intersectionId, " 安全区...");
// 如果安全区未激活,跳过
if (zone->getState() == SafetyZoneState::INACTIVE) {
Logger::debug("路口 ", intersectionId, " 安全区未激活,跳过");
continue;
}
@ -805,7 +828,7 @@ void System::checkUnmannedVehicleSafetyZones(const std::vector<Vehicle>& vehicle
std::chrono::system_clock::now().time_since_epoch()).count();
broadcastVehicleCommand(cmd);
controllableVehicles_->sendCommand(vehicle.id, cmd);
controllableVehicles_.sendCommand(vehicle.id, cmd);
lastVehiclesWithSafetyZoneRisk_.erase(it);
} else if (hasRisk) {
@ -823,7 +846,7 @@ bool System::handleSafetyZoneRisk(const Vehicle& vehicle,
CommandType cmdType,
const std::string& riskLevel) {
// 检查是否为无人车
if (!controllableVehicles_->isControllable(vehicle.id)) {
if (!controllableVehicles_.isControllable(vehicle.id)) {
return false;
}
@ -871,7 +894,7 @@ bool System::handleSafetyZoneRisk(const Vehicle& vehicle,
}
broadcastVehicleCommand(cmd);
controllableVehicles_->sendCommand(vehicle.id, cmd);
controllableVehicles_.sendCommand(vehicle.id, cmd);
CollisionRisk risk;
risk.id1 = vehicle.id;

View File

@ -75,7 +75,7 @@ private:
std::thread processThread_;
std::unique_ptr<AirportBounds> airportBounds_;
std::unique_ptr<ControllableVehicles> controllableVehicles_;
ControllableVehicles& controllableVehicles_{ControllableVehicles::getInstance()};
std::unique_ptr<CollisionDetector> collisionDetector_;
std::unique_ptr<TrafficLightDetector> trafficLightDetector_;
std::unique_ptr<DataCollector> dataCollector_;

View File

@ -8,7 +8,7 @@
CollisionDetector::CollisionDetector(const AirportBounds& bounds, const ControllableVehicles& controllableVehicles)
: airportBounds_(bounds)
, vehicleTree_(bounds.getAirportBounds(), 8) // 使用机场总边界初始化四叉树
, controllableVehicles_(&controllableVehicles) {
, controllableVehicles_(controllableVehicles) {
// 记录初始化信息
const auto& airportBounds = bounds.getAirportBounds();
@ -37,7 +37,7 @@ void CollisionDetector::updateTraffic(const std::vector<Aircraft>& aircraft,
// 根据配置设置车辆类型
Vehicle updatedVehicle = vehicle;
const auto* config = controllableVehicles_->findVehicle(vehicle.vehicleNo);
const auto* config = controllableVehicles_.findVehicle(vehicle.vehicleNo);
if (config) {
if (config->type == "UNMANNED") {
updatedVehicle.type = MovingObjectType::UNMANNED;

View File

@ -74,7 +74,7 @@ private:
const AirportBounds& airportBounds_;
QuadTree<Vehicle> vehicleTree_;
std::vector<Aircraft> aircraftData_;
const ControllableVehicles* controllableVehicles_;
const ControllableVehicles& controllableVehicles_;
// 冲突记录映射表:<(id1,id2), CollisionRecord>
std::unordered_map<std::pair<std::string, std::string>, CollisionRecord, CollisionPairHash> collisionRecords_;

View File

@ -37,96 +37,32 @@ void SafetyZone::resetType() {
Logger::debug("重置安全区类型为 NONE");
}
bool SafetyZone::trySetType(const MovingObject& object) {
// 判断目标类型
bool isAircraft = object.isAircraft();
bool isSpecialVehicle = object.isSpecialVehicle();
// 如果既不是飞机也不是特勤车,返回 false
if (!isAircraft && !isSpecialVehicle) {
return false;
double SafetyZone::getObjectRadius(const MovingObject& object) const {
const auto& config = SystemConfig::instance().collision_detection.prediction;
if (object.isAircraft()) {
double radius = config.aircraft_size / 2.0;
Logger::debug("目标 ", object.id, " 是飞机,尺寸半径: ", radius);
return radius;
} else if (object.isSpecialVehicle()) {
double radius = config.vehicle_size / 2.0;
Logger::debug("目标 ", object.id, " 是特勤车,尺寸半径: ", radius);
return radius;
}
// 如果安全区已有类型,检查是否可以改变
if (type_ != SafetyZoneType::NONE) {
// 如果当前类型与目标类型不匹配,不允许改变
if ((type_ == SafetyZoneType::AIRCRAFT && !isAircraft) ||
(type_ == SafetyZoneType::VEHICLE && !isSpecialVehicle)) {
return false;
}
// 如果类型匹配,保持当前类型
return 0.0;
}
bool SafetyZone::isTypeMatched(const MovingObject& object) const {
// 如果安全区类型未设置,返回 true
if (type_ == SafetyZoneType::NONE) {
return true;
}
// 设置新的类型和尺寸
if (isAircraft) {
type_ = SafetyZoneType::AIRCRAFT;
currentRadius_ = aircraftRadius_;
state_ = SafetyZoneState::ACTIVE; // 激活安全区
Logger::debug("安全区设置为飞机类型,半径: ", currentRadius_);
} else {
type_ = SafetyZoneType::VEHICLE;
currentRadius_ = specialVehicleRadius_;
state_ = SafetyZoneState::ACTIVE; // 激活安全区
Logger::debug("安全区设置为特勤车类型,半径: ", currentRadius_);
}
return true;
}
bool SafetyZone::isObjectInZone(const MovingObject& object) const {
// 如果是无人车,直接返回 false
// 如果是无人车,返回 true
if (object.isUnmannedVehicle()) {
Logger::debug("目标 ", object.id, " 是无人车,不触发安全区");
return false;
return true;
}
// 从 SystemConfig 获取物体尺寸(半长度)
const auto& config = SystemConfig::instance().collision_detection.prediction;
double objectRadius = 0.0;
// 获取物体半径
if (object.isAircraft()) {
objectRadius = config.aircraft_size / 2.0;
Logger::debug("目标 ", object.id, " 是飞机,尺寸半径: ", objectRadius);
} else if (object.isSpecialVehicle()) {
objectRadius = config.vehicle_size / 2.0;
Logger::debug("目标 ", object.id, " 是特勤车,尺寸半径: ", objectRadius);
} else {
return false;
}
// 如果安全区类型未设置,尝试设置类型
if (type_ == SafetyZoneType::NONE) {
// 临时设置类型和半径来检查是否在区内
SafetyZoneType tempType = object.isAircraft() ? SafetyZoneType::AIRCRAFT : SafetyZoneType::VEHICLE;
double tempRadius = object.isAircraft() ? aircraftRadius_ : specialVehicleRadius_;
// 暂存当前值
auto currentType = type_;
auto currentRadius = currentRadius_;
// 临时设置类型和半径
const_cast<SafetyZone*>(this)->type_ = tempType;
const_cast<SafetyZone*>(this)->currentRadius_ = tempRadius;
// 检查是否在区内
bool inZone = isInZone(object.position);
// 如果在区内,保持新的类型和半径,否则恢复原值
if (!inZone) {
const_cast<SafetyZone*>(this)->type_ = currentType;
const_cast<SafetyZone*>(this)->currentRadius_ = currentRadius;
} else {
// 设置状态为激活
const_cast<SafetyZone*>(this)->state_ = SafetyZoneState::ACTIVE;
Logger::debug("安全区设置为", object.isAircraft() ? "飞机" : "特勤车", "类型,半径: ", tempRadius);
}
return inZone;
}
// 如果类型已设置,检查是否匹配
// 如果安全区类型已设置,检查是否匹配
if ((type_ == SafetyZoneType::AIRCRAFT && !object.isAircraft()) ||
(type_ == SafetyZoneType::VEHICLE && !object.isSpecialVehicle())) {
Logger::debug("目标 ", object.id, " 类型与安全区类型不匹配,当前安全区类型: ",
@ -134,6 +70,57 @@ bool SafetyZone::isObjectInZone(const MovingObject& object) const {
return false;
}
// 使用 isInZone 检查是否在区内
return isInZone(object.position);
return true;
}
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)
? (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, ")");
return distance <= checkRadius;
}
bool SafetyZone::tryActivate(const MovingObject& object) {
// 如果是无人车,不激活安全区
if (object.isUnmannedVehicle()) {
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;
}

View File

@ -34,9 +34,12 @@ public:
// 检查点是否在安全区内
bool isInZone(const Vector2D& point) const;
// 检查目标是否在安全区内
// 检查目标是否在安全区内 - 纯检测函数,不修改状态
bool isObjectInZone(const MovingObject& object) const;
// 尝试更新安全区状态 - 处理状态更新
bool tryActivate(const MovingObject& object);
// 获取中心点
const Vector2D& getCenter() const { return center_; }
@ -52,9 +55,12 @@ private:
double aircraftRadius_; // 飞机安全区半径
double specialVehicleRadius_; // 特勤车安全区半径
double currentRadius_; // 当前安全区半径
SafetyZoneState state_; // 当前状态
SafetyZoneType type_; // 当前类型
SafetyZoneState state_; // 当前状态
SafetyZoneType type_; // 当前类型
// 尝试设置安全区类型和尺寸
bool trySetType(const MovingObject& object);
// 检查物体类型是否匹配当前安全区类型
bool isTypeMatched(const MovingObject& object) const;
// 获取物体的有效半径
double getObjectRadius(const MovingObject& object) const;
};

View File

@ -5,7 +5,7 @@
#include <chrono>
TrafficLightDetector::TrafficLightDetector(const IntersectionConfig& intersectionConfig,
const ControllableVehicles& controllableVehicles,
ControllableVehicles& controllableVehicles,
System& system)
: intersection_config_(intersectionConfig)
, controllable_vehicles_(controllableVehicles)
@ -68,4 +68,4 @@ void TrafficLightDetector::sendSignalCommand(const std::string& vehicleNo, Signa
Logger::debug("发送信号灯指令到车辆: ", vehicleNo,
" 路口: ", cmd.intersectionId,
" 状态: ", state == SignalState::RED ? "红灯" : state == SignalState::GREEN ? "绿灯" : "黄灯");
}
}

View File

@ -10,7 +10,7 @@ class System;
class TrafficLightDetector {
public:
TrafficLightDetector(const IntersectionConfig& intersectionConfig,
const ControllableVehicles& controllableVehicles,
ControllableVehicles& controllableVehicles,
System& system);
void processSignal(const TrafficLightSignal& signal,
@ -18,7 +18,7 @@ public:
private:
const IntersectionConfig& intersection_config_;
const ControllableVehicles& controllable_vehicles_;
ControllableVehicles& controllable_vehicles_;
System& system_;
TrafficLightSignal current_signal_;

View File

@ -6,16 +6,19 @@
using json = nlohmann::json;
size_t HTTPDataSource::WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
size_t total_size = size * nmemb;
std::string* response = static_cast<std::string*>(userp);
response->append(static_cast<char*>(contents), total_size);
return total_size;
}
size_t HTTPDataSource::DebugCallback(CURL* handle, curl_infotype type, char* data, size_t size, void* userp) {
return 0;
}
HTTPDataSource::HTTPDataSource(const DataSourceConfig& config)
: config_(config)
, host_(config.host)
, port_(std::to_string(config.port))
, last_health_check_(std::chrono::steady_clock::now())
, is_authenticated_(false) {
, last_health_check_(std::chrono::steady_clock::now()) {
curl_ = curl_easy_init();
if (!curl_) {
Logger::error("Failed to initialize CURL");
@ -37,9 +40,7 @@ bool HTTPDataSource::connect() {
return false;
}
}
// 进行认证
return authenticate();
return true;
}
void HTTPDataSource::disconnect() {
@ -55,7 +56,7 @@ bool HTTPDataSource::isAvailable() const {
bool HTTPDataSource::tryReconnect() {
if (is_reconnecting_) {
return false; // 已经在重连中
return false;
}
is_reconnecting_ = true;
@ -65,24 +66,21 @@ bool HTTPDataSource::tryReconnect() {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_connect_attempt_).count();
if (elapsed < config_.refresh_interval_ms) {
if (elapsed < config_.position.timeout_ms) {
is_reconnecting_ = false;
return false; // 距离上次尝试时间太短
return false;
}
Logger::info("Attempting to reconnect to ", config_.host, ":", config_.port);
last_connect_attempt_ = now;
for (int retry = 0; retry < MAX_RETRIES; ++retry) {
if (connect()) {
Logger::info("Successfully reconnected");
is_reconnecting_ = false;
return true;
}
if (retry < MAX_RETRIES - 1) {
Logger::info("Reconnection attempt ", retry + 1, " failed, retrying...");
std::this_thread::sleep_for(std::chrono::milliseconds(config_.refresh_interval_ms/3));
std::this_thread::sleep_for(std::chrono::milliseconds(config_.position.timeout_ms/3));
}
}
@ -103,7 +101,7 @@ bool HTTPDataSource::checkConnectionHealth() {
now - last_health_check_).count();
if (elapsed < HEALTH_CHECK_INTERVAL) {
return true; // 距离上次检查时间不够长
return true;
}
last_health_check_ = now;
@ -118,108 +116,125 @@ bool HTTPDataSource::ensureConnected() {
return tryReconnect();
}
bool HTTPDataSource::sendRequest(const std::string& path, std::string& response, HttpMethod method) {
bool HTTPDataSource::sendRequest(const std::string& url, const AuthState* auth_state,
std::string& response, HttpMethod method, const std::string& body) {
if (!curl_) {
Logger::error("CURL not initialized");
return false;
}
// 检查认证状态
if (!is_authenticated_) {
Logger::error("Not authenticated");
if (!authenticate()) {
return false;
}
}
std::string url = "http://" + host_ + ":" + port_ + path;
Logger::debug("Sending request to: ", url);
curl_easy_reset(curl_);
curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl_, CURLOPT_TIMEOUT, config_.read_timeout_ms / 1000);
curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT, config_.timeout_ms / 1000);
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);
} else if (method == HttpMethod::POST) {
curl_easy_setopt(curl_, CURLOPT_POST, 1L);
curl_easy_setopt(curl_, CURLOPT_HTTPGET, 0L);
if (!body.empty()) {
curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, body.c_str());
} else {
curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, "");
}
}
// 添加 HTTP 头
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:");
// 添加认证 token
if (!auth_token_.empty()) {
std::string auth_header = "Authorization: " + auth_token_;
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("Failed to send request: ", curl_easy_strerror(res));
Logger::error("请求失败: ", curl_easy_strerror(res));
return false;
}
long http_code = 0;
curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &http_code);
// 如果返回 401尝试重新认证
if (http_code == 401) {
Logger::warning("Token expired, trying to re-authenticate");
if (authenticate()) {
// 重新发送请求
return sendRequest(path, response, method);
}
return false;
}
if (http_code != 200) {
Logger::error("HTTP request failed with code: ", http_code);
Logger::error("HTTP 请求失败,状态码: ", http_code);
return false;
}
return true;
}
bool HTTPDataSource::fetchAircraftData(std::vector<Aircraft>& aircraft) {
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)) {
Logger::error("认证失败");
return false;
}
std::string url = "http://" + config_.position.host + ":" +
std::to_string(config_.position.port) +
config_.position.aircraft_path;
std::string response;
if (!sendRequest(config_.aircraft_path, response, HttpMethod::GET)) {
if (!sendRequest(url, &position_auth_, response, HttpMethod::GET)) {
Logger::error("请求失败");
return false;
}
return parseAircraftResponse(response, aircraft);
return parsePositionAircraftResponse(response, aircraft);
}
bool HTTPDataSource::fetchVehicleData(std::vector<Vehicle>& vehicles) {
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)) {
return false;
}
std::string url = "http://" + config_.position.host + ":" +
std::to_string(config_.position.port) +
config_.position.vehicle_path;
std::string response;
if (!sendRequest(config_.vehicle_path, response, HttpMethod::GET)) {
if (!sendRequest(url, &position_auth_, response, HttpMethod::GET)) {
return false;
}
return parseVehicleResponse(response, vehicles);
return parsePositionVehicleResponse(response, vehicles);
}
bool HTTPDataSource::fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals) {
@ -229,224 +244,164 @@ bool HTTPDataSource::fetchTrafficLightSignals(std::vector<TrafficLightSignal>& s
return false;
}
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 response;
if (!sendRequest(config_.traffic_light_path, response, HttpMethod::GET)) {
if (!sendRequest(url, &traffic_light_auth_, response, HttpMethod::GET)) {
return false;
}
return parseTrafficLightResponse(response, signals);
}
bool HTTPDataSource::parseTrafficLightResponse(const std::string& response, std::vector<TrafficLightSignal>& signals) {
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)) {
return false;
}
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},
{"vehicleID", command.vehicleId},
{"commandType", [&]() {
switch (command.type) {
case CommandType::SIGNAL: return "SIGNAL";
case CommandType::ALERT: return "ALERT";
case CommandType::WARNING: return "WARNING";
case CommandType::RESUME: return "RESUME";
default: return "UNKNOWN";
}
}()},
{"commandReason", [&]() {
switch (command.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";
default: return "UNKNOWN";
}
}()},
{"latitude", command.latitude},
{"longitude", command.longitude}
};
if (command.type == CommandType::SIGNAL) {
request["signalState"] = command.signalState == SignalState::RED ? "RED" : "GREEN";
request["intersectionId"] = command.intersectionId;
}
if (command.type == CommandType::ALERT || command.type == CommandType::WARNING) {
request["relativeSpeed"] = command.relativeSpeed;
request["relativeMotionX"] = command.relativeMotionX;
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 {
json j = json::parse(response);
if (!j.is_array()) {
Logger::error("Invalid traffic light response format: expected array");
nlohmann::json j = nlohmann::json::parse(response);
if (!j.contains("code") || !j.contains("msg")) {
Logger::error("Invalid command response format");
return false;
}
signals.clear();
signals.reserve(j.size());
for (const auto& item : j) {
TrafficLightSignal signal;
// 解析必需字段
if (!item.contains("id") || !item.contains("state")) {
Logger::error("Missing required fields in traffic light signal");
continue;
}
signal.trafficLightId = item["id"].get<std::string>();
// 支持数字或字符串类型的状态
if (item["state"].is_number()) {
signal.status = static_cast<SignalStatus>(item["state"].get<int>());
} else {
signal.status = TrafficLightSignal::parseStatus(item["state"].get<std::string>());
}
// 解析可选字段
if (item.contains("timestamp")) {
signal.timestamp = item["timestamp"].get<int64_t>();
}
signals.push_back(signal);
if (j["code"].get<int>() != 200) {
Logger::error("Command failed: ", j["msg"].get<std::string>());
return false;
}
return !signals.empty();
}
catch (const json::exception& e) {
Logger::error("Failed to parse traffic light response: ", e.what());
return false;
return true;
}
catch (const std::exception& e) {
Logger::error("Error processing traffic light response: ", e.what());
Logger::error("Failed to parse command response: ", e.what());
return false;
}
}
bool HTTPDataSource::parseVehicleResponse(const std::string& response, std::vector<Vehicle>& vehicles) {
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)) {
return false;
}
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 {
json j = json::parse(response);
// 检查顶层结构
nlohmann::json j = nlohmann::json::parse(response);
if (!j.contains("status") || !j.contains("data")) {
Logger::error("Invalid vehicle response format: missing status or data field");
return false;
}
// 检查状态码
int status = j["status"].get<int>();
if (status != 200) {
Logger::error("Vehicle response error, status: " + std::to_string(status));
return false;
}
// 获取数据数组
const auto& data = j["data"];
if (!data.is_array()) {
Logger::error("Invalid vehicle response format: data is not an array");
Logger::error("Invalid vehicle status response format");
return false;
}
vehicles.clear();
vehicles.reserve(data.size());
for (const auto& item : data) {
Vehicle veh;
// 解析必需字段
if (!item.contains("vehicleNo") || !item.contains("latitude") ||
!item.contains("longitude") || !item.contains("time")) {
Logger::error("Missing required fields in vehicle data");
continue;
}
veh.vehicleNo = item["vehicleNo"].get<std::string>();
veh.id = veh.vehicleNo; // 使用车辆号作为ID
veh.geo.latitude = item["latitude"].get<double>();
veh.geo.longitude = item["longitude"].get<double>();
// 时间戳转换
veh.timestamp = item["time"].get<uint64_t>();
// 解析可选字段
if (item.contains("direction")) {
veh.heading = item["direction"].get<double>();
}
if (item.contains("speed")) {
veh.speed = item["speed"].get<double>();
}
// 更新位置信息
veh.position = coordinateConverter_.toLocalXY(veh.geo.latitude, veh.geo.longitude);
veh.updateMotion(veh.geo, veh.timestamp); // 更新速度和航向
vehicles.push_back(veh);
if (j["status"].get<int>() != 200) {
Logger::error("Failed to get vehicle status");
return false;
}
return !vehicles.empty();
}
catch (const json::exception& e) {
Logger::error("Failed to parse vehicle response: ", e.what());
return false;
status = j["data"].dump();
return true;
}
catch (const std::exception& e) {
Logger::error("Error processing vehicle response: ", e.what());
Logger::error("Failed to parse vehicle status response: ", e.what());
return false;
}
}
bool HTTPDataSource::parseAircraftResponse(const std::string& response, std::vector<Aircraft>& aircraft) {
try {
json j = json::parse(response);
// 检查顶层结构
if (!j.contains("status") || !j.contains("data")) {
Logger::error("Invalid aircraft response format: missing status or data field");
return false;
}
// 检查状态码
int status = j["status"].get<int>();
if (status != 200) {
Logger::error("Aircraft response error, status: " + std::to_string(status));
return false;
}
// 获取数据数组
const auto& data = j["data"];
if (!data.is_array()) {
Logger::error("Invalid aircraft response format: data is not an array");
return false;
}
aircraft.clear();
aircraft.reserve(data.size());
// 获取当前时间戳,用于临时替代
uint64_t current_timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
for (const auto& item : data) {
Aircraft ac;
// 解析必需字段
if (!item.contains("flightNo") || !item.contains("latitude") ||
!item.contains("longitude") || !item.contains("time")) {
Logger::error("Missing required fields in aircraft data");
continue;
}
ac.flightNo = item["flightNo"].get<std::string>();
ac.id = ac.flightNo; // 使用航班号作为ID
ac.geo.latitude = item["latitude"].get<double>();
ac.geo.longitude = item["longitude"].get<double>();
// 时间戳处理:暂时使用本地时间戳
// 注意:机场给的时间戳可能是 double 格式,我们暂时不使用它
ac.timestamp = current_timestamp;
// 解析可选字段
if (item.contains("altitude")) {
ac.altitude = item["altitude"].get<double>();
} else {
ac.altitude = 0.0; // 默认值
}
if (item.contains("trackNumber")) {
// 航迹号可能是数字类型,需要转换为字符串
if (item["trackNumber"].is_number()) {
ac.trackNumber = std::to_string(item["trackNumber"].get<int>());
} else {
ac.trackNumber = item["trackNumber"].get<std::string>();
}
} else {
ac.trackNumber = "TN" + ac.flightNo.substr(2); // 默认使用航班号生成跟踪号
}
// 更新位置信息
ac.position = coordinateConverter_.toLocalXY(ac.geo.latitude, ac.geo.longitude);
ac.updateMotion(ac.geo, ac.timestamp); // 更新速度和航向
aircraft.push_back(ac);
}
return !aircraft.empty();
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;
}
catch (const json::exception& e) {
Logger::error("Failed to parse aircraft response: ", e.what());
return false;
}
catch (const std::exception& e) {
Logger::error("Error processing aircraft response: ", e.what());
return false;
}
}
bool HTTPDataSource::authenticate() {
std::lock_guard<std::mutex> lock(auth_mutex_);
if (!curl_) {
@ -454,70 +409,193 @@ bool HTTPDataSource::authenticate() {
return false;
}
// 构造带参数的认证URL
std::string url = "http://" + host_ + ":" + port_ + config_.auth_path +
"?username=" + config_.username +
"&password=" + config_.password;
Logger::debug("Authentication URL: ", url);
std::string url = "http://" + host + ":" + std::to_string(port) +
auth_config.auth_path + "?username=" + auth_config.username +
"&password=" + auth_config.password;
std::string response;
// 设置CURL选项
curl_easy_reset(curl_);
curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl_, CURLOPT_POST, 1L); // 使用POST方法
curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, ""); // 空的POST数据
curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &response);
// 设置请求头
struct curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "Accept: application/json");
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
// 发送请求
CURLcode res = curl_easy_perform(curl_);
curl_slist_free_all(headers);
if (res != CURLE_OK) {
Logger::error("Failed to authenticate: ", 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("Authentication failed with HTTP code: ", http_code);
Logger::error("Response body: ", response);
if (!sendRequest(url, nullptr, response, HttpMethod::POST, "")) {
Logger::error("认证请求失败");
return false;
}
try {
Logger::debug("Authentication response: ", response);
// 解析响应获取 token
nlohmann::json j = nlohmann::json::parse(response);
if (!j.contains("status") || !j.contains("msg") || !j.contains("data")) {
Logger::error("Authentication response missing required fields");
Logger::error("认证响应缺少必需字段");
return false;
}
if (j["status"].get<int>() != 200) {
Logger::error("Authentication failed: ", j["msg"].get<std::string>());
Logger::error("认证失败: ", j["msg"].get<std::string>());
return false;
}
auth_token_ = j["data"].get<std::string>();
is_authenticated_ = true;
Logger::debug("Stored token: ", auth_token_);
Logger::info("Successfully authenticated: ", j["msg"].get<std::string>());
std::string token = j["data"].get<std::string>();
if (token.substr(0, 7) != "Bearer ") {
token = "Bearer " + token;
}
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) {
Logger::error("Failed to parse authentication response: ", e.what());
Logger::error("Raw response: ", response);
Logger::error("解析认证响应失败: ", e.what(), ", 响应内容: ", response);
return false;
}
}
}
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,
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) {
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;
}
}
return true;
}
bool HTTPDataSource::parsePositionAircraftResponse(const std::string& response, std::vector<Aircraft>& aircraft) {
try {
nlohmann::json j = nlohmann::json::parse(response);
if (!j.contains("status") || !j.contains("data")) {
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) {
Aircraft ac;
ac.flightNo = item["flightNo"].get<std::string>();
ac.id = ac.flightNo;
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>();
}
if (item.contains("trackNumber")) {
ac.trackNumber = item["trackNumber"].get<int64_t>();
}
aircraft.push_back(ac);
}
return true;
}
catch (const std::exception& e) {
Logger::error("Failed to parse aircraft response: ", e.what());
return false;
}
}
bool HTTPDataSource::parsePositionVehicleResponse(const std::string& response, std::vector<Vehicle>& vehicles) {
try {
nlohmann::json j = nlohmann::json::parse(response);
if (!j.contains("status") || !j.contains("data")) {
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) {
Vehicle vehicle;
vehicle.vehicleNo = item["vehicleNo"].get<std::string>();
vehicle.id = vehicle.vehicleNo;
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>();
}
if (item.contains("speed")) {
vehicle.speed = item["speed"].get<double>();
}
vehicles.push_back(vehicle);
}
return true;
}
catch (const std::exception& e) {
Logger::error("Failed to parse vehicle response: ", e.what());
return false;
}
}
bool HTTPDataSource::parseTrafficLightResponse(const std::string& response, std::vector<TrafficLightSignal>& signals) {
try {
nlohmann::json j = nlohmann::json::parse(response);
if (!j.contains("status") || !j.contains("data")) {
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) {
TrafficLightSignal signal;
signal.trafficLightId = item["id"].get<std::string>();
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;
}
}();
signal.timestamp = item["timestamp"].get<uint64_t>();
signal.intersectionId = item["intersection"].get<std::string>();
signal.latitude = item["position"]["latitude"].get<double>();
signal.longitude = item["position"]["longitude"].get<double>();
signals.push_back(signal);
}
return true;
}
catch (const std::exception& e) {
Logger::error("Failed to parse traffic light response: ", e.what());
return false;
}
}

View File

@ -10,6 +10,7 @@
#include <chrono>
#include "core/System.h"
#include <curl/curl.h>
#include "types/VehicleCommand.h"
class HTTPDataSource : public DataSource {
public:
@ -28,13 +29,18 @@ public:
void disconnect() override;
bool isAvailable() const override;
bool fetchAircraftData(std::vector<Aircraft>& aircraft) override;
bool fetchVehicleData(std::vector<Vehicle>& vehicles) override;
// 位置数据接口
bool fetchPositionAircraftData(std::vector<Aircraft>& aircraft) override;
bool fetchPositionVehicleData(std::vector<Vehicle>& vehicles) override;
// 无人车接口
bool sendUnmannedVehicleCommand(const std::string& vehicle_id, const VehicleCommand& command) override;
bool fetchUnmannedVehicleStatus(const std::string& vehicle_id, std::string& status) override;
// 红绿灯接口
bool fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals) override;
private:
std::string host_;
std::string port_;
CoordinateConverter coordinateConverter_;
std::mutex mutex_;
CURL* curl_;
@ -45,23 +51,47 @@ private:
static constexpr int HEALTH_CHECK_INTERVAL = 60; // 健康检查间隔(秒)
std::atomic<bool> is_reconnecting_{false}; // 重连状态标志
std::string auth_token_; // 认证 token
bool is_authenticated_; // 认证状态
std::mutex auth_mutex_; // 认证相关的互斥锁
// 每个数据源的认证状态
struct AuthState {
bool is_authenticated = false;
std::string token;
std::chrono::steady_clock::time_point last_auth_time;
};
bool authenticate(); // 认证方法
bool refreshToken(); // 刷新 token
AuthState position_auth_; // 位置数据源认证状态
AuthState unmanned_vehicle_auth_; // 无人车数据源认证状态
AuthState traffic_light_auth_; // 红绿灯数据源认证状态
std::mutex auth_mutex_; // 认证相关的互斥锁
// 认证相关方法
bool ensureAuthenticated(const AuthConfig& auth_config, const std::string& host,
int port, AuthState& auth_state, DataSourceType type);
bool tryReconnect();
bool ensureConnected();
bool checkConnectionHealth(); // 检查连接健康状况
bool sendRequest(const std::string& path, std::string& response, HttpMethod method = HttpMethod::GET);
bool parseAircraftResponse(const std::string& response, std::vector<Aircraft>& aircraft);
bool parseVehicleResponse(const std::string& response, std::vector<Vehicle>& vehicles);
bool sendRequest(const std::string& url, const AuthState* auth_state,
std::string& response, HttpMethod method = HttpMethod::GET,
const std::string& body = "");
// 位置数据响应解析
bool parsePositionAircraftResponse(const std::string& response, std::vector<Aircraft>& aircraft);
bool parsePositionVehicleResponse(const std::string& response, std::vector<Vehicle>& vehicles);
// 红绿灯响应解析
bool parseTrafficLightResponse(const std::string& response, std::vector<TrafficLightSignal>& signals);
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp);
static size_t DebugCallback(CURL* handle, curl_infotype type, char* data, size_t size, void* userp);
// 不同数据源的认证方法
bool authenticatePosition(const AuthConfig& auth_config, const std::string& host,
int port, AuthState& auth_state);
bool authenticateUnmanned(const AuthConfig& auth_config, const std::string& host,
int port, AuthState& auth_state);
bool authenticateTrafficLight(const AuthConfig& auth_config, const std::string& host,
int port, AuthState& auth_state);
};
#endif // AIRPORT_NETWORK_HTTP_DATA_SOURCE_H

View File

@ -1,5 +1,6 @@
#include "types/BasicTypes.h"
#include "utils/Logger.h"
#include "spatial/CoordinateConverter.h"
#include <cmath>
double Vector2D::magnitude() const {
@ -124,15 +125,16 @@ void MovingObject::updateMotion(const GeoPosition& newPos, uint64_t newTime) {
// 计算并直接更新航向,不需要平滑处理
heading = calculateHeading(prev.geo, curr.geo);
} else {
// Logger::debug("[Motion] No update: distance=", distance, "m < ", MIN_DISTANCE,
// "m or timeDiff=", timeDiff, "s < ", MIN_TIME, "s");
}
}
// 更新当前位置
geo = newPos;
timestamp = newTime;
// 使用坐标转换器更新平面坐标
static CoordinateConverter converter;
position = converter.toLocalXY(geo.latitude, geo.longitude);
}
void MovingObject::copyHistoryFrom(const MovingObject& other) {

View File

@ -4,6 +4,16 @@
#include <fstream>
#include <stdexcept>
// 定义静态成员变量
ControllableVehicles* ControllableVehicles::instance_ = nullptr;
ControllableVehicles& ControllableVehicles::getInstance() {
if (instance_ == nullptr) {
instance_ = new ControllableVehicles("config/vehicle_control.json");
}
return *instance_;
}
ControllableVehicles::ControllableVehicles(const std::string& configFile)
: http_client_(std::make_unique<HTTPClient>()) {
if (!configFile.empty()) {
@ -11,6 +21,13 @@ ControllableVehicles::ControllableVehicles(const std::string& configFile)
}
}
ControllableVehicles::~ControllableVehicles() {
if (instance_ != nullptr) {
delete instance_;
instance_ = nullptr;
}
}
const std::vector<ControllableVehicleConfig>& ControllableVehicles::getVehicles() const {
return vehicles_;
}
@ -26,7 +43,7 @@ const ControllableVehicleConfig* ControllableVehicles::findVehicle(const std::st
bool ControllableVehicles::isControllable(const std::string& vehicleNo) const {
// 查找车辆配置
const auto* config = findVehicle(vehicleNo);
auto* config = findVehicle(vehicleNo);
if (!config) {
return false;
}
@ -34,13 +51,14 @@ bool ControllableVehicles::isControllable(const std::string& vehicleNo) const {
return config->type == "UNMANNED";
}
void ControllableVehicles::loadConfig(const std::string& configFile) {
std::ifstream file(configFile);
if (!file.is_open()) {
throw std::runtime_error("Failed to open controllable vehicles config file: " + configFile);
}
bool ControllableVehicles::loadConfig(const std::string& configFile) {
try {
std::ifstream file(configFile);
if (!file.is_open()) {
Logger::error("Failed to open controllable vehicles config file: ", configFile);
return false;
}
nlohmann::json jsonConfig;
file >> jsonConfig;
@ -55,12 +73,14 @@ void ControllableVehicles::loadConfig(const std::string& configFile) {
}
Logger::info("Loaded ", vehicles_.size(), " controllable vehicles");
return true;
} catch (const std::exception& e) {
throw std::runtime_error("Failed to parse controllable vehicles config: " + std::string(e.what()));
Logger::error("Failed to parse controllable vehicles config: ", e.what());
return false;
}
}
bool ControllableVehicles::sendCommand(const std::string& vehicleNo, const VehicleCommand& command) const {
bool ControllableVehicles::sendCommand(const std::string& vehicleNo, const VehicleCommand& command) {
auto vehicle = findVehicle(vehicleNo);
if (!vehicle) {
Logger::error("Vehicle ", vehicleNo, " not found in controllable vehicles");

View File

@ -14,24 +14,21 @@ struct ControllableVehicleConfig {
};
class ControllableVehicles {
public:
explicit ControllableVehicles(const std::string& configFile);
virtual ~ControllableVehicles() = default;
// 获取所有可控车辆配置
const std::vector<ControllableVehicleConfig>& getVehicles() const;
// 根据车牌号查找可控车辆配置
const ControllableVehicleConfig* findVehicle(const std::string& vehicleNo) const;
// 检查车辆是否可控
virtual bool isControllable(const std::string& vehicleNo) const;
// 发送控制命令,返回是否发送成功
virtual bool sendCommand(const std::string& vehicleNo, const VehicleCommand& command) const;
private:
static ControllableVehicles* instance_;
ControllableVehicles(const std::string& configFile);
ControllableVehicles(const ControllableVehicles&) = delete;
ControllableVehicles& operator=(const ControllableVehicles&) = delete;
~ControllableVehicles();
std::vector<ControllableVehicleConfig> vehicles_;
std::unique_ptr<HTTPClient> http_client_;
void loadConfig(const std::string& configFile);
bool loadConfig(const std::string& configFile);
public:
static ControllableVehicles& getInstance();
const std::vector<ControllableVehicleConfig>& getVehicles() const;
const ControllableVehicleConfig* findVehicle(const std::string& vehicleNo) const;
bool isControllable(const std::string& vehicleNo) const;
bool sendCommand(const std::string& vehicleNo, const VehicleCommand& command);
};

View File

@ -1,15 +1,26 @@
#include "detector/CollisionDetector.h"
#include "vehicle/ControllableVehicles.h"
#include "config/AirportBounds.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <memory>
#include "detector/CollisionDetector.h"
#include "vehicle/ControllableVehicles.h"
#include "utils/Logger.h"
#include "config/SystemConfig.h"
#include "config/AirportBounds.h"
// Mock ControllableVehicles 类
class MockControllableVehicles : public ControllableVehicles {
class MockControllableVehicles {
public:
MockControllableVehicles() : ControllableVehicles("") {}
MOCK_CONST_METHOD1(isControllable, bool(const std::string& vehicleNo));
MockControllableVehicles() = default;
MOCK_METHOD(bool, isControllable, (const std::string& vehicleNo), (const));
MOCK_METHOD(const ControllableVehicleConfig*, findVehicle, (const std::string& vehicleNo), (const));
MOCK_METHOD(void, sendCommand, (const std::string& vehicleNo, const VehicleCommand& cmd));
// 转换为 ControllableVehicles& 的操作符
operator const ControllableVehicles&() const {
return ControllableVehicles::getInstance();
}
};
// Mock AirportBounds 类
@ -33,7 +44,7 @@ public:
system_config.collision_detection.prediction.time_window = 30.0; // 设置预测时间窗口为30秒
}
MOCK_CONST_METHOD1(getAreaType, AreaType(const Vector2D& position));
MOCK_METHOD(AreaType, getAreaType, (const Vector2D& position), (const));
const AreaConfig& getAreaConfig(AreaType type) const override {
auto it = areaConfigs_.find(type);
@ -56,6 +67,10 @@ protected:
EXPECT_CALL(*airportBounds_, getAreaType(::testing::_))
.WillRepeatedly(::testing::Return(AreaType::TEST_ZONE));
// 设置 Mock ControllableVehicles 的行为
EXPECT_CALL(*mockControllableVehicles_, isControllable(::testing::_))
.WillRepeatedly(::testing::Return(true));
detector_ = std::make_unique<CollisionDetector>(*airportBounds_, *mockControllableVehicles_);
}
@ -165,7 +180,7 @@ TEST_F(BasicCollisionTest, ParallelMotion) {
auto result = detector_->checkCollision(v1, v2, 30.0);
EXPECT_FALSE(result.willCollide) << "平行运动且距离大于碰撞半径的物体不应该检测为碰<EFBFBD><EFBFBD>";
EXPECT_FALSE(result.willCollide) << "平行运动且距离大于碰撞半径的物体不应该检测为碰";
EXPECT_EQ(result.type, CollisionType::PARALLEL) << "应该识别为平行运动";
// 增加更多验证
@ -210,26 +225,26 @@ TEST_F(BasicCollisionTest, CrossingPaths) {
// v2: 速度分量 (-2.588, 9.659) m/s // 10 m/s * (cos(75°), sin(75°))
// 相对速度: (-6.47, -4.83) m/s
// 相对速度大小: 8.06 m/s
// 碰撞点在两车轨迹交点前的安全距离处
double collision_time = 2.6; // 根据实际计算得到
Vector2D collision_point = {126.68, 156.39}; // 两车位置的中点
// 碰撞点在两车位置的中点
double collision_time = 2.07; // 根据实际计算得到
Vector2D collision_point = {126.34, 150.006}; // 两车位置的中点
EXPECT_NEAR(result.timeToCollision, collision_time, 0.1) << "考虑碰撞半径25米碰撞时间应该接近2.6";
EXPECT_NEAR(result.collisionPoint.x, collision_point.x, 0.1) << "碰撞点x坐标应该在126.68";
EXPECT_NEAR(result.collisionPoint.y, collision_point.y, 0.1) << "碰撞点y坐标应该在156.39";
EXPECT_NEAR(result.timeToCollision, collision_time, 0.1) << "考虑碰撞半径25米碰撞时间应该接近2.07";
EXPECT_NEAR(result.collisionPoint.x, collision_point.x, 0.1) << "碰撞点x坐标应该在126.34";
EXPECT_NEAR(result.collisionPoint.y, collision_point.y, 0.1) << "碰撞点y坐标应该在150.006";
// 增加更多验证
EXPECT_NEAR(result.minDistance, 50.0, 0.1) << "最小距离应该是碰撞半径之和50米";
EXPECT_NEAR(result.timeToMinDistance, collision_time, 0.1) << "最小距离时间应该等于碰撞时间";
// 验证物体状态
EXPECT_NEAR(result.object1State.position.x, 110.09, 0.1);
EXPECT_NEAR(result.object1State.position.y, 137.67, 0.1);
EXPECT_DOUBLE_EQ(result.object1State.speed, 15.0); // 更新为正确的速度
EXPECT_NEAR(result.object1State.position.x, 108.04, 0.1);
EXPECT_NEAR(result.object1State.position.y, 130.007, 0.1);
EXPECT_DOUBLE_EQ(result.object1State.speed, 15.0);
EXPECT_DOUBLE_EQ(result.object1State.heading, 75.0);
EXPECT_NEAR(result.object2State.position.x, 143.27, 0.1);
EXPECT_NEAR(result.object2State.position.y, 175.11, 0.1);
EXPECT_NEAR(result.object2State.position.x, 144.64, 0.1);
EXPECT_NEAR(result.object2State.position.y, 170.005, 0.1);
EXPECT_DOUBLE_EQ(result.object2State.speed, 10.0);
EXPECT_DOUBLE_EQ(result.object2State.heading, 105.0);
}
@ -257,17 +272,17 @@ TEST_F(BasicCollisionTest, PerpendicularCrossingPaths) {
EXPECT_EQ(result.type, CollisionType::CROSSING) << "应该识别为交叉碰撞";
// 计算碰撞时间和位置:
// v1: 速度分量 (0.0, 10.0) m/s
// v2: 速度分量 (-10.0, 0.0) m/s
// v1: 速度分量 (10.0, 0.0) m/s
// v2: 速度分量 (0.0, -10.0) m/s
// 相对速度: (-10.0, -10.0) m/s
// 相对速度大小: 14.14 m/s
// 由于碰撞半径为 25m实际碰撞会提前发生
double collision_time = 1.46; // 根据实际计算得到
Vector2D collision_point = {117.68, 132.32}; // 根据实际计算得到
// 当前距离小于安全距离时,立即判定为碰撞
double collision_time = 0.0; // 当前距离小于安全距离,立即碰撞
Vector2D collision_point = {125.0, 125.0}; // 两车当前位置的中点
EXPECT_NEAR(result.timeToCollision, collision_time, 0.1) << "考虑碰撞半径25米碰时间应该接近1.46秒";
EXPECT_NEAR(result.collisionPoint.x, collision_point.x, 0.1) << "碰撞点x坐标应该在117.68";
EXPECT_NEAR(result.collisionPoint.y, collision_point.y, 0.1) << "碰撞点y坐标应该在132.32";
EXPECT_NEAR(result.timeToCollision, collision_time, 0.1) << "当前距离小于安全距离碰撞时间应该为0";
EXPECT_NEAR(result.collisionPoint.x, collision_point.x, 0.1) << "碰撞点x坐标应该在125.0";
EXPECT_NEAR(result.collisionPoint.y, collision_point.y, 0.1) << "碰撞点y坐标应该在125.0";
// 增加更多验证
EXPECT_NEAR(result.minDistance, 50.0, 0.1) << "最小距离应该是碰撞半径之和50米";
@ -275,11 +290,11 @@ TEST_F(BasicCollisionTest, PerpendicularCrossingPaths) {
// 验证物体状态
EXPECT_NEAR(result.object1State.position.x, 100.0, 0.1);
EXPECT_NEAR(result.object1State.position.y, 114.64, 0.1);
EXPECT_NEAR(result.object1State.position.y, 100.0, 0.1);
EXPECT_DOUBLE_EQ(result.object1State.speed, 10.0);
EXPECT_DOUBLE_EQ(result.object1State.heading, 90.0);
EXPECT_NEAR(result.object2State.position.x, 135.36, 0.1);
EXPECT_NEAR(result.object2State.position.x, 150.0, 0.1);
EXPECT_NEAR(result.object2State.position.y, 150.0, 0.1);
EXPECT_DOUBLE_EQ(result.object2State.speed, 10.0);
EXPECT_DOUBLE_EQ(result.object2State.heading, 180.0);
@ -486,7 +501,7 @@ TEST_F(BasicCollisionTest, CrossingBeforeAndAfter) {
// 3. 测试交叉后的情况
// 航空器已经通过交叉点并远离安全距离,无人车还在交叉点附近
aircraft.position = {crossPoint.x + 120.0, crossPoint.y}; // 航空器在交叉点东侧120米
aircraft.position = {crossPoint.x + 160.0, crossPoint.y}; // 航空器在交叉点东侧160米大于安全距离150米
vehicle.position = {crossPoint.x, crossPoint.y - 10.0}; // 无人车还在交叉点附近
// 更新交通数据
@ -504,7 +519,7 @@ TEST_F(BasicCollisionTest, CrossingBeforeAndAfter) {
EXPECT_TRUE(risks.empty()) << "航空器通过远离安全距离后,应该解除冲突";
// 4. 测试无<EFBFBD><EFBFBD><EFBFBD>车继续运动
// 4. 测试无车继续运动
// 无人车向北移动到交叉点
vehicle.position = {crossPoint.x, crossPoint.y};

View File

@ -8,10 +8,18 @@
#include <filesystem>
// Mock ControllableVehicles 类
class MockControllableVehicles : public ControllableVehicles {
class MockControllableVehicles {
public:
MockControllableVehicles() : ControllableVehicles("") {} // 使用空字符串,避免加载实际配置
MOCK_CONST_METHOD1(isControllable, bool(const std::string& vehicleNo));
MockControllableVehicles() = default;
MOCK_METHOD(bool, isControllable, (const std::string& vehicleNo), (const));
MOCK_METHOD(const ControllableVehicleConfig*, findVehicle, (const std::string& vehicleNo), (const));
MOCK_METHOD(void, sendCommand, (const std::string& vehicleNo, const VehicleCommand& cmd));
// 转换为 ControllableVehicles& 的操作符
operator const ControllableVehicles&() const {
return ControllableVehicles::getInstance();
}
};
// Mock AirportBounds 类
@ -199,7 +207,7 @@ TEST_F(CollisionDetectorTest, PerpendicularCrossingPathsCollision) {
EXPECT_TRUE(collisionResult.willCollide) << "垂直交叉路径的车辆应该检测到碰撞";
// 测试2不同速度比
v1.speed = 5.0; // 降<EFBFBD><EFBFBD><EFBFBD>速度
v1.speed = 5.0; // 降速度
v2.speed = 15.0; // 提高速度
collisionResult = detector_->checkCollision(v1, v2, 30.0);
EXPECT_TRUE(collisionResult.willCollide) << "不同速度的垂直交叉路径车辆应该检测到碰撞";
@ -265,7 +273,7 @@ TEST_F(CollisionDetectorTest, TailgatingCollision) {
v2.type = MovingObjectType::UNMANNED;
v2.isControllable = true;
// 测试1同向<EFBFBD><EFBFBD>
// 测试1同向
auto collisionResult = detector_->checkCollision(v1, v2, 30.0);
EXPECT_FALSE(collisionResult.willCollide) << "同向同速的车辆不应该检测到碰撞";

View File

@ -11,9 +11,11 @@ public:
MOCK_METHOD0(connect, bool());
MOCK_METHOD0(disconnect, void());
MOCK_CONST_METHOD0(isAvailable, bool());
MOCK_METHOD1(fetchAircraftData, bool(std::vector<Aircraft>&));
MOCK_METHOD1(fetchVehicleData, bool(std::vector<Vehicle>&));
MOCK_METHOD1(fetchPositionAircraftData, bool(std::vector<Aircraft>&));
MOCK_METHOD1(fetchPositionVehicleData, bool(std::vector<Vehicle>&));
MOCK_METHOD1(fetchTrafficLightSignals, bool(std::vector<TrafficLightSignal>&));
MOCK_METHOD2(sendUnmannedVehicleCommand, bool(const std::string&, const VehicleCommand&));
MOCK_METHOD2(fetchUnmannedVehicleStatus, bool(const std::string&, std::string&));
};
class DataCollectorTest : public ::testing::Test {
@ -70,9 +72,14 @@ protected:
// 测试初始化
TEST_F(DataCollectorTest, Initialization) {
DataSourceConfig dataSourceConfig;
dataSourceConfig.host = "localhost";
dataSourceConfig.port = 8080;
dataSourceConfig.aircraft_path = "/api/getCurrentFlightPositions";
// 设置位置数据配置
dataSourceConfig.position.host = "localhost";
dataSourceConfig.position.port = 8080;
dataSourceConfig.position.aircraft_path = "/api/getCurrentFlightPositions";
dataSourceConfig.position.refresh_interval_ms = 1000;
dataSourceConfig.position.timeout_ms = 5000;
dataSourceConfig.position.read_timeout_ms = 1000;
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 1000;
@ -94,13 +101,13 @@ TEST_F(DataCollectorTest, RefreshTest) {
};
// 设置 Mock 数据返回
EXPECT_CALL(*mockSource, fetchAircraftData(::testing::_))
EXPECT_CALL(*mockSource, fetchPositionAircraftData(::testing::_))
.WillOnce(::testing::DoAll(
::testing::SetArgReferee<0>(testAircraft),
::testing::Return(true)
));
EXPECT_CALL(*mockSource, fetchVehicleData(::testing::_))
EXPECT_CALL(*mockSource, fetchPositionVehicleData(::testing::_))
.WillOnce(::testing::DoAll(
::testing::SetArgReferee<0>(testVehicles),
::testing::Return(true)
@ -136,13 +143,13 @@ TEST_F(DataCollectorTest, DataCollectionLoop) {
};
// 设置 Mock 数据返回
EXPECT_CALL(*mockSource, fetchAircraftData(::testing::_))
EXPECT_CALL(*mockSource, fetchPositionAircraftData(::testing::_))
.WillRepeatedly(::testing::DoAll(
::testing::SetArgReferee<0>(testAircraft),
::testing::Return(true)
));
EXPECT_CALL(*mockSource, fetchVehicleData(::testing::_))
EXPECT_CALL(*mockSource, fetchPositionVehicleData(::testing::_))
.WillRepeatedly(::testing::DoAll(
::testing::SetArgReferee<0>(testVehicles),
::testing::Return(true)
@ -168,9 +175,9 @@ TEST_F(DataCollectorTest, DataCollectionLoop) {
// 测试错误处理
TEST_F(DataCollectorTest, ErrorHandling) {
// 设置 Mock 返回错误
EXPECT_CALL(*mockSource, fetchAircraftData(::testing::_))
EXPECT_CALL(*mockSource, fetchPositionAircraftData(::testing::_))
.WillOnce(::testing::Return(false));
EXPECT_CALL(*mockSource, fetchVehicleData(::testing::_))
EXPECT_CALL(*mockSource, fetchPositionVehicleData(::testing::_))
.WillOnce(::testing::Return(false));
// 执行刷新
@ -230,10 +237,13 @@ TEST_F(DataCollectorTest, TrafficLightSignalsErrorTest) {
// 测试连接健康检查和超时告警
TEST_F(DataCollectorTest, ConnectionHealthAndTimeout) {
DataSourceConfig dataSourceConfig;
dataSourceConfig.host = "localhost";
dataSourceConfig.port = 8080;
dataSourceConfig.timeout_ms = 1000; // 1秒超时
dataSourceConfig.refresh_interval_ms = 100; // 100ms刷新间隔
// 设置位置数据配置
dataSourceConfig.position.host = "localhost";
dataSourceConfig.position.port = 8080;
dataSourceConfig.position.timeout_ms = 1000; // 1秒超时
dataSourceConfig.position.read_timeout_ms = 500; // 0.5秒读取超时
dataSourceConfig.position.refresh_interval_ms = 100; // 100ms刷新间隔
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 500; // 500ms告警间隔
@ -245,9 +255,9 @@ TEST_F(DataCollectorTest, ConnectionHealthAndTimeout) {
.WillRepeatedly(::testing::Return(false));
// 模拟数据获取失败
EXPECT_CALL(*mockSource, fetchAircraftData(::testing::_))
EXPECT_CALL(*mockSource, fetchPositionAircraftData(::testing::_))
.WillRepeatedly(::testing::Return(false));
EXPECT_CALL(*mockSource, fetchVehicleData(::testing::_))
EXPECT_CALL(*mockSource, fetchPositionVehicleData(::testing::_))
.WillRepeatedly(::testing::Return(false));
// 启动采集
@ -269,9 +279,13 @@ TEST_F(DataCollectorTest, ConnectionHealthAndTimeout) {
// 测试连接恢复
TEST_F(DataCollectorTest, ConnectionRecovery) {
DataSourceConfig config;
config.host = "localhost";
config.port = 8080;
config.refresh_interval_ms = 10;
// 设置位置数据配置
config.position.host = "localhost";
config.position.port = 8080;
config.position.refresh_interval_ms = 10;
config.position.timeout_ms = 1000;
config.position.read_timeout_ms = 500;
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 20;
@ -283,14 +297,14 @@ TEST_F(DataCollectorTest, ConnectionRecovery) {
};
// 移除 InSequence直接设置期望
EXPECT_CALL(*mockSource, fetchAircraftData(::testing::_))
EXPECT_CALL(*mockSource, fetchPositionAircraftData(::testing::_))
.WillOnce(::testing::Return(false))
.WillOnce(::testing::DoAll(
::testing::SetArgReferee<0>(testAircraft),
::testing::Return(true)
));
// 行两次刷新
// 行两次刷新
collector->refresh(); // 第一次失败
collector->refresh(); // 第二次成功
@ -305,9 +319,13 @@ TEST_F(DataCollectorTest, ConnectionRecovery) {
// 测试长连接下的数据获取
TEST_F(DataCollectorTest, LongConnectionDataFetch) {
DataSourceConfig dataSourceConfig;
dataSourceConfig.host = "localhost";
dataSourceConfig.port = 8080;
dataSourceConfig.refresh_interval_ms = 100;
// 设置位置数据配置
dataSourceConfig.position.host = "localhost";
dataSourceConfig.position.port = 8080;
dataSourceConfig.position.refresh_interval_ms = 100;
dataSourceConfig.position.timeout_ms = 1000;
dataSourceConfig.position.read_timeout_ms = 500;
collector->initialize(dataSourceConfig, WarnConfig{});
@ -315,23 +333,52 @@ TEST_F(DataCollectorTest, LongConnectionDataFetch) {
createTestAircraft("TEST1", 36.36, 120.08)
};
std::vector<Vehicle> testVehicles = {
createTestVehicle("VEH1", 36.36, 120.08)
};
std::vector<TrafficLightSignal> testSignals = {
createTestTrafficLight("TL001", 0)
};
// 模拟连接保持可用
EXPECT_CALL(*mockSource, isAvailable())
.WillRepeatedly(::testing::Return(true));
// 模拟多次成功获取数据
EXPECT_CALL(*mockSource, fetchAircraftData(::testing::_))
EXPECT_CALL(*mockSource, fetchPositionAircraftData(::testing::_))
.Times(::testing::AtLeast(3))
.WillRepeatedly(::testing::DoAll(
::testing::SetArgReferee<0>(testAircraft),
::testing::Return(true)
));
// 添加其他数据源的 mock 行为
EXPECT_CALL(*mockSource, fetchPositionVehicleData(::testing::_))
.WillRepeatedly(::testing::DoAll(
::testing::SetArgReferee<0>(testVehicles),
::testing::Return(true)
));
EXPECT_CALL(*mockSource, fetchTrafficLightSignals(::testing::_))
.WillRepeatedly(::testing::DoAll(
::testing::SetArgReferee<0>(testSignals),
::testing::Return(true)
));
// 添加无人车状态获取的 mock 行为
std::string testStatus = "NORMAL";
EXPECT_CALL(*mockSource, fetchUnmannedVehicleStatus(::testing::_, ::testing::_))
.WillRepeatedly(::testing::DoAll(
::testing::SetArgReferee<1>(testStatus),
::testing::Return(true)
));
// 启动采集
collector->start();
// 等待多个刷新周期
std::this_thread::sleep_for(std::chrono::milliseconds(350)); // 应该执行至少3次获取
std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 增加等待时间确保执行次数
// 验证数据
auto aircraft = collector->getAircraftData();
@ -340,17 +387,31 @@ TEST_F(DataCollectorTest, LongConnectionDataFetch) {
EXPECT_EQ(aircraft[0].flightNo, "TEST1");
}
auto vehicles = collector->getVehicleData();
EXPECT_FALSE(vehicles.empty());
if (!vehicles.empty()) {
EXPECT_EQ(vehicles[0].vehicleNo, "VEH1");
}
auto signals = collector->getTrafficLightSignals();
EXPECT_FALSE(signals.empty());
if (!signals.empty()) {
EXPECT_EQ(signals[0].trafficLightId, "TL001");
}
// 停止采集
collector->stop();
}
TEST_F(DataCollectorTest, InitializeWithTimeoutConfig) {
DataSourceConfig config;
config.host = "localhost";
config.port = 8080;
config.timeout_ms = 5000; // 连接超时 5 秒
config.read_timeout_ms = 2000; // 读取超时 2 秒
config.refresh_interval_ms = 100;
// 设置位置数据配置
config.position.host = "localhost";
config.position.port = 8080;
config.position.timeout_ms = 5000; // 连接超时 5 秒
config.position.read_timeout_ms = 2000; // 读取超时 2 秒
config.position.refresh_interval_ms = 100;
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 500;
@ -360,11 +421,13 @@ TEST_F(DataCollectorTest, InitializeWithTimeoutConfig) {
TEST_F(DataCollectorTest, TimeoutWarningTest) {
DataSourceConfig config;
config.host = "localhost";
config.port = 8080;
config.timeout_ms = 1000; // 连接超时 1 秒
config.read_timeout_ms = 500; // 读取超时 0.5 秒
config.refresh_interval_ms = 100;
// 设置位置数据配置
config.position.host = "localhost";
config.position.port = 8080;
config.position.timeout_ms = 1000; // 连接超时 1 秒
config.position.read_timeout_ms = 500; // 读取超时 0.5 秒
config.position.refresh_interval_ms = 100;
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 200; // 警告间隔 0.2 秒
@ -375,7 +438,7 @@ TEST_F(DataCollectorTest, TimeoutWarningTest) {
EXPECT_CALL(*mockSource, isAvailable())
.WillRepeatedly(::testing::Return(true));
EXPECT_CALL(*mockSource, fetchAircraftData(::testing::_))
EXPECT_CALL(*mockSource, fetchPositionAircraftData(::testing::_))
.WillRepeatedly(::testing::Return(false)); // 持续读取超时
// 启动采集
@ -391,11 +454,13 @@ TEST_F(DataCollectorTest, TimeoutWarningTest) {
// 测试超时警告机制
TEST_F(DataCollectorTest, TimeoutWarningMechanism) {
DataSourceConfig config;
config.host = "localhost";
config.port = 8080;
config.timeout_ms = 200; // 连接超时 200ms
config.read_timeout_ms = 100; // 读取超时 100ms
config.refresh_interval_ms = 10;
// 设置位置数据配置
config.position.host = "localhost";
config.position.port = 8080;
config.position.timeout_ms = 200; // 连接超时 200ms
config.position.read_timeout_ms = 100; // 读取超时 100ms
config.position.refresh_interval_ms = 10;
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 50; // 警告间隔 50ms
@ -416,9 +481,12 @@ TEST_F(DataCollectorTest, TimeoutWarningMechanism) {
.WillRepeatedly(::testing::Invoke([&warningCount](const network::TimeoutWarningMessage& warning) {
warningCount++;
// 验证警告消息的内容
EXPECT_EQ(warning.source, "data_source");
EXPECT_THAT(warning.source, ::testing::AnyOf(
::testing::StrEq("position"),
::testing::StrEq("unmanned"),
::testing::StrEq("traffic_light")
));
EXPECT_GT(warning.elapsed_ms, 0);
// 不检查具体的超时类型,因为这取决于实际的时间流逝
}));
// 设置 mock system 到 collector
@ -428,12 +496,17 @@ TEST_F(DataCollectorTest, TimeoutWarningMechanism) {
// 启动采集器
collector->start();
// 模拟数据获取失败
EXPECT_CALL(*mockSource, fetchAircraftData(::testing::_))
// 模拟所有数据源获取失败
EXPECT_CALL(*mockSource, fetchPositionAircraftData(::testing::_))
.WillRepeatedly(::testing::Return(false));
EXPECT_CALL(*mockSource, fetchPositionVehicleData(::testing::_))
.WillRepeatedly(::testing::Return(false));
EXPECT_CALL(*mockSource, fetchTrafficLightSignals(::testing::_))
.WillRepeatedly(::testing::Return(false));
// 等待足够长的时间以确保警告被发送
// 等待时间应该超过 refresh_interval_ms + timeout_ms + warning_interval_ms
std::this_thread::sleep_for(std::chrono::milliseconds(300));
// 停止采集器
@ -446,11 +519,13 @@ TEST_F(DataCollectorTest, TimeoutWarningMechanism) {
// 测试读取超时机制
TEST_F(DataCollectorTest, ReadTimeoutMechanism) {
DataSourceConfig config;
config.host = "localhost";
config.port = 8080;
config.timeout_ms = 100; // 连接超时 100ms
config.read_timeout_ms = 50; // 读取超时 50ms
config.refresh_interval_ms = 10;
// 设置位置数据配置
config.position.host = "localhost";
config.position.port = 8080;
config.position.timeout_ms = 100; // 连接超时 100ms
config.position.read_timeout_ms = 50; // 读取超时 50ms
config.position.refresh_interval_ms = 10;
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 20; // 警告间隔 20ms
@ -471,7 +546,11 @@ TEST_F(DataCollectorTest, ReadTimeoutMechanism) {
.WillRepeatedly(::testing::Invoke([&warningCount](const network::TimeoutWarningMessage& warning) {
warningCount++;
// 验证警告消息的内容
EXPECT_EQ(warning.source, "data_source");
EXPECT_THAT(warning.source, ::testing::AnyOf(
::testing::StrEq("position"),
::testing::StrEq("unmanned"),
::testing::StrEq("traffic_light")
));
EXPECT_GT(warning.elapsed_ms, 0);
EXPECT_TRUE(warning.is_read_timeout); // 标记为读取超时
}));
@ -483,16 +562,27 @@ TEST_F(DataCollectorTest, ReadTimeoutMechanism) {
// 启动采集器
collector->start();
// 模拟读取超时
EXPECT_CALL(*mockSource, fetchAircraftData(::testing::_))
// 模拟所有数据源读取超时
EXPECT_CALL(*mockSource, fetchPositionAircraftData(::testing::_))
.WillRepeatedly(::testing::Invoke([](std::vector<Aircraft>& aircraft) {
std::this_thread::sleep_for(std::chrono::milliseconds(60)); // 睡眠超过读取超时时间但小于连接超时
return false;
}));
EXPECT_CALL(*mockSource, fetchPositionVehicleData(::testing::_))
.WillRepeatedly(::testing::Invoke([](std::vector<Vehicle>& vehicles) {
std::this_thread::sleep_for(std::chrono::milliseconds(60));
return false;
}));
EXPECT_CALL(*mockSource, fetchTrafficLightSignals(::testing::_))
.WillRepeatedly(::testing::Invoke([](std::vector<TrafficLightSignal>& signals) {
std::this_thread::sleep_for(std::chrono::milliseconds(60));
return false;
}));
// 等待足够长的时间以确保警告被发送
// 等待时间应该超过 refresh_interval_ms + read_timeout_ms + warning_interval_ms
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::this_thread::sleep_for(std::chrono::milliseconds(300));
// 停止采集器
collector->stop();

View File

@ -1,214 +1,247 @@
#include "network/HTTPDataSource.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "network/HTTPDataSource.h"
#include "utils/Logger.h"
#include <set>
#include <memory>
#include <thread>
class HTTPDataSourceTest : public ::testing::Test {
protected:
std::unique_ptr<HTTPDataSource> source;
void SetUp() override {
DataSourceConfig config;
config.host = "localhost";
config.port = 8081;
config.aircraft_path = "/api/getCurrentFlightPositions";
config.vehicle_path = "/api/getCurrentVehiclePositions";
config.traffic_light_path = "/api/getTrafficLightSignals";
source = std::make_unique<HTTPDataSource>(config);
}
void TearDown() override {
source.reset();
}
void SetUp() override {}
void TearDown() override {}
};
TEST_F(HTTPDataSourceTest, ConnectTest) {
EXPECT_FALSE(source->isAvailable()); // 初始状态应该是未连接
EXPECT_TRUE(source->connect()); // 连接应该成功
EXPECT_TRUE(source->isAvailable()); // 连接后应该可用
}
TEST_F(HTTPDataSourceTest, DisconnectTest) {
source->connect();
EXPECT_TRUE(source->isAvailable());
source->disconnect();
EXPECT_FALSE(source->isAvailable());
}
TEST_F(HTTPDataSourceTest, FetchAircraftDataTest) {
TEST_F(HTTPDataSourceTest, BasicFunctionality) {
DataSourceConfig config;
config.position.host = "localhost";
config.position.port = 8081;
config.position.aircraft_path = "/openApi/getCurrentFlightPositions";
config.position.vehicle_path = "/openApi/getCurrentVehiclePositions";
config.position.auth.auth_required = true;
config.position.auth.auth_path = "/login";
config.position.auth.username = "dianxin";
config.position.auth.password = "dianxin@123";
config.position.timeout_ms = 5000;
config.position.read_timeout_ms = 2000;
config.position.refresh_interval_ms = 100;
HTTPDataSource source(config);
ASSERT_TRUE(source.connect()) << "连接失败";
std::vector<Aircraft> aircraft;
bool success = source.fetchPositionAircraftData(aircraft);
ASSERT_TRUE(success) << "获取航空器数据失败";
ASSERT_FALSE(aircraft.empty()) << "航空器数据为空";
// 尝试连接
bool connected = source->connect();
Logger::info("Connection result: ", connected ? "success" : "failed");
EXPECT_TRUE(connected);
// 检查连接状态
bool available = source->isAvailable();
Logger::info("Connection available: ", available ? "yes" : "no");
EXPECT_TRUE(available);
// 获取数据
bool fetchResult = source->fetchAircraftData(aircraft);
Logger::info("Fetch result: ", fetchResult ? "success" : "failed");
EXPECT_TRUE(fetchResult);
// 检查数据
Logger::info("Aircraft data size: ", aircraft.size());
EXPECT_FALSE(aircraft.empty());
}
TEST_F(HTTPDataSourceTest, FetchVehicleDataTest) {
std::vector<Vehicle> vehicles;
EXPECT_TRUE(source->connect()); // 确保连接成功
EXPECT_TRUE(source->fetchVehicleData(vehicles));
EXPECT_FALSE(vehicles.empty());
ASSERT_TRUE(source.fetchPositionVehicleData(vehicles)) << "获取车辆数据失败";
ASSERT_FALSE(vehicles.empty()) << "车辆数据为空";
}
TEST_F(HTTPDataSourceTest, FetchTrafficLightSignalsTest) {
std::vector<TrafficLightSignal> signals;
EXPECT_TRUE(source->connect()); // 确保连接成功
EXPECT_TRUE(source->fetchTrafficLightSignals(signals));
EXPECT_FALSE(signals.empty());
}
TEST_F(HTTPDataSourceTest, CustomPathTest) {
// 测试使用自定义路径创建数据源
DataSourceConfig config;
config.host = "localhost";
config.port = 8081;
config.aircraft_path = "/custom/path";
auto customSource = std::make_unique<HTTPDataSource>(config);
EXPECT_TRUE(customSource->connect());
EXPECT_TRUE(customSource->isAvailable());
}
TEST_F(HTTPDataSourceTest, ConnectionFailureTest) {
// 测试连接到不存在的服务器
DataSourceConfig config;
config.host = "localhost"; // 使用本地地址
config.port = 1; // 使用一个通常不会被使用的端口
auto invalidSource = std::make_unique<HTTPDataSource>(config);
EXPECT_FALSE(invalidSource->connect());
EXPECT_FALSE(invalidSource->isAvailable());
}
TEST_F(HTTPDataSourceTest, FetchDataWithoutConnectionTest) {
// 测试在未连接状态下获取数据
std::vector<Aircraft> aircraft;
std::vector<Vehicle> vehicles;
TEST_F(HTTPDataSourceTest, AuthenticationFailure) {
DataSourceConfig config;
config.position.host = "localhost";
config.position.port = 8081;
config.position.aircraft_path = "/openApi/getCurrentFlightPositions";
config.position.vehicle_path = "/openApi/getCurrentVehiclePositions";
config.position.refresh_interval_ms = 1000;
config.position.timeout_ms = 1000;
config.position.read_timeout_ms = 500;
config.position.auth.auth_required = true;
config.position.auth.username = "wrong_user";
config.position.auth.password = "wrong_pass";
config.position.auth.auth_path = "/login";
HTTPDataSource source(config);
std::vector<Aircraft> aircraft;
EXPECT_FALSE(source.fetchPositionAircraftData(aircraft));
}
TEST_F(HTTPDataSourceTest, Reconnection) {
DataSourceConfig config;
config.position.host = "localhost";
config.position.port = 8081;
config.position.aircraft_path = "/openApi/getCurrentFlightPositions";
config.position.vehicle_path = "/openApi/getCurrentVehiclePositions";
config.position.refresh_interval_ms = 1000;
config.position.timeout_ms = 1000;
config.position.read_timeout_ms = 500;
config.position.auth.auth_required = true;
config.position.auth.username = "dianxin";
config.position.auth.password = "dianxin@123";
config.position.auth.auth_path = "/login";
HTTPDataSource source(config);
EXPECT_TRUE(source.connect());
std::vector<Aircraft> aircraft;
EXPECT_TRUE(source.fetchPositionAircraftData(aircraft));
source.disconnect();
EXPECT_TRUE(source.connect());
aircraft.clear();
EXPECT_TRUE(source.fetchPositionAircraftData(aircraft));
}
TEST_F(HTTPDataSourceTest, AuthenticationOnly) {
DataSourceConfig config;
config.position.host = "localhost";
config.position.port = 8081;
config.position.aircraft_path = "/openApi/getCurrentFlightPositions";
config.position.vehicle_path = "/openApi/getCurrentVehiclePositions";
config.position.refresh_interval_ms = 1000;
config.position.timeout_ms = 1000;
config.position.read_timeout_ms = 500;
config.position.auth.auth_required = true;
config.position.auth.username = "dianxin";
config.position.auth.password = "dianxin@123";
config.position.auth.auth_path = "/login";
HTTPDataSource source(config);
EXPECT_TRUE(source.connect());
std::vector<Aircraft> aircraft;
EXPECT_TRUE(source.fetchPositionAircraftData(aircraft));
}
TEST_F(HTTPDataSourceTest, TrafficLightSignals) {
DataSourceConfig config;
config.position.host = "localhost";
config.position.port = 8081;
config.traffic_light.host = "localhost";
config.traffic_light.port = 8081;
config.traffic_light.signal_path = "/api/VehicleCommandInfo";
config.vehicle.host = "localhost";
config.vehicle.port = 8081;
config.vehicle.command_path = "/api/VehicleCommandInfo";
HTTPDataSource source(config);
ASSERT_TRUE(source.connect());
// 先发送 GREEN 指令解除 RED 状态
VehicleCommand green_command;
green_command.vehicleId = "QN001";
green_command.type = CommandType::SIGNAL;
green_command.reason = CommandReason::TRAFFIC_LIGHT;
green_command.signalState = SignalState::GREEN;
green_command.intersectionId = "INTERSECTION_001";
green_command.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
ASSERT_TRUE(source.sendUnmannedVehicleCommand(green_command.vehicleId, green_command)) << "发送绿灯指令失败";
// 等待一小段时间确保 GREEN 指令生效
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// 再发送 RESUME 指令解除其他可能的状态
VehicleCommand resume_command;
resume_command.vehicleId = "QN001";
resume_command.type = CommandType::RESUME;
resume_command.reason = CommandReason::RESUME_TRAFFIC;
resume_command.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
ASSERT_TRUE(source.sendUnmannedVehicleCommand(resume_command.vehicleId, resume_command)) << "发送恢复指令失败";
// 等待一小段时间确保 RESUME 指令生效
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// 发送红绿灯信号指令
VehicleCommand command;
command.vehicleId = "QN001";
command.type = CommandType::SIGNAL;
command.reason = CommandReason::TRAFFIC_LIGHT;
command.signalState = SignalState::RED;
command.intersectionId = "INTERSECTION_001";
command.latitude = 36.36;
command.longitude = 120.08;
command.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
ASSERT_TRUE(source.sendUnmannedVehicleCommand(command.vehicleId, command)) << "获取红绿灯信号失败";
}
TEST_F(HTTPDataSourceTest, UnmannedVehicleCommand) {
DataSourceConfig config;
config.vehicle.host = "localhost";
config.vehicle.port = 8081;
config.vehicle.command_path = "/api/VehicleCommandInfo";
HTTPDataSource source(config);
ASSERT_TRUE(source.connect());
// 先发送 GREEN 指令解除 RED 状态
VehicleCommand green_command;
green_command.vehicleId = "QN001";
green_command.type = CommandType::SIGNAL;
green_command.reason = CommandReason::TRAFFIC_LIGHT;
green_command.signalState = SignalState::GREEN;
green_command.intersectionId = "INTERSECTION_001";
green_command.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
ASSERT_TRUE(source.sendUnmannedVehicleCommand(green_command.vehicleId, green_command)) << "发送绿灯指令失败";
// 等待一小段时间确保 GREEN 指令生效
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// 再发送 RESUME 指令解除其他可能的状态
VehicleCommand resume_command;
resume_command.vehicleId = "QN001";
resume_command.type = CommandType::RESUME;
resume_command.reason = CommandReason::RESUME_TRAFFIC;
resume_command.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
ASSERT_TRUE(source.sendUnmannedVehicleCommand(resume_command.vehicleId, resume_command)) << "发送恢复指令失败";
// 等待一小段时间确保 RESUME 指令生效
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// 发送告警指令
VehicleCommand command;
command.vehicleId = "QN001";
command.type = CommandType::ALERT;
command.reason = CommandReason::AIRCRAFT_CROSSING;
command.relativeSpeed = 100;
command.relativeMotionX = 100;
command.relativeMotionY = 100;
command.minDistance = 100;
command.latitude = 36.36;
command.longitude = 120.08;
command.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
ASSERT_TRUE(source.sendUnmannedVehicleCommand(command.vehicleId, command)) << "发送无人车指令失败";
}
TEST_F(HTTPDataSourceTest, TrafficLightAndUnmannedAuthFailure) {
DataSourceConfig config;
config.traffic_light.host = "localhost";
config.traffic_light.port = 8081;
config.traffic_light.signal_path = "/api/VehicleCommandInfo";
config.traffic_light.auth.auth_required = true;
config.traffic_light.auth.username = "wrong_user";
config.traffic_light.auth.password = "wrong_pass";
config.vehicle.host = "localhost";
config.vehicle.port = 8081;
config.vehicle.command_path = "/api/VehicleCommandInfo";
config.vehicle.auth.auth_required = true;
config.vehicle.auth.username = "wrong_user";
config.vehicle.auth.password = "wrong_pass";
HTTPDataSource source(config);
std::vector<TrafficLightSignal> signals;
EXPECT_FALSE(source.fetchTrafficLightSignals(signals));
// 配置一个很短的刷新间隔,这样重连会快速失败
DataSourceConfig config;
config.host = "localhost";
config.port = 1; // 使用一个通常不会被使用的端口
config.refresh_interval_ms = 1; // 设置很短的刷新间隔
source = std::make_unique<HTTPDataSource>(config);
// 测试所有数据获取方法
EXPECT_FALSE(source->fetchAircraftData(aircraft));
EXPECT_FALSE(source->fetchVehicleData(vehicles));
EXPECT_FALSE(source->fetchTrafficLightSignals(signals));
// 验证数据集为空
EXPECT_TRUE(aircraft.empty());
EXPECT_TRUE(vehicles.empty());
EXPECT_TRUE(signals.empty());
}
// 添加一个新的测试用例,专门测试重连机制
TEST_F(HTTPDataSourceTest, ReconnectionTest) {
// 配置一个较短的刷新间隔
DataSourceConfig config;
config.host = "localhost";
config.port = 1; // 使用一个通常不会被使用的端口
config.refresh_interval_ms = 100; // 100毫秒的刷新间隔
auto reconnectSource = std::make_unique<HTTPDataSource>(config);
// 第一次连接应该失败
EXPECT_FALSE(reconnectSource->connect());
EXPECT_FALSE(reconnectSource->isAvailable());
// 尝试获取数据,这会触发重连
std::vector<Aircraft> aircraft;
EXPECT_FALSE(reconnectSource->fetchAircraftData(aircraft));
EXPECT_TRUE(aircraft.empty());
}
// 如果你的环境支持模拟网络响应,可以添加更多测试
TEST_F(HTTPDataSourceTest, InvalidResponseTest) {
// 这个测试需要模拟无效的服务器响应
// 你可能需要使用 mock 对象或者设置一个返回无效数据的测试服务器
source->connect();
std::vector<Aircraft> aircraft;
// 假设服务器返回无效的 JSON 数据
// EXPECT_FALSE(source->fetchAircraftData(aircraft));
}
// 测试不同的超时配置
TEST_F(HTTPDataSourceTest, DifferentTimeoutConfigTest) {
// 测试读取超时小于连接超时
{
DataSourceConfig config;
config.host = "localhost";
config.port = 8081;
config.timeout_ms = 5000; // 连接超时 5 秒
config.read_timeout_ms = 2000; // 读取超时 2 秒
auto source = std::make_unique<HTTPDataSource>(config);
EXPECT_FALSE(source->isAvailable()); // 只验证初始状态
std::vector<Aircraft> aircraft;
EXPECT_FALSE(source->fetchAircraftData(aircraft));
EXPECT_TRUE(aircraft.empty());
}
// 测试读取超时等于连接超时
{
DataSourceConfig config;
config.host = "localhost";
config.port = 8081;
config.timeout_ms = 5000; // 连接超时 5 秒
config.read_timeout_ms = 5000; // 读取超时 5 秒
auto source = std::make_unique<HTTPDataSource>(config);
EXPECT_FALSE(source->isAvailable()); // 只验证初始状态
std::vector<Aircraft> aircraft;
EXPECT_FALSE(source->fetchAircraftData(aircraft));
EXPECT_TRUE(aircraft.empty());
}
}
// 修改读取超时测试
TEST_F(HTTPDataSourceTest, ReadTimeoutTest) {
DataSourceConfig config;
config.host = "localhost";
config.port = 8081;
config.timeout_ms = 5000; // 连接超时 5 秒
config.read_timeout_ms = 2000; // 读取超时 2 秒
auto source = std::make_unique<HTTPDataSource>(config);
EXPECT_FALSE(source->isAvailable()); // 验证初始状态
std::vector<Aircraft> aircraft;
EXPECT_FALSE(source->fetchAircraftData(aircraft));
EXPECT_TRUE(aircraft.empty());
}
// 修改连接健康和超时测试
TEST_F(HTTPDataSourceTest, ConnectionHealthAndTimeout) {
DataSourceConfig config;
config.host = "localhost";
config.port = 8081;
config.timeout_ms = 5000; // 连接超时 5 秒
config.read_timeout_ms = 2000; // 读取超时 2 秒
auto source = std::make_unique<HTTPDataSource>(config);
EXPECT_FALSE(source->isAvailable()); // 验证初始状态
std::vector<Aircraft> aircraft;
EXPECT_FALSE(source->fetchAircraftData(aircraft));
EXPECT_TRUE(aircraft.empty());
VehicleCommand command;
command.vehicleId = "TEST_VEHICLE_001";
command.type = CommandType::SIGNAL;
command.reason = CommandReason::RESUME_TRAFFIC;
command.signalState = SignalState::GREEN;
command.timestamp = 0;
EXPECT_FALSE(source.sendUnmannedVehicleCommand(command.vehicleId, command));
}

View File

@ -76,6 +76,9 @@ TEST_F(SafetyZoneTest, AircraftEntering) {
// 检查飞机是否在安全区内
EXPECT_TRUE(safetyZone->isObjectInZone(aircraft));
// 尝试激活安全区
EXPECT_TRUE(safetyZone->tryActivate(aircraft));
EXPECT_EQ(safetyZone->getType(), SafetyZoneType::AIRCRAFT);
EXPECT_EQ(safetyZone->getCurrentRadius(), 50.0);
@ -93,6 +96,9 @@ TEST_F(SafetyZoneTest, SpecialVehicleEntering) {
// 检查特勤车是否在安全区内
EXPECT_TRUE(safetyZone->isObjectInZone(vehicle));
// 尝试激活安全区
EXPECT_TRUE(safetyZone->tryActivate(vehicle));
EXPECT_EQ(safetyZone->getType(), SafetyZoneType::VEHICLE);
EXPECT_EQ(safetyZone->getCurrentRadius(), 30.0);
@ -107,7 +113,8 @@ TEST_F(SafetyZoneTest, UnmannedVehicleEntering) {
auto vehicle = createUnmannedVehicle("UV001", 10.0, 0.0);
// 检查无人车是否在安全区内应该返回false因为无人车不能设置安全区类型
EXPECT_FALSE(safetyZone->isObjectInZone(vehicle));
EXPECT_TRUE(safetyZone->isObjectInZone(vehicle));
EXPECT_FALSE(safetyZone->tryActivate(vehicle));
EXPECT_EQ(safetyZone->getType(), SafetyZoneType::NONE);
EXPECT_EQ(safetyZone->getCurrentRadius(), 0.0);
}
@ -120,13 +127,15 @@ TEST_F(SafetyZoneTest, SafetyZoneTypeLocking) {
// 先让飞机进入安全区
auto aircraft = createAircraft("AC001", aircraftEffectiveDistance - 5.0, 0.0);
EXPECT_TRUE(safetyZone->isObjectInZone(aircraft));
EXPECT_TRUE(safetyZone->tryActivate(aircraft));
EXPECT_EQ(safetyZone->getType(), SafetyZoneType::AIRCRAFT);
// 尝试让特勤车进入已经被飞机设置类型的安全区
auto vehicle = createSpecialVehicle("TQ001", vehicleEffectiveDistance - 5.0, 0.0);
EXPECT_FALSE(safetyZone->isObjectInZone(vehicle));
EXPECT_TRUE(safetyZone->isObjectInZone(vehicle));
EXPECT_FALSE(safetyZone->tryActivate(vehicle));
// 确认安全区类型和尺寸没有改<EFBFBD><EFBFBD><EFBFBD>
// 确认安全区类型和尺寸没有改
EXPECT_EQ(safetyZone->getType(), SafetyZoneType::AIRCRAFT);
EXPECT_EQ(safetyZone->getCurrentRadius(), 50.0);
}
@ -139,15 +148,19 @@ TEST_F(SafetyZoneTest, BoundaryConditions) {
// 测试飞机在边界上
auto aircraft1 = createAircraft("AC001", aircraftEffectiveDistance, 0.0);
EXPECT_TRUE(safetyZone->isObjectInZone(aircraft1));
EXPECT_TRUE(safetyZone->tryActivate(aircraft1));
// 测试飞机在边界外
auto aircraft2 = createAircraft("AC002", aircraftEffectiveDistance + 0.1, 0.0);
EXPECT_FALSE(safetyZone->isObjectInZone(aircraft2));
// 测试特勤车在边界上
// 重新创建安全区用于测试特勤车
safetyZone = std::make_unique<SafetyZone>(Vector2D{0, 0}, 50.0, 30.0);
// 测试特勤车在边界上
auto vehicle1 = createSpecialVehicle("TQ001", vehicleEffectiveDistance, 0.0);
EXPECT_TRUE(safetyZone->isObjectInZone(vehicle1));
EXPECT_TRUE(safetyZone->tryActivate(vehicle1));
// 测试特勤车在边界外
auto vehicle2 = createSpecialVehicle("TQ002", vehicleEffectiveDistance + 0.1, 0.0);
@ -161,6 +174,7 @@ TEST_F(SafetyZoneTest, StateReset) {
// 先让飞机进入安全区
auto aircraft = createAircraft("AC001", effectiveDistance - 5.0, 0.0);
EXPECT_TRUE(safetyZone->isObjectInZone(aircraft));
EXPECT_TRUE(safetyZone->tryActivate(aircraft));
// 设置状态为激活
safetyZone->setState(SafetyZoneState::ACTIVE);

303
tools/map.html Normal file
View File

@ -0,0 +1,303 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>机场路口地图</title>
<style>
body {
margin: 0;
padding: 20px;
background: #f0f0f0;
font-family: Arial, sans-serif;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
canvas {
border: 1px solid #ddd;
margin: 20px 0;
}
.legend {
margin-top: 20px;
padding: 10px;
background: #f8f8f8;
border-radius: 4px;
}
.legend-item {
margin: 5px 0;
display: flex;
align-items: center;
}
.legend-color {
width: 20px;
height: 20px;
margin-right: 10px;
border: 1px solid #999;
}
</style>
</head>
<body>
<div class="container">
<h1>机场路口地图</h1>
<canvas id="mapCanvas" width="1000" height="800"></canvas>
<div class="legend">
<h3>图例</h3>
<div class="legend-item">
<div class="legend-color" style="background: #666;"></div>
<span>道路</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background: #f00;"></div>
<span>路口</span>
</div>
</div>
</div>
<script>
// 路口坐标数据
const intersections = [
{point: "T1", longitude: 120.0868853, latitude: 36.35496367},
{point: "T2", longitude: 120.08502054, latitude: 36.35448347},
{point: "T3", longitude: 120.08341044, latitude: 36.35406879},
{point: "T4", longitude: 120.08558121, latitude: 36.35305878},
{point: "T5", longitude: 120.08400957, latitude: 36.35265197},
{point: "T6", longitude: 120.08649105, latitude: 36.35074527},
{point: "T7", longitude: 120.08562915, latitude: 36.35052372},
{point: "T8", longitude: 120.08676664, latitude: 36.35004529},
{point: "T9", longitude: 120.08520616, latitude: 36.34964473},
{point: "T10", longitude: 120.08710569, latitude: 36.34917893},
{point: "T11", longitude: 120.0873865, latitude: 36.3509885},
{point: "T12", longitude: 120.08603613, latitude: 36.35190217}
];
// 参考点
const referencePoint = {
latitude: 36.34807893,
longitude: 120.08201044
};
// 获取 Canvas 上下文
const canvas = document.getElementById('mapCanvas');
const ctx = canvas.getContext('2d');
// 计算坐标范围
let minLat = Math.min(...intersections.map(p => p.latitude));
let maxLat = Math.max(...intersections.map(p => p.latitude));
let minLon = Math.min(...intersections.map(p => p.longitude));
let maxLon = Math.max(...intersections.map(p => p.longitude));
// 添加边距
const margin = 0.0001;
minLat -= margin;
maxLat += margin;
minLon -= margin;
maxLon += margin;
// 计算中心纬度
const centerLat = (minLat + maxLat) / 2;
// 地球半径(米)
const EARTH_RADIUS = 6378137.0;
// 计算1度经度和纬度对应的实际距离
const metersPerLatDegree = (Math.PI / 180) * EARTH_RADIUS;
const metersPerLonDegree = metersPerLatDegree * Math.cos(centerLat * Math.PI / 180);
// 计算实际距离范围(米)
const totalWidthMeters = (maxLon - minLon) * metersPerLonDegree;
const totalHeightMeters = (maxLat - minLat) * metersPerLatDegree;
// 计算画布缩放比例,保持宽高比
const scale = Math.min(canvas.width / totalWidthMeters, canvas.height / totalHeightMeters);
// 坐标转换函数
function convertToCanvas(lon, lat) {
// 将经纬度转换为实际距离(米)
const x = (lon - minLon) * metersPerLonDegree;
const y = (lat - minLat) * metersPerLatDegree;
// 使用统一的缩放比例转换为画布坐标
const canvasX = x * scale;
const canvasY = canvas.height - (y * scale);
// 居中显示
const offsetX = (canvas.width - totalWidthMeters * scale) / 2;
const offsetY = (canvas.height - totalHeightMeters * scale) / 2;
return {
x: canvasX + offsetX,
y: canvasY - offsetY
};
}
// 绘制道路
function drawRoad(start, end) {
const startPos = convertToCanvas(start.longitude, start.latitude);
const endPos = convertToCanvas(end.longitude, end.latitude);
ctx.beginPath();
ctx.moveTo(startPos.x, startPos.y);
ctx.lineTo(endPos.x, endPos.y);
ctx.strokeStyle = '#666';
ctx.lineWidth = 3;
ctx.stroke();
}
// 绘制路口
function drawIntersection(intersection) {
const pos = convertToCanvas(intersection.longitude, intersection.latitude);
// 绘制圆点
ctx.beginPath();
ctx.arc(pos.x, pos.y, 5, 0, Math.PI * 2);
ctx.fillStyle = '#f00';
ctx.fill();
// 绘制标签
ctx.font = '12px Arial';
ctx.fillStyle = '#000';
ctx.textAlign = 'center';
ctx.fillText(intersection.point, pos.x, pos.y - 10);
}
// 计算 T2-T6 的旋转角度
const t2 = intersections.find(p => p.point === "T2");
const t6 = intersections.find(p => p.point === "T6");
const angle = Math.atan2(
convertToCanvas(t6.longitude, t6.latitude).y - convertToCanvas(t2.longitude, t2.latitude).y,
convertToCanvas(t6.longitude, t6.latitude).x - convertToCanvas(t2.longitude, t2.latitude).x
);
// 设置画布中心点
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
// 清空画布
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 绘制网格
const gridSize = 25; // 网格大小为25像素
ctx.beginPath();
ctx.strokeStyle = '#ccc';
ctx.lineWidth = 1;
// 绘制垂直线
for(let x = 0; x <= canvas.width; x += gridSize) {
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
}
// 绘制水平线
for(let y = 0; y <= canvas.height; y += gridSize) {
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
}
ctx.stroke();
// 保存当前状态
ctx.save();
// 将原点移到画布中心
ctx.translate(centerX, centerY);
// 旋转画布
ctx.rotate(-angle);
// 将原点移回去
ctx.translate(-centerX, -centerY);
// 计算并显示道路的偏离角度
function calculateRoadAngle(start, end) {
const startPos = convertToCanvas(start.longitude, start.latitude);
const endPos = convertToCanvas(end.longitude, end.latitude);
// 将坐标点转换到旋转后的坐标系
const rotatedStartX = (startPos.x - centerX) * Math.cos(-angle) - (startPos.y - centerY) * Math.sin(-angle);
const rotatedStartY = (startPos.x - centerX) * Math.sin(-angle) + (startPos.y - centerY) * Math.cos(-angle);
const rotatedEndX = (endPos.x - centerX) * Math.cos(-angle) - (endPos.y - centerY) * Math.sin(-angle);
const rotatedEndY = (endPos.x - centerX) * Math.sin(-angle) + (endPos.y - centerY) * Math.cos(-angle);
// 计算在旋转坐标系中的方向向量(从起点指向终点)
const dx = rotatedEndX - rotatedStartX;
const dy = rotatedEndY - rotatedStartY;
// 计算与垂直方向的夹角(弧度)
const angleRad = Math.atan2(-dx, dy);
// 转换为角度并取绝对值
const deviationAngle = Math.abs(angleRad * 180 / Math.PI);
// 显示角度(文字方向跟随旋转后的坐标系)
ctx.save();
// 将文字位置移到终点附近
ctx.translate(endPos.x, endPos.y + 15);
ctx.rotate(angle);
ctx.font = '12px Arial';
ctx.fillStyle = '#000';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.fillText(deviationAngle.toFixed(1) + '°', 0, 0);
ctx.restore();
}
// 绘制道路网络
// 计算指定道路的角度
drawRoad(intersections[0], intersections[2]); // T1-T3
calculateRoadAngle(intersections[0], intersections[2]);
drawRoad(intersections[3], intersections[4]); // T4-T5
calculateRoadAngle(intersections[3], intersections[4]);
drawRoad(intersections[10], intersections[6]); // T11-T7 (修改顺序)
calculateRoadAngle(intersections[10], intersections[6]);
drawRoad(intersections[7], intersections[8]); // T8-T9
calculateRoadAngle(intersections[7], intersections[8]);
// 绘制其他道路
drawRoad(intersections[1], intersections[9]); // T2-T10
drawRoad(intersections[5], intersections[7]); // T6-T8
drawRoad(intersections[11], intersections[3]); // T12-T4
// 绘制所有路口
intersections.forEach(drawIntersection);
// 恢复画布状态
ctx.restore();
// 绘制比例尺
ctx.beginPath();
ctx.moveTo(gridSize * 2, canvas.height - 20);
ctx.lineTo(gridSize * 5, canvas.height - 20);
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.stroke();
// 绘制刻度
ctx.beginPath();
ctx.moveTo(gridSize * 2, canvas.height - 15);
ctx.lineTo(gridSize * 2, canvas.height - 25);
ctx.moveTo(gridSize * 3, canvas.height - 15);
ctx.lineTo(gridSize * 3, canvas.height - 25);
ctx.moveTo(gridSize * 4, canvas.height - 15);
ctx.lineTo(gridSize * 4, canvas.height - 25);
ctx.moveTo(gridSize * 5, canvas.height - 15);
ctx.lineTo(gridSize * 5, canvas.height - 25);
ctx.lineWidth = 1;
ctx.stroke();
// 计算两个网格单位代表的实际距离(米)
const gridMeters = gridSize / scale;
ctx.fillStyle = '#000';
ctx.textAlign = 'center';
// 在刻度处显示距离
ctx.fillText('0', gridSize * 2, canvas.height - 5);
ctx.fillText(Math.round(gridMeters) + '', gridSize * 3, canvas.height - 5);
ctx.fillText(Math.round(gridMeters * 1) + '', gridSize * 4, canvas.height - 5);
ctx.fillText(Math.round(gridMeters * 2) + '米', gridSize * 5, canvas.height - 5);
</script>
</body>
</html>

View File

@ -90,6 +90,9 @@
.traffic-light-green {
background-color: green;
}
.traffic-light-yellow {
background-color: yellow;
}
.countdown-label {
background: #000;
border: 1px solid #666;
@ -114,6 +117,9 @@
.countdown-green {
color: #33ff33;
}
.countdown-yellow {
color: #ffff33;
}
.distance-label {
background: none;
border: none;
@ -303,7 +309,7 @@
function updateTrafficLight(data) {
const id = data.id;
const position = [data.position.latitude, data.position.longitude];
const state = data.status === 0 ? 'red' : 'green';
const state = data.status === 0 ? 'red' : data.status === 1 ? 'green' : 'yellow';
// 检查是否是西路口TL001的红绿灯状态变化
if (id === 'TL001' && lastTrafficLightState !== state) {
@ -464,7 +470,7 @@
case 'traffic_light_status':
type = 'info';
updateTrafficLight(data);
message = `红绿灯状态更新:\n信号灯: ${data.id}\n状态: ${data.status === 0 ? '红灯' : '绿灯'}`;
message = `红绿灯状态更新:\n信号灯: ${data.id}\n状态: ${data.status === 0 ? '红灯' : data.status === 1 ? '绿灯' : '黄灯'}`;
break;
case 'collision_warning':
type = 'warning';

View File

@ -3,6 +3,21 @@ import time
import math
import random
import logging
import os
# 创建 logs 目录(如果不存在)
if not os.path.exists('logs'):
os.makedirs('logs')
# 配置日志
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('logs/mock_server.log'),
logging.StreamHandler()
]
)
app = Flask(__name__)
@ -11,7 +26,8 @@ EARTH_RADIUS = 6378137.0
COMMAND_PRIORITIES = {
"ALERT": 5,
"SIGNAL": 4,
"RED": 4,
"YELLOW": 3,
"WARNING": 3,
"GREEN": 2,
"RESUME": 1
@ -164,6 +180,7 @@ class VehicleState:
priority_map = {
"ALERT": 5, # 告警指令,最高优先级
"RED": 4, # 红灯指令,次高优先级
"YELLOW": 3, # 黄灯指令,表示红绿灯故障
"WARNING": 3, # 预警指令,中等优先级
"GREEN": 2, # 绿灯状态,低优先级
"RESUME": 1 # 恢复指令,最低优先级
@ -175,19 +192,23 @@ class VehicleState:
if self.current_command == "ALERT":
return command_type == "RESUME"
# RED 指令可以被 GREEN 或更高优先级指令覆盖
# RED 指令可以被 GREEN 或相同及更高优先级指令覆盖
if self.current_command == "RED":
return command_type == "GREEN" or new_priority > current_priority
return command_type == "GREEN" or new_priority >= current_priority
# WARNING 指令可以被 RESUME 解除
# YELLOW 指令(红绿灯故障)可以被任何新指令覆盖
if self.current_command == "YELLOW":
return True
# WARNING 指令可以被 RESUME 或相同及更高优先级指令覆盖
if self.current_command == "WARNING":
return command_type == "RESUME" or new_priority > current_priority
return command_type == "RESUME" or new_priority >= current_priority
# GREEN 不阻止任何指令
if self.current_command == "GREEN":
return True
# 其他情况按优先级判断
# 其他情况下,相同或更高优先级可以覆盖
return new_priority >= current_priority
def update_command(self, command_type, target_lat=None, target_lon=None):
@ -195,6 +216,7 @@ class VehicleState:
priority_map = {
"ALERT": 5, # 告警指令,最高优先级
"RED": 4, # 红灯指令,次高优先级
"YELLOW": 3, # 黄灯指令,表示红绿灯故障
"WARNING": 3, # 预警指令,中等优先级
"GREEN": 2, # 绿灯状态,低优先级
"RESUME": 1 # 恢复指令,最低优先级
@ -206,13 +228,19 @@ class VehicleState:
self.target_lon = target_lon
# 如果是红绿灯状态,更新状态和指令
if command_type in ["RED", "GREEN"]:
if command_type in ["RED", "GREEN", "YELLOW"]:
self.traffic_light_state = command_type
if command_type == "RED":
self.current_command = command_type
self.command_priority = priority_map[command_type]
self.is_running = False
print(f"车辆 {self.vehicle_no} 收到红灯指令,停止运行")
elif command_type == "YELLOW":
# 黄灯表示红绿灯故障,清除当前红绿灯状态,允许车辆继续行驶
self.current_command = command_type
self.command_priority = priority_map[command_type]
self.is_running = True
print(f"车辆 {self.vehicle_no} 收到黄灯指令(红绿灯故障),继续行驶")
else: # GREEN
# 清除 RED 指令
if self.current_command == "RED":
@ -297,9 +325,9 @@ def handle_vehicle_command():
print(f"收到车辆控制指令: vehicle_id={vehicle_id}, type={command_type}, reason={reason}, signal_state={signal_state}")
print(f"完整请求数据: {data}")
# 将 SIGNAL 指令转换为 RED 或 GREEN
# 将 SIGNAL 指令转换为 RED、YELLOW 或 GREEN
if command_type == "SIGNAL":
command_type = "RED" if signal_state == "RED" else "GREEN"
command_type = signal_state # 直接使用信号状态作为命令类型
print(f"转换 SIGNAL 指令为: {command_type}")
# 检查车辆是否存在
@ -804,9 +832,24 @@ def switch_traffic_light_state():
# 西路口红绿灯每15秒切换一次
elapsed_since_switch = current_time - last_light_switch_time
if elapsed_since_switch >= TRAFFIC_LIGHT_SWITCH_INTERVAL: # 使用常量
traffic_light_data[0]["state"] = 1 if traffic_light_data[0]["state"] == 0 else 0
# 获取当前状态
current_state = traffic_light_data[0]["state"]
# 根据当前状态决定下一个状态
if current_state == 0: # 当前是红灯
# 切换到黄灯(表示红绿灯故障)
traffic_light_data[0]["state"] = 2 # 2 表示黄灯
print(f"西路口红绿灯状态切换为: 黄灯(红绿灯故障)")
elif current_state == 2: # 当前是黄灯
# 从黄灯切换到绿灯
traffic_light_data[0]["state"] = 1 # 1 表示绿灯
print(f"西路口红绿灯状态切换为: 绿灯")
else: # 当前是绿灯
# 从绿灯切换到红灯
traffic_light_data[0]["state"] = 0 # 0 表示红灯
print(f"西路口红绿灯状态切换为: 红灯")
last_light_switch_time = current_time
print(f"西路口红绿灯状态切换为: {'绿灯' if traffic_light_data[0]['state'] == 1 else '红灯'}")
# 更新东路口红绿灯(根据航空器位置)
if aircraft_data:
@ -859,26 +902,24 @@ def get_vehicle_positions():
"data": None
}), 500
@app.route('/getTrafficLightSignals', methods=['GET', 'OPTIONS'])
@app.route('/openApi/getTrafficLightSignals', methods=['GET', 'OPTIONS'])
def get_traffic_light_signals():
"""获取红绿灯信号状态"""
if request.method == 'OPTIONS':
return '', 204
if not check_auth():
return jsonify({
"status": 401,
"msg": "认证失败",
"data": None
}), 401
current_time = time.time()
# 更新时间戳
for light in traffic_light_data:
light["timestamp"] = int(current_time * 1000)
# 统一处理红绿灯状态切换
# 更新红绿灯状态
switch_traffic_light_state()
return jsonify(traffic_light_data)
# 更新时间戳
for signal in traffic_light_data:
signal["timestamp"] = int(time.time() * 1000)
return jsonify({
"status": 200,
"msg": "获取红绿灯信号成功",
"data": traffic_light_data
})
@app.after_request
def add_cors_headers(response):
@ -932,5 +973,42 @@ def login():
"data": None
}), 401
@app.route('/openApi/getVehicleStatus', methods=['GET', 'OPTIONS'])
def get_vehicle_status():
"""获取无人车状态信息"""
if request.method == 'OPTIONS':
return '', 204
vehicle_id = request.args.get('vehicleId')
if not vehicle_id:
return jsonify({
"status": 400,
"msg": "缺少 vehicleId 参数",
"data": None
}), 400
# 查找对应的车辆
vehicle_state = vehicle_states.get(vehicle_id)
if not vehicle_state:
return jsonify({
"status": 404,
"msg": f"未找到车辆 {vehicle_id}",
"data": None
}), 404
# 返回车辆状态
return jsonify({
"status": 200,
"msg": "获取车辆状态成功",
"data": {
"vehicleId": vehicle_id,
"status": "NORMAL" if vehicle_state.is_running else "STOPPED",
"command": vehicle_state.current_command,
"commandPriority": vehicle_state.command_priority,
"trafficLightState": vehicle_state.traffic_light_state,
"timestamp": int(time.time() * 1000)
}
})
if __name__ == '__main__':
app.run(host='localhost', port=8081, debug=True)