更新了测试场景,增加了红绿灯机制

This commit is contained in:
Tian jianyong 2024-12-09 17:50:05 +08:00
parent b642bf3a6f
commit 49f40cc56e
51 changed files with 2222 additions and 759 deletions

View File

@ -6,7 +6,7 @@ using Newtonsoft.Json.Linq;
public class WebSocketClient : MonoBehaviour public class WebSocketClient : MonoBehaviour
{ {
private WebSocket websocket; private WebSocket websocket;
private readonly string serverUrl = "ws://localhost:8080"; private readonly string serverUrl = "ws://localhost:8010";
async void Start() async void Start()
{ {

View File

@ -33,6 +33,9 @@ set(Boost_USE_STATIC_RUNTIME OFF)
set(Boost_ROOT /opt/homebrew/Cellar/boost/1.86.0_2) set(Boost_ROOT /opt/homebrew/Cellar/boost/1.86.0_2)
find_package(Boost REQUIRED COMPONENTS system) find_package(Boost REQUIRED COMPONENTS system)
# libcurl
find_package(CURL REQUIRED)
# nlohmann_json # nlohmann_json
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
@ -54,6 +57,8 @@ set(LIB_SOURCES
src/types/VehicleData.cpp src/types/VehicleData.cpp
src/vehicle/ControllableVehicles.cpp src/vehicle/ControllableVehicles.cpp
src/config/SystemConfig.cpp src/config/SystemConfig.cpp
src/config/IntersectionConfig.cpp
src/detector/TrafficLightDetector.cpp
) )
# #
@ -63,12 +68,14 @@ target_include_directories(${PROJECT_NAME}_lib
PUBLIC PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src
${Boost_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}
${CURL_INCLUDE_DIRS}
) )
target_link_libraries(${PROJECT_NAME}_lib target_link_libraries(${PROJECT_NAME}_lib
PUBLIC PUBLIC
Boost::system Boost::system
nlohmann_json::nlohmann_json nlohmann_json::nlohmann_json
CURL::libcurl
) )
# #
@ -124,7 +131,7 @@ configure_file(
) )
configure_file( configure_file(
${CMAKE_SOURCE_DIR}/config/controllable_vehicles.json ${CMAKE_SOURCE_DIR}/config/vehicle_speed_limits.json
${CMAKE_BINARY_DIR}/bin/config/controllable_vehicles.json ${CMAKE_BINARY_DIR}/bin/config/vehicle_speed_limits.json
COPYONLY COPYONLY
) )

28
config/intersections.json Normal file
View File

@ -0,0 +1,28 @@
{
"intersections": [
{
"id": "intersection_001",
"name": "无人车与特勤车交叉路口",
"trafficLightId": "TL001",
"position": {
"longitude": 120.088003,
"latitude": 36.361999,
"altitude": 12.5
},
"radius": 100.0,
"stopDistance": 50.0
},
{
"id": "intersection_002",
"name": "无人车与飞机交叉路口",
"trafficLightId": "TL002",
"position": {
"longitude": 120.088303,
"latitude": 36.361999,
"altitude": 12.5
},
"radius": 100.0,
"stopDistance": 50.0
}
]
}

View File

@ -10,11 +10,13 @@
}, },
"data_source": { "data_source": {
"host": "localhost", "host": "localhost",
"port": 8080, "port": 8081,
"aircraft_path": "/api/getCurrentFlightPositions", "aircraft_path": "/api/getCurrentFlightPositions",
"vehicle_path": "/api/getCurrentVehiclePositions", "vehicle_path": "/api/getCurrentVehiclePositions",
"traffic_light_path": "/api/getTrafficLightSignals",
"refresh_interval_ms": 1000, "refresh_interval_ms": 1000,
"timeout_ms": 5000 "timeout_ms": 5000,
"read_timeout_ms": 2000
}, },
"warning": { "warning": {
"warning_interval_ms": 1000, "warning_interval_ms": 1000,
@ -26,7 +28,8 @@
"ping_interval_ms": 30000, "ping_interval_ms": 30000,
"position_update": { "position_update": {
"aircraft_interval_ms": 300, "aircraft_interval_ms": 300,
"vehicle_interval_ms": 500 "vehicle_interval_ms": 500,
"traffic_light_interval_ms": 1000
} }
}, },
"collision_detection": { "collision_detection": {
@ -51,7 +54,7 @@
} }
}, },
"logging": { "logging": {
"level": "info", "level": "debug",
"file": "logs/system.log", "file": "logs/system.log",
"max_size_mb": 10, "max_size_mb": 10,
"max_files": 5, "max_files": 5,

View File

@ -1,17 +1,17 @@
{ {
"vehicles": [ "vehicles": [
{ {
"vehicleNo": "VEH001", "vehicleNo": "QN001",
"ip": "192.168.1.101", "ip": "192.168.1.101",
"port": 8080 "port": 8080
}, },
{ {
"vehicleNo": "VEH002", "vehicleNo": "QN002",
"ip": "192.168.1.102", "ip": "192.168.1.102",
"port": 8080 "port": 8080
}, },
{ {
"vehicleNo": "VEH003", "vehicleNo": "TQ001",
"ip": "192.168.1.103", "ip": "192.168.1.103",
"port": 8080 "port": 8080
} }

41
docs/traffic_light_api.md Normal file
View File

@ -0,0 +1,41 @@
# 红绿灯信号接口文档
## 1. 上报接口定义
### 1.1 信号机实时状态上报
#### Topic
| Topic | cuse/v2/{tenantCode}/{deviceId}/data |
|-------|--------------------------------------|
| 消息发送方 | 设备 |
| 消息接收方 | 物联网平台 |
#### 参数说明
| 参数 | 必选/可选 | 类型 | 描述 |
|------|-----------|------|------|
| topic | 必选 | String | 设备参数上报固定为"deviceReq" |
| encryptFlag | 必选 | Short | 加密方式0-不加密 1-AES128 加密 |
| serviceId | 必选 | String | 上报服务的标识 |
| reportTime | 必选 | Long | 设备参数上报时间 |
| serviceType | 必选 | String | 服务类型(设备上报数据类型) 固定值"ras-traffic-lights" |
| serviceData | 必选 | Object | 具体上报的数据集合,若采用 SM4 加密方式,则进行数据加密 |
#### serviceData
| 参数 | 必选/可选 | 类型 | 描述 |
|------|-----------|------|------|
| collectTime | 必选 | Long | 上报时间 |
| deviceSn | 可选 | String | 终端设备序列号 |
| deviceName | 可选 | String | 终端设备名称 |
| manufacturer | 可选 | String | 厂商编码 |
| modelName | 可选 | String | 设备类型名称 |
| trafficLightId | 必选 | String | 红绿灯设备 ID |
| signalStatus | 必选 | String | 信号灯状态 |
| longitude | 必选 | Double | 经度坐标14位小数 |
| latitude | 必选 | Double | 纬度坐标14位小数 |
| altitude | 必选 | Double | 海拔高度6位小数 |
| areaId | 必选 | String | 信号机所处区域 ID |
| intersection | 必选 | String | 信号机所处路口名称 |
| generateTime | 必选 | Long | unix 时间戳 |

View File

@ -43,3 +43,71 @@
| 4 | time | 时间戳 | double | 是 | | 4 | time | 时间戳 | double | 是 |
| 5 | direction | 方向 | double | 否 | | 5 | direction | 方向 | double | 否 |
| 6 | speed | 速度 | double | 否 | | 6 | speed | 速度 | double | 否 |
## 第2章 无人车控制接口对接方案
### 2.1 无人车控制指令
2.1.1 接口地址: <http://IP:端口/openApi/emergency_control>
2.1.2 请求方法POST
2.1.3 请求参数:
| 序号 | 字段名字 | 是否必选 | 类型 | 说明 |
|-----|------|------|----------|----------|
| 1 | messageName | 是 | string | messageName 是 EmergencyControl 固定值 |
| 2 | messageUniqueId | 是 | string | messageUniqueId 是 string |
| 3 | messageTimestamp | 是 | string | messageTimestamp 是 string 2023-04-24 17:15:44.276341 |
| 4 | origin | 可选 | string | "Vehicle" |
| 5 | vehicleID | 是 | string | 根据项目车号的格式 |
| 6 | operationType | 是 | string | "RESUME" or "STOP" |
| 7 | StopType | 是 | string | "jerk" : 急停 "slow" : 缓停 |
| 8 | reason | 可选 | string | 停止/恢复的原因 |
2.1.4 请求参数:
``` http
request : {
"messageName" : "EmergencyControl",
"messageUniqueId" : "68f79d1a-e27f-11ed-b28c-2cf05d9c2649", "messageTimestamp":"2023-04-24 17:15:44.276341",
"origin" : "Vehicle",
"vehicleID" : "A001",
"operationType" : "RESUME", # RESUME | STOP
"StopType" : "jerk"
}
```
2.1.5 返回格式:以 JSON 格式返回数据
``` http
response : {
"code" : 200,
"msg" : "success",
"error" : ""
}
```

133
docs/warning_strategy.md Normal file
View File

@ -0,0 +1,133 @@
# 机场场景告警策略需求文档
## 1. 控制指令定义
系统对无人车的控制分为三种指令类型:
### 1.1 信号灯指令SIGNAL
- **目的**:响应交通信号灯
- **触发条件**:接收到红绿灯状态变化
- **执行动作**:根据信号灯状态停车或通行
- **优先级**:高(最高优先级)
### 1.2 告警指令ALERT
- **目的**:应对紧急情况
- **触发条件**:检测到直接碰撞风险
- **执行动作**:强制停车或紧急避让
- **优先级**:中
### 1.3 预警指令WARNING
- **目的**:提前告知可能的风险
- **触发条件**:进入监测区域但尚未到达关键区域
- **执行动作**:降低速度,提高警惕
- **优先级**:低
### 1.4 恢复指令RESUME
- **目的**:恢复到正常行驶状态
- **触发条件**:航空器或特勤车辆通过后,距离路口 50 m后
- **执行动作**:恢复到正常行驶状态
- **优先级**:低
## 2. 红绿灯条件下的告警策略
### 2.1 适用场景
- 无人车接近带有红绿灯的交叉路口时的行为控制
- 主要针对机场内各主要道路交叉口
### 2.2 告警条件
1. **距离阈值**
- 车辆进入路口 200 米范围内开始监测
- 在距离路口 50 米处为停车点
2. **信号灯状态响应**
- 红灯:持续发送 SIGNAL 指令,要求停车
- 绿灯:持续发送 SIGNAL 指令,允许通过
## 3. 航空器或特勤车辆,和无人车垂直经过交叉路口的告警策略
### 3.1 适用场景
- 滑行道与车行道的交叉路口
- 航空器或特勤车辆,和无人车垂直交叉,预判可能冲突的情况
### 3.2 告警条件
1. **距离监测**
- 航空器或特勤车辆通过路口无人车距离路口阈值100 米
2. **控制策略**
- 预警阶段100m-50m发送 WARNING 指令
- 告警阶段50m 以内):发送 ALERT 指令,要求立即停车
- 航空器或特勤车辆通过后,距离路口大于 50 m后发送 RESUME 指令,允许恢复正常行驶
3. **优先级规则**
- 航空器或特勤车辆具有绝对优先权
- 无人车必须避让航空器或特勤车辆
## 4. 航空器出库时的告警策略
### 4.1 适用场景
- 航空器从机库或维修区驶出
- 与服务区道路的交叉路口
### 4.2 告警条件
1. **距离监测**
- 航空器通过路口无人车距离路口阈值100 米
2. **控制策略**
- 预警阶段100m-50m发送 WARNING 指令
- 告警阶段50m 以内):发送 ALERT 指令,要求立即停车
- 航空器离开后,距离路口大于 50 m后发送 RESUME 指令,允许恢复正常行驶
### 4.3 特殊考虑
1. **多航空器情况**
- 就近原则处理
- 使用 ALERT 指令确保安全间距
## 5. 通用要求
### 5.1 指令优先级处理
1. SIGNAL 指令(最高优先级)
- 立即执行
- 覆盖其他所有指令
- 红绿灯控制具有最高优先级,确保交通安全
2. ALERT 指令(中等优先级)
- 可被 SIGNAL 指令覆盖
- 优先于 WARNING 指令
- 用于碰撞风险等紧急情况
3. WARNING 指令(最低优先级)
- 可被其他指令覆盖
- 用于预防性控制
- 在无更高优先级指令时执行
4. RESUME 指令(最低优先级)
- 可被其他指令覆盖
- 用于恢复到正常行驶状态
### 5.2 系统响应时间
- 告警信号处理延迟 < 100ms
- 控制指令执行延迟 < 200ms
- 状态反馈延迟 < 300ms
### 5.3 降级处理
- 在通信中断时,无人车应自动降速或停车
- 在传感器失效时,扩大安全距离
### 5.4 提前量
- 无人车在距离路口 110 米处开始监测,提前 10 米发送 WARNING 指令
- 无人车在距离路口 60 米处开始监测,提前 10 米发送 ALERT 指令

View File

@ -1,14 +0,0 @@
#
set(SOURCES
collector/DataCollector.cpp
core/System.cpp
detector/CollisionDetector.cpp
mock/MockDataService.cpp
network/HTTPDataSource.cpp
spatial/AirportBounds.cpp
spatial/CoordinateConverter.cpp
types/BasicTypes.cpp
types/VehicleData.cpp
)
# ... ...

View File

@ -1,6 +1,8 @@
#include "collector/DataCollector.h" #include "collector/DataCollector.h"
#include "network/HTTPDataSource.h" #include "network/HTTPDataSource.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include <chrono>
#include <mutex>
DataCollector::DataCollector() DataCollector::DataCollector()
: lastSuccessfulFetch_(std::chrono::steady_clock::now()) // 初始化时间戳 : lastSuccessfulFetch_(std::chrono::steady_clock::now()) // 初始化时间戳
@ -62,19 +64,38 @@ void DataCollector::stop() {
} }
void DataCollector::collectLoop() { void DataCollector::collectLoop() {
Logger::debug("数据采集循环启动,刷新间隔: ", dataSourceConfig_.refresh_interval_ms, "ms");
auto next_collection_time = std::chrono::steady_clock::now();
while (running_) { while (running_) {
loopCount_++; loopCount_++;
Logger::debug("开始第 ", loopCount_.load(), " 次数据采集循环");
bool success = fetchData(); bool success = fetchData();
if (!success) { if (!success) {
Logger::warning("Failed to fetch data in loop ", loopCount_.load()); Logger::warning(" ", loopCount_.load(), " 次数据采集失败");
checkTimeout(); // 每次获取失败都检查超时 checkTimeout(); // 每次获取失败都检查超时
} }
// 即使成功获取数据,也定期检查超时状态 // 即使成功获取数据,也定期检查超时状态
checkTimeout(); checkTimeout();
std::this_thread::sleep_for(std::chrono::milliseconds(dataSourceConfig_.refresh_interval_ms)); // 计算下一次采集时间
next_collection_time += std::chrono::milliseconds(dataSourceConfig_.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();
}
} }
} }
@ -83,15 +104,22 @@ void DataCollector::checkTimeout() {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - lastSuccessfulFetch_).count(); now - lastSuccessfulFetch_).count();
if (elapsed > dataSourceConfig_.timeout_ms) { // 检查是否达到告警间隔
// 检查是否达到告警间隔 auto warning_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
auto warning_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( now - last_warning_time_).count();
now - last_warning_time_).count();
if (warning_elapsed >= warnConfig_.warning_interval_ms) { if (warning_elapsed >= warnConfig_.warning_interval_ms) {
// 如果超过了超时阈值,发送警告
if (elapsed > dataSourceConfig_.read_timeout_ms) {
sendTimeoutWarning(elapsed); sendTimeoutWarning(elapsed);
last_warning_time_ = now; last_warning_time_ = now;
Logger::warning("Data source timeout: No response for ", elapsed, "ms");
// 只在非测试环境中记录警告日志
if (system_) {
Logger::warning("Data source timeout: No response for ", elapsed, "ms");
} else {
Logger::debug("Data source timeout in test environment: ", elapsed, "ms");
}
} }
} }
} }
@ -110,97 +138,102 @@ void DataCollector::resetTimeout() {
} }
void DataCollector::sendTimeoutWarning(int64_t elapsed_ms) { void DataCollector::sendTimeoutWarning(int64_t elapsed_ms) {
network::TimeoutWarningMessage msg; if (!system_) {
msg.type = "data_source_timeout"; // 确保类型正确 Logger::debug("System not set, skipping timeout warning");
msg.host = dataSourceConfig_.host; return;
msg.port = dataSourceConfig_.port;
msg.elapsed_ms = elapsed_ms;
msg.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
if (System::instance()) {
System::instance()->broadcastTimeoutWarning(msg);
Logger::debug("Sent timeout warning: host=", msg.host,
", port=", msg.port,
", elapsed=", msg.elapsed_ms, "ms");
} else {
Logger::error("Failed to send timeout warning: System instance not available");
} }
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 DataCollector::fetchData() {
bool success = true;
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_->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;
}
if (success) {
resetTimeout(); // 重置超时计时器
Logger::debug("数据获取完成");
} else {
Logger::warning("数据获取过程中存在错误");
}
return success;
}
std::vector<TrafficLightSignal> DataCollector::getTrafficLightSignals() {
std::lock_guard<std::mutex> lock(cacheMutex_);
return trafficLightCache_;
}
bool DataCollector::fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals) {
if (!dataSource_) { if (!dataSource_) {
return false; return false;
} }
return dataSource_->fetchTrafficLightSignals(signals);
try {
bool success = false;
// 获取航空器数据
std::vector<Aircraft> aircraft;
if (dataSource_->fetchAircraftData(aircraft)) {
std::lock_guard<std::mutex> lock(cacheMutex_);
// 更新运动信息
for (auto& a : aircraft) {
auto it = lastAircraftPositions_.find(a.id);
if (it != lastAircraftPositions_.end()) {
Logger::debug("Updating motion for aircraft ", a.id,
", prev pos: lat=", it->second.geo.latitude,
", lon=", it->second.geo.longitude,
", time=", it->second.timestamp,
", curr pos: lat=", a.geo.latitude,
", lon=", a.geo.longitude,
", time=", a.timestamp);
// 复制历史记录
a.copyHistoryFrom(it->second);
// 更新运动信息
a.updateMotion(a.geo, a.timestamp);
} else {
Logger::debug("First position for aircraft ", a.id);
a.updateMotion(a.geo, a.timestamp);
}
lastAircraftPositions_[a.id] = a;
}
aircraftCache_ = std::move(aircraft);
Logger::debug("Cached ", aircraftCache_.size(), " aircraft");
success = true;
}
// 获取车辆数据
std::vector<Vehicle> vehicles;
if (dataSource_->fetchVehicleData(vehicles)) {
std::lock_guard<std::mutex> lock(cacheMutex_);
// 更新运动信息
for (auto& v : vehicles) {
auto it = lastVehiclePositions_.find(v.id);
if (it != lastVehiclePositions_.end()) {
Logger::debug("Updating motion for vehicle ", v.id);
// 复制历史记录
v.copyHistoryFrom(it->second);
// 更新运动信息
v.updateMotion(v.geo, v.timestamp);
} else {
Logger::debug("First position for vehicle ", v.id);
v.updateMotion(v.geo, v.timestamp);
}
lastVehiclePositions_[v.id] = v;
}
vehicleCache_ = std::move(vehicles);
Logger::debug("Cached ", vehicleCache_.size(), " vehicles");
success = true;
}
if (success) {
resetTimeout(); // 成功获取数据后重置超时
}
return success;
}
catch (const std::exception& e) {
Logger::error("Exception in fetchData: ", e.what());
return false;
}
} }
std::vector<Aircraft> DataCollector::getAircraftData() { std::vector<Aircraft> DataCollector::getAircraftData() {
@ -218,6 +251,13 @@ void DataCollector::refresh() {
// 从数据源获取最新数据 // 从数据源获取最新数据
std::vector<Aircraft> newAircraft; std::vector<Aircraft> newAircraft;
std::vector<Vehicle> newVehicles; std::vector<Vehicle> newVehicles;
std::vector<TrafficLightSignal> newSignals;
// 获取红绿灯信号
if (dataSource_->fetchTrafficLightSignals(newSignals)) {
std::lock_guard<std::mutex> lock(cacheMutex_);
trafficLightCache_ = std::move(newSignals);
}
if (dataSource_->fetchAircraftData(newAircraft)) { if (dataSource_->fetchAircraftData(newAircraft)) {
std::lock_guard<std::mutex> lock(cacheMutex_); std::lock_guard<std::mutex> lock(cacheMutex_);

View File

@ -11,6 +11,8 @@
#include "DataSourceConfig.h" #include "DataSourceConfig.h"
#include "types/BasicTypes.h" #include "types/BasicTypes.h"
#include "config/WarnConfig.h" #include "config/WarnConfig.h"
#include "types/TrafficLightTypes.h"
#include "core/System.h"
class DataCollector { class DataCollector {
public: public:
@ -20,21 +22,25 @@ public:
bool initialize(const DataSourceConfig& config, const WarnConfig& warnConfig); bool initialize(const DataSourceConfig& config, const WarnConfig& warnConfig);
void start(); void start();
void stop(); void stop();
void refresh();
void setSystem(std::shared_ptr<System> system) { system_ = system; }
// 获取数据的接口 // 获取数据的接口
std::vector<Aircraft> getAircraftData(); std::vector<Aircraft> getAircraftData();
std::vector<Vehicle> getVehicleData(); std::vector<Vehicle> getVehicleData();
std::vector<TrafficLightSignal> getTrafficLightSignals();
bool fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals);
// 添加设置数据源的方法(用于测试) // 添加设置数据源的方法(用于测试)
void setDataSource(std::shared_ptr<DataSource> source) { void setDataSource(std::shared_ptr<DataSource> source) {
dataSource_ = source; dataSource_ = source;
} }
// 添加刷新方法
void refresh();
private: private:
std::shared_ptr<DataSource> dataSource_; std::shared_ptr<DataSource> dataSource_;
std::shared_ptr<System> system_;
DataSourceConfig dataSourceConfig_; DataSourceConfig dataSourceConfig_;
WarnConfig warnConfig_; WarnConfig warnConfig_;
std::thread collectorThread_; std::thread collectorThread_;
@ -42,14 +48,19 @@ private:
std::atomic<uint64_t> loopCount_{0}; std::atomic<uint64_t> loopCount_{0};
// 缓存当前数据 // 缓存当前数据
std::mutex cacheMutex_; mutable std::mutex cacheMutex_;
std::vector<Aircraft> aircraftCache_; std::vector<Aircraft> aircraftCache_;
std::vector<Vehicle> vehicleCache_; std::vector<Vehicle> vehicleCache_;
std::vector<TrafficLightSignal> trafficLightCache_;
// 保存上一次位置信息(用于计算速度) // 保存上一次位置信息(用于计算速度)
std::map<std::string, Aircraft> lastAircraftPositions_; std::map<std::string, Aircraft> lastAircraftPositions_;
std::map<std::string, Vehicle> lastVehiclePositions_; std::map<std::string, Vehicle> lastVehiclePositions_;
// 数据时间戳
std::atomic<int64_t> lastAircraftTimestamp_{0};
std::atomic<int64_t> lastVehicleTimestamp_{0};
// 超时检测相关 // 超时检测相关
std::chrono::steady_clock::time_point lastSuccessfulFetch_; std::chrono::steady_clock::time_point lastSuccessfulFetch_;
std::chrono::steady_clock::time_point last_warning_time_; std::chrono::steady_clock::time_point last_warning_time_;

View File

@ -2,6 +2,7 @@
#define AIRPORT_COLLECTOR_DATA_SOURCE_H #define AIRPORT_COLLECTOR_DATA_SOURCE_H
#include "types/BasicTypes.h" #include "types/BasicTypes.h"
#include "types/TrafficLightTypes.h"
#include <optional> #include <optional>
#include <vector> #include <vector>
@ -16,6 +17,7 @@ public:
// 获取数据的接口 // 获取数据的接口
virtual bool fetchAircraftData(std::vector<Aircraft>& aircraft) = 0; virtual bool fetchAircraftData(std::vector<Aircraft>& aircraft) = 0;
virtual bool fetchVehicleData(std::vector<Vehicle>& vehicles) = 0; virtual bool fetchVehicleData(std::vector<Vehicle>& vehicles) = 0;
virtual bool fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals) = 0;
}; };
#endif // AIRPORT_COLLECTOR_DATA_SOURCE_H #endif // AIRPORT_COLLECTOR_DATA_SOURCE_H

View File

@ -8,8 +8,10 @@ struct DataSourceConfig {
uint16_t port; uint16_t port;
std::string aircraft_path; std::string aircraft_path;
std::string vehicle_path; std::string vehicle_path;
std::string traffic_light_path;
int refresh_interval_ms; int refresh_interval_ms;
int timeout_ms; int timeout_ms; // 连接超时时间, 单位: 毫秒
int read_timeout_ms; // 读取超时时间, 单位: 毫秒, 小于连接超时时间
}; };
#endif #endif

View File

@ -1,45 +0,0 @@
#include "CommandSender.h"
#include <chrono>
CommandSender::CommandSender()
: network_(std::make_unique<NetworkInterface>()) {
}
bool CommandSender::sendCommand(const VehicleCommand& cmd, Priority priority) {
PrioritizedCommand pcmd{
.command = cmd,
.priority = priority,
.timestamp = std::chrono::system_clock::now().time_since_epoch().count()
};
cmdQueue_.push(pcmd);
processQueue();
return true;
}
void CommandSender::processQueue() {
while (!cmdQueue_.empty()) {
auto cmd = cmdQueue_.top();
cmdQueue_.pop();
if (!network_->send(cmd.command)) {
// 发送失败,进入重试机制
retryMechanism(cmd.command);
}
}
}
void CommandSender::retryMechanism(const VehicleCommand& cmd) {
constexpr int MAX_RETRIES = 3;
constexpr int RETRY_DELAY_MS = 100;
for (int i = 0; i < MAX_RETRIES; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(RETRY_DELAY_MS));
if (network_->send(cmd)) {
return;
}
}
// 所有重试都失败,记录错误
// TODO: 添加错误日志
}

View File

@ -1,39 +0,0 @@
#ifndef AIRPORT_COMMAND_COMMAND_SENDER_H
#define AIRPORT_COMMAND_COMMAND_SENDER_H
#include <queue>
#include <memory>
#include "types/VehicleCommand.h"
#include "network/NetworkInterface.h"
class CommandSender {
public:
enum class Priority {
EMERGENCY,
HIGH,
NORMAL,
LOW
};
CommandSender();
bool sendCommand(const VehicleCommand& cmd, Priority priority = Priority::NORMAL);
private:
struct PrioritizedCommand {
VehicleCommand command;
Priority priority;
uint64_t timestamp;
bool operator<(const PrioritizedCommand& other) const {
return priority < other.priority;
}
};
std::priority_queue<PrioritizedCommand> cmdQueue_;
std::unique_ptr<NetworkInterface> network_;
void retryMechanism(const VehicleCommand& cmd);
void processQueue();
};
#endif // AIRPORT_COMMAND_COMMAND_SENDER_H

View File

@ -1,58 +0,0 @@
#ifndef AIRPORT_CONCURRENT_LOCK_FREE_QUEUE_H
#define AIRPORT_CONCURRENT_LOCK_FREE_QUEUE_H
#include <atomic>
#include <optional>
template<typename T>
class LockFreeQueue {
public:
LockFreeQueue() : head_(new Node(T())), tail_(head_.load()) {}
~LockFreeQueue() {
while (Node* const old_head = head_.load()) {
head_.store(old_head->next);
delete old_head;
}
}
void push(T value) {
Node* new_node = new Node(std::move(value));
Node* old_tail = tail_.load();
while (!tail_.compare_exchange_weak(old_tail, new_node)) {
old_tail = tail_.load();
}
old_tail->next.store(new_node);
}
std::optional<T> pop() {
Node* old_head = head_.load();
Node* new_head;
do {
new_head = old_head->next;
if (!new_head) {
return std::nullopt;
}
} while (!head_.compare_exchange_weak(old_head, new_head));
T value = std::move(new_head->value);
delete old_head;
return value;
}
bool empty() const {
return head_.load()->next == nullptr;
}
private:
struct Node {
T value;
std::atomic<Node*> next{nullptr};
explicit Node(T v) : value(std::move(v)) {}
};
std::atomic<Node*> head_;
std::atomic<Node*> tail_;
};
#endif // AIRPORT_CONCURRENT_LOCK_FREE_QUEUE_H

View File

@ -1,81 +0,0 @@
#ifndef AIRPORT_CONCURRENT_THREAD_POOL_H
#define AIRPORT_CONCURRENT_THREAD_POOL_H
#include <vector>
#include <thread>
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <future>
#include <type_traits>
class ThreadPool {
public:
explicit ThreadPool(size_t numThreads) : stop_(false) {
for(size_t i = 0; i < numThreads; ++i) {
workers_.emplace_back([this] {
while(true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queue_mutex_);
condition_.wait(lock, [this] {
return stop_ || !tasks_.empty();
});
if(stop_ && tasks_.empty()) {
return;
}
task = std::move(tasks_.front());
tasks_.pop();
}
task();
}
});
}
}
template<class F, class... Args>
auto enqueue(F&& f, Args&&... args)
-> std::future<std::invoke_result_t<F, Args...>> {
using return_type = std::invoke_result_t<F, Args...>;
auto task = std::make_shared<std::packaged_task<return_type()>>(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex_);
if(stop_) {
throw std::runtime_error("enqueue on stopped ThreadPool");
}
tasks_.emplace([task]() { (*task)(); });
}
condition_.notify_one();
return res;
}
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queue_mutex_);
stop_ = true;
}
condition_.notify_all();
for(std::thread &worker: workers_) {
worker.join();
}
}
private:
std::vector<std::thread> workers_;
std::queue<std::function<void()>> tasks_;
std::mutex queue_mutex_;
std::condition_variable condition_;
bool stop_;
};
#endif // AIRPORT_CONCURRENT_THREAD_POOL_H

View File

@ -0,0 +1,64 @@
#include "config/IntersectionConfig.h"
#include "utils/Logger.h"
#include <nlohmann/json.hpp>
#include <fstream>
#include <stdexcept>
#include <algorithm>
IntersectionConfig IntersectionConfig::load(const std::string& configFile) {
IntersectionConfig config;
std::ifstream file(configFile);
if (!file.is_open()) {
throw std::runtime_error("Failed to open intersection config file: " + configFile);
}
try {
nlohmann::json jsonConfig;
file >> jsonConfig;
for (const auto& item : jsonConfig["intersections"]) {
Intersection info;
info.id = item["id"].get<std::string>();
info.name = item["name"].get<std::string>();
info.trafficLightId = item["trafficLightId"].get<std::string>();
// 加载位置信息
info.position.longitude = item["position"]["longitude"].get<double>();
info.position.latitude = item["position"]["latitude"].get<double>();
info.position.altitude = item["position"]["altitude"].get<double>();
// 加载距离阈值
info.radius = item["radius"].get<double>();
info.stopDistance = item["stopDistance"].get<double>();
config.intersections_.push_back(info);
Logger::debug("Loaded intersection: id=", info.id,
" name=", info.name,
" trafficLightId=", info.trafficLightId);
}
Logger::info("Loaded ", config.intersections_.size(), " intersections");
} catch (const std::exception& e) {
throw std::runtime_error("Failed to parse intersection config: " + std::string(e.what()));
}
return config;
}
const Intersection* IntersectionConfig::findByTrafficLightId(const std::string& trafficLightId) const {
auto iter = std::find_if(intersections_.begin(), intersections_.end(),
[&](const Intersection& info) {
return info.trafficLightId == trafficLightId;
});
return iter != intersections_.end() ? &(*iter) : nullptr;
}
const Intersection* IntersectionConfig::findById(const std::string& intersectionId) const {
auto iter = std::find_if(intersections_.begin(), intersections_.end(),
[&](const Intersection& info) {
return info.id == intersectionId;
});
return iter != intersections_.end() ? &(*iter) : nullptr;
}

View File

@ -0,0 +1,37 @@
#pragma once
#include <string>
#include <vector>
#include "types/BasicTypes.h"
struct IntersectionPosition {
double longitude;
double latitude;
double altitude;
};
struct Intersection {
std::string id;
std::string name;
std::string trafficLightId;
IntersectionPosition position;
double radius; // 路口影响半径,单位:米
double stopDistance; // 建议停车距离,单位:米
};
class IntersectionConfig {
public:
static IntersectionConfig load(const std::string& configFile);
// 根据红绿灯ID查找路口
const Intersection* findByTrafficLightId(const std::string& trafficLightId) const;
// 根据路口ID查找路口
const Intersection* findById(const std::string& intersectionId) const;
// 获取所有路口配置
const std::vector<Intersection>& getIntersections() const { return intersections_; }
private:
std::vector<Intersection> intersections_;
};

View File

@ -26,8 +26,10 @@ SystemConfig SystemConfig::load(const std::string& filename) {
config.data_source.port = j["data_source"]["port"]; config.data_source.port = j["data_source"]["port"];
config.data_source.aircraft_path = j["data_source"]["aircraft_path"]; config.data_source.aircraft_path = j["data_source"]["aircraft_path"];
config.data_source.vehicle_path = j["data_source"]["vehicle_path"]; config.data_source.vehicle_path = j["data_source"]["vehicle_path"];
config.data_source.traffic_light_path = j["data_source"]["traffic_light_path"];
config.data_source.refresh_interval_ms = j["data_source"]["refresh_interval_ms"]; config.data_source.refresh_interval_ms = j["data_source"]["refresh_interval_ms"];
config.data_source.timeout_ms = j["data_source"]["timeout_ms"]; config.data_source.timeout_ms = j["data_source"]["timeout_ms"];
config.data_source.read_timeout_ms = j["data_source"]["read_timeout_ms"];
// 加载 WebSocket 配置 // 加载 WebSocket 配置
config.websocket.port = j["websocket"]["port"]; config.websocket.port = j["websocket"]["port"];

View File

@ -19,17 +19,20 @@ struct SystemConfig {
uint16_t port; uint16_t port;
std::string aircraft_path; std::string aircraft_path;
std::string vehicle_path; std::string vehicle_path;
std::string traffic_light_path;
int refresh_interval_ms; int refresh_interval_ms;
int timeout_ms; int timeout_ms;
int read_timeout_ms;
} data_source; } data_source;
struct WebSocket { struct WebSocket {
int port; uint16_t port;
int max_connections; int max_connections;
int ping_interval_ms; int ping_interval_ms;
struct PositionUpdate { struct PositionUpdate {
int aircraft_interval_ms; int aircraft_interval_ms;
int vehicle_interval_ms; int vehicle_interval_ms;
int traffic_light_interval_ms;
} position_update; } position_update;
} websocket; } websocket;

View File

@ -1,6 +1,7 @@
#include <csignal> #include <csignal>
#include <typeinfo> #include <typeinfo>
#include <chrono> #include <chrono>
#include <cmath>
#include "core/System.h" #include "core/System.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "collector/DataCollector.h" #include "collector/DataCollector.h"
@ -31,6 +32,10 @@ bool System::initialize() {
// 加载系统配置 // 加载系统配置
system_config_ = SystemConfig::load("config/system_config.json"); system_config_ = SystemConfig::load("config/system_config.json");
// 加载路口配置
intersection_config_ = IntersectionConfig::load("config/intersections.json");
Logger::info("Loaded {} intersections", intersection_config_.getIntersections().size());
// 初始化 WebSocket 服务器 // 初始化 WebSocket 服务器
ws_server_ = std::make_unique<network::WebSocketServer>(system_config_.websocket.port); ws_server_ = std::make_unique<network::WebSocketServer>(system_config_.websocket.port);
ws_thread_ = std::thread([this]() { ws_thread_ = std::thread([this]() {
@ -41,13 +46,14 @@ bool System::initialize() {
airportBounds_ = std::make_unique<AirportBounds>("config/airport_bounds.json"); airportBounds_ = std::make_unique<AirportBounds>("config/airport_bounds.json");
// 加载可控车辆配置 // 加载可控车辆配置
controllableVehicles_ = std::make_unique<ControllableVehicles>("config/controllable_vehicles.json"); controllableVehicles_ = std::make_unique<ControllableVehicles>("config/vehicle_speed_limits.json");
// 初始化冲突检测器
// 初始化碰撞检测器
collisionDetector_ = std::make_unique<CollisionDetector>(*airportBounds_, *controllableVehicles_); collisionDetector_ = std::make_unique<CollisionDetector>(*airportBounds_, *controllableVehicles_);
// 初始化红绿灯检测器
trafficLightDetector_ = std::make_unique<TrafficLightDetector>(intersection_config_, *controllableVehicles_);
// 创建数据采集器 // 创建数据采集器
dataCollector_ = std::make_unique<DataCollector>(); dataCollector_ = std::make_unique<DataCollector>();
@ -57,6 +63,7 @@ bool System::initialize() {
system_config_.data_source.port, system_config_.data_source.port,
system_config_.data_source.aircraft_path, system_config_.data_source.aircraft_path,
system_config_.data_source.vehicle_path, system_config_.data_source.vehicle_path,
system_config_.data_source.traffic_light_path,
system_config_.data_source.refresh_interval_ms, system_config_.data_source.refresh_interval_ms,
system_config_.data_source.timeout_ms system_config_.data_source.timeout_ms
}; };
@ -116,57 +123,151 @@ void System::stop() {
void System::processLoop() { void System::processLoop() {
auto last_aircraft_update = std::chrono::steady_clock::now(); auto last_aircraft_update = std::chrono::steady_clock::now();
auto last_vehicle_update = std::chrono::steady_clock::now(); auto last_vehicle_update = std::chrono::steady_clock::now();
auto last_data_refresh = std::chrono::steady_clock::now(); auto last_collision_update = std::chrono::steady_clock::now();
auto last_traffic_light_update = 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;
Logger::debug("数据处理循环启动");
while (running_) { while (running_) {
try { try {
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
auto last_data_collection = now;
if (std::chrono::duration_cast<std::chrono::milliseconds>( Logger::debug("开始获取数据...");
now - last_data_refresh).count() >= system_config_.data_source.refresh_interval_ms) {
dataCollector_->refresh(); // 获取最新数据
last_data_refresh = now; dataCollector_->refresh(); // 先刷新数据
auto aircraft = dataCollector_->getAircraftData();
auto aircraft = dataCollector_->getAircraftData(); auto vehicles = dataCollector_->getVehicleData();
auto vehicles = dataCollector_->getVehicleData(); auto traffic_lights = dataCollector_->getTrafficLightSignals();
// 检查航空器更新
auto aircraft_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_aircraft_update).count();
if (aircraft_elapsed >= system_config_.websocket.position_update.aircraft_interval_ms) {
bool has_new_aircraft = false;
int64_t max_timestamp = last_aircraft_timestamp;
Logger::debug("Got data: ", aircraft.size(), " aircraft, ", vehicles.size(), " vehicles"); Logger::debug("开始检查航空器数据更新,当前时间戳: ", last_aircraft_timestamp);
for (const auto& ac : aircraft) {
Logger::debug("航空器 ", ac.flightNo, " 时间戳: current=", ac.timestamp, " last=", last_aircraft_timestamp);
if (ac.timestamp > last_aircraft_timestamp) { // 改为大于比较
has_new_aircraft = true;
if (ac.timestamp > max_timestamp) {
max_timestamp = ac.timestamp;
}
Logger::debug("发现新数据: ", ac.flightNo, " (新时间戳: ", ac.timestamp, ")");
}
}
// 检查航空器更新 if (has_new_aircraft) {
auto aircraft_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( Logger::debug("广播 ", aircraft.size(), " 架航空器位置, 时间戳更新: ", last_aircraft_timestamp, " -> ", max_timestamp);
now - last_aircraft_update).count();
if (aircraft_elapsed >= system_config_.websocket.position_update.aircraft_interval_ms) {
Logger::debug("Broadcasting aircraft positions (", aircraft.size(), " aircraft)");
for (const auto& ac : aircraft) { for (const auto& ac : aircraft) {
broadcastPositionUpdate(ac); broadcastPositionUpdate(ac);
} }
last_aircraft_update = now; last_aircraft_timestamp = max_timestamp;
} else {
Logger::debug("没有新的航空器数据,当前时间戳: ", last_aircraft_timestamp);
}
last_aircraft_update = now;
}
// 检查车辆更新
auto vehicle_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_vehicle_update).count();
if (vehicle_elapsed >= system_config_.websocket.position_update.vehicle_interval_ms) {
bool has_new_vehicles = false;
int64_t max_timestamp = last_vehicle_timestamp;
Logger::debug("开始检查车辆数据更新,当前时间戳: ", last_vehicle_timestamp);
for (const auto& veh : vehicles) {
Logger::debug("车辆 ", veh.vehicleNo, " 时间戳: current=", veh.timestamp, " last=", last_vehicle_timestamp);
if (veh.timestamp > last_vehicle_timestamp) { // 改为大于比较
has_new_vehicles = true;
if (veh.timestamp > max_timestamp) {
max_timestamp = veh.timestamp;
}
Logger::debug("发现新数据: ", veh.vehicleNo, " (新时间戳: ", veh.timestamp, ")");
}
} }
// 检查车辆更新 if (has_new_vehicles) {
auto vehicle_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( Logger::debug("广播 ", vehicles.size(), " 辆车辆位置, 时间戳更新: ", last_vehicle_timestamp, " -> ", max_timestamp);
now - last_vehicle_update).count();
if (vehicle_elapsed >= system_config_.websocket.position_update.vehicle_interval_ms) {
Logger::debug("Broadcasting vehicle positions (", vehicles.size(), " vehicles)");
for (const auto& veh : vehicles) { for (const auto& veh : vehicles) {
broadcastPositionUpdate(veh); broadcastPositionUpdate(veh);
} }
last_vehicle_update = now; last_vehicle_timestamp = max_timestamp;
} } else {
Logger::debug("没有新的车辆数据,当前时间戳: ", last_vehicle_timestamp);
// 更新碰撞检测器
collisionDetector_->updateTraffic(aircraft, vehicles);
auto collisions = collisionDetector_->detectCollisions();
if (!collisions.empty()) {
processCollisions(collisions);
} }
last_vehicle_update = now;
} }
// 处理间隔设置为数据刷新间隔的 1/10 // 检查冲突更新
std::this_thread::sleep_for(std::chrono::milliseconds( auto collision_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
system_config_.data_source.refresh_interval_ms / 10 now - last_collision_update).count();
)); if (collision_elapsed >= system_config_.collision_detection.update_interval_ms) {
// 只有当有新的数据时才更新冲突检测
if (last_aircraft_timestamp > last_collision_timestamp ||
last_vehicle_timestamp > last_collision_timestamp) {
// 更新冲突检测器
collisionDetector_->updateTraffic(aircraft, vehicles);
auto collisions = collisionDetector_->detectCollisions();
if (!collisions.empty()) {
processCollisions(collisions);
}
last_collision_timestamp = std::max(last_aircraft_timestamp, last_vehicle_timestamp);
}
last_collision_update = now;
}
// 处理红绿灯信号
auto traffic_light_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - last_traffic_light_update).count();
if (traffic_light_elapsed >= system_config_.websocket.position_update.traffic_light_interval_ms) {
bool has_new_signals = false;
int64_t max_timestamp = last_traffic_light_timestamp;
Logger::debug("开始检查红绿灯数据更新,当前时间戳: ", last_traffic_light_timestamp);
for (const auto& signal : traffic_lights) {
if (signal.timestamp > last_traffic_light_timestamp) {
has_new_signals = true;
if (signal.timestamp > max_timestamp) {
max_timestamp = signal.timestamp;
}
Logger::debug("红绿灯 ", signal.trafficLightId, " 时间戳: current=", signal.timestamp, " last=", last_traffic_light_timestamp);
Logger::debug("发现新数据: ", signal.trafficLightId, " (新时间戳: ", signal.timestamp, ")");
// 广播红绿灯状态更新
broadcastTrafficLightUpdate(signal);
// 处理红绿灯信号并检查是否需要停车
trafficLightDetector_->processSignal(signal, vehicles);
}
}
if (has_new_signals) {
Logger::debug("处理了 ", traffic_lights.size(), " 个红绿灯信号, 时间戳更新: ",
last_traffic_light_timestamp, " -> ", max_timestamp);
last_traffic_light_timestamp = max_timestamp;
}
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();
if (wait_time > 0) {
Logger::debug("等待 ", wait_time, "ms 进行下一次采集");
std::this_thread::sleep_for(std::chrono::milliseconds(wait_time));
}
} }
catch (const std::exception& e) { catch (const std::exception& e) {
Logger::error("处理循环发生错误: ", e.what()); Logger::error("处理循环发生错误: ", e.what());
@ -176,28 +277,22 @@ void System::processLoop() {
void System::processCollisions(const std::vector<CollisionRisk>& collisions) { void System::processCollisions(const std::vector<CollisionRisk>& collisions) {
if (!collisions.empty()) { if (!collisions.empty()) {
Logger::info("检测到 ", collisions.size(), "碰撞风险"); Logger::info("检测到 ", collisions.size(), "冲突风险");
for (const auto& risk : collisions) { for (const auto& risk : collisions) {
// 发送碰撞警告到 Unity 客户端 // 发送冲突警告到 Unity 客户端
broadcastCollisionWarning(risk); broadcastCollisionWarning(risk);
// 根据风险等级选择不同的日志级别 // 根据风险等级选择不同的日志级别
switch (risk.level) { switch (risk.level) {
case RiskLevel::EMERGENCY:
Logger::error("严重碰撞风险: ", risk.id1, "", risk.id2,
", 距离: ", risk.distance, "",
", 相对速度: ", risk.relativeSpeed, "m/s");
break;
case RiskLevel::CRITICAL: case RiskLevel::CRITICAL:
Logger::warning("高度碰撞风险: ", risk.id1, "", risk.id2, Logger::warning("高度冲突风险: ", risk.id1, "", risk.id2,
", 距离: ", risk.distance, "", ", 距离: ", risk.distance, "",
", 相对速度: ", risk.relativeSpeed, "m/s"); ", 相对速度: ", risk.relativeSpeed, "m/s");
break; break;
case RiskLevel::WARNING: case RiskLevel::WARNING:
Logger::warning("低度碰撞风险: ", risk.id1, "", risk.id2, Logger::warning("低度冲突风险: ", risk.id1, "", risk.id2,
", 距离: ", risk.distance, "", ", 距离: ", risk.distance, "",
", 相对速度: ", risk.relativeSpeed, "m/s"); ", 相对速度: ", risk.relativeSpeed, "m/s");
break; break;
@ -209,53 +304,83 @@ void System::processCollisions(const std::vector<CollisionRisk>& collisions) {
} }
void System::broadcastPositionUpdate(const MovingObject& obj) { void System::broadcastPositionUpdate(const MovingObject& obj) {
if (!ws_server_) {
return;
}
network::PositionUpdateMessage msg; network::PositionUpdateMessage msg;
msg.objectId = obj.id; msg.objectId = obj.id;
msg.objectType = (typeid(obj) == typeid(Aircraft)) ? "aircraft" : "vehicle"; msg.objectType = (typeid(obj) == typeid(Aircraft)) ? "aircraft" : "vehicle";
msg.timestamp = obj.timestamp;
msg.longitude = obj.geo.longitude; msg.longitude = obj.geo.longitude;
msg.latitude = obj.geo.latitude; msg.latitude = obj.geo.latitude;
msg.heading = obj.heading; msg.heading = obj.heading;
msg.speed = obj.speed; msg.speed = obj.speed;
msg.timestamp = obj.timestamp;
if (ws_server_) { nlohmann::json j = {
ws_server_->broadcast(msg.toJson().dump()); {"type", "position_update"},
Logger::debug("Broadcast position update: type=", msg.objectType, {"object_id", msg.objectId},
", id=", msg.objectId, {"object_type", msg.objectType},
", pos=(", msg.longitude, ",", msg.latitude, ")"); {"timestamp", msg.timestamp},
{"position", {
{"longitude", msg.longitude},
{"latitude", msg.latitude}
}},
{"heading", msg.heading},
{"speed", msg.speed}
};
ws_server_->broadcast(j.dump());
Logger::debug("广播位置更新: id=", msg.objectId,
" type=", msg.objectType,
" pos=(", msg.longitude, ",", msg.latitude, ")",
" heading=", msg.heading,
" speed=", msg.speed,
" timestamp=", msg.timestamp);
}
void System::broadcastTrafficLightUpdate(const TrafficLightSignal& signal) {
if (!ws_server_) {
return;
}
nlohmann::json j = {
{"type", "traffic_light_update"},
{"id", signal.trafficLightId},
{"status", static_cast<int>(signal.status)},
{"timestamp", signal.timestamp}
};
ws_server_->broadcast(j.dump());
Logger::debug("广播红绿灯状态: id=", signal.trafficLightId,
" status=", static_cast<int>(signal.status),
" timestamp=", signal.timestamp);
}
std::string getRiskLevelString(RiskLevel level) {
switch (level) {
case RiskLevel::CRITICAL: return "critical";
case RiskLevel::WARNING: return "warning";
default: return "none";
} }
} }
void System::broadcastCollisionWarning(const CollisionRisk& risk) { void System::broadcastCollisionWarning(const CollisionRisk& risk) {
network::CollisionWarningMessage msg; if (!ws_server_) {
msg.id1 = risk.id1; return;
msg.id2 = risk.id2;
// 根据风险等级设置预警级别和阈值
switch (risk.level) {
case RiskLevel::EMERGENCY:
msg.warningLevel = "high";
msg.threshold = system_config_.collision_detection.thresholds.runway.aircraft_ground;
break;
case RiskLevel::CRITICAL:
msg.warningLevel = "medium";
msg.threshold = system_config_.collision_detection.thresholds.taxiway.aircraft_ground;
break;
case RiskLevel::WARNING:
msg.warningLevel = "low";
msg.threshold = system_config_.collision_detection.thresholds.apron.aircraft_ground;
break;
default:
return; // 不发送无风险的警告
} }
msg.distance = risk.distance; nlohmann::json j = {
msg.relativeSpeed = risk.relativeSpeed; {"type", "collision_warning"},
msg.timestamp = std::chrono::system_clock::now().time_since_epoch().count(); {"id1", risk.id1},
{"id2", risk.id2},
{"distance", risk.distance},
{"relativeSpeed", risk.relativeSpeed},
{"warningLevel", getRiskLevelString(risk.level)},
{"timestamp", std::chrono::system_clock::now().time_since_epoch().count()}
};
if (ws_server_) { ws_server_->broadcast(j.dump());
ws_server_->broadcast(msg.toJson().dump());
}
} }
void System::broadcastTimeoutWarning(const network::TimeoutWarningMessage& warning) { void System::broadcastTimeoutWarning(const network::TimeoutWarningMessage& warning) {
@ -264,10 +389,10 @@ void System::broadcastTimeoutWarning(const network::TimeoutWarningMessage& warni
// 根据超时时间记录不同级别的日志 // 根据超时时间记录不同级别的日志
if (warning.elapsed_ms > 30000) { // 30秒以上 if (warning.elapsed_ms > 30000) { // 30秒以上
Logger::error("Severe timeout: ", warning.host, ":", warning.port, " - ", Logger::error("Severe timeout: ", warning.source, " - ",
warning.elapsed_ms, "ms without response"); warning.elapsed_ms, "ms without response");
} else { } else {
Logger::warning("Connection timeout: ", warning.host, ":", warning.port, " - ", Logger::warning("Connection timeout: ", warning.source, " - ",
warning.elapsed_ms, "ms without response"); warning.elapsed_ms, "ms without response");
} }
} }

View File

@ -6,11 +6,13 @@
#include <atomic> #include <atomic>
#include "types/BasicTypes.h" #include "types/BasicTypes.h"
#include "detector/CollisionDetector.h" #include "detector/CollisionDetector.h"
#include "detector/TrafficLightDetector.h"
#include "spatial/AirportBounds.h" #include "spatial/AirportBounds.h"
#include "vehicle/ControllableVehicles.h" #include "vehicle/ControllableVehicles.h"
#include "network/WebSocketServer.h" #include "network/WebSocketServer.h"
#include "network/MessageTypes.h" #include "network/MessageTypes.h"
#include "config/SystemConfig.h" #include "config/SystemConfig.h"
#include "config/IntersectionConfig.h"
// 前向声明 // 前向声明
class DataCollector; class DataCollector;
@ -18,7 +20,7 @@ class DataCollector;
class System { class System {
public: public:
System(); System();
~System(); virtual ~System();
bool initialize(); bool initialize();
void start(); void start();
@ -27,22 +29,28 @@ public:
static System* instance() { return instance_; } static System* instance() { return instance_; }
static void signalHandler(int signal); static void signalHandler(int signal);
void broadcastTimeoutWarning(const network::TimeoutWarningMessage& warning); // 广播消息给客户端
virtual void broadcastTimeoutWarning(const network::TimeoutWarningMessage& warning);
void broadcastPositionUpdate(const MovingObject& obj);
void broadcastCollisionWarning(const CollisionRisk& risk);
void broadcastTrafficLightUpdate(const TrafficLightSignal& signal);
const SystemConfig& getSystemConfig() const { return system_config_; }
const IntersectionConfig& getIntersectionConfig() const { return intersection_config_; }
private: private:
void processLoop(); void processLoop();
void processCollisions(const std::vector<CollisionRisk>& collisions); void processCollisions(const std::vector<CollisionRisk>& collisions);
// WebSocket 相关方法
void broadcastPositionUpdate(const MovingObject& obj);
void broadcastCollisionWarning(const CollisionRisk& risk);
std::atomic<bool> running_{false}; std::atomic<bool> running_{false};
std::thread processThread_; std::thread processThread_;
std::unique_ptr<AirportBounds> airportBounds_; std::unique_ptr<AirportBounds> airportBounds_;
std::unique_ptr<ControllableVehicles> controllableVehicles_; std::unique_ptr<ControllableVehicles> controllableVehicles_;
std::unique_ptr<CollisionDetector> collisionDetector_; std::unique_ptr<CollisionDetector> collisionDetector_;
std::unique_ptr<TrafficLightDetector> trafficLightDetector_;
std::unique_ptr<DataCollector> dataCollector_; std::unique_ptr<DataCollector> dataCollector_;
// WebSocket 服务器 // WebSocket 服务器
@ -52,5 +60,8 @@ private:
// 系统配置 // 系统配置
SystemConfig system_config_; SystemConfig system_config_;
// 路口配置
IntersectionConfig intersection_config_;
static System* instance_; static System* instance_;
}; };

View File

@ -61,6 +61,12 @@ std::vector<CollisionRisk> CollisionDetector::detectCollisions() {
// 计算相对速度大小 // 计算相对速度大小
double relativeSpeed = std::sqrt(vx*vx + vy*vy); double relativeSpeed = std::sqrt(vx*vx + vy*vy);
// 记录调试信息
Logger::debug("航空器与车辆相对速度计算: ", aircraft.flightNo, " vs ", vehicle.vehicleNo,
", 航空器: speed=", aircraft.speed, "m/s, heading=", aircraft.heading, "°",
", 车辆: speed=", vehicle.speed, "m/s, heading=", vehicle.heading, "°",
", 相对速度: vx=", vx, "m/s, vy=", vy, "m/s, magnitude=", relativeSpeed, "m/s");
// 计算风险等级 // 计算风险等级
RiskLevel level = calculateRiskLevel(distance, aircraft.position, true, false); RiskLevel level = calculateRiskLevel(distance, aircraft.position, true, false);
@ -100,6 +106,12 @@ std::vector<CollisionRisk> CollisionDetector::detectCollisions() {
double vy = v1v.vy - v2v.vy; double vy = v1v.vy - v2v.vy;
double relativeSpeed = std::sqrt(vx*vx + vy*vy); double relativeSpeed = std::sqrt(vx*vx + vy*vy);
// 记录调试信息
Logger::debug("车辆间相对速度计算: ", controlVehicle.vehicleNo, " vs ", otherVehicle.vehicleNo,
", 车辆1: speed=", controlVehicle.speed, "m/s, heading=", controlVehicle.heading, "°",
", 车辆2: speed=", otherVehicle.speed, "m/s, heading=", otherVehicle.heading, "°",
", 相对速度: vx=", vx, "m/s, vy=", vy, "m/s, magnitude=", relativeSpeed, "m/s");
// 计算风险等级 // 计算风险等级
RiskLevel level = calculateRiskLevel(distance, controlVehicle.position, false, false); RiskLevel level = calculateRiskLevel(distance, controlVehicle.position, false, false);
@ -128,9 +140,7 @@ RiskLevel CollisionDetector::calculateRiskLevel(double distance, const Vector2D&
auto thresholds = areaConfig.getThresholds(isAircraft1, isAircraft2); auto thresholds = areaConfig.getThresholds(isAircraft1, isAircraft2);
// 直接比较距离和阈值 // 直接比较距离和阈值
if (distance <= thresholds.emergency) { if (distance <= thresholds.critical) {
return RiskLevel::EMERGENCY;
} else if (distance <= thresholds.critical) {
return RiskLevel::CRITICAL; return RiskLevel::CRITICAL;
} else if (distance <= thresholds.warning) { } else if (distance <= thresholds.warning) {
return RiskLevel::WARNING; return RiskLevel::WARNING;
@ -149,10 +159,10 @@ bool CollisionDetector::checkAircraftVehicleCollision(const Aircraft& aircraft,
// 获取该区域的告警阈值 // 获取该区域的告警阈值
auto thresholds = areaConfig.getThresholds(true, false); // aircraft vs vehicle auto thresholds = areaConfig.getThresholds(true, false); // aircraft vs vehicle
double emergencyThresholdSquared = thresholds.emergency * thresholds.emergency; double criticalThresholdSquared = thresholds.critical * thresholds.critical;
// 如果距离小于紧急阈值,直接报警 // 如果距离小于紧急阈值,直接报警
if (distanceSquared < emergencyThresholdSquared) { if (distanceSquared < criticalThresholdSquared) {
return true; return true;
} }
@ -186,7 +196,7 @@ bool CollisionDetector::checkVehicleCollision(const Vehicle& v1,
auto thresholds = areaConfig.getThresholds(false, false); // vehicle vs vehicle auto thresholds = areaConfig.getThresholds(false, false); // vehicle vs vehicle
// 如果距离小于紧急阈值,直接报警 // 如果距离小于紧急阈值,直接报警
if (distance < thresholds.emergency) { if (distance < thresholds.critical) {
return true; return true;
} }

View File

@ -7,13 +7,13 @@
#include "vehicle/ControllableVehicles.h" #include "vehicle/ControllableVehicles.h"
#include <vector> #include <vector>
#include <utility> #include <utility>
#include <utils/Logger.h>
// 碰撞风险等级 // 碰撞风险等级
enum class RiskLevel { enum class RiskLevel {
NONE = 0, // 无风险 NONE = 0, // 无风险
WARNING = 1, // 低风险:距离在阈值的 50%-100% 之间 WARNING = 1, // 低风险:距离在阈值的 50%-100% 之间
CRITICAL = 2, // 中等风险:距离在阈值的 25%-50% 之间 CRITICAL = 2, // 中等风险:距离在阈值的 0%-50% 之间
EMERGENCY = 3, // 高风险:距离在阈值的 0%-25% 之间
}; };
// 碰撞风险信息 // 碰撞风险信息
@ -62,9 +62,17 @@ private:
double vx, vy; double vx, vy;
MovementVector(double speed, double heading) { MovementVector(double speed, double heading) {
double radians = (90 - heading) * M_PI / 180.0; // 航向角是以正北为0度顺时针增加
vx = speed * std::cos(radians); // 需要转换为数学坐标系以正东为0度逆时针增加
vy = speed * std::sin(radians); double radians = (90.0 - heading) * M_PI / 180.0;
// 计算速度分量
vx = speed * std::cos(radians); // 东向分量
vy = speed * std::sin(radians); // 北向分量
// 记录调试信息
Logger::debug("速度分量计算: speed=", speed, "m/s, heading=", heading,
"°, vx=", vx, "m/s, vy=", vy, "m/s");
} }
}; };
}; };

View File

@ -0,0 +1,75 @@
#include "detector/TrafficLightDetector.h"
#include "utils/Logger.h"
#include "types/BasicTypes.h"
#include <chrono>
TrafficLightDetector::TrafficLightDetector(const IntersectionConfig& intersectionConfig,
const ControllableVehicles& controllableVehicles)
: intersection_config_(intersectionConfig)
, controllable_vehicles_(controllableVehicles) {
}
void TrafficLightDetector::processSignal(const TrafficLightSignal& signal,
const std::vector<Vehicle>& vehicles) {
// 根据红绿灯ID查找对应的路口
const Intersection* intersection = intersection_config_.findByTrafficLightId(signal.trafficLightId);
if (!intersection) {
Logger::warning("未找到红绿灯对应的路口: ", signal.trafficLightId);
return;
}
// 检查每个车辆
for (const auto& vehicle : vehicles) {
if (controllable_vehicles_.isControllable(vehicle.vehicleNo)) {
checkVehicleIntersectionDistance(vehicle, *intersection, signal.status);
}
}
}
void TrafficLightDetector::checkVehicleIntersectionDistance(const Vehicle& vehicle,
const Intersection& intersection,
SignalStatus status) {
// 创建 GeoPosition 用于距离计算
GeoPosition intersectionPos{
intersection.position.latitude,
intersection.position.longitude
};
// 计算车辆到路口的距离
double distance = MovingObject::calculateDistance(vehicle.geo, intersectionPos);
// 如果车辆在路口影响范围内
if (distance <= intersection.radius) {
switch (status) {
case SignalStatus::RED:
sendStopCommand(vehicle.vehicleNo);
break;
case SignalStatus::GREEN:
sendContinueCommand(vehicle.vehicleNo);
break;
default:
Logger::warning("未知的信号灯状态");
break;
}
}
}
void TrafficLightDetector::sendStopCommand(const std::string& vehicleNo) {
VehicleCommand cmd;
cmd.type = CommandType::SIGNAL;
cmd.reason = CommandReason::TRAFFIC_LIGHT;
cmd.signalState = SignalState::RED;
cmd.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
const_cast<ControllableVehicles&>(controllable_vehicles_).sendCommand(vehicleNo, cmd);
Logger::debug("发送停止指令到车辆: ", vehicleNo);
}
void TrafficLightDetector::sendContinueCommand(const std::string& vehicleNo) {
VehicleCommand cmd;
cmd.type = CommandType::SIGNAL;
cmd.reason = CommandReason::TRAFFIC_LIGHT;
cmd.signalState = SignalState::GREEN;
cmd.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
const_cast<ControllableVehicles&>(controllable_vehicles_).sendCommand(vehicleNo, cmd);
Logger::debug("发送继续指令到车辆: ", vehicleNo);
}

View File

@ -0,0 +1,28 @@
#pragma once
#include <memory>
#include "types/TrafficLightTypes.h"
#include "types/VehicleCommand.h"
#include "config/IntersectionConfig.h"
#include "vehicle/ControllableVehicles.h"
class TrafficLightDetector {
public:
TrafficLightDetector(const IntersectionConfig& intersectionConfig,
const ControllableVehicles& controllableVehicles);
// 处理红绿灯信号
void processSignal(const TrafficLightSignal& signal, const std::vector<Vehicle>& vehicles);
private:
// 检查车辆与路口的距离,决定是否需要发送指令
void checkVehicleIntersectionDistance(const Vehicle& vehicle,
const Intersection& intersection,
SignalStatus status);
// 发送车辆控制指令
void sendStopCommand(const std::string& vehicleId);
void sendContinueCommand(const std::string& vehicleId);
const IntersectionConfig& intersection_config_;
const ControllableVehicles& controllable_vehicles_;
};

View File

@ -1,8 +1,10 @@
#include "core/System.h" #include "core/System.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "config/SystemConfig.h"
#include <iostream> #include <iostream>
#include <thread> #include <thread>
#include <csignal> #include <csignal>
#include <filesystem>
volatile sig_atomic_t running = true; volatile sig_atomic_t running = true;
@ -11,14 +13,30 @@ void signalHandler(int signum) {
running = false; running = false;
} }
LogLevel getLogLevelFromString(const std::string& level) {
if (level == "debug") return LogLevel::DEBUG;
if (level == "info") return LogLevel::INFO;
if (level == "warning") return LogLevel::WARNING;
if (level == "error") return LogLevel::ERROR;
return LogLevel::INFO; // 默认级别
}
int main() { int main() {
try { try {
// 设置信号处理 // 设置信号处理
signal(SIGINT, signalHandler); signal(SIGINT, signalHandler);
signal(SIGTERM, signalHandler); signal(SIGTERM, signalHandler);
// 配置日志级别 // 创建日志目录
Logger::setLogLevel(LogLevel::INFO); std::filesystem::create_directories("logs");
// 加载系统配置
auto config = SystemConfig::load("config/system_config.json");
// 初始化日志系统
Logger::initialize(config.logging.file, getLogLevelFromString(config.logging.level));
Logger::info("Starting system...");
Logger::debug("Log level set to: ", config.logging.level);
// 初始化系统 // 初始化系统
System system; System system;
@ -43,7 +61,7 @@ int main() {
return 0; return 0;
} }
catch (const std::exception& e) { catch (const std::exception& e) {
Logger::error("Fatal error: ", e.what()); std::cerr << "Fatal error: " << e.what() << std::endl;
return 1; return 1;
} }
} }

View File

@ -1,5 +1,7 @@
#include "mock/MockDataService.h" #include "mock/MockDataService.h"
#include <format> #include <format>
#include <sstream>
#include <iomanip>
MockDataService::MockDataService(const AirportBounds& bounds) MockDataService::MockDataService(const AirportBounds& bounds)
: bounds_(bounds) : bounds_(bounds)
@ -124,9 +126,43 @@ double MockDataService::generateAltitude(AreaType areaType) {
} }
std::string MockDataService::generateAircraftId() { std::string MockDataService::generateAircraftId() {
return std::format("AC{:04d}", ++aircraftCounter_); std::stringstream ss;
ss << "AC" << std::setw(4) << std::setfill('0') << ++aircraftCounter_;
return ss.str();
} }
std::string MockDataService::generateVehicleId() { std::string MockDataService::generateVehicleId() {
return std::format("VH{:04d}", ++vehicleCounter_); std::stringstream ss;
ss << "VH" << std::setw(4) << std::setfill('0') << ++vehicleCounter_;
return ss.str();
}
std::vector<TrafficLightSignal> MockDataService::generateTrafficLightData() {
std::vector<TrafficLightSignal> signals;
// 生成 5-10 个红绿灯信号
std::uniform_int_distribution<> numSignals(5, 10);
int count = numSignals(rng_);
for (int i = 0; i < count; ++i) {
TrafficLightSignal signal;
signal.trafficLightId = generateTrafficLightId();
signal.status = generateTrafficLightState();
signal.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
signals.push_back(signal);
}
return signals;
}
std::string MockDataService::generateTrafficLightId() {
std::stringstream ss;
ss << "TL" << std::setw(4) << std::setfill('0') << ++trafficLightCounter_;
return ss.str();
}
SignalStatus MockDataService::generateTrafficLightState() {
std::uniform_int_distribution<> dist(0, 2);
int state = dist(rng_);
return static_cast<SignalStatus>(state);
} }

View File

@ -3,6 +3,7 @@
#include "types/BasicTypes.h" #include "types/BasicTypes.h"
#include "spatial/AirportBounds.h" #include "spatial/AirportBounds.h"
#include "types/TrafficLightTypes.h"
#include <vector> #include <vector>
#include <random> #include <random>
@ -12,6 +13,7 @@ public:
std::vector<Aircraft> generateAircraftData(); std::vector<Aircraft> generateAircraftData();
std::vector<Vehicle> generateVehicleData(); std::vector<Vehicle> generateVehicleData();
std::vector<TrafficLightSignal> generateTrafficLightData();
private: private:
const AirportBounds& bounds_; const AirportBounds& bounds_;
@ -28,9 +30,14 @@ private:
// 生成ID // 生成ID
std::string generateAircraftId(); std::string generateAircraftId();
std::string generateVehicleId(); std::string generateVehicleId();
std::string generateTrafficLightId();
// 生成红绿灯状态
SignalStatus generateTrafficLightState();
int aircraftCounter_ = 0; int aircraftCounter_ = 0;
int vehicleCounter_ = 0; int vehicleCounter_ = 0;
int trafficLightCounter_ = 0;
}; };
#endif // AIRPORT_MOCK_MOCKDATASERVICE_H #endif // AIRPORT_MOCK_MOCKDATASERVICE_H

View File

@ -1,50 +1,53 @@
#include "network/HTTPDataSource.h" #include "network/HTTPDataSource.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <regex> #include <sstream>
using json = nlohmann::json; 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;
}
HTTPDataSource::HTTPDataSource(const DataSourceConfig& config) HTTPDataSource::HTTPDataSource(const DataSourceConfig& config)
: config_(config) : config_(config)
, host_(config.host) , host_(config.host)
, port_(std::to_string(config.port)) , port_(std::to_string(config.port))
, socket_(std::make_unique<asio::ip::tcp::socket>(io_context_)) { , last_health_check_(std::chrono::steady_clock::now()) {
curl_ = curl_easy_init();
if (!curl_) {
Logger::error("Failed to initialize CURL");
}
} }
HTTPDataSource::~HTTPDataSource() { HTTPDataSource::~HTTPDataSource() {
disconnect(); disconnect();
if (curl_) {
curl_easy_cleanup(curl_);
}
} }
bool HTTPDataSource::connect() { bool HTTPDataSource::connect() {
try { if (!curl_) {
Logger::debug("Connecting to ", host_, ":", port_); curl_ = curl_easy_init();
if (!curl_) {
auto resolver = asio::ip::tcp::resolver(io_context_); Logger::error("Failed to initialize CURL");
auto endpoints = resolver.resolve(host_, port_); return false;
}
socket_ = std::make_unique<asio::ip::tcp::socket>(io_context_);
asio::connect(*socket_, endpoints);
return true;
}
catch (const std::exception& e) {
Logger::error("Connection error: ", e.what());
return false;
} }
return true;
} }
void HTTPDataSource::disconnect() { void HTTPDataSource::disconnect() {
if (socket_ && socket_->is_open()) { if (curl_) {
boost::system::error_code ec; curl_easy_cleanup(curl_);
socket_->shutdown(asio::ip::tcp::socket::shutdown_both, ec); curl_ = nullptr;
socket_->close(ec);
} }
socket_.reset();
} }
bool HTTPDataSource::isAvailable() const { bool HTTPDataSource::isAvailable() const {
return socket_ && socket_->is_open(); return curl_ != nullptr;
} }
bool HTTPDataSource::tryReconnect() { bool HTTPDataSource::tryReconnect() {
@ -91,14 +94,68 @@ bool HTTPDataSource::tryReconnect() {
} }
} }
bool HTTPDataSource::checkConnectionHealth() {
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(
now - last_health_check_).count();
if (elapsed < HEALTH_CHECK_INTERVAL) {
return true; // 距离上次检查时间不够长
}
last_health_check_ = now;
return curl_ != nullptr;
}
bool HTTPDataSource::ensureConnected() { bool HTTPDataSource::ensureConnected() {
if (isAvailable()) { if (isAvailable() && checkConnectionHealth()) {
return true; return true;
} }
return tryReconnect(); return tryReconnect();
} }
bool HTTPDataSource::sendRequest(const std::string& path, std::string& response) {
if (!curl_) {
Logger::error("CURL not initialized");
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_NOSIGNAL, 1L);
// 添加 HTTP 头
struct curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Cache-Control: no-cache");
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 send request: ", curl_easy_strerror(res));
return false;
}
long http_code = 0;
curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &http_code);
if (http_code != 200) {
Logger::error("HTTP request failed with code: ", http_code);
return false;
}
return true;
}
bool HTTPDataSource::fetchAircraftData(std::vector<Aircraft>& aircraft) { bool HTTPDataSource::fetchAircraftData(std::vector<Aircraft>& aircraft) {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
@ -107,8 +164,7 @@ bool HTTPDataSource::fetchAircraftData(std::vector<Aircraft>& aircraft) {
} }
std::string response; std::string response;
if (!sendRequest(config_.aircraft_path) || !readResponse(response)) { if (!sendRequest(config_.aircraft_path, response)) {
disconnect();
return false; return false;
} }
@ -123,192 +179,198 @@ bool HTTPDataSource::fetchVehicleData(std::vector<Vehicle>& vehicles) {
} }
std::string response; std::string response;
if (!sendRequest(config_.vehicle_path) || !readResponse(response)) { if (!sendRequest(config_.vehicle_path, response)) {
disconnect();
return false; return false;
} }
return parseVehicleResponse(response, vehicles); return parseVehicleResponse(response, vehicles);
} }
bool HTTPDataSource::sendRequest(const std::string& path) { bool HTTPDataSource::fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals) {
try { std::lock_guard<std::mutex> lock(mutex_);
if (!connect()) {
return false; if (!ensureConnected()) {
}
std::stringstream request;
request << "GET " << path << " HTTP/1.1\r\n"
<< "Host: " << host_ << "\r\n"
<< "Connection: close\r\n"
<< "Accept: application/json\r\n"
<< "\r\n";
std::string request_str = request.str();
Logger::debug("Sending request:\n", request_str);
asio::write(*socket_, asio::buffer(request_str));
return true;
}
catch (const std::exception& e) {
Logger::error("Failed to send request: ", e.what());
return false; return false;
} }
std::string response;
if (!sendRequest(config_.traffic_light_path, response)) {
return false;
}
return parseTrafficLightResponse(response, signals);
} }
bool HTTPDataSource::readResponse(std::string& response) { bool HTTPDataSource::parseTrafficLightResponse(const std::string& response, std::vector<TrafficLightSignal>& signals) {
try { try {
Logger::debug("Reading response"); json j = json::parse(response);
asio::streambuf response_buf; if (!j.is_array()) {
boost::system::error_code ec; Logger::error("Invalid traffic light response format: expected array");
// 读取响应头
size_t header_length = asio::read_until(*socket_, response_buf, "\r\n\r\n", ec);
if (ec) {
Logger::error("Failed to read headers: ", ec.message());
return false; return false;
} }
// 提取响应头 signals.clear();
std::string header{asio::buffers_begin(response_buf.data()), signals.reserve(j.size());
asio::buffers_begin(response_buf.data()) + header_length};
Logger::debug("Header received:\n", header);
// 解析 Content-Length for (const auto& item : j) {
std::regex content_length_regex("Content-Length: (\\d+)"); TrafficLightSignal signal;
std::smatch matches;
if (!std::regex_search(header, matches, content_length_regex)) {
Logger::error("No Content-Length found");
return false;
}
size_t content_length = std::stoul(matches[1]);
Logger::debug("Content-Length: ", content_length);
// 读取响应体
std::string body;
size_t total_read = 0;
// 首先处理已经在缓冲区中的数据
size_t body_part = response_buf.size() - header_length;
if (body_part > 0) {
body.append(
asio::buffers_begin(response_buf.data()) + header_length,
asio::buffers_end(response_buf.data())
);
total_read += body_part;
Logger::debug("Read ", body_part, " bytes from buffer");
}
// 读取剩余数据
while (total_read < content_length) {
std::vector<char> buf(1024);
size_t to_read = std::min(buf.size(), content_length - total_read);
size_t bytes_read = socket_->read_some(asio::buffer(buf, to_read), ec);
if (ec) { // 解析必需字段
Logger::error("Error reading body: ", ec.message()); if (!item.contains("id") || !item.contains("state")) {
return false; Logger::error("Missing required fields in traffic light signal");
continue;
} }
body.append(buf.data(), bytes_read); signal.trafficLightId = item["id"].get<std::string>();
total_read += bytes_read;
Logger::debug("Read ", total_read, " of ", content_length, " bytes"); // 支持数字或字符串类型的状态
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);
} }
response = body; return !signals.empty();
//Logger::info("Successfully read complete response"); }
return true; catch (const json::exception& e) {
Logger::error("Failed to parse traffic light response: ", e.what());
return false;
} }
catch (const std::exception& e) { catch (const std::exception& e) {
Logger::error("Exception in readResponse: ", e.what()); Logger::error("Error processing traffic light response: ", e.what());
return false; return false;
} }
} }
bool HTTPDataSource::parseVehicleResponse(const std::string& response, std::vector<Vehicle>& vehicles) { bool HTTPDataSource::parseVehicleResponse(const std::string& response, std::vector<Vehicle>& vehicles) {
try { try {
auto j = json::parse(response); json j = json::parse(response);
if (!j.is_array()) {
Logger::error("Invalid vehicle response format: expected array");
return false;
}
vehicles.clear();
vehicles.reserve(j.size());
for (const auto& item : j) { for (const auto& item : j) {
// 检查必需字段 Vehicle veh;
if (!item.contains("vehicleNo") || !item.contains("longitude") ||
!item.contains("latitude") || !item.contains("time")) { // 解析必需字段
if (!item.contains("vehicleNo") || !item.contains("latitude") ||
!item.contains("longitude") || !item.contains("time")) {
Logger::error("Missing required fields in vehicle data"); Logger::error("Missing required fields in vehicle data");
continue; continue;
} }
Vehicle v; veh.vehicleNo = item["vehicleNo"].get<std::string>();
v.vehicleNo = item["vehicleNo"].get<std::string>(); veh.id = veh.vehicleNo; // 使用车辆号作为ID
v.id = v.vehicleNo; veh.geo.latitude = item["latitude"].get<double>();
veh.geo.longitude = item["longitude"].get<double>();
// 获取经纬度信息 // 时间戳转换
GeoPosition pos; int64_t time = item["time"].get<int64_t>();
pos.longitude = item["longitude"].get<double>(); if (time < 0) {
pos.latitude = item["latitude"].get<double>(); Logger::warning("收到负数时间戳: ", time, "设置为0");
time = 0;
}
veh.timestamp = static_cast<uint64_t>(time);
// 设置时间戳 // 解析可选字段
v.timestamp = item["time"].get<uint64_t>(); if (item.contains("direction")) {
veh.heading = item["direction"].get<double>();
}
// 更新位置和运动信息 // 更新位置信息
v.geo = pos; veh.position = coordinateConverter_.toLocalXY(veh.geo.latitude, veh.geo.longitude);
v.position = coordinateConverter_.toLocalXY(pos.latitude, pos.longitude); veh.updateMotion(veh.geo, veh.timestamp); // 更新速度和航向
v.updateMotion(pos, v.timestamp); // 更新速度和航向
vehicles.push_back(v); Logger::debug("Parsed vehicle: id=", veh.id, " vehicleNo=", veh.vehicleNo,
" timestamp=", veh.timestamp, " pos=(", veh.geo.longitude, ",", veh.geo.latitude, ")");
vehicles.push_back(veh);
} }
return !vehicles.empty(); return !vehicles.empty();
} }
catch (const json::exception& e) { catch (const json::exception& e) {
Logger::error("JSON parsing error: ", e.what()); Logger::error("Failed to parse vehicle response: ", e.what());
Logger::error("Response data: ", response); return false;
}
catch (const std::exception& e) {
Logger::error("Error processing vehicle response: ", e.what());
return false; return false;
} }
} }
bool HTTPDataSource::parseAircraftResponse(const std::string& response, std::vector<Aircraft>& aircraft) { bool HTTPDataSource::parseAircraftResponse(const std::string& response, std::vector<Aircraft>& aircraft) {
try { try {
auto j = json::parse(response); json j = json::parse(response);
Logger::debug("Parsing aircraft data: ", response);
if (!j.is_array()) {
Logger::error("Invalid aircraft response format: expected array");
return false;
}
aircraft.clear();
aircraft.reserve(j.size());
for (const auto& item : j) { for (const auto& item : j) {
// 检查必需字段 Aircraft ac;
if (!item.contains("flightNo") || !item.contains("longitude") ||
!item.contains("latitude") || !item.contains("time")) { // 解析必需字段
if (!item.contains("flightNo") || !item.contains("latitude") ||
!item.contains("longitude") || !item.contains("time")) {
Logger::error("Missing required fields in aircraft data"); Logger::error("Missing required fields in aircraft data");
continue; continue;
} }
Aircraft a; ac.flightNo = item["flightNo"].get<std::string>();
a.flightNo = item["flightNo"].get<std::string>(); ac.id = ac.flightNo; // 使用航班号作为ID
a.id = a.flightNo; ac.geo.latitude = item["latitude"].get<double>();
ac.geo.longitude = item["longitude"].get<double>();
// 获取经纬度信息 // 时间戳转换
GeoPosition pos; int64_t time = item["time"].get<int64_t>();
pos.longitude = item["longitude"].get<double>(); if (time < 0) {
pos.latitude = item["latitude"].get<double>(); Logger::warning("收到负数时间戳: ", time, "设置为0");
time = 0;
}
ac.timestamp = static_cast<uint64_t>(time);
// 设置时间戳 // 解析可选字段
a.timestamp = item["time"].get<uint64_t>(); if (item.contains("direction")) {
ac.heading = item["direction"].get<double>();
}
// 更新位置和运动信息 // 更新位置信息
a.geo = pos; ac.position = coordinateConverter_.toLocalXY(ac.geo.latitude, ac.geo.longitude);
a.position = coordinateConverter_.toLocalXY(pos.latitude, pos.longitude); ac.updateMotion(ac.geo, ac.timestamp); // 更新速度和航向
a.updateMotion(pos, a.timestamp); // 更新速度和航向
// 默认高度为0地面 Logger::debug("Parsed aircraft: id=", ac.id, " flightNo=", ac.flightNo,
a.altitude = 0.0; " timestamp=", ac.timestamp, " pos=(", ac.geo.longitude, ",", ac.geo.latitude, ")");
aircraft.push_back(a); aircraft.push_back(ac);
} }
return !aircraft.empty(); return !aircraft.empty();
} }
catch (const json::exception& e) { catch (const json::exception& e) {
Logger::error("JSON parsing error: ", e.what()); Logger::error("Failed to parse aircraft response: ", e.what());
Logger::error("Response data: ", response); return false;
}
catch (const std::exception& e) {
Logger::error("Error processing aircraft response: ", e.what());
return false; return false;
} }
} }

View File

@ -3,21 +3,21 @@
#include "collector/DataSource.h" #include "collector/DataSource.h"
#include "spatial/CoordinateConverter.h" #include "spatial/CoordinateConverter.h"
#include <boost/asio.hpp>
#include <string> #include <string>
#include "collector/DataSourceConfig.h" #include "collector/DataSourceConfig.h"
#include <mutex> #include <mutex>
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
#include "core/System.h" #include "core/System.h"
#include <curl/curl.h>
namespace asio = boost::asio;
class HTTPDataSource : public DataSource { class HTTPDataSource : public DataSource {
public: public:
explicit HTTPDataSource(const DataSourceConfig& config); explicit HTTPDataSource(const DataSourceConfig& config);
~HTTPDataSource() override; ~HTTPDataSource() override;
const DataSourceConfig& getConfig() const { return config_; }
DataSourceConfig config_; DataSourceConfig config_;
bool connect() override; bool connect() override;
void disconnect() override; void disconnect() override;
@ -25,26 +25,31 @@ public:
bool fetchAircraftData(std::vector<Aircraft>& aircraft) override; bool fetchAircraftData(std::vector<Aircraft>& aircraft) override;
bool fetchVehicleData(std::vector<Vehicle>& vehicles) override; bool fetchVehicleData(std::vector<Vehicle>& vehicles) override;
bool fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals) override;
private: private:
std::string host_; std::string host_;
std::string port_; std::string port_;
asio::io_context io_context_;
std::unique_ptr<asio::ip::tcp::socket> socket_;
CoordinateConverter coordinateConverter_; CoordinateConverter coordinateConverter_;
std::mutex mutex_; std::mutex mutex_;
CURL* curl_;
std::chrono::steady_clock::time_point last_connect_attempt_; std::chrono::steady_clock::time_point last_connect_attempt_;
std::chrono::steady_clock::time_point last_health_check_; // 上次健康检查时间
static constexpr int MAX_RETRIES = 3; // 单次连接最大重试次数 static constexpr int MAX_RETRIES = 3; // 单次连接最大重试次数
static constexpr int HEALTH_CHECK_INTERVAL = 60; // 健康检查间隔(秒)
std::atomic<bool> is_reconnecting_{false}; // 重连状态标志 std::atomic<bool> is_reconnecting_{false}; // 重连状态标志
bool tryReconnect(); bool tryReconnect();
bool ensureConnected(); bool ensureConnected();
bool checkConnectionHealth(); // 检查连接健康状况
bool sendRequest(const std::string& path); bool sendRequest(const std::string& path, std::string& response);
bool readResponse(std::string& response);
bool parseAircraftResponse(const std::string& response, std::vector<Aircraft>& aircraft); bool parseAircraftResponse(const std::string& response, std::vector<Aircraft>& aircraft);
bool parseVehicleResponse(const std::string& response, std::vector<Vehicle>& vehicles); bool parseVehicleResponse(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);
}; };
#endif // AIRPORT_NETWORK_HTTP_DATA_SOURCE_H #endif // AIRPORT_NETWORK_HTTP_DATA_SOURCE_H

View File

@ -55,19 +55,15 @@ struct CollisionWarningMessage {
}; };
struct TimeoutWarningMessage { struct TimeoutWarningMessage {
std::string type = "data_source_timeout"; std::string source; // 超时来源
std::string host; int64_t elapsed_ms; // 超时时长
int port; bool is_read_timeout; // 是否是读取超时
int64_t elapsed_ms;
uint64_t timestamp;
nlohmann::json toJson() const { nlohmann::json toJson() const {
return { return {
{"type", type}, {"source", source},
{"host", host},
{"port", port},
{"elapsed_ms", elapsed_ms}, {"elapsed_ms", elapsed_ms},
{"timestamp", timestamp} {"is_read_timeout", is_read_timeout}
}; };
} }
}; };

View File

@ -3,7 +3,6 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <boost/asio.hpp>
#include "types/VehicleCommand.h" #include "types/VehicleCommand.h"
class NetworkInterface { class NetworkInterface {
@ -13,9 +12,6 @@ public:
virtual void disconnect() = 0; virtual void disconnect() = 0;
virtual bool send(const VehicleCommand& cmd) = 0; virtual bool send(const VehicleCommand& cmd) = 0;
virtual bool isConnected() const = 0; virtual bool isConnected() const = 0;
protected:
boost::asio::io_context io_context_;
}; };
#endif // AIRPORT_NETWORK_INTERFACE_H #endif // AIRPORT_NETWORK_INTERFACE_H

View File

@ -1,4 +1,5 @@
#include "network/WebSocketServer.h" #include "network/WebSocketServer.h"
#include "utils/Logger.h"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
namespace network { namespace network {
@ -24,6 +25,7 @@ WebSocketServer::~WebSocketServer() {
} }
void WebSocketServer::start() { void WebSocketServer::start() {
Logger::info("WebSocket 服务器启动,监听端口: ", acceptor_.local_endpoint().port());
handleAccept(); handleAccept();
ioc_.run(); ioc_.run();
} }
@ -31,24 +33,35 @@ void WebSocketServer::start() {
void WebSocketServer::broadcast(const std::string& message) { void WebSocketServer::broadcast(const std::string& message) {
std::lock_guard<std::mutex> lock(sessions_mutex_); std::lock_guard<std::mutex> lock(sessions_mutex_);
auto it = sessions_.begin(); auto it = sessions_.begin();
int successCount = 0;
int failCount = 0;
while (it != sessions_.end()) { while (it != sessions_.end()) {
if (auto session = it->lock()) { // 获取 shared_ptr if (auto session = it->lock()) { // 获取 shared_ptr
try { try {
session->write(boost::asio::buffer(message)); session->write(boost::asio::buffer(message));
++successCount;
++it; ++it;
} catch (...) { } catch (...) {
Logger::warning("广播消息到客户端失败");
++failCount;
it = sessions_.erase(it); // 移除失败的会话 it = sessions_.erase(it); // 移除失败的会话
} }
} else { } else {
Logger::debug("移除失效的会话");
it = sessions_.erase(it); // 移除已失效的会话 it = sessions_.erase(it); // 移除已失效的会话
} }
} }
Logger::debug("广播消息完成: ", successCount, " 个成功, ", failCount, " 个失败, 当前共 ", sessions_.size(), " 个连接");
} }
void WebSocketServer::handleAccept() { void WebSocketServer::handleAccept() {
acceptor_.async_accept( acceptor_.async_accept(
[this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket) { [this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket) {
if (!ec) { if (!ec) {
Logger::info("接受新的 WebSocket 连接: ", socket.remote_endpoint().address().to_string());
// 创建新的 WebSocket 会话 // 创建新的 WebSocket 会话
auto ws = std::make_shared<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>>(std::move(socket)); auto ws = std::make_shared<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>>(std::move(socket));
@ -60,10 +73,15 @@ void WebSocketServer::handleAccept() {
{ {
std::lock_guard<std::mutex> lock(sessions_mutex_); std::lock_guard<std::mutex> lock(sessions_mutex_);
sessions_.push_back(ws); // 存储 weak_ptr sessions_.push_back(ws); // 存储 weak_ptr
Logger::info("WebSocket 握手成功,当前连接数: ", sessions_.size());
} }
doRead(ws); doRead(ws);
} else {
Logger::error("WebSocket 握手失败: ", ec.message());
} }
}); });
} else {
Logger::error("接受 WebSocket 连接失败: ", ec.message());
} }
// 继续接受新的连接 // 继续接受新的连接
@ -99,6 +117,7 @@ void WebSocketServer::doRead(std::shared_ptr<boost::beast::websocket::stream<boo
void WebSocketServer::stop() { void WebSocketServer::stop() {
running_ = false; running_ = false;
Logger::info("正在停止 WebSocket 服务器...");
ioc_.stop(); // 停止 io_context ioc_.stop(); // 停止 io_context
// 关闭所有连接 // 关闭所有连接
@ -107,12 +126,14 @@ void WebSocketServer::stop() {
if (auto ws = session.lock()) { if (auto ws = session.lock()) {
try { try {
ws->close(boost::beast::websocket::close_code::normal); ws->close(boost::beast::websocket::close_code::normal);
Logger::debug("关闭 WebSocket 连接");
} catch (...) { } catch (...) {
// 忽略关闭时的错误 Logger::warning("关闭 WebSocket 连接时发生错误");
} }
} }
} }
sessions_.clear(); sessions_.clear();
Logger::info("WebSocket 服务器已停止");
} }
} // namespace network } // namespace network

View File

@ -15,15 +15,14 @@ enum class AreaType {
// 区域配置 // 区域配置
struct AreaConfig { struct AreaConfig {
double vehicleCollisionRadius; // 车辆间碰撞检测半径 double vehicleCollisionRadius; // 车辆间冲突检测半径
double aircraftGroundRadius; // 航空器与车辆碰撞检测半径 double aircraftGroundRadius; // 航空器与车辆冲突检测半径
double heightThreshold; // 高度阈值 double heightThreshold; // 高度阈值
// 获取不同类型交通工具间的告警阈值 // 获取不同类型交通工具间的告警阈值
struct CollisionThresholds { struct CollisionThresholds {
double warning; // 警告距离 double warning; // 预警距离
double critical; // 危险距离 double critical; // 告警距离
double emergency; // 紧急距离
}; };
// 计算两个交通工具之间的告警阈值 // 计算两个交通工具之间的告警阈值
@ -34,9 +33,8 @@ struct AreaConfig {
// 计算不同级别的阈值 // 计算不同级别的阈值
return { return {
baseRadius, // 警告距离:为基础距离 baseRadius, // 预警距离:为基础距离
baseRadius / 2.0, // 危险距离:为基础距离的一半 baseRadius / 2.0 // 告警距离:为基础距离的一半
baseRadius / 4.0 // 紧急距离:为基础距离的四分之一
}; };
} }
}; };

View File

@ -82,6 +82,8 @@ bool Vehicle::isValidSpeed(double speed) const {
void MovingObject::updateMotion(const GeoPosition& newPos, uint64_t newTime) { void MovingObject::updateMotion(const GeoPosition& newPos, uint64_t newTime) {
// 检查时间戳 // 检查时间戳
if (!positionHistory.empty() && newTime <= positionHistory.back().timestamp) { if (!positionHistory.empty() && newTime <= positionHistory.back().timestamp) {
Logger::debug("[Motion] Ignore outdated data: current=", newTime,
" last=", positionHistory.back().timestamp);
return; // 忽略重复或过时的数据 return; // 忽略重复或过时的数据
} }
@ -93,35 +95,66 @@ void MovingObject::updateMotion(const GeoPosition& newPos, uint64_t newTime) {
// 计算速度和航向 // 计算速度和航向
if (positionHistory.size() >= 2) { if (positionHistory.size() >= 2) {
// 使用所有可用的历史记录计算平均速度 // 找到合适的历史点来计算速度和航向
double totalDistance = 0; const auto& curr = positionHistory.back();
double totalTime = 0; const auto& prev = positionHistory.front(); // 使用最早的历史点
for (size_t i = 1; i < positionHistory.size(); ++i) { // 计算距离和时间差
const auto& prev = positionHistory[i-1]; double distance = calculateDistance(prev.geo, curr.geo); // 单位:米
const auto& curr = positionHistory[i]; double timeDiff = static_cast<double>(curr.timestamp - prev.timestamp) / 1000.0; // 转换为秒
Logger::debug("[Motion] Position update: ",
"\n Current: lat=", curr.geo.latitude, ", lon=", curr.geo.longitude,
"\n Previous: lat=", prev.geo.latitude, ", lon=", prev.geo.longitude,
"\n Distance=", distance, "m, TimeDiff=", timeDiff, "s");
// 只有当位置变化足够大且时间差足够长时才更新速度和航向
static const double MIN_DISTANCE = 0.5; // 最小位置变化阈值(米)
static const double MIN_TIME = 0.2; // 最小时间差阈值(秒)
if (distance > MIN_DISTANCE && timeDiff > MIN_TIME) {
// 计算新的速度
double newSpeed = distance / timeDiff; // 米/秒
totalDistance += calculateDistance(prev.geo, curr.geo); if (isValidSpeed(newSpeed)) {
totalTime += static_cast<double>(curr.timestamp - prev.timestamp);
}
if (totalTime > 0) {
double avgSpeed = totalDistance / totalTime;
if (isValidSpeed(avgSpeed)) {
// 使用指数移动平均来平滑速度 // 使用指数移动平均来平滑速度
const double alpha = getSpeedSmoothingFactor(); const double alpha = getSpeedSmoothingFactor();
if (speed == 0) { if (speed == 0) {
speed = avgSpeed; // 第一次计算 speed = newSpeed; // 第一次计算
} else { } else {
speed = speed * (1 - alpha) + avgSpeed * alpha; // 平滑更新 speed = speed * (1 - alpha) + newSpeed * alpha; // 平滑更新
} }
Logger::debug("[Motion] Speed updated: distance=", distance, "m, timeDiff=", timeDiff,
"s, newSpeed=", newSpeed, "m/s, smoothedSpeed=", speed, "m/s");
} else {
Logger::debug("[Motion] Invalid speed: ", newSpeed, "m/s (exceeds limit)");
} }
// 计算新的航向
double newHeading = calculateHeading(prev.geo, curr.geo);
// 使用指数移动平均来平滑航向
const double beta = 0.3; // 航向平滑因子
if (heading == 0) {
heading = newHeading; // 第一次计算
} else {
// 处理航向角的循环性0-360度
double diff = newHeading - heading;
if (diff > 180) {
diff -= 360;
} else if (diff < -180) {
diff += 360;
}
heading = fmod(heading + diff * beta + 360, 360);
}
Logger::debug("[Motion] Heading updated: newHeading=", newHeading,
"deg, smoothedHeading=", heading, "deg");
} else {
Logger::debug("[Motion] No update: distance=", distance, "m < ", MIN_DISTANCE,
"m or timeDiff=", timeDiff, "s < ", MIN_TIME, "s");
} }
// 使用最近两个点计算航向
const auto& prev = positionHistory[positionHistory.size()-2];
const auto& curr = positionHistory.back();
heading = calculateHeading(prev.geo, curr.geo);
} }
// 更新当前位置 // 更新当前位置

View File

@ -28,6 +28,7 @@ struct PositionRecord {
// 移动物体基类 // 移动物体基类
class MovingObject { class MovingObject {
public: public:
MovingObject() : heading(0.0), speed(0.0), timestamp(0) {} // 添加构造函数
virtual ~MovingObject() = default; // 添加虚析构函数以支持 dynamic_cast virtual ~MovingObject() = default; // 添加虚析构函数以支持 dynamic_cast
std::string id; std::string id;

View File

@ -0,0 +1,22 @@
#pragma once
#include <string>
#include <cstdint>
enum class SignalStatus {
RED,
GREEN,
UNKNOWN
};
struct TrafficLightSignal {
std::string trafficLightId; // 红绿灯设备 ID
SignalStatus status; // 信号灯状态
uint64_t timestamp; // 时间戳
static SignalStatus parseStatus(const std::string& status) {
if (status == "red") return SignalStatus::RED;
if (status == "green") return SignalStatus::GREEN;
return SignalStatus::UNKNOWN;
}
};

View File

@ -4,24 +4,54 @@
#include <string> #include <string>
#include "VehicleData.h" #include "VehicleData.h"
// 指令类型
enum class CommandType { enum class CommandType {
CONTINUE, // 继续运行 SIGNAL, // 信号灯指令(最高优先级)
STOP, // 停止 ALERT, // 告警指令(中等优先级)
SLOW_DOWN, // 减速 WARNING, // 预警指令(最低优先级)
TURN_LEFT, // 左转 RESUME // 恢复指令(最低优先级)
TURN_RIGHT // 右转 };
// 信号灯状态
enum class SignalState {
RED, // 红灯
GREEN // 绿灯
};
// 指令原因
enum class CommandReason {
TRAFFIC_LIGHT, // 红绿灯控制
AIRCRAFT_CROSSING, // 航空器交叉
SPECIAL_VEHICLE, // 特勤车辆
AIRCRAFT_PUSH, // 航空器推出
RESUME_TRAFFIC // 恢复通行
}; };
struct VehicleCommand { struct VehicleCommand {
std::string vehicleId; // 车辆ID std::string vehicleId; // 车辆ID
CommandType type; // 命令类型 CommandType type; // 命令类型
Vector2D avoidanceVector; // 避障向量 CommandReason reason; // 命令原因
SignalState signalState; // 信号灯状态(仅当 type 为 SIGNAL 时有效)
Vector2D targetPosition; // 目标位置(如交叉路口)
double speed{0.0}; // 目标速度 double speed{0.0}; // 目标速度
double heading{0.0}; // 目标航向 double distance{0.0}; // 与关键点的距离
uint64_t timestamp; // 时间戳 uint64_t timestamp; // 时间戳
// 可选的优先级 // 获取指令优先级(数字越大优先级越高)
int priority{0}; // 数字越大优先级越高 int getPriority() const {
switch(type) {
case CommandType::SIGNAL: return 4;
case CommandType::ALERT: return 3;
case CommandType::WARNING: return 2;
case CommandType::RESUME: return 1;
default: return 0;
}
}
// 判断是否可以被新指令覆盖
bool canBeOverriddenBy(const VehicleCommand& newCmd) const {
return newCmd.getPriority() >= this->getPriority();
}
}; };
#endif // AIRPORT_TYPES_VEHICLE_COMMAND_H #endif // AIRPORT_TYPES_VEHICLE_COMMAND_H

View File

@ -2,9 +2,11 @@
#define AIRPORT_UTILS_LOGGER_H #define AIRPORT_UTILS_LOGGER_H
#include <iostream> #include <iostream>
#include <fstream>
#include <chrono> #include <chrono>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <mutex>
enum class LogLevel { enum class LogLevel {
DEBUG, DEBUG,
@ -15,7 +17,14 @@ enum class LogLevel {
class Logger { class Logger {
public: public:
static void initialize(const std::string& filename, LogLevel level = LogLevel::INFO) {
std::lock_guard<std::mutex> lock(getMutex());
currentLevel() = level;
logFile().open(filename, std::ios::app);
}
static void setLogLevel(LogLevel level) { static void setLogLevel(LogLevel level) {
std::lock_guard<std::mutex> lock(getMutex());
currentLevel() = level; currentLevel() = level;
} }
@ -53,6 +62,16 @@ private:
return level; return level;
} }
static std::ofstream& logFile() {
static std::ofstream file;
return file;
}
static std::mutex& getMutex() {
static std::mutex mutex;
return mutex;
}
template<typename... Args> template<typename... Args>
static void log(const char* level, Args... args) { static void log(const char* level, Args... args) {
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
@ -66,7 +85,13 @@ private:
<< " [" << level << "] "; << " [" << level << "] ";
(ss << ... << args) << std::endl; (ss << ... << args) << std::endl;
std::lock_guard<std::mutex> lock(getMutex());
std::cerr << ss.str(); std::cerr << ss.str();
if (logFile().is_open()) {
logFile() << ss.str();
logFile().flush();
}
} }
}; };

View File

@ -49,4 +49,11 @@ void ControllableVehicles::loadConfig(const std::string& configFile) {
} catch (const std::exception& e) { } catch (const std::exception& e) {
throw std::runtime_error("Failed to parse controllable vehicles config: " + std::string(e.what())); throw std::runtime_error("Failed to parse controllable vehicles config: " + std::string(e.what()));
} }
} }
void ControllableVehicles::sendCommand(const std::string& vehicleNo, const VehicleCommand& command) {
auto vehicle = findVehicle(vehicleNo);
if (vehicle) {
// TODO: 发送控制命令
}
}

View File

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include "types/VehicleCommand.h"
struct ControllableVehicleConfig { struct ControllableVehicleConfig {
std::string vehicleNo; // 车牌号 std::string vehicleNo; // 车牌号
@ -13,7 +14,7 @@ struct ControllableVehicleConfig {
class ControllableVehicles { class ControllableVehicles {
public: public:
explicit ControllableVehicles(const std::string& configFile); explicit ControllableVehicles(const std::string& configFile);
~ControllableVehicles() = default; virtual ~ControllableVehicles() = default;
// 获取所有可控车辆配置 // 获取所有可控车辆配置
const std::vector<ControllableVehicleConfig>& getVehicles() const; const std::vector<ControllableVehicleConfig>& getVehicles() const;
@ -24,6 +25,9 @@ public:
// 检查车辆是否可控 // 检查车辆是否可控
virtual bool isControllable(const std::string& vehicleNo) const; virtual bool isControllable(const std::string& vehicleNo) const;
// 发送控制命令
virtual void sendCommand(const std::string& vehicleNo, const VehicleCommand& command);
private: private:
std::vector<ControllableVehicleConfig> vehicles_; std::vector<ControllableVehicleConfig> vehicles_;
void loadConfig(const std::string& configFile); void loadConfig(const std::string& configFile);

View File

@ -39,8 +39,8 @@ protected:
TEST_F(BasicTypesTest, MovingObjectSpeedCalculation) { TEST_F(BasicTypesTest, MovingObjectSpeedCalculation) {
TestMovingObject obj; TestMovingObject obj;
// 获取当前时间戳 // 使用毫秒级时间戳
time_t now = time(nullptr); uint64_t now = 1000000;
// 第一个位置 // 第一个位置
GeoPosition pos1{36.0, 120.0}; GeoPosition pos1{36.0, 120.0};
@ -49,7 +49,12 @@ TEST_F(BasicTypesTest, MovingObjectSpeedCalculation) {
// 第二个位置向东移动约90米/秒) // 第二个位置向东移动约90米/秒)
GeoPosition pos2{36.0, 120.001}; GeoPosition pos2{36.0, 120.001};
obj.updateMotion(pos2, now + 1); // 1秒后 double distance = obj.calculateDistance(pos1, pos2);
printf("Distance: %.2f meters\n", distance);
obj.updateMotion(pos2, now + 1000); // 1000毫秒后
printf("Speed: %.2f m/s\n", obj.speed);
EXPECT_NEAR(obj.speed, 90.0, 2.0); // 使用 Haversine 公式计算的实际距离 EXPECT_NEAR(obj.speed, 90.0, 2.0); // 使用 Haversine 公式计算的实际距离
// 检查航向角 // 检查航向角
@ -61,15 +66,15 @@ TEST_F(BasicTypesTest, AircraftValidation) {
Aircraft aircraft; Aircraft aircraft;
aircraft.flightNo = "CES2501"; aircraft.flightNo = "CES2501";
// 获取当前时间戳 // 使用毫秒级时间戳
time_t now = time(nullptr); uint64_t now = 1000000;
// 测试位置更新 // 测试位置更新
GeoPosition pos1{36.0, 120.0}; GeoPosition pos1{36.0, 120.0};
aircraft.updateMotion(pos1, now); aircraft.updateMotion(pos1, now);
GeoPosition pos2{36.0, 120.001}; // 约90米 GeoPosition pos2{36.0, 120.001}; // 约90米
aircraft.updateMotion(pos2, now + 2); // 2秒后 aircraft.updateMotion(pos2, now + 2000); // 2000毫秒后
// 速度应该约为45米/秒 // 速度应该约为45米/秒
EXPECT_NEAR(aircraft.speed, 45.0, 2.0); EXPECT_NEAR(aircraft.speed, 45.0, 2.0);
@ -80,23 +85,44 @@ TEST_F(BasicTypesTest, VehicleValidation) {
Vehicle vehicle; Vehicle vehicle;
vehicle.vehicleNo = "VEH001"; vehicle.vehicleNo = "VEH001";
// 获取当前时间戳 // 使用毫秒级时间戳
time_t now = time(nullptr); uint64_t now = 1000000;
// 测试位置更新 // 测试位置更新
GeoPosition pos1{36.0, 120.0}; GeoPosition pos1{36.0, 120.0};
vehicle.updateMotion(pos1, now); vehicle.updateMotion(pos1, now);
// 使用约22米的位置变化小于50米的跳变限制 // 使用约9米的位置变化小于10米的跳变限制
GeoPosition pos2{36.0002, 120.0}; // 约22米 GeoPosition pos2{36.00008, 120.0}; // 约9米
vehicle.updateMotion(pos2, now + 1.0); // 1秒后 double distance = vehicle.calculateDistance(pos1, pos2);
printf("Vehicle distance: %.2f meters\n", distance);
// 速度应该约为22米/秒 vehicle.updateMotion(pos2, now + 1000); // 1000毫秒后
EXPECT_NEAR(vehicle.speed, 22.0, 2.0); printf("Vehicle speed: %.2f m/s\n", vehicle.speed);
// 速度应该约为9米/秒小于20米/秒的限制)
EXPECT_NEAR(vehicle.speed, 9.0, 1.0);
} }
// 添加边界条件测试 // 添加边界条件测试
TEST_F(BasicTypesTest, VectorZeroMagnitude) { TEST_F(BasicTypesTest, VectorZeroMagnitude) {
Vector2D v(0.0, 0.0); Vector2D v(0.0, 0.0);
EXPECT_DOUBLE_EQ(v.magnitude(), 0.0); EXPECT_DOUBLE_EQ(v.magnitude(), 0.0);
}
// 测试距离计算
TEST_F(BasicTypesTest, DistanceCalculation) {
TestMovingObject obj;
// 测试经度方向的距离约90米
GeoPosition pos1{36.0, 120.0};
GeoPosition pos2{36.0, 120.001};
double distance1 = obj.calculateDistance(pos1, pos2);
EXPECT_NEAR(distance1, 90.0, 1.0);
// 测试纬度方向的距离约22米
GeoPosition pos3{36.0, 120.0};
GeoPosition pos4{36.0002, 120.0};
double distance2 = obj.calculateDistance(pos3, pos4);
EXPECT_NEAR(distance2, 22.0, 1.0);
} }

View File

@ -4,6 +4,7 @@ set(TEST_SOURCES
BasicTypesTest.cpp BasicTypesTest.cpp
HTTPDataSourceTest.cpp HTTPDataSourceTest.cpp
DataCollectorTest.cpp DataCollectorTest.cpp
TrafficLightDetectorTest.cpp
) )
# #

View File

@ -46,7 +46,7 @@ protected:
airportBounds_ = std::make_unique<MockAirportBounds>(); airportBounds_ = std::make_unique<MockAirportBounds>();
mockControllableVehicles_ = std::make_unique<MockControllableVehicles>(); mockControllableVehicles_ = std::make_unique<MockControllableVehicles>();
// 创建碰撞检测器 // 创建冲突检测器
detector_ = std::make_unique<CollisionDetector>(*airportBounds_, *mockControllableVehicles_); detector_ = std::make_unique<CollisionDetector>(*airportBounds_, *mockControllableVehicles_);
} }
@ -55,7 +55,7 @@ protected:
std::unique_ptr<CollisionDetector> detector_; std::unique_ptr<CollisionDetector> detector_;
}; };
// 测试可控车辆与航空器的碰撞检测 // 测试可控车辆与航空器的冲突检测
TEST_F(CollisionDetectorTest, DetectControllableVehicleAircraftCollision) { TEST_F(CollisionDetectorTest, DetectControllableVehicleAircraftCollision) {
// 设置 Mock 期望 - 在创建数据之前设置 // 设置 Mock 期望 - 在创建数据之前设置
EXPECT_CALL(*mockControllableVehicles_, isControllable("VEH001")) EXPECT_CALL(*mockControllableVehicles_, isControllable("VEH001"))
@ -83,22 +83,22 @@ TEST_F(CollisionDetectorTest, DetectControllableVehicleAircraftCollision) {
detector_->updateTraffic({aircraft}, {vehicle}); detector_->updateTraffic({aircraft}, {vehicle});
Logger::info("Updated traffic data"); Logger::info("Updated traffic data");
// 执行碰撞检测 // 执行冲突检测
auto risks = detector_->detectCollisions(); auto risks = detector_->detectCollisions();
// 验证结果 // 验证结果
ASSERT_EQ(risks.size(), 1); // 应该检测到一个碰撞风险 ASSERT_EQ(risks.size(), 1); // 应该检测到一个冲突风险
if (!risks.empty()) { if (!risks.empty()) {
const auto& risk = risks[0]; const auto& risk = risks[0];
EXPECT_EQ(risk.id1, "TEST001"); // 航空器ID EXPECT_EQ(risk.id1, "TEST001"); // 航空器ID
EXPECT_EQ(risk.id2, "VEH001"); // 车辆ID EXPECT_EQ(risk.id2, "VEH001"); // 车辆ID
EXPECT_EQ(risk.distance, 20); // 距离应该是20米 EXPECT_EQ(risk.distance, 20); // 距离应该是20米
EXPECT_EQ(risk.level, RiskLevel::EMERGENCY); // 20米距离应该是严重风险 EXPECT_EQ(risk.level, RiskLevel::CRITICAL); // 20米距离应该是严重风险
EXPECT_GT(risk.relativeSpeed, 0); // 相对速度应该大于0 EXPECT_GT(risk.relativeSpeed, 0); // 相对速度应该大于0
} }
} }
// 测试可控车辆与其他车辆的碰撞检测 // 测试可控车辆与其他车辆的冲突检测
TEST_F(CollisionDetectorTest, DetectControllableVehicleOtherVehicleCollision) { TEST_F(CollisionDetectorTest, DetectControllableVehicleOtherVehicleCollision) {
// 设置 Mock 期望 // 设置 Mock 期望
EXPECT_CALL(*mockControllableVehicles_, isControllable("VEH001")) EXPECT_CALL(*mockControllableVehicles_, isControllable("VEH001"))
@ -122,20 +122,20 @@ TEST_F(CollisionDetectorTest, DetectControllableVehicleOtherVehicleCollision) {
// 更新交通数据 // 更新交通数据
detector_->updateTraffic({}, {controlVehicle, otherVehicle}); detector_->updateTraffic({}, {controlVehicle, otherVehicle});
// 执行碰撞检测 // 执行冲突检测
auto risks = detector_->detectCollisions(); auto risks = detector_->detectCollisions();
// 验证结果 // 验证结果
ASSERT_EQ(risks.size(), 1); // 应该检测到一个碰撞风险 ASSERT_EQ(risks.size(), 1); // 应该检测到一个冲突风险
if (!risks.empty()) { if (!risks.empty()) {
EXPECT_EQ(risks[0].id1, "VEH001"); // 可控车辆ID EXPECT_EQ(risks[0].id1, "VEH001"); // 可控车辆ID
EXPECT_EQ(risks[0].id2, "VEH002"); // 其他车辆ID EXPECT_EQ(risks[0].id2, "VEH002"); // 其他车辆ID
EXPECT_EQ(risks[0].distance, 20); // 距离应该是20米 EXPECT_EQ(risks[0].distance, 20); // 距离应该是20米
EXPECT_EQ(risks[0].level, RiskLevel::CRITICAL); // 20米距离应该是严重风险 EXPECT_EQ(risks[0].level, RiskLevel::CRITICAL); // 20米距离应该是告警
} }
} }
// 测试非可控车辆的碰撞检测(不应该产生碰撞风险) // 测试非可控车辆的冲突检测(不应该产生冲突风险)
TEST_F(CollisionDetectorTest, NonControllableVehicleCollision) { TEST_F(CollisionDetectorTest, NonControllableVehicleCollision) {
// 设置测试数据 // 设置测试数据
Vehicle vehicle1; Vehicle vehicle1;
@ -157,14 +157,14 @@ TEST_F(CollisionDetectorTest, NonControllableVehicleCollision) {
// 更新交通数据 // 更新交通数据
detector_->updateTraffic({}, {vehicle1, vehicle2}); detector_->updateTraffic({}, {vehicle1, vehicle2});
// 执行碰撞检测 // 执行冲突检测
auto risks = detector_->detectCollisions(); auto risks = detector_->detectCollisions();
// 验证结果 // 验证结果
EXPECT_EQ(risks.size(), 0); // 非可控车辆之间的碰撞不应该被检测 EXPECT_EQ(risks.size(), 0); // 非可控车辆之间的冲突不应该被检测
} }
// 测试多个可控车辆之间的碰撞检测 // 测试多个可控车辆之间的冲突检测
TEST_F(CollisionDetectorTest, MultipleControllableVehiclesCollision) { TEST_F(CollisionDetectorTest, MultipleControllableVehiclesCollision) {
// 设置 Mock 期望 - 所有车辆都是可控的 // 设置 Mock 期望 - 所有车辆都是可控的
ON_CALL(*mockControllableVehicles_, isControllable(testing::_)) ON_CALL(*mockControllableVehicles_, isControllable(testing::_))
@ -203,18 +203,18 @@ TEST_F(CollisionDetectorTest, MultipleControllableVehiclesCollision) {
// 更新交通数据 // 更新交通数据
detector_->updateTraffic({}, vehicles); detector_->updateTraffic({}, vehicles);
// 执行碰撞检测 // 执行冲突检测
auto risks = detector_->detectCollisions(); auto risks = detector_->detectCollisions();
// 验证结果 // 验证结果
EXPECT_EQ(risks.size(), 1); // 应该只检测到1个碰撞风险VEH001和VEH002之间 EXPECT_EQ(risks.size(), 1); // 应该只检测到1个冲突风险VEH001和VEH002之间
if (risks.size() == 1) { if (risks.size() == 1) {
// 验证碰撞风险的详细信息 // 验证冲突风险的详细信息
EXPECT_TRUE((risks[0].id1 == "VEH001" && risks[0].id2 == "VEH002") || EXPECT_TRUE((risks[0].id1 == "VEH001" && risks[0].id2 == "VEH002") ||
(risks[0].id1 == "VEH002" && risks[0].id2 == "VEH001")); (risks[0].id1 == "VEH002" && risks[0].id2 == "VEH001"));
EXPECT_EQ(risks[0].distance, 20.0); EXPECT_EQ(risks[0].distance, 20.0);
EXPECT_EQ(risks[0].level, RiskLevel::CRITICAL); // 20米<EFBFBD><EFBFBD><EFBFBD>该是危险级别 EXPECT_EQ(risks[0].level, RiskLevel::CRITICAL); // 20米应该是告警级别
} }
} }
@ -341,7 +341,7 @@ TEST_F(CollisionDetectorTest, PerformanceTest) {
Logger::info("Updated traffic data with ", aircraft.size(), " aircraft and ", Logger::info("Updated traffic data with ", aircraft.size(), " aircraft and ",
vehicles.size(), " vehicles (including 3 controllable vehicles)"); vehicles.size(), " vehicles (including 3 controllable vehicles)");
// 执行碰撞检测并记录时间 // 执行冲突检测并记录时间
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
auto risks = detector_->detectCollisions(); auto risks = detector_->detectCollisions();
@ -352,7 +352,7 @@ TEST_F(CollisionDetectorTest, PerformanceTest) {
Logger::info("Found ", risks.size(), " risks"); Logger::info("Found ", risks.size(), " risks");
// 验证结果 // 验证结果
ASSERT_GT(risks.size(), 0); // 应该检测到一些碰撞风险 ASSERT_GT(risks.size(), 0); // 应该检测到一些冲突风险
// 验证性能要求 // 验证性能要求
EXPECT_LT(duration.count(), 100000); // 期望处理时间小于100ms EXPECT_LT(duration.count(), 100000); // 期望处理时间小于100ms

View File

@ -2,6 +2,8 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include "collector/DataCollector.h" #include "collector/DataCollector.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "network/MessageTypes.h"
#include "core/System.h"
// 创建一个 Mock DataSource 类 // 创建一个 Mock DataSource 类
class MockDataSource : public DataSource { class MockDataSource : public DataSource {
@ -11,6 +13,7 @@ public:
MOCK_METHOD(bool, isAvailable, (), (const, override)); MOCK_METHOD(bool, isAvailable, (), (const, override));
MOCK_METHOD(bool, fetchAircraftData, (std::vector<Aircraft>&), (override)); MOCK_METHOD(bool, fetchAircraftData, (std::vector<Aircraft>&), (override));
MOCK_METHOD(bool, fetchVehicleData, (std::vector<Vehicle>&), (override)); MOCK_METHOD(bool, fetchVehicleData, (std::vector<Vehicle>&), (override));
MOCK_METHOD(bool, fetchTrafficLightSignals, (std::vector<TrafficLightSignal>&), (override));
}; };
class DataCollectorTest : public ::testing::Test { class DataCollectorTest : public ::testing::Test {
@ -48,6 +51,15 @@ protected:
return v; return v;
} }
// 创建测试用的红绿灯信号
TrafficLightSignal createTestTrafficLight(const std::string& id, int state) {
TrafficLightSignal signal;
signal.trafficLightId = id;
signal.status = static_cast<SignalStatus>(state);
signal.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
return signal;
}
std::unique_ptr<DataCollector> collector; std::unique_ptr<DataCollector> collector;
std::shared_ptr<MockDataSource> mockSource; std::shared_ptr<MockDataSource> mockSource;
}; };
@ -167,4 +179,323 @@ TEST_F(DataCollectorTest, ErrorHandling) {
auto vehicles = collector->getVehicleData(); auto vehicles = collector->getVehicleData();
EXPECT_TRUE(vehicles.empty()); EXPECT_TRUE(vehicles.empty());
}
// 测试红绿灯信号获取
TEST_F(DataCollectorTest, TrafficLightSignalsTest) {
std::vector<TrafficLightSignal> testSignals = {
createTestTrafficLight("TL001", 0), // RED = 0
createTestTrafficLight("TL002", 1), // GREEN = 1
createTestTrafficLight("TL003", 2) // YELLOW = 2
};
// 设置 Mock 数据返回
EXPECT_CALL(*mockSource, fetchTrafficLightSignals)
.WillOnce(::testing::DoAll(
::testing::SetArgReferee<0>(testSignals),
::testing::Return(true)
));
// 执行刷新
collector->refresh();
// 验证数据
auto signals = collector->getTrafficLightSignals();
EXPECT_EQ(signals.size(), 3);
if (signals.size() >= 3) {
EXPECT_EQ(signals[0].trafficLightId, "TL001");
EXPECT_EQ(signals[0].status, SignalStatus::RED); // RED = 0
EXPECT_EQ(signals[1].trafficLightId, "TL002");
EXPECT_EQ(signals[1].status, SignalStatus::GREEN); // GREEN = 1
EXPECT_EQ(signals[2].trafficLightId, "TL003");
EXPECT_EQ(signals[2].status, SignalStatus::YELLOW); // YELLOW = 2
}
}
// 测试红绿灯信号错误处理
TEST_F(DataCollectorTest, TrafficLightSignalsErrorTest) {
// 设置 Mock 返回错误
EXPECT_CALL(*mockSource, fetchTrafficLightSignals)
.WillOnce(::testing::Return(false));
// 执行刷新
collector->refresh();
// 验证数据为空
auto signals = collector->getTrafficLightSignals();
EXPECT_TRUE(signals.empty());
}
// 测试连接健康检查和超时告警
TEST_F(DataCollectorTest, ConnectionHealthAndTimeout) {
DataSourceConfig dataSourceConfig;
dataSourceConfig.host = "localhost";
dataSourceConfig.port = 8080;
dataSourceConfig.timeout_ms = 1000; // 1秒超时
dataSourceConfig.refresh_interval_ms = 100; // 100ms刷新间隔
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 500; // 500ms告警间隔
collector->initialize(dataSourceConfig, warnConfig);
// 模拟连接健康检查失败
EXPECT_CALL(*mockSource, isAvailable())
.WillRepeatedly(::testing::Return(false));
// 模据<E6A8A1><E68DAE>取失败
EXPECT_CALL(*mockSource, fetchAircraftData)
.WillRepeatedly(::testing::Return(false));
EXPECT_CALL(*mockSource, fetchVehicleData)
.WillRepeatedly(::testing::Return(false));
// 启动采集
collector->start();
// 等待超过超时时间
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
// 停止采集
collector->stop();
// 验证数据为空
auto aircraft = collector->getAircraftData();
EXPECT_TRUE(aircraft.empty());
auto vehicles = collector->getVehicleData();
EXPECT_TRUE(vehicles.empty());
}
// 测试连接恢复
TEST_F(DataCollectorTest, ConnectionRecovery) {
DataSourceConfig config;
config.host = "localhost";
config.port = 8080;
config.refresh_interval_ms = 10;
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 20;
collector->initialize(config, warnConfig);
std::vector<Aircraft> testAircraft = {
createTestAircraft("TEST1", 36.36, 120.08)
};
// 移除 InSequence直接设置期望
EXPECT_CALL(*mockSource, fetchAircraftData)
.WillOnce(::testing::Return(false))
.WillOnce(::testing::DoAll(
::testing::SetArgReferee<0>(testAircraft),
::testing::Return(true)
));
// <20><><EFBFBD>行两次刷新
collector->refresh(); // 第一次失败
collector->refresh(); // 第二次成功
// 验证数据
auto aircraft = collector->getAircraftData();
EXPECT_EQ(aircraft.size(), 1);
if (!aircraft.empty()) {
EXPECT_EQ(aircraft[0].flightNo, "TEST1");
}
}
// 测试长连接下的数据获取
TEST_F(DataCollectorTest, LongConnectionDataFetch) {
DataSourceConfig dataSourceConfig;
dataSourceConfig.host = "localhost";
dataSourceConfig.port = 8080;
dataSourceConfig.refresh_interval_ms = 100;
collector->initialize(dataSourceConfig, WarnConfig{});
std::vector<Aircraft> testAircraft = {
createTestAircraft("TEST1", 36.36, 120.08)
};
// 模拟连接保持可用
EXPECT_CALL(*mockSource, isAvailable())
.WillRepeatedly(::testing::Return(true));
// 模拟多次成功获取数据
EXPECT_CALL(*mockSource, fetchAircraftData)
.Times(::testing::AtLeast(3))
.WillRepeatedly(::testing::DoAll(
::testing::SetArgReferee<0>(testAircraft),
::testing::Return(true)
));
// 启动采集
collector->start();
// 等待多个刷新周期
std::this_thread::sleep_for(std::chrono::milliseconds(350)); // 应该执行至少3次获取
// 验证数据
auto aircraft = collector->getAircraftData();
EXPECT_FALSE(aircraft.empty());
if (!aircraft.empty()) {
EXPECT_EQ(aircraft[0].flightNo, "TEST1");
}
// 停止采集
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;
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 500;
EXPECT_NO_THROW(collector->initialize(config, warnConfig));
}
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;
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 200; // 警告间隔 0.2 秒
collector->initialize(config, warnConfig);
// 模拟连接成功但读取超时
EXPECT_CALL(*mockSource, isAvailable())
.WillRepeatedly(::testing::Return(true));
EXPECT_CALL(*mockSource, fetchAircraftData)
.WillRepeatedly(::testing::Return(false)); // 持续读取超时
// 启动采集
collector->start();
// 等待足够长的时间以触发多次警告
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// 停止采集
collector->stop();
}
// 测试超时警告机制
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;
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 50; // 警告间隔 50ms
// 创建一个计数器来记录收到的警告数量
int warningCount = 0;
// 创建一个 mock System 来捕获警告
class MockSystem : public System {
public:
MOCK_METHOD(void, broadcastTimeoutWarning, (const network::TimeoutWarningMessage&), (override));
};
auto mockSystem = std::make_shared<MockSystem>();
// 设置期望接收到超时警告
EXPECT_CALL(*mockSystem, broadcastTimeoutWarning)
.WillRepeatedly(::testing::Invoke([&warningCount](const network::TimeoutWarningMessage& warning) {
warningCount++;
// 验证警告消息的内容
EXPECT_EQ(warning.source, "data_source");
EXPECT_GT(warning.elapsed_ms, 0);
// 不检查具体的超时类型,因为这取决于实际的时间流逝
}));
// 设置 mock system 到 collector
collector->setSystem(mockSystem);
collector->initialize(config, warnConfig);
// 启动采集器
collector->start();
// 模拟数据获取失败
EXPECT_CALL(*mockSource, fetchAircraftData)
.WillRepeatedly(::testing::Return(false));
// 等待足够长的时间以确保警告被发送
// 等待时间应该超过 refresh_interval_ms + timeout_ms + warning_interval_ms
std::this_thread::sleep_for(std::chrono::milliseconds(300));
// 停止采集器
collector->stop();
// 验证是否收到了警告
EXPECT_GT(warningCount, 0);
}
// 测试读取超时机制
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;
WarnConfig warnConfig;
warnConfig.warning_interval_ms = 20; // 警告间隔 20ms
// 创建一个计数器来记录收到的警告数量
int warningCount = 0;
// 创建一个 mock System 来捕获警告
class MockSystem : public System {
public:
MOCK_METHOD(void, broadcastTimeoutWarning, (const network::TimeoutWarningMessage&), (override));
};
auto mockSystem = std::make_shared<MockSystem>();
// 设置期望接收到超时警告
EXPECT_CALL(*mockSystem, broadcastTimeoutWarning)
.WillRepeatedly(::testing::Invoke([&warningCount](const network::TimeoutWarningMessage& warning) {
warningCount++;
// 验证警告消息的内容
EXPECT_EQ(warning.source, "data_source");
EXPECT_GT(warning.elapsed_ms, 0);
EXPECT_TRUE(warning.is_read_timeout); // 标记为读取超时
}));
// 设置 mock system 到 collector
collector->setSystem(mockSystem);
collector->initialize(config, warnConfig);
// 启动采集器
collector->start();
// 模拟读取超时
EXPECT_CALL(*mockSource, fetchAircraftData)
.WillRepeatedly(::testing::Invoke([](std::vector<Aircraft>& aircraft) {
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));
// 停止采集器
collector->stop();
// 验证是否收到了警告
EXPECT_GT(warningCount, 0);
} }

View File

@ -1,6 +1,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include "network/HTTPDataSource.h" #include "network/HTTPDataSource.h"
#include "utils/Logger.h"
class HTTPDataSourceTest : public ::testing::Test { class HTTPDataSourceTest : public ::testing::Test {
protected: protected:
@ -9,9 +10,10 @@ protected:
void SetUp() override { void SetUp() override {
DataSourceConfig config; DataSourceConfig config;
config.host = "localhost"; config.host = "localhost";
config.port = 8080; config.port = 8081;
config.aircraft_path = "/api/getCurrentFlightPositions"; config.aircraft_path = "/api/getCurrentFlightPositions";
config.vehicle_path = "/api/getCurrentVehiclePositions"; config.vehicle_path = "/api/getCurrentVehiclePositions";
config.traffic_light_path = "/api/getTrafficLightSignals";
source = std::make_unique<HTTPDataSource>(config); source = std::make_unique<HTTPDataSource>(config);
} }
@ -35,23 +37,46 @@ TEST_F(HTTPDataSourceTest, DisconnectTest) {
TEST_F(HTTPDataSourceTest, FetchAircraftDataTest) { TEST_F(HTTPDataSourceTest, FetchAircraftDataTest) {
std::vector<Aircraft> aircraft; std::vector<Aircraft> aircraft;
source->connect();
EXPECT_TRUE(source->fetchAircraftData(aircraft)); // 尝试连接
// 注意:这里的具体数据验证取决于你的测试环境和模拟数据 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) { TEST_F(HTTPDataSourceTest, FetchVehicleDataTest) {
std::vector<Vehicle> vehicles; std::vector<Vehicle> vehicles;
source->connect(); EXPECT_TRUE(source->connect()); // 确保连接成功
EXPECT_TRUE(source->fetchVehicleData(vehicles)); EXPECT_TRUE(source->fetchVehicleData(vehicles));
// 注意:这里的具体数据验证取决于你的测试环境和模拟数据 EXPECT_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) { TEST_F(HTTPDataSourceTest, CustomPathTest) {
// 测试使用自定义路径创建数据源 // 测试使用自定义路径创建数据源
DataSourceConfig config; DataSourceConfig config;
config.host = "localhost"; config.host = "localhost";
config.port = 8080; config.port = 8081;
config.aircraft_path = "/custom/path"; config.aircraft_path = "/custom/path";
auto customSource = std::make_unique<HTTPDataSource>(config); auto customSource = std::make_unique<HTTPDataSource>(config);
EXPECT_TRUE(customSource->connect()); EXPECT_TRUE(customSource->connect());
@ -61,8 +86,8 @@ TEST_F(HTTPDataSourceTest, CustomPathTest) {
TEST_F(HTTPDataSourceTest, ConnectionFailureTest) { TEST_F(HTTPDataSourceTest, ConnectionFailureTest) {
// 测试连接到不存在的服务器 // 测试连接到不存在的服务器
DataSourceConfig config; DataSourceConfig config;
config.host = "invalid-host"; config.host = "localhost"; // 使用本地地址
config.port = 9999; config.port = 1; // 使用一个通常不会被使用的端口
auto invalidSource = std::make_unique<HTTPDataSource>(config); auto invalidSource = std::make_unique<HTTPDataSource>(config);
EXPECT_FALSE(invalidSource->connect()); EXPECT_FALSE(invalidSource->connect());
EXPECT_FALSE(invalidSource->isAvailable()); EXPECT_FALSE(invalidSource->isAvailable());
@ -72,9 +97,43 @@ TEST_F(HTTPDataSourceTest, FetchDataWithoutConnectionTest) {
// 测试在未连接状态下获取数据 // 测试在未连接状态下获取数据
std::vector<Aircraft> aircraft; std::vector<Aircraft> aircraft;
std::vector<Vehicle> vehicles; std::vector<Vehicle> vehicles;
std::vector<TrafficLightSignal> 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->fetchAircraftData(aircraft));
EXPECT_FALSE(source->fetchVehicleData(vehicles)); 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());
} }
// 如果你的环境支持模拟网络响应,可以添加更多测试 // 如果你的环境支持模拟网络响应,可以添加更多测试
@ -85,4 +144,71 @@ TEST_F(HTTPDataSourceTest, InvalidResponseTest) {
std::vector<Aircraft> aircraft; std::vector<Aircraft> aircraft;
// 假设服务器返回无效的 JSON 数据 // 假设服务器返回无效的 JSON 数据
// EXPECT_FALSE(source->fetchAircraftData(aircraft)); // 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());
} }

View File

@ -0,0 +1,145 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <fstream>
#include <nlohmann/json.hpp>
#include "detector/TrafficLightDetector.h"
#include "config/IntersectionConfig.h"
#include "vehicle/ControllableVehicles.h"
#include "utils/Logger.h"
#include "types/VehicleCommand.h"
#include "types/TrafficLightTypes.h"
class MockControllableVehicles : public ControllableVehicles {
public:
explicit MockControllableVehicles() : ControllableVehicles("config/vehicle_speed_limits.json") {}
MOCK_METHOD(bool, isControllable, (const std::string& vehicleNo), (const, override));
MOCK_METHOD(void, sendCommand, (const std::string& vehicleNo, const VehicleCommand& command), (override));
};
class TrafficLightDetectorTest : public ::testing::Test {
protected:
void SetUp() override {
// 创建测试用的交叉口配置
Intersection intersection;
intersection.id = "INT001";
intersection.name = "Test Intersection";
intersection.position.latitude = 36.36;
intersection.position.longitude = 120.08;
intersection.radius = 50.0;
intersection.trafficLightId = "TL001";
// 创建临时配置文件
std::ofstream config_file("test_intersections.json");
nlohmann::json j;
j["intersections"] = nlohmann::json::array();
j["intersections"].push_back({
{"id", intersection.id},
{"name", intersection.name},
{"position", {
{"latitude", intersection.position.latitude},
{"longitude", intersection.position.longitude}
}},
{"radius", intersection.radius},
{"trafficLightId", intersection.trafficLightId}
});
config_file << j.dump(4);
config_file.close();
intersectionConfig = IntersectionConfig::load("test_intersections.json");
mockControllableVehicles = std::make_shared<::testing::NiceMock<MockControllableVehicles>>();
detector = std::make_unique<TrafficLightDetector>(intersectionConfig, *mockControllableVehicles);
}
void TearDown() override {
detector.reset();
std::remove("test_intersections.json");
}
// 创建测试用的红绿灯信号
TrafficLightSignal createTestSignal(const std::string& id, SignalStatus status) {
TrafficLightSignal signal;
signal.trafficLightId = id;
signal.status = status;
signal.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
return signal;
}
// 创建测试用的车辆数据
Vehicle createTestVehicle(const std::string& id, double lat, double lon) {
Vehicle v;
v.id = id;
v.vehicleNo = id;
v.geo.latitude = lat;
v.geo.longitude = lon;
v.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
return v;
}
std::unique_ptr<TrafficLightDetector> detector;
IntersectionConfig intersectionConfig;
std::shared_ptr<MockControllableVehicles> mockControllableVehicles;
};
// 测试红绿灯信号处理
TEST_F(TrafficLightDetectorTest, SignalProcessing) {
// 创建一个在交叉口范围内的车辆
Vehicle vehicle = createTestVehicle("V001", 36.36, 120.08);
std::vector<Vehicle> vehicles = {vehicle};
// 测试红灯
TrafficLightSignal redSignal = createTestSignal("TL001", SignalStatus::RED);
EXPECT_CALL(*mockControllableVehicles, isControllable("V001"))
.WillOnce(::testing::Return(true));
EXPECT_CALL(*mockControllableVehicles, sendCommand("V001", ::testing::_))
.Times(1);
detector->processSignal(redSignal, vehicles);
// 测试绿灯
TrafficLightSignal greenSignal = createTestSignal("TL001", SignalStatus::GREEN);
EXPECT_CALL(*mockControllableVehicles, isControllable("V001"))
.WillOnce(::testing::Return(true));
EXPECT_CALL(*mockControllableVehicles, sendCommand("V001", ::testing::_))
.Times(1);
detector->processSignal(greenSignal, vehicles);
}
// 测试无效的红绿灯信号
TEST_F(TrafficLightDetectorTest, InvalidSignal) {
Vehicle vehicle = createTestVehicle("V001", 36.36, 120.08);
std::vector<Vehicle> vehicles = {vehicle};
// 测试无效的红绿灯ID
TrafficLightSignal invalidSignal = createTestSignal("TL999", SignalStatus::RED);
EXPECT_CALL(*mockControllableVehicles, isControllable(::testing::_))
.Times(0);
EXPECT_CALL(*mockControllableVehicles, sendCommand(::testing::_, ::testing::_))
.Times(0);
detector->processSignal(invalidSignal, vehicles);
}
// 测试边界情况
TEST_F(TrafficLightDetectorTest, EdgeCases) {
// 测试空车辆列表
std::vector<Vehicle> emptyVehicles;
TrafficLightSignal signal = createTestSignal("TL001", SignalStatus::RED);
EXPECT_CALL(*mockControllableVehicles, isControllable(::testing::_))
.Times(0);
EXPECT_CALL(*mockControllableVehicles, sendCommand(::testing::_, ::testing::_))
.Times(0);
detector->processSignal(signal, emptyVehicles);
// 测试交叉口范围外的车辆
Vehicle farVehicle = createTestVehicle("V002", 36.40, 120.12);
std::vector<Vehicle> vehicles = {farVehicle};
EXPECT_CALL(*mockControllableVehicles, isControllable(::testing::_))
.Times(0);
EXPECT_CALL(*mockControllableVehicles, sendCommand(::testing::_, ::testing::_))
.Times(0);
detector->processSignal(signal, vehicles);
}

View File

@ -4,96 +4,208 @@ import math
app = Flask(__name__) app = Flask(__name__)
# 基准坐标(青岛胶东机场) # 坐标转换常数粗略计算1度约等于111km所以1米约等于0.000009度)
BASE_LAT = 36.361999 METERS_TO_DEG = 0.000009
BASE_LON = 120.088003
# 模拟数据 # 距离配置(米)
DIST_150M = 150
DIST_100M = 100
DIST_50M = 50
# 两个路口的位置
WEST_INTERSECTION = {"longitude": 120.088003, "latitude": 36.361999}
EAST_INTERSECTION = {"longitude": 120.088303, "latitude": 36.361999}
# 位置更新间隔(秒)
UPDATE_INTERVAL = 1.0
# 航空器数据
aircraft_data = [ aircraft_data = [
{ {
"flightNo": "CES2501", # 在跑道上 "flightNo": "AC001", # 航空器
"latitude": BASE_LAT, "latitude": EAST_INTERSECTION["latitude"] - (DIST_150M * METERS_TO_DEG), # 东路口南150米
"longitude": BASE_LON + 0.001, # 在跑道中间位置 "longitude": EAST_INTERSECTION["longitude"],
"time": int(time.time()), "time": int(time.time() * 1000),
"direction": 1 # 1表示向东-1表示向西 "direction": 1, # 1表示向北
}, "speed": 50.0 # 滑行速度 50km/h
{
"flightNo": "CES2502", # 在滑行道上
"latitude": BASE_LAT - 0.001,
"longitude": BASE_LON,
"time": int(time.time()),
"direction": 1 # 1表示向东-1表示向西
} }
] ]
# 车辆数据
vehicle_data = [ vehicle_data = [
{ {
"vehicleNo": "VEH001", # 南北方向移动 "vehicleNo": "QN001", # 无人车1西路口
"latitude": BASE_LAT - 0.0005, "latitude": WEST_INTERSECTION["latitude"] + (DIST_50M * METERS_TO_DEG), # 西路口北50米
"longitude": BASE_LON + 0.001, "longitude": WEST_INTERSECTION["longitude"],
"time": int(time.time()), "time": int(time.time() * 1000),
"direction": 1, # 1表示向北,-1表示向南 "direction": 1, # 1表示向
"phase": 0 # 用于控制循环运动 "speed": 36.0 # 36km/h
}, },
{ {
"vehicleNo": "VEH002", # 南北方向移动 "vehicleNo": "QN002", # 无人车2东路口
"latitude": BASE_LAT + 0.0005, "latitude": EAST_INTERSECTION["latitude"],
"longitude": BASE_LON + 0.001, "longitude": EAST_INTERSECTION["longitude"] - (DIST_150M * METERS_TO_DEG), # 东路口西150米
"time": int(time.time()), "time": int(time.time() * 1000),
"direction": -1, # 1表示向北-1表示向南 "direction": 1, # 1表示向东
"phase": math.pi # 与VEH001相位差180度 "speed": 36.0 # 36km/h
},
{
"vehicleNo": "TQ001", # 特勤车(西路口)
"latitude": WEST_INTERSECTION["latitude"],
"longitude": WEST_INTERSECTION["longitude"] + (DIST_50M * METERS_TO_DEG), # 西路口东50米
"time": int(time.time() * 1000),
"direction": -1, # -1表示向西
"speed": 36.0 # 36km/h
} }
] ]
# 定义运动范围 def update_vehicle_position(vehicle, elapsed_time):
LAT_RANGE = 0.001 # 约110米 """更新车辆位置"""
LON_RANGE = 0.002 # 约220米 # 计算一次更新的移动距离(度)
VEHICLE_SPEED = 0.00005 # 每次更新的位置变化 speed_mps = vehicle["speed"] * 1000 / 3600 # 速度转换为米/秒
AIRCRAFT_SPEED = 0.00002 # 每次更新的位置变化 distance = speed_mps * elapsed_time
distance_deg = distance * METERS_TO_DEG
if vehicle["vehicleNo"] == "QN001":
# 无人车1东西方向往返西路口
new_lon = vehicle["longitude"] + (distance_deg * vehicle["direction"])
if new_lon >= WEST_INTERSECTION["longitude"] + (DIST_50M * METERS_TO_DEG):
vehicle["direction"] = -1 # 向西
elif new_lon <= WEST_INTERSECTION["longitude"]:
vehicle["direction"] = 1 # 向东
vehicle["longitude"] = new_lon
elif vehicle["vehicleNo"] == "QN002":
# 无人车2东西方向往返东路口
new_lon = vehicle["longitude"] + (distance_deg * vehicle["direction"])
if new_lon >= EAST_INTERSECTION["longitude"] + (DIST_100M * METERS_TO_DEG):
vehicle["direction"] = -1 # 向西
elif new_lon <= EAST_INTERSECTION["longitude"] - (DIST_150M * METERS_TO_DEG):
vehicle["direction"] = 1 # 向东
vehicle["longitude"] = new_lon
elif vehicle["vehicleNo"] == "TQ001":
# 特勤车:先东西后南北,然后返回(西路口)
if "phase" not in vehicle:
vehicle["phase"] = 0
if vehicle["phase"] == 0: # 东西移动
new_lon = vehicle["longitude"] + (distance_deg * vehicle["direction"])
if vehicle["direction"] == -1 and new_lon <= WEST_INTERSECTION["longitude"]:
vehicle["phase"] = 1 # 切换到南北移动
vehicle["direction"] = -1 # 向南
elif vehicle["direction"] == 1 and new_lon >= WEST_INTERSECTION["longitude"] + (DIST_50M * METERS_TO_DEG):
vehicle["direction"] = -1 # 向西
vehicle["longitude"] = new_lon
else: # 南北移动
new_lat = vehicle["latitude"] + (distance_deg * vehicle["direction"])
if new_lat <= WEST_INTERSECTION["latitude"] - (DIST_100M * METERS_TO_DEG): # 到达南端
vehicle["direction"] = 1 # 向北
elif new_lat >= WEST_INTERSECTION["latitude"]: # 返回到路口
vehicle["phase"] = 0 # 切换回东西移动
vehicle["direction"] = 1 # 向东
vehicle["longitude"] = WEST_INTERSECTION["longitude"] # 重置到起始位置
vehicle["latitude"] = new_lat
def update_aircraft_position(aircraft, elapsed_time):
"""更新航空器位置"""
# 计算一次更新的移动距离(度)
speed_mps = aircraft["speed"] * 1000 / 3600 # 速度转换为米/秒
distance = speed_mps * elapsed_time
distance_deg = distance * METERS_TO_DEG
new_lat = aircraft["latitude"] + (distance_deg * aircraft["direction"])
if new_lat >= EAST_INTERSECTION["latitude"] + (DIST_150M * METERS_TO_DEG):
aircraft["direction"] = -1 # 向南
elif new_lat <= EAST_INTERSECTION["latitude"] - (DIST_150M * METERS_TO_DEG):
aircraft["direction"] = 1 # 向北
aircraft["latitude"] = new_lat
def update_traffic_light_for_aircraft():
"""根据航空器位置更新东路口红绿灯状态"""
if not aircraft_data:
return
aircraft = aircraft_data[0]
# 计算到东路口的距离(米)
lat_diff = abs(aircraft["latitude"] - EAST_INTERSECTION["latitude"]) / METERS_TO_DEG
# 更新TL002东路口的状态
for light in traffic_light_data:
if light["id"] == "TL002":
if lat_diff <= 50.0: # 距离小于50米时为红灯
light["state"] = 0 # 红灯
else:
light["state"] = 1 # 绿灯
break
# 红绿灯数据
traffic_light_data = [
{
"id": "TL001", # 西侧路口
"state": 1, # 0: 红灯, 1: 绿灯
"timestamp": int(time.time() * 1000)
},
{
"id": "TL002", # 东侧路口
"state": 1, # 初始为绿灯
"timestamp": int(time.time() * 1000)
}
]
last_update_time = time.time()
last_light_switch_time = time.time()
@app.route('/api/getCurrentFlightPositions') @app.route('/api/getCurrentFlightPositions')
def get_flight_positions(): def get_flight_positions():
current_time = int(time.time()) global last_update_time
current_time = time.time()
elapsed_time = current_time - last_update_time
# 更新航空器位置 # 只在达到更新间隔时更新位置
for aircraft in aircraft_data: if elapsed_time >= UPDATE_INTERVAL:
aircraft["time"] = current_time for aircraft in aircraft_data:
update_aircraft_position(aircraft, UPDATE_INTERVAL)
# 更新经度位置 aircraft["time"] = int(current_time * 1000)
new_lon = aircraft["longitude"] + (AIRCRAFT_SPEED * aircraft["direction"]) last_update_time = current_time
# 检查是否需要改变方向
if new_lon > BASE_LON + LON_RANGE:
aircraft["direction"] = -1 # 向西移动
elif new_lon < BASE_LON - LON_RANGE:
aircraft["direction"] = 1 # 向东移动
aircraft["longitude"] = new_lon
return jsonify(aircraft_data) return jsonify(aircraft_data)
@app.route('/api/getCurrentVehiclePositions') @app.route('/api/getCurrentVehiclePositions')
def get_vehicle_positions(): def get_vehicle_positions():
current_time = int(time.time()) global last_update_time
current_time = time.time()
elapsed_time = current_time - last_update_time
# 更新车辆位置 # 只在达到更新间隔时更新位置
for vehicle in vehicle_data: if elapsed_time >= UPDATE_INTERVAL:
vehicle["time"] = current_time for vehicle in vehicle_data:
update_vehicle_position(vehicle, UPDATE_INTERVAL)
# 更新相位 vehicle["time"] = int(current_time * 1000)
vehicle["phase"] += 0.1 # 调整此值可以改变运动速度 last_update_time = current_time
if vehicle["phase"] > 2 * math.pi:
vehicle["phase"] -= 2 * math.pi
# 使用正弦函数生成循环运动
offset = math.sin(vehicle["phase"]) * LAT_RANGE
vehicle["latitude"] = BASE_LAT + offset
# 可以添加一些随机扰动使运动更自然
vehicle["longitude"] = BASE_LON + 0.001 + (math.sin(vehicle["phase"] * 0.5) * 0.0001)
return jsonify(vehicle_data) return jsonify(vehicle_data)
@app.route('/api/getTrafficLightSignals')
def get_traffic_light_signals():
global last_light_switch_time
current_time = time.time()
# 更新时间戳
for light in traffic_light_data:
light["timestamp"] = int(current_time * 1000)
# TL001西路口的状态每15秒切换一次
elapsed_since_switch = current_time - last_light_switch_time
if elapsed_since_switch >= 15: # 每15秒切换一次
traffic_light_data[0]["state"] = 2 if traffic_light_data[0]["state"] == 0 else 0
last_light_switch_time = current_time
# 根据航空器位置更新TL002东路口
update_traffic_light_for_aircraft()
return jsonify(traffic_light_data)
@app.after_request @app.after_request
def add_cors_headers(response): def add_cors_headers(response):
response.headers['Access-Control-Allow-Origin'] = '*' response.headers['Access-Control-Allow-Origin'] = '*'
@ -102,4 +214,4 @@ def add_cors_headers(response):
return response return response
if __name__ == '__main__': if __name__ == '__main__':
app.run(host='localhost', port=8080, debug=True) app.run(host='localhost', port=8081, debug=True)

View File

@ -5,7 +5,7 @@
<style> <style>
#messages { #messages {
width: 100%; width: 100%;
height: 400px; height: 800px;
overflow-y: auto; overflow-y: auto;
border: 1px solid #ccc; border: 1px solid #ccc;
margin-bottom: 10px; margin-bottom: 10px;