更新了测试场景,增加了红绿灯机制
This commit is contained in:
parent
b642bf3a6f
commit
49f40cc56e
@ -6,7 +6,7 @@ using Newtonsoft.Json.Linq;
|
||||
public class WebSocketClient : MonoBehaviour
|
||||
{
|
||||
private WebSocket websocket;
|
||||
private readonly string serverUrl = "ws://localhost:8080";
|
||||
private readonly string serverUrl = "ws://localhost:8010";
|
||||
|
||||
async void Start()
|
||||
{
|
||||
|
||||
@ -33,6 +33,9 @@ set(Boost_USE_STATIC_RUNTIME OFF)
|
||||
set(Boost_ROOT /opt/homebrew/Cellar/boost/1.86.0_2)
|
||||
find_package(Boost REQUIRED COMPONENTS system)
|
||||
|
||||
# 添加 libcurl
|
||||
find_package(CURL REQUIRED)
|
||||
|
||||
# 添加 nlohmann_json
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
@ -54,6 +57,8 @@ set(LIB_SOURCES
|
||||
src/types/VehicleData.cpp
|
||||
src/vehicle/ControllableVehicles.cpp
|
||||
src/config/SystemConfig.cpp
|
||||
src/config/IntersectionConfig.cpp
|
||||
src/detector/TrafficLightDetector.cpp
|
||||
)
|
||||
|
||||
# 创建主库
|
||||
@ -63,12 +68,14 @@ target_include_directories(${PROJECT_NAME}_lib
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${CURL_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}_lib
|
||||
PUBLIC
|
||||
Boost::system
|
||||
nlohmann_json::nlohmann_json
|
||||
CURL::libcurl
|
||||
)
|
||||
|
||||
# 创建主可执行文件
|
||||
@ -124,7 +131,7 @@ configure_file(
|
||||
)
|
||||
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/config/controllable_vehicles.json
|
||||
${CMAKE_BINARY_DIR}/bin/config/controllable_vehicles.json
|
||||
${CMAKE_SOURCE_DIR}/config/vehicle_speed_limits.json
|
||||
${CMAKE_BINARY_DIR}/bin/config/vehicle_speed_limits.json
|
||||
COPYONLY
|
||||
)
|
||||
28
config/intersections.json
Normal file
28
config/intersections.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -10,11 +10,13 @@
|
||||
},
|
||||
"data_source": {
|
||||
"host": "localhost",
|
||||
"port": 8080,
|
||||
"port": 8081,
|
||||
"aircraft_path": "/api/getCurrentFlightPositions",
|
||||
"vehicle_path": "/api/getCurrentVehiclePositions",
|
||||
"traffic_light_path": "/api/getTrafficLightSignals",
|
||||
"refresh_interval_ms": 1000,
|
||||
"timeout_ms": 5000
|
||||
"timeout_ms": 5000,
|
||||
"read_timeout_ms": 2000
|
||||
},
|
||||
"warning": {
|
||||
"warning_interval_ms": 1000,
|
||||
@ -26,7 +28,8 @@
|
||||
"ping_interval_ms": 30000,
|
||||
"position_update": {
|
||||
"aircraft_interval_ms": 300,
|
||||
"vehicle_interval_ms": 500
|
||||
"vehicle_interval_ms": 500,
|
||||
"traffic_light_interval_ms": 1000
|
||||
}
|
||||
},
|
||||
"collision_detection": {
|
||||
@ -51,7 +54,7 @@
|
||||
}
|
||||
},
|
||||
"logging": {
|
||||
"level": "info",
|
||||
"level": "debug",
|
||||
"file": "logs/system.log",
|
||||
"max_size_mb": 10,
|
||||
"max_files": 5,
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
{
|
||||
"vehicles": [
|
||||
{
|
||||
"vehicleNo": "VEH001",
|
||||
"vehicleNo": "QN001",
|
||||
"ip": "192.168.1.101",
|
||||
"port": 8080
|
||||
},
|
||||
{
|
||||
"vehicleNo": "VEH002",
|
||||
"vehicleNo": "QN002",
|
||||
"ip": "192.168.1.102",
|
||||
"port": 8080
|
||||
},
|
||||
{
|
||||
"vehicleNo": "VEH003",
|
||||
"vehicleNo": "TQ001",
|
||||
"ip": "192.168.1.103",
|
||||
"port": 8080
|
||||
}
|
||||
41
docs/traffic_light_api.md
Normal file
41
docs/traffic_light_api.md
Normal 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 时间戳 |
|
||||
@ -43,3 +43,71 @@
|
||||
| 4 | time | 时间戳 | double | 是 |
|
||||
| 5 | direction | 方向 | 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
133
docs/warning_strategy.md
Normal 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 指令
|
||||
@ -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
|
||||
)
|
||||
|
||||
# ... 其余部分保持不变 ...
|
||||
@ -1,6 +1,8 @@
|
||||
#include "collector/DataCollector.h"
|
||||
#include "network/HTTPDataSource.h"
|
||||
#include "utils/Logger.h"
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
|
||||
DataCollector::DataCollector()
|
||||
: lastSuccessfulFetch_(std::chrono::steady_clock::now()) // 初始化时间戳
|
||||
@ -62,19 +64,38 @@ void DataCollector::stop() {
|
||||
}
|
||||
|
||||
void DataCollector::collectLoop() {
|
||||
Logger::debug("数据采集循环启动,刷新间隔: ", dataSourceConfig_.refresh_interval_ms, "ms");
|
||||
auto next_collection_time = std::chrono::steady_clock::now();
|
||||
|
||||
while (running_) {
|
||||
loopCount_++;
|
||||
Logger::debug("开始第 ", loopCount_.load(), " 次数据采集循环");
|
||||
|
||||
bool success = fetchData();
|
||||
if (!success) {
|
||||
Logger::warning("Failed to fetch data in loop ", loopCount_.load());
|
||||
Logger::warning("第 ", loopCount_.load(), " 次数据采集失败");
|
||||
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>(
|
||||
now - lastSuccessfulFetch_).count();
|
||||
|
||||
if (elapsed > dataSourceConfig_.timeout_ms) {
|
||||
// 检查是否达到告警间隔
|
||||
auto warning_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_warning_time_).count();
|
||||
// 检查是否达到告警间隔
|
||||
auto warning_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_warning_time_).count();
|
||||
|
||||
if (warning_elapsed >= warnConfig_.warning_interval_ms) {
|
||||
if (warning_elapsed >= warnConfig_.warning_interval_ms) {
|
||||
// 如果超过了超时阈值,发送警告
|
||||
if (elapsed > dataSourceConfig_.read_timeout_ms) {
|
||||
sendTimeoutWarning(elapsed);
|
||||
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) {
|
||||
network::TimeoutWarningMessage msg;
|
||||
msg.type = "data_source_timeout"; // 确保类型正确
|
||||
msg.host = dataSourceConfig_.host;
|
||||
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");
|
||||
if (!system_) {
|
||||
Logger::debug("System not set, skipping timeout warning");
|
||||
return;
|
||||
}
|
||||
|
||||
network::TimeoutWarningMessage msg;
|
||||
msg.source = "data_source";
|
||||
msg.elapsed_ms = elapsed_ms;
|
||||
// 如果超时时间超过了读取超时阈值,就是读取超时
|
||||
msg.is_read_timeout = (elapsed_ms > dataSourceConfig_.read_timeout_ms);
|
||||
|
||||
system_->broadcastTimeoutWarning(msg);
|
||||
Logger::debug("Sent timeout warning: source=", msg.source,
|
||||
" elapsed=", msg.elapsed_ms, "ms",
|
||||
" is_read_timeout=", msg.is_read_timeout ? "true" : "false",
|
||||
" read_timeout=", dataSourceConfig_.read_timeout_ms,
|
||||
" connect_timeout=", dataSourceConfig_.timeout_ms);
|
||||
}
|
||||
|
||||
bool DataCollector::fetchData() {
|
||||
bool success = true;
|
||||
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_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return dataSource_->fetchTrafficLightSignals(signals);
|
||||
}
|
||||
|
||||
std::vector<Aircraft> DataCollector::getAircraftData() {
|
||||
@ -218,6 +251,13 @@ void DataCollector::refresh() {
|
||||
// 从数据源获取最新数据
|
||||
std::vector<Aircraft> newAircraft;
|
||||
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)) {
|
||||
std::lock_guard<std::mutex> lock(cacheMutex_);
|
||||
|
||||
@ -11,6 +11,8 @@
|
||||
#include "DataSourceConfig.h"
|
||||
#include "types/BasicTypes.h"
|
||||
#include "config/WarnConfig.h"
|
||||
#include "types/TrafficLightTypes.h"
|
||||
#include "core/System.h"
|
||||
|
||||
class DataCollector {
|
||||
public:
|
||||
@ -20,21 +22,25 @@ public:
|
||||
bool initialize(const DataSourceConfig& config, const WarnConfig& warnConfig);
|
||||
void start();
|
||||
void stop();
|
||||
void refresh();
|
||||
|
||||
void setSystem(std::shared_ptr<System> system) { system_ = system; }
|
||||
|
||||
// 获取数据的接口
|
||||
std::vector<Aircraft> getAircraftData();
|
||||
std::vector<Vehicle> getVehicleData();
|
||||
std::vector<TrafficLightSignal> getTrafficLightSignals();
|
||||
bool fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals);
|
||||
|
||||
// 添加设置数据源的方法(用于测试)
|
||||
void setDataSource(std::shared_ptr<DataSource> source) {
|
||||
dataSource_ = source;
|
||||
}
|
||||
|
||||
// 添加刷新方法
|
||||
void refresh();
|
||||
|
||||
private:
|
||||
std::shared_ptr<DataSource> dataSource_;
|
||||
std::shared_ptr<System> system_;
|
||||
DataSourceConfig dataSourceConfig_;
|
||||
WarnConfig warnConfig_;
|
||||
std::thread collectorThread_;
|
||||
@ -42,14 +48,19 @@ private:
|
||||
std::atomic<uint64_t> loopCount_{0};
|
||||
|
||||
// 缓存当前数据
|
||||
std::mutex cacheMutex_;
|
||||
mutable std::mutex cacheMutex_;
|
||||
std::vector<Aircraft> aircraftCache_;
|
||||
std::vector<Vehicle> vehicleCache_;
|
||||
std::vector<TrafficLightSignal> trafficLightCache_;
|
||||
|
||||
// 保存上一次位置信息(用于计算速度)
|
||||
std::map<std::string, Aircraft> lastAircraftPositions_;
|
||||
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 last_warning_time_;
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#define AIRPORT_COLLECTOR_DATA_SOURCE_H
|
||||
|
||||
#include "types/BasicTypes.h"
|
||||
#include "types/TrafficLightTypes.h"
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
@ -16,6 +17,7 @@ public:
|
||||
// 获取数据的接口
|
||||
virtual bool fetchAircraftData(std::vector<Aircraft>& aircraft) = 0;
|
||||
virtual bool fetchVehicleData(std::vector<Vehicle>& vehicles) = 0;
|
||||
virtual bool fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals) = 0;
|
||||
};
|
||||
|
||||
#endif // AIRPORT_COLLECTOR_DATA_SOURCE_H
|
||||
@ -8,8 +8,10 @@ struct DataSourceConfig {
|
||||
uint16_t port;
|
||||
std::string aircraft_path;
|
||||
std::string vehicle_path;
|
||||
std::string traffic_light_path;
|
||||
int refresh_interval_ms;
|
||||
int timeout_ms;
|
||||
int timeout_ms; // 连接超时时间, 单位: 毫秒
|
||||
int read_timeout_ms; // 读取超时时间, 单位: 毫秒, 小于连接超时时间
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -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: 添加错误日志
|
||||
}
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
64
src/config/IntersectionConfig.cpp
Normal file
64
src/config/IntersectionConfig.cpp
Normal 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;
|
||||
}
|
||||
37
src/config/IntersectionConfig.h
Normal file
37
src/config/IntersectionConfig.h
Normal 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_;
|
||||
};
|
||||
@ -26,8 +26,10 @@ SystemConfig SystemConfig::load(const std::string& filename) {
|
||||
config.data_source.port = j["data_source"]["port"];
|
||||
config.data_source.aircraft_path = j["data_source"]["aircraft_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.timeout_ms = j["data_source"]["timeout_ms"];
|
||||
config.data_source.read_timeout_ms = j["data_source"]["read_timeout_ms"];
|
||||
|
||||
// 加载 WebSocket 配置
|
||||
config.websocket.port = j["websocket"]["port"];
|
||||
|
||||
@ -19,17 +19,20 @@ struct SystemConfig {
|
||||
uint16_t port;
|
||||
std::string aircraft_path;
|
||||
std::string vehicle_path;
|
||||
std::string traffic_light_path;
|
||||
int refresh_interval_ms;
|
||||
int timeout_ms;
|
||||
int read_timeout_ms;
|
||||
} data_source;
|
||||
|
||||
struct WebSocket {
|
||||
int port;
|
||||
uint16_t port;
|
||||
int max_connections;
|
||||
int ping_interval_ms;
|
||||
struct PositionUpdate {
|
||||
int aircraft_interval_ms;
|
||||
int vehicle_interval_ms;
|
||||
int traffic_light_interval_ms;
|
||||
} position_update;
|
||||
} websocket;
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include <csignal>
|
||||
#include <typeinfo>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include "core/System.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "collector/DataCollector.h"
|
||||
@ -31,6 +32,10 @@ bool System::initialize() {
|
||||
// 加载系统配置
|
||||
system_config_ = SystemConfig::load("config/system_config.json");
|
||||
|
||||
// 加载路口配置
|
||||
intersection_config_ = IntersectionConfig::load("config/intersections.json");
|
||||
Logger::info("Loaded {} intersections", intersection_config_.getIntersections().size());
|
||||
|
||||
// 初始化 WebSocket 服务器
|
||||
ws_server_ = std::make_unique<network::WebSocketServer>(system_config_.websocket.port);
|
||||
ws_thread_ = std::thread([this]() {
|
||||
@ -41,13 +46,14 @@ bool System::initialize() {
|
||||
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_);
|
||||
|
||||
// 初始化红绿灯检测器
|
||||
trafficLightDetector_ = std::make_unique<TrafficLightDetector>(intersection_config_, *controllableVehicles_);
|
||||
|
||||
// 创建数据采集器
|
||||
dataCollector_ = std::make_unique<DataCollector>();
|
||||
|
||||
@ -57,6 +63,7 @@ bool System::initialize() {
|
||||
system_config_.data_source.port,
|
||||
system_config_.data_source.aircraft_path,
|
||||
system_config_.data_source.vehicle_path,
|
||||
system_config_.data_source.traffic_light_path,
|
||||
system_config_.data_source.refresh_interval_ms,
|
||||
system_config_.data_source.timeout_ms
|
||||
};
|
||||
@ -116,57 +123,151 @@ void System::stop() {
|
||||
void System::processLoop() {
|
||||
auto last_aircraft_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_) {
|
||||
try {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto last_data_collection = now;
|
||||
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_data_refresh).count() >= system_config_.data_source.refresh_interval_ms) {
|
||||
dataCollector_->refresh();
|
||||
last_data_refresh = now;
|
||||
|
||||
auto aircraft = dataCollector_->getAircraftData();
|
||||
auto vehicles = dataCollector_->getVehicleData();
|
||||
Logger::debug("开始获取数据...");
|
||||
|
||||
// 获取最新数据
|
||||
dataCollector_->refresh(); // 先刷新数据
|
||||
auto aircraft = dataCollector_->getAircraftData();
|
||||
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, ")");
|
||||
}
|
||||
}
|
||||
|
||||
// 检查航空器更新
|
||||
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) {
|
||||
Logger::debug("Broadcasting aircraft positions (", aircraft.size(), " aircraft)");
|
||||
if (has_new_aircraft) {
|
||||
Logger::debug("广播 ", aircraft.size(), " 架航空器位置, 时间戳更新: ", last_aircraft_timestamp, " -> ", max_timestamp);
|
||||
for (const auto& ac : aircraft) {
|
||||
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, ")");
|
||||
}
|
||||
}
|
||||
|
||||
// 检查车辆更新
|
||||
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) {
|
||||
Logger::debug("Broadcasting vehicle positions (", vehicles.size(), " vehicles)");
|
||||
if (has_new_vehicles) {
|
||||
Logger::debug("广播 ", vehicles.size(), " 辆车辆位置, 时间戳更新: ", last_vehicle_timestamp, " -> ", max_timestamp);
|
||||
for (const auto& veh : vehicles) {
|
||||
broadcastPositionUpdate(veh);
|
||||
}
|
||||
last_vehicle_update = now;
|
||||
}
|
||||
|
||||
// 更新碰撞检测器
|
||||
collisionDetector_->updateTraffic(aircraft, vehicles);
|
||||
auto collisions = collisionDetector_->detectCollisions();
|
||||
|
||||
if (!collisions.empty()) {
|
||||
processCollisions(collisions);
|
||||
last_vehicle_timestamp = max_timestamp;
|
||||
} else {
|
||||
Logger::debug("没有新的车辆数据,当前时间戳: ", last_vehicle_timestamp);
|
||||
}
|
||||
last_vehicle_update = now;
|
||||
}
|
||||
|
||||
// 处理间隔设置为数据刷新间隔的 1/10
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(
|
||||
system_config_.data_source.refresh_interval_ms / 10
|
||||
));
|
||||
// 检查冲突更新
|
||||
auto collision_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
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) {
|
||||
Logger::error("处理循环发生错误: ", e.what());
|
||||
@ -176,28 +277,22 @@ void System::processLoop() {
|
||||
|
||||
void System::processCollisions(const std::vector<CollisionRisk>& collisions) {
|
||||
if (!collisions.empty()) {
|
||||
Logger::info("检测到 ", collisions.size(), " 个碰撞风险");
|
||||
Logger::info("检测到 ", collisions.size(), " 个冲突风险");
|
||||
|
||||
for (const auto& risk : collisions) {
|
||||
// 发送碰撞警告到 Unity 客户端
|
||||
// 发送冲突警告到 Unity 客户端
|
||||
broadcastCollisionWarning(risk);
|
||||
|
||||
// 根据风险等级选择不同的日志级别
|
||||
switch (risk.level) {
|
||||
case RiskLevel::EMERGENCY:
|
||||
Logger::error("严重碰撞风险: ", risk.id1, " 与 ", risk.id2,
|
||||
", 距离: ", risk.distance, "米",
|
||||
", 相对速度: ", risk.relativeSpeed, "m/s");
|
||||
break;
|
||||
|
||||
case RiskLevel::CRITICAL:
|
||||
Logger::warning("高度碰撞风险: ", risk.id1, " 与 ", risk.id2,
|
||||
Logger::warning("高度冲突风险: ", risk.id1, " 与 ", risk.id2,
|
||||
", 距离: ", risk.distance, "米",
|
||||
", 相对速度: ", risk.relativeSpeed, "m/s");
|
||||
break;
|
||||
|
||||
case RiskLevel::WARNING:
|
||||
Logger::warning("低度碰撞风险: ", risk.id1, " 与 ", risk.id2,
|
||||
Logger::warning("低度冲突风险: ", risk.id1, " 与 ", risk.id2,
|
||||
", 距离: ", risk.distance, "米",
|
||||
", 相对速度: ", risk.relativeSpeed, "m/s");
|
||||
break;
|
||||
@ -209,53 +304,83 @@ void System::processCollisions(const std::vector<CollisionRisk>& collisions) {
|
||||
}
|
||||
|
||||
void System::broadcastPositionUpdate(const MovingObject& obj) {
|
||||
if (!ws_server_) {
|
||||
return;
|
||||
}
|
||||
|
||||
network::PositionUpdateMessage msg;
|
||||
msg.objectId = obj.id;
|
||||
msg.objectType = (typeid(obj) == typeid(Aircraft)) ? "aircraft" : "vehicle";
|
||||
msg.timestamp = obj.timestamp;
|
||||
msg.longitude = obj.geo.longitude;
|
||||
msg.latitude = obj.geo.latitude;
|
||||
msg.heading = obj.heading;
|
||||
msg.speed = obj.speed;
|
||||
msg.timestamp = obj.timestamp;
|
||||
|
||||
if (ws_server_) {
|
||||
ws_server_->broadcast(msg.toJson().dump());
|
||||
Logger::debug("Broadcast position update: type=", msg.objectType,
|
||||
", id=", msg.objectId,
|
||||
", pos=(", msg.longitude, ",", msg.latitude, ")");
|
||||
nlohmann::json j = {
|
||||
{"type", "position_update"},
|
||||
{"object_id", msg.objectId},
|
||||
{"object_type", msg.objectType},
|
||||
{"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) {
|
||||
network::CollisionWarningMessage msg;
|
||||
msg.id1 = risk.id1;
|
||||
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; // 不发送无风险的警告
|
||||
if (!ws_server_) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg.distance = risk.distance;
|
||||
msg.relativeSpeed = risk.relativeSpeed;
|
||||
msg.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
nlohmann::json j = {
|
||||
{"type", "collision_warning"},
|
||||
{"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(msg.toJson().dump());
|
||||
}
|
||||
ws_server_->broadcast(j.dump());
|
||||
}
|
||||
|
||||
void System::broadcastTimeoutWarning(const network::TimeoutWarningMessage& warning) {
|
||||
@ -264,10 +389,10 @@ void System::broadcastTimeoutWarning(const network::TimeoutWarningMessage& warni
|
||||
|
||||
// 根据超时时间记录不同级别的日志
|
||||
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");
|
||||
} else {
|
||||
Logger::warning("Connection timeout: ", warning.host, ":", warning.port, " - ",
|
||||
Logger::warning("Connection timeout: ", warning.source, " - ",
|
||||
warning.elapsed_ms, "ms without response");
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,11 +6,13 @@
|
||||
#include <atomic>
|
||||
#include "types/BasicTypes.h"
|
||||
#include "detector/CollisionDetector.h"
|
||||
#include "detector/TrafficLightDetector.h"
|
||||
#include "spatial/AirportBounds.h"
|
||||
#include "vehicle/ControllableVehicles.h"
|
||||
#include "network/WebSocketServer.h"
|
||||
#include "network/MessageTypes.h"
|
||||
#include "config/SystemConfig.h"
|
||||
#include "config/IntersectionConfig.h"
|
||||
|
||||
// 前向声明
|
||||
class DataCollector;
|
||||
@ -18,7 +20,7 @@ class DataCollector;
|
||||
class System {
|
||||
public:
|
||||
System();
|
||||
~System();
|
||||
virtual ~System();
|
||||
|
||||
bool initialize();
|
||||
void start();
|
||||
@ -27,22 +29,28 @@ public:
|
||||
static System* instance() { return instance_; }
|
||||
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:
|
||||
void processLoop();
|
||||
void processCollisions(const std::vector<CollisionRisk>& collisions);
|
||||
|
||||
// WebSocket 相关方法
|
||||
void broadcastPositionUpdate(const MovingObject& obj);
|
||||
void broadcastCollisionWarning(const CollisionRisk& risk);
|
||||
|
||||
|
||||
|
||||
std::atomic<bool> running_{false};
|
||||
std::thread processThread_;
|
||||
|
||||
std::unique_ptr<AirportBounds> airportBounds_;
|
||||
std::unique_ptr<ControllableVehicles> controllableVehicles_;
|
||||
std::unique_ptr<CollisionDetector> collisionDetector_;
|
||||
std::unique_ptr<TrafficLightDetector> trafficLightDetector_;
|
||||
std::unique_ptr<DataCollector> dataCollector_;
|
||||
|
||||
// WebSocket 服务器
|
||||
@ -52,5 +60,8 @@ private:
|
||||
// 系统配置
|
||||
SystemConfig system_config_;
|
||||
|
||||
// 路口配置
|
||||
IntersectionConfig intersection_config_;
|
||||
|
||||
static System* instance_;
|
||||
};
|
||||
@ -61,6 +61,12 @@ std::vector<CollisionRisk> CollisionDetector::detectCollisions() {
|
||||
// 计算相对速度大小
|
||||
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);
|
||||
|
||||
@ -100,6 +106,12 @@ std::vector<CollisionRisk> CollisionDetector::detectCollisions() {
|
||||
double vy = v1v.vy - v2v.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);
|
||||
|
||||
@ -128,9 +140,7 @@ RiskLevel CollisionDetector::calculateRiskLevel(double distance, const Vector2D&
|
||||
auto thresholds = areaConfig.getThresholds(isAircraft1, isAircraft2);
|
||||
|
||||
// 直接比较距离和阈值
|
||||
if (distance <= thresholds.emergency) {
|
||||
return RiskLevel::EMERGENCY;
|
||||
} else if (distance <= thresholds.critical) {
|
||||
if (distance <= thresholds.critical) {
|
||||
return RiskLevel::CRITICAL;
|
||||
} else if (distance <= thresholds.warning) {
|
||||
return RiskLevel::WARNING;
|
||||
@ -149,10 +159,10 @@ bool CollisionDetector::checkAircraftVehicleCollision(const Aircraft& aircraft,
|
||||
|
||||
// 获取该区域的告警阈值
|
||||
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;
|
||||
}
|
||||
|
||||
@ -186,7 +196,7 @@ bool CollisionDetector::checkVehicleCollision(const Vehicle& v1,
|
||||
auto thresholds = areaConfig.getThresholds(false, false); // vehicle vs vehicle
|
||||
|
||||
// 如果距离小于紧急阈值,直接报警
|
||||
if (distance < thresholds.emergency) {
|
||||
if (distance < thresholds.critical) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -7,13 +7,13 @@
|
||||
#include "vehicle/ControllableVehicles.h"
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
// 碰撞风险等级
|
||||
enum class RiskLevel {
|
||||
NONE = 0, // 无风险
|
||||
WARNING = 1, // 低风险:距离在阈值的 50%-100% 之间
|
||||
CRITICAL = 2, // 中等风险:距离在阈值的 25%-50% 之间
|
||||
EMERGENCY = 3, // 高风险:距离在阈值的 0%-25% 之间
|
||||
CRITICAL = 2, // 中等风险:距离在阈值的 0%-50% 之间
|
||||
};
|
||||
|
||||
// 碰撞风险信息
|
||||
@ -62,9 +62,17 @@ private:
|
||||
double vx, vy;
|
||||
|
||||
MovementVector(double speed, double heading) {
|
||||
double radians = (90 - heading) * M_PI / 180.0;
|
||||
vx = speed * std::cos(radians);
|
||||
vy = speed * std::sin(radians);
|
||||
// 航向角是以正北为0度,顺时针增加
|
||||
// 需要转换为数学坐标系(以正东为0度,逆时针增加)
|
||||
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");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
75
src/detector/TrafficLightDetector.cpp
Normal file
75
src/detector/TrafficLightDetector.cpp
Normal 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);
|
||||
}
|
||||
28
src/detector/TrafficLightDetector.h
Normal file
28
src/detector/TrafficLightDetector.h
Normal 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_;
|
||||
};
|
||||
24
src/main.cpp
24
src/main.cpp
@ -1,8 +1,10 @@
|
||||
#include "core/System.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "config/SystemConfig.h"
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <csignal>
|
||||
#include <filesystem>
|
||||
|
||||
volatile sig_atomic_t running = true;
|
||||
|
||||
@ -11,14 +13,30 @@ void signalHandler(int signum) {
|
||||
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() {
|
||||
try {
|
||||
// 设置信号处理
|
||||
signal(SIGINT, 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;
|
||||
@ -43,7 +61,7 @@ int main() {
|
||||
return 0;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::error("Fatal error: ", e.what());
|
||||
std::cerr << "Fatal error: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
#include "mock/MockDataService.h"
|
||||
#include <format>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
MockDataService::MockDataService(const AirportBounds& bounds)
|
||||
: bounds_(bounds)
|
||||
@ -124,9 +126,43 @@ double MockDataService::generateAltitude(AreaType areaType) {
|
||||
}
|
||||
|
||||
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() {
|
||||
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);
|
||||
}
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#include "types/BasicTypes.h"
|
||||
#include "spatial/AirportBounds.h"
|
||||
#include "types/TrafficLightTypes.h"
|
||||
#include <vector>
|
||||
#include <random>
|
||||
|
||||
@ -12,6 +13,7 @@ public:
|
||||
|
||||
std::vector<Aircraft> generateAircraftData();
|
||||
std::vector<Vehicle> generateVehicleData();
|
||||
std::vector<TrafficLightSignal> generateTrafficLightData();
|
||||
|
||||
private:
|
||||
const AirportBounds& bounds_;
|
||||
@ -28,9 +30,14 @@ private:
|
||||
// 生成ID
|
||||
std::string generateAircraftId();
|
||||
std::string generateVehicleId();
|
||||
std::string generateTrafficLightId();
|
||||
|
||||
// 生成红绿灯状态
|
||||
SignalStatus generateTrafficLightState();
|
||||
|
||||
int aircraftCounter_ = 0;
|
||||
int vehicleCounter_ = 0;
|
||||
int trafficLightCounter_ = 0;
|
||||
};
|
||||
|
||||
#endif // AIRPORT_MOCK_MOCKDATASERVICE_H
|
||||
@ -1,50 +1,53 @@
|
||||
#include "network/HTTPDataSource.h"
|
||||
#include "utils/Logger.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
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)
|
||||
: config_(config)
|
||||
, host_(config.host)
|
||||
, 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() {
|
||||
disconnect();
|
||||
if (curl_) {
|
||||
curl_easy_cleanup(curl_);
|
||||
}
|
||||
}
|
||||
|
||||
bool HTTPDataSource::connect() {
|
||||
try {
|
||||
Logger::debug("Connecting to ", host_, ":", port_);
|
||||
|
||||
auto resolver = asio::ip::tcp::resolver(io_context_);
|
||||
auto endpoints = resolver.resolve(host_, port_);
|
||||
|
||||
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;
|
||||
if (!curl_) {
|
||||
curl_ = curl_easy_init();
|
||||
if (!curl_) {
|
||||
Logger::error("Failed to initialize CURL");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HTTPDataSource::disconnect() {
|
||||
if (socket_ && socket_->is_open()) {
|
||||
boost::system::error_code ec;
|
||||
socket_->shutdown(asio::ip::tcp::socket::shutdown_both, ec);
|
||||
socket_->close(ec);
|
||||
if (curl_) {
|
||||
curl_easy_cleanup(curl_);
|
||||
curl_ = nullptr;
|
||||
}
|
||||
socket_.reset();
|
||||
}
|
||||
|
||||
bool HTTPDataSource::isAvailable() const {
|
||||
return socket_ && socket_->is_open();
|
||||
return curl_ != nullptr;
|
||||
}
|
||||
|
||||
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() {
|
||||
if (isAvailable()) {
|
||||
if (isAvailable() && checkConnectionHealth()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
@ -107,8 +164,7 @@ bool HTTPDataSource::fetchAircraftData(std::vector<Aircraft>& aircraft) {
|
||||
}
|
||||
|
||||
std::string response;
|
||||
if (!sendRequest(config_.aircraft_path) || !readResponse(response)) {
|
||||
disconnect();
|
||||
if (!sendRequest(config_.aircraft_path, response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -123,192 +179,198 @@ bool HTTPDataSource::fetchVehicleData(std::vector<Vehicle>& vehicles) {
|
||||
}
|
||||
|
||||
std::string response;
|
||||
if (!sendRequest(config_.vehicle_path) || !readResponse(response)) {
|
||||
disconnect();
|
||||
if (!sendRequest(config_.vehicle_path, response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parseVehicleResponse(response, vehicles);
|
||||
}
|
||||
|
||||
bool HTTPDataSource::sendRequest(const std::string& path) {
|
||||
try {
|
||||
if (!connect()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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());
|
||||
bool HTTPDataSource::fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (!ensureConnected()) {
|
||||
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 {
|
||||
Logger::debug("Reading response");
|
||||
json j = json::parse(response);
|
||||
|
||||
asio::streambuf response_buf;
|
||||
boost::system::error_code ec;
|
||||
|
||||
// 读取响应头
|
||||
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());
|
||||
if (!j.is_array()) {
|
||||
Logger::error("Invalid traffic light response format: expected array");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 提取响应头
|
||||
std::string header{asio::buffers_begin(response_buf.data()),
|
||||
asio::buffers_begin(response_buf.data()) + header_length};
|
||||
Logger::debug("Header received:\n", header);
|
||||
signals.clear();
|
||||
signals.reserve(j.size());
|
||||
|
||||
// 解析 Content-Length
|
||||
std::regex content_length_regex("Content-Length: (\\d+)");
|
||||
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);
|
||||
for (const auto& item : j) {
|
||||
TrafficLightSignal signal;
|
||||
|
||||
if (ec) {
|
||||
Logger::error("Error reading body: ", ec.message());
|
||||
return false;
|
||||
// 解析必需字段
|
||||
if (!item.contains("id") || !item.contains("state")) {
|
||||
Logger::error("Missing required fields in traffic light signal");
|
||||
continue;
|
||||
}
|
||||
|
||||
body.append(buf.data(), bytes_read);
|
||||
total_read += bytes_read;
|
||||
Logger::debug("Read ", total_read, " of ", content_length, " bytes");
|
||||
signal.trafficLightId = item["id"].get<std::string>();
|
||||
|
||||
// 支持数字或字符串类型的状态
|
||||
if (item["state"].is_number()) {
|
||||
signal.status = static_cast<SignalStatus>(item["state"].get<int>());
|
||||
} else {
|
||||
signal.status = TrafficLightSignal::parseStatus(item["state"].get<std::string>());
|
||||
}
|
||||
|
||||
// 解析可选字段
|
||||
if (item.contains("timestamp")) {
|
||||
signal.timestamp = item["timestamp"].get<int64_t>();
|
||||
}
|
||||
|
||||
signals.push_back(signal);
|
||||
}
|
||||
|
||||
response = body;
|
||||
//Logger::info("Successfully read complete response");
|
||||
return true;
|
||||
return !signals.empty();
|
||||
}
|
||||
catch (const json::exception& e) {
|
||||
Logger::error("Failed to parse traffic light response: ", e.what());
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::error("Exception in readResponse: ", e.what());
|
||||
Logger::error("Error processing traffic light response: ", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool HTTPDataSource::parseVehicleResponse(const std::string& response, std::vector<Vehicle>& vehicles) {
|
||||
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) {
|
||||
// 检查必需字段
|
||||
if (!item.contains("vehicleNo") || !item.contains("longitude") ||
|
||||
!item.contains("latitude") || !item.contains("time")) {
|
||||
Vehicle veh;
|
||||
|
||||
// 解析必需字段
|
||||
if (!item.contains("vehicleNo") || !item.contains("latitude") ||
|
||||
!item.contains("longitude") || !item.contains("time")) {
|
||||
Logger::error("Missing required fields in vehicle data");
|
||||
continue;
|
||||
}
|
||||
|
||||
Vehicle v;
|
||||
v.vehicleNo = item["vehicleNo"].get<std::string>();
|
||||
v.id = v.vehicleNo;
|
||||
veh.vehicleNo = item["vehicleNo"].get<std::string>();
|
||||
veh.id = veh.vehicleNo; // 使用车辆号作为ID
|
||||
veh.geo.latitude = item["latitude"].get<double>();
|
||||
veh.geo.longitude = item["longitude"].get<double>();
|
||||
|
||||
// 获取经纬度信息
|
||||
GeoPosition pos;
|
||||
pos.longitude = item["longitude"].get<double>();
|
||||
pos.latitude = item["latitude"].get<double>();
|
||||
// 时间戳转换
|
||||
int64_t time = item["time"].get<int64_t>();
|
||||
if (time < 0) {
|
||||
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;
|
||||
v.position = coordinateConverter_.toLocalXY(pos.latitude, pos.longitude);
|
||||
v.updateMotion(pos, v.timestamp); // 更新速度和航向
|
||||
// 更新位置信息
|
||||
veh.position = coordinateConverter_.toLocalXY(veh.geo.latitude, veh.geo.longitude);
|
||||
veh.updateMotion(veh.geo, veh.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();
|
||||
}
|
||||
catch (const json::exception& e) {
|
||||
Logger::error("JSON parsing error: ", e.what());
|
||||
Logger::error("Response data: ", response);
|
||||
Logger::error("Failed to parse vehicle response: ", e.what());
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::error("Error processing vehicle response: ", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool HTTPDataSource::parseAircraftResponse(const std::string& response, std::vector<Aircraft>& aircraft) {
|
||||
try {
|
||||
auto j = json::parse(response);
|
||||
Logger::debug("Parsing aircraft data: ", response);
|
||||
json j = json::parse(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) {
|
||||
// 检查必需字段
|
||||
if (!item.contains("flightNo") || !item.contains("longitude") ||
|
||||
!item.contains("latitude") || !item.contains("time")) {
|
||||
Aircraft ac;
|
||||
|
||||
// 解析必需字段
|
||||
if (!item.contains("flightNo") || !item.contains("latitude") ||
|
||||
!item.contains("longitude") || !item.contains("time")) {
|
||||
Logger::error("Missing required fields in aircraft data");
|
||||
continue;
|
||||
}
|
||||
|
||||
Aircraft a;
|
||||
a.flightNo = item["flightNo"].get<std::string>();
|
||||
a.id = a.flightNo;
|
||||
ac.flightNo = item["flightNo"].get<std::string>();
|
||||
ac.id = ac.flightNo; // 使用航班号作为ID
|
||||
ac.geo.latitude = item["latitude"].get<double>();
|
||||
ac.geo.longitude = item["longitude"].get<double>();
|
||||
|
||||
// 获取经纬度信息
|
||||
GeoPosition pos;
|
||||
pos.longitude = item["longitude"].get<double>();
|
||||
pos.latitude = item["latitude"].get<double>();
|
||||
// 时间戳转换
|
||||
int64_t time = item["time"].get<int64_t>();
|
||||
if (time < 0) {
|
||||
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;
|
||||
a.position = coordinateConverter_.toLocalXY(pos.latitude, pos.longitude);
|
||||
a.updateMotion(pos, a.timestamp); // 更新速度和航向
|
||||
// 更新位置信息
|
||||
ac.position = coordinateConverter_.toLocalXY(ac.geo.latitude, ac.geo.longitude);
|
||||
ac.updateMotion(ac.geo, ac.timestamp); // 更新速度和航向
|
||||
|
||||
// 默认高度为0(地面)
|
||||
a.altitude = 0.0;
|
||||
Logger::debug("Parsed aircraft: id=", ac.id, " flightNo=", ac.flightNo,
|
||||
" timestamp=", ac.timestamp, " pos=(", ac.geo.longitude, ",", ac.geo.latitude, ")");
|
||||
|
||||
aircraft.push_back(a);
|
||||
aircraft.push_back(ac);
|
||||
}
|
||||
|
||||
return !aircraft.empty();
|
||||
}
|
||||
catch (const json::exception& e) {
|
||||
Logger::error("JSON parsing error: ", e.what());
|
||||
Logger::error("Response data: ", response);
|
||||
Logger::error("Failed to parse aircraft response: ", e.what());
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::error("Error processing aircraft response: ", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -3,21 +3,21 @@
|
||||
|
||||
#include "collector/DataSource.h"
|
||||
#include "spatial/CoordinateConverter.h"
|
||||
#include <boost/asio.hpp>
|
||||
#include <string>
|
||||
#include "collector/DataSourceConfig.h"
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include "core/System.h"
|
||||
|
||||
namespace asio = boost::asio;
|
||||
#include <curl/curl.h>
|
||||
|
||||
class HTTPDataSource : public DataSource {
|
||||
public:
|
||||
explicit HTTPDataSource(const DataSourceConfig& config);
|
||||
~HTTPDataSource() override;
|
||||
|
||||
const DataSourceConfig& getConfig() const { return config_; }
|
||||
|
||||
DataSourceConfig config_;
|
||||
bool connect() override;
|
||||
void disconnect() override;
|
||||
@ -25,26 +25,31 @@ public:
|
||||
|
||||
bool fetchAircraftData(std::vector<Aircraft>& aircraft) override;
|
||||
bool fetchVehicleData(std::vector<Vehicle>& vehicles) override;
|
||||
bool fetchTrafficLightSignals(std::vector<TrafficLightSignal>& signals) override;
|
||||
|
||||
private:
|
||||
std::string host_;
|
||||
std::string port_;
|
||||
asio::io_context io_context_;
|
||||
std::unique_ptr<asio::ip::tcp::socket> socket_;
|
||||
CoordinateConverter coordinateConverter_;
|
||||
std::mutex mutex_;
|
||||
CURL* curl_;
|
||||
|
||||
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 HEALTH_CHECK_INTERVAL = 60; // 健康检查间隔(秒)
|
||||
std::atomic<bool> is_reconnecting_{false}; // 重连状态标志
|
||||
|
||||
bool tryReconnect();
|
||||
bool ensureConnected();
|
||||
bool checkConnectionHealth(); // 检查连接健康状况
|
||||
|
||||
bool sendRequest(const std::string& path);
|
||||
bool readResponse(std::string& response);
|
||||
bool sendRequest(const std::string& path, std::string& response);
|
||||
bool parseAircraftResponse(const std::string& response, std::vector<Aircraft>& aircraft);
|
||||
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
|
||||
|
||||
@ -55,19 +55,15 @@ struct CollisionWarningMessage {
|
||||
};
|
||||
|
||||
struct TimeoutWarningMessage {
|
||||
std::string type = "data_source_timeout";
|
||||
std::string host;
|
||||
int port;
|
||||
int64_t elapsed_ms;
|
||||
uint64_t timestamp;
|
||||
std::string source; // 超时来源
|
||||
int64_t elapsed_ms; // 超时时长
|
||||
bool is_read_timeout; // 是否是读取超时
|
||||
|
||||
nlohmann::json toJson() const {
|
||||
return {
|
||||
{"type", type},
|
||||
{"host", host},
|
||||
{"port", port},
|
||||
{"source", source},
|
||||
{"elapsed_ms", elapsed_ms},
|
||||
{"timestamp", timestamp}
|
||||
{"is_read_timeout", is_read_timeout}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include "types/VehicleCommand.h"
|
||||
|
||||
class NetworkInterface {
|
||||
@ -13,9 +12,6 @@ public:
|
||||
virtual void disconnect() = 0;
|
||||
virtual bool send(const VehicleCommand& cmd) = 0;
|
||||
virtual bool isConnected() const = 0;
|
||||
|
||||
protected:
|
||||
boost::asio::io_context io_context_;
|
||||
};
|
||||
|
||||
#endif // AIRPORT_NETWORK_INTERFACE_H
|
||||
@ -1,4 +1,5 @@
|
||||
#include "network/WebSocketServer.h"
|
||||
#include "utils/Logger.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace network {
|
||||
@ -24,6 +25,7 @@ WebSocketServer::~WebSocketServer() {
|
||||
}
|
||||
|
||||
void WebSocketServer::start() {
|
||||
Logger::info("WebSocket 服务器启动,监听端口: ", acceptor_.local_endpoint().port());
|
||||
handleAccept();
|
||||
ioc_.run();
|
||||
}
|
||||
@ -31,24 +33,35 @@ void WebSocketServer::start() {
|
||||
void WebSocketServer::broadcast(const std::string& message) {
|
||||
std::lock_guard<std::mutex> lock(sessions_mutex_);
|
||||
auto it = sessions_.begin();
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
while (it != sessions_.end()) {
|
||||
if (auto session = it->lock()) { // 获取 shared_ptr
|
||||
try {
|
||||
session->write(boost::asio::buffer(message));
|
||||
++successCount;
|
||||
++it;
|
||||
} catch (...) {
|
||||
Logger::warning("广播消息到客户端失败");
|
||||
++failCount;
|
||||
it = sessions_.erase(it); // 移除失败的会话
|
||||
}
|
||||
} else {
|
||||
Logger::debug("移除失效的会话");
|
||||
it = sessions_.erase(it); // 移除已失效的会话
|
||||
}
|
||||
}
|
||||
|
||||
Logger::debug("广播消息完成: ", successCount, " 个成功, ", failCount, " 个失败, 当前共 ", sessions_.size(), " 个连接");
|
||||
}
|
||||
|
||||
void WebSocketServer::handleAccept() {
|
||||
acceptor_.async_accept(
|
||||
[this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket) {
|
||||
if (!ec) {
|
||||
Logger::info("接受新的 WebSocket 连接: ", socket.remote_endpoint().address().to_string());
|
||||
|
||||
// 创建新的 WebSocket 会话
|
||||
auto ws = std::make_shared<boost::beast::websocket::stream<boost::asio::ip::tcp::socket>>(std::move(socket));
|
||||
|
||||
@ -60,10 +73,15 @@ void WebSocketServer::handleAccept() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sessions_mutex_);
|
||||
sessions_.push_back(ws); // 存储 weak_ptr
|
||||
Logger::info("WebSocket 握手成功,当前连接数: ", sessions_.size());
|
||||
}
|
||||
doRead(ws);
|
||||
} else {
|
||||
Logger::error("WebSocket 握手失败: ", ec.message());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Logger::error("接受 WebSocket 连接失败: ", ec.message());
|
||||
}
|
||||
|
||||
// 继续接受新的连接
|
||||
@ -99,6 +117,7 @@ void WebSocketServer::doRead(std::shared_ptr<boost::beast::websocket::stream<boo
|
||||
|
||||
void WebSocketServer::stop() {
|
||||
running_ = false;
|
||||
Logger::info("正在停止 WebSocket 服务器...");
|
||||
ioc_.stop(); // 停止 io_context
|
||||
|
||||
// 关闭所有连接
|
||||
@ -107,12 +126,14 @@ void WebSocketServer::stop() {
|
||||
if (auto ws = session.lock()) {
|
||||
try {
|
||||
ws->close(boost::beast::websocket::close_code::normal);
|
||||
Logger::debug("关闭 WebSocket 连接");
|
||||
} catch (...) {
|
||||
// 忽略关闭时的错误
|
||||
Logger::warning("关闭 WebSocket 连接时发生错误");
|
||||
}
|
||||
}
|
||||
}
|
||||
sessions_.clear();
|
||||
Logger::info("WebSocket 服务器已停止");
|
||||
}
|
||||
|
||||
} // namespace network
|
||||
@ -15,15 +15,14 @@ enum class AreaType {
|
||||
|
||||
// 区域配置
|
||||
struct AreaConfig {
|
||||
double vehicleCollisionRadius; // 车辆间碰撞检测半径
|
||||
double aircraftGroundRadius; // 航空器与车辆碰撞检测半径
|
||||
double vehicleCollisionRadius; // 车辆间冲突检测半径
|
||||
double aircraftGroundRadius; // 航空器与车辆冲突检测半径
|
||||
double heightThreshold; // 高度阈值
|
||||
|
||||
// 获取不同类型交通工具间的告警阈值
|
||||
struct CollisionThresholds {
|
||||
double warning; // 警告距离
|
||||
double critical; // 危险距离
|
||||
double emergency; // 紧急距离
|
||||
double warning; // 预警距离
|
||||
double critical; // 告警距离
|
||||
};
|
||||
|
||||
// 计算两个交通工具之间的告警阈值
|
||||
@ -34,9 +33,8 @@ struct AreaConfig {
|
||||
|
||||
// 计算不同级别的阈值
|
||||
return {
|
||||
baseRadius, // 警告距离:为基础距离
|
||||
baseRadius / 2.0, // 危险距离:为基础距离的一半
|
||||
baseRadius / 4.0 // 紧急距离:为基础距离的四分之一
|
||||
baseRadius, // 预警距离:为基础距离
|
||||
baseRadius / 2.0 // 告警距离:为基础距离的一半
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -82,6 +82,8 @@ bool Vehicle::isValidSpeed(double speed) const {
|
||||
void MovingObject::updateMotion(const GeoPosition& newPos, uint64_t newTime) {
|
||||
// 检查时间戳
|
||||
if (!positionHistory.empty() && newTime <= positionHistory.back().timestamp) {
|
||||
Logger::debug("[Motion] Ignore outdated data: current=", newTime,
|
||||
" last=", positionHistory.back().timestamp);
|
||||
return; // 忽略重复或过时的数据
|
||||
}
|
||||
|
||||
@ -93,35 +95,66 @@ void MovingObject::updateMotion(const GeoPosition& newPos, uint64_t newTime) {
|
||||
|
||||
// 计算速度和航向
|
||||
if (positionHistory.size() >= 2) {
|
||||
// 使用所有可用的历史记录计算平均速度
|
||||
double totalDistance = 0;
|
||||
double totalTime = 0;
|
||||
// 找到合适的历史点来计算速度和航向
|
||||
const auto& curr = positionHistory.back();
|
||||
const auto& prev = positionHistory.front(); // 使用最早的历史点
|
||||
|
||||
for (size_t i = 1; i < positionHistory.size(); ++i) {
|
||||
const auto& prev = positionHistory[i-1];
|
||||
const auto& curr = positionHistory[i];
|
||||
// 计算距离和时间差
|
||||
double distance = calculateDistance(prev.geo, curr.geo); // 单位:米
|
||||
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);
|
||||
totalTime += static_cast<double>(curr.timestamp - prev.timestamp);
|
||||
}
|
||||
|
||||
if (totalTime > 0) {
|
||||
double avgSpeed = totalDistance / totalTime;
|
||||
if (isValidSpeed(avgSpeed)) {
|
||||
if (isValidSpeed(newSpeed)) {
|
||||
// 使用指数移动平均来平滑速度
|
||||
const double alpha = getSpeedSmoothingFactor();
|
||||
if (speed == 0) {
|
||||
speed = avgSpeed; // 第一次计算
|
||||
speed = newSpeed; // 第一次计算
|
||||
} 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);
|
||||
}
|
||||
|
||||
// 更新当前位置
|
||||
|
||||
@ -28,6 +28,7 @@ struct PositionRecord {
|
||||
// 移动物体基类
|
||||
class MovingObject {
|
||||
public:
|
||||
MovingObject() : heading(0.0), speed(0.0), timestamp(0) {} // 添加构造函数
|
||||
virtual ~MovingObject() = default; // 添加虚析构函数以支持 dynamic_cast
|
||||
|
||||
std::string id;
|
||||
|
||||
22
src/types/TrafficLightTypes.h
Normal file
22
src/types/TrafficLightTypes.h
Normal 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;
|
||||
}
|
||||
};
|
||||
@ -4,24 +4,54 @@
|
||||
#include <string>
|
||||
#include "VehicleData.h"
|
||||
|
||||
// 指令类型
|
||||
enum class CommandType {
|
||||
CONTINUE, // 继续运行
|
||||
STOP, // 停止
|
||||
SLOW_DOWN, // 减速
|
||||
TURN_LEFT, // 左转
|
||||
TURN_RIGHT // 右转
|
||||
SIGNAL, // 信号灯指令(最高优先级)
|
||||
ALERT, // 告警指令(中等优先级)
|
||||
WARNING, // 预警指令(最低优先级)
|
||||
RESUME // 恢复指令(最低优先级)
|
||||
};
|
||||
|
||||
// 信号灯状态
|
||||
enum class SignalState {
|
||||
RED, // 红灯
|
||||
GREEN // 绿灯
|
||||
};
|
||||
|
||||
// 指令原因
|
||||
enum class CommandReason {
|
||||
TRAFFIC_LIGHT, // 红绿灯控制
|
||||
AIRCRAFT_CROSSING, // 航空器交叉
|
||||
SPECIAL_VEHICLE, // 特勤车辆
|
||||
AIRCRAFT_PUSH, // 航空器推出
|
||||
RESUME_TRAFFIC // 恢复通行
|
||||
};
|
||||
|
||||
struct VehicleCommand {
|
||||
std::string vehicleId; // 车辆ID
|
||||
CommandType type; // 命令类型
|
||||
Vector2D avoidanceVector; // 避障向量
|
||||
CommandReason reason; // 命令原因
|
||||
SignalState signalState; // 信号灯状态(仅当 type 为 SIGNAL 时有效)
|
||||
Vector2D targetPosition; // 目标位置(如交叉路口)
|
||||
double speed{0.0}; // 目标速度
|
||||
double heading{0.0}; // 目标航向
|
||||
double distance{0.0}; // 与关键点的距离
|
||||
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
|
||||
@ -2,9 +2,11 @@
|
||||
#define AIRPORT_UTILS_LOGGER_H
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <mutex>
|
||||
|
||||
enum class LogLevel {
|
||||
DEBUG,
|
||||
@ -15,7 +17,14 @@ enum class LogLevel {
|
||||
|
||||
class Logger {
|
||||
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) {
|
||||
std::lock_guard<std::mutex> lock(getMutex());
|
||||
currentLevel() = level;
|
||||
}
|
||||
|
||||
@ -53,6 +62,16 @@ private:
|
||||
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>
|
||||
static void log(const char* level, Args... args) {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
@ -66,7 +85,13 @@ private:
|
||||
<< " [" << level << "] ";
|
||||
|
||||
(ss << ... << args) << std::endl;
|
||||
|
||||
std::lock_guard<std::mutex> lock(getMutex());
|
||||
std::cerr << ss.str();
|
||||
if (logFile().is_open()) {
|
||||
logFile() << ss.str();
|
||||
logFile().flush();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -49,4 +49,11 @@ void ControllableVehicles::loadConfig(const std::string& configFile) {
|
||||
} catch (const std::exception& e) {
|
||||
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: 发送控制命令
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "types/VehicleCommand.h"
|
||||
|
||||
struct ControllableVehicleConfig {
|
||||
std::string vehicleNo; // 车牌号
|
||||
@ -13,7 +14,7 @@ struct ControllableVehicleConfig {
|
||||
class ControllableVehicles {
|
||||
public:
|
||||
explicit ControllableVehicles(const std::string& configFile);
|
||||
~ControllableVehicles() = default;
|
||||
virtual ~ControllableVehicles() = default;
|
||||
|
||||
// 获取所有可控车辆配置
|
||||
const std::vector<ControllableVehicleConfig>& getVehicles() const;
|
||||
@ -24,6 +25,9 @@ public:
|
||||
// 检查车辆是否可控
|
||||
virtual bool isControllable(const std::string& vehicleNo) const;
|
||||
|
||||
// 发送控制命令
|
||||
virtual void sendCommand(const std::string& vehicleNo, const VehicleCommand& command);
|
||||
|
||||
private:
|
||||
std::vector<ControllableVehicleConfig> vehicles_;
|
||||
void loadConfig(const std::string& configFile);
|
||||
|
||||
@ -39,8 +39,8 @@ protected:
|
||||
TEST_F(BasicTypesTest, MovingObjectSpeedCalculation) {
|
||||
TestMovingObject obj;
|
||||
|
||||
// 获取当前时间戳
|
||||
time_t now = time(nullptr);
|
||||
// 使用毫秒级时间戳
|
||||
uint64_t now = 1000000;
|
||||
|
||||
// 第一个位置
|
||||
GeoPosition pos1{36.0, 120.0};
|
||||
@ -49,7 +49,12 @@ TEST_F(BasicTypesTest, MovingObjectSpeedCalculation) {
|
||||
|
||||
// 第二个位置(向东移动约90米/秒)
|
||||
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 公式计算的实际距离
|
||||
|
||||
// 检查航向角
|
||||
@ -61,15 +66,15 @@ TEST_F(BasicTypesTest, AircraftValidation) {
|
||||
Aircraft aircraft;
|
||||
aircraft.flightNo = "CES2501";
|
||||
|
||||
// 获取当前时间戳
|
||||
time_t now = time(nullptr);
|
||||
// 使用毫秒级时间戳
|
||||
uint64_t now = 1000000;
|
||||
|
||||
// 测试位置更新
|
||||
GeoPosition pos1{36.0, 120.0};
|
||||
aircraft.updateMotion(pos1, now);
|
||||
|
||||
GeoPosition pos2{36.0, 120.001}; // 约90米
|
||||
aircraft.updateMotion(pos2, now + 2); // 2秒后
|
||||
aircraft.updateMotion(pos2, now + 2000); // 2000毫秒后
|
||||
|
||||
// 速度应该约为45米/秒
|
||||
EXPECT_NEAR(aircraft.speed, 45.0, 2.0);
|
||||
@ -80,23 +85,44 @@ TEST_F(BasicTypesTest, VehicleValidation) {
|
||||
Vehicle vehicle;
|
||||
vehicle.vehicleNo = "VEH001";
|
||||
|
||||
// 获取当前时间戳
|
||||
time_t now = time(nullptr);
|
||||
// 使用毫秒级时间戳
|
||||
uint64_t now = 1000000;
|
||||
|
||||
// 测试位置更新
|
||||
GeoPosition pos1{36.0, 120.0};
|
||||
vehicle.updateMotion(pos1, now);
|
||||
|
||||
// 使用约22米的位置变化(小于50米的跳变限制)
|
||||
GeoPosition pos2{36.0002, 120.0}; // 约22米
|
||||
vehicle.updateMotion(pos2, now + 1.0); // 1秒后
|
||||
// 使用约9米的位置变化(小于10米的跳变限制)
|
||||
GeoPosition pos2{36.00008, 120.0}; // 约9米
|
||||
double distance = vehicle.calculateDistance(pos1, pos2);
|
||||
printf("Vehicle distance: %.2f meters\n", distance);
|
||||
|
||||
// 速度应该约为22米/秒
|
||||
EXPECT_NEAR(vehicle.speed, 22.0, 2.0);
|
||||
vehicle.updateMotion(pos2, now + 1000); // 1000毫秒后
|
||||
printf("Vehicle speed: %.2f m/s\n", vehicle.speed);
|
||||
|
||||
// 速度应该约为9米/秒(小于20米/秒的限制)
|
||||
EXPECT_NEAR(vehicle.speed, 9.0, 1.0);
|
||||
}
|
||||
|
||||
// 添加边界条件测试
|
||||
TEST_F(BasicTypesTest, VectorZeroMagnitude) {
|
||||
Vector2D v(0.0, 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);
|
||||
}
|
||||
@ -4,6 +4,7 @@ set(TEST_SOURCES
|
||||
BasicTypesTest.cpp
|
||||
HTTPDataSourceTest.cpp
|
||||
DataCollectorTest.cpp
|
||||
TrafficLightDetectorTest.cpp
|
||||
)
|
||||
|
||||
# 创建测试可执行文件
|
||||
|
||||
@ -46,7 +46,7 @@ protected:
|
||||
airportBounds_ = std::make_unique<MockAirportBounds>();
|
||||
mockControllableVehicles_ = std::make_unique<MockControllableVehicles>();
|
||||
|
||||
// 创建碰撞检测器
|
||||
// 创建冲突检测器
|
||||
detector_ = std::make_unique<CollisionDetector>(*airportBounds_, *mockControllableVehicles_);
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ protected:
|
||||
std::unique_ptr<CollisionDetector> detector_;
|
||||
};
|
||||
|
||||
// 测试可控车辆与航空器的碰撞检测
|
||||
// 测试可控车辆与航空器的冲突检测
|
||||
TEST_F(CollisionDetectorTest, DetectControllableVehicleAircraftCollision) {
|
||||
// 设置 Mock 期望 - 在创建数据之前设置
|
||||
EXPECT_CALL(*mockControllableVehicles_, isControllable("VEH001"))
|
||||
@ -83,22 +83,22 @@ TEST_F(CollisionDetectorTest, DetectControllableVehicleAircraftCollision) {
|
||||
detector_->updateTraffic({aircraft}, {vehicle});
|
||||
Logger::info("Updated traffic data");
|
||||
|
||||
// 执行碰撞检测
|
||||
// 执行冲突检测
|
||||
auto risks = detector_->detectCollisions();
|
||||
|
||||
// 验证结果
|
||||
ASSERT_EQ(risks.size(), 1); // 应该检测到一个碰撞风险
|
||||
ASSERT_EQ(risks.size(), 1); // 应该检测到一个冲突风险
|
||||
if (!risks.empty()) {
|
||||
const auto& risk = risks[0];
|
||||
EXPECT_EQ(risk.id1, "TEST001"); // 航空器ID
|
||||
EXPECT_EQ(risk.id2, "VEH001"); // 车辆ID
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 测试可控车辆与其他车辆的碰撞检测
|
||||
// 测试可控车辆与其他车辆的冲突检测
|
||||
TEST_F(CollisionDetectorTest, DetectControllableVehicleOtherVehicleCollision) {
|
||||
// 设置 Mock 期望
|
||||
EXPECT_CALL(*mockControllableVehicles_, isControllable("VEH001"))
|
||||
@ -122,20 +122,20 @@ TEST_F(CollisionDetectorTest, DetectControllableVehicleOtherVehicleCollision) {
|
||||
// 更新交通数据
|
||||
detector_->updateTraffic({}, {controlVehicle, otherVehicle});
|
||||
|
||||
// 执行碰撞检测
|
||||
// 执行冲突检测
|
||||
auto risks = detector_->detectCollisions();
|
||||
|
||||
// 验证结果
|
||||
ASSERT_EQ(risks.size(), 1); // 应该检测到一个碰撞风险
|
||||
ASSERT_EQ(risks.size(), 1); // 应该检测到一个冲突风险
|
||||
if (!risks.empty()) {
|
||||
EXPECT_EQ(risks[0].id1, "VEH001"); // 可控车辆ID
|
||||
EXPECT_EQ(risks[0].id2, "VEH002"); // 其他车辆ID
|
||||
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) {
|
||||
// 设置测试数据
|
||||
Vehicle vehicle1;
|
||||
@ -157,14 +157,14 @@ TEST_F(CollisionDetectorTest, NonControllableVehicleCollision) {
|
||||
// 更新交通数据
|
||||
detector_->updateTraffic({}, {vehicle1, vehicle2});
|
||||
|
||||
// 执行碰撞检测
|
||||
// 执行冲突检测
|
||||
auto risks = detector_->detectCollisions();
|
||||
|
||||
// 验证结果
|
||||
EXPECT_EQ(risks.size(), 0); // 非可控车辆之间的碰撞不应该被检测
|
||||
EXPECT_EQ(risks.size(), 0); // 非可控车辆之间的冲突不应该被检测
|
||||
}
|
||||
|
||||
// 测试多个可控车辆之间的碰撞检测
|
||||
// 测试多个可控车辆之间的冲突检测
|
||||
TEST_F(CollisionDetectorTest, MultipleControllableVehiclesCollision) {
|
||||
// 设置 Mock 期望 - 所有车辆都是可控的
|
||||
ON_CALL(*mockControllableVehicles_, isControllable(testing::_))
|
||||
@ -203,18 +203,18 @@ TEST_F(CollisionDetectorTest, MultipleControllableVehiclesCollision) {
|
||||
// 更新交通数据
|
||||
detector_->updateTraffic({}, vehicles);
|
||||
|
||||
// 执行碰撞检测
|
||||
// 执行冲突检测
|
||||
auto risks = detector_->detectCollisions();
|
||||
|
||||
// 验证结果
|
||||
EXPECT_EQ(risks.size(), 1); // 应该只检测到1个碰撞风险(VEH001和VEH002之间)
|
||||
EXPECT_EQ(risks.size(), 1); // 应该只检测到1个冲突风险(VEH001和VEH002之间)
|
||||
|
||||
if (risks.size() == 1) {
|
||||
// 验证碰撞风险的详细信息
|
||||
// 验证冲突风险的详细信息
|
||||
EXPECT_TRUE((risks[0].id1 == "VEH001" && risks[0].id2 == "VEH002") ||
|
||||
(risks[0].id1 == "VEH002" && risks[0].id2 == "VEH001"));
|
||||
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 ",
|
||||
vehicles.size(), " vehicles (including 3 controllable vehicles)");
|
||||
|
||||
// 执行碰撞检测并记录时间
|
||||
// 执行冲突检测并记录时间
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
auto risks = detector_->detectCollisions();
|
||||
@ -352,7 +352,7 @@ TEST_F(CollisionDetectorTest, PerformanceTest) {
|
||||
Logger::info("Found ", risks.size(), " risks");
|
||||
|
||||
// 验证结果
|
||||
ASSERT_GT(risks.size(), 0); // 应该检测到一些碰撞风险
|
||||
ASSERT_GT(risks.size(), 0); // 应该检测到一些冲突风险
|
||||
|
||||
// 验证性能要求
|
||||
EXPECT_LT(duration.count(), 100000); // 期望处理时间小于100ms
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include "collector/DataCollector.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "network/MessageTypes.h"
|
||||
#include "core/System.h"
|
||||
|
||||
// 创建一个 Mock DataSource 类
|
||||
class MockDataSource : public DataSource {
|
||||
@ -11,6 +13,7 @@ public:
|
||||
MOCK_METHOD(bool, isAvailable, (), (const, override));
|
||||
MOCK_METHOD(bool, fetchAircraftData, (std::vector<Aircraft>&), (override));
|
||||
MOCK_METHOD(bool, fetchVehicleData, (std::vector<Vehicle>&), (override));
|
||||
MOCK_METHOD(bool, fetchTrafficLightSignals, (std::vector<TrafficLightSignal>&), (override));
|
||||
};
|
||||
|
||||
class DataCollectorTest : public ::testing::Test {
|
||||
@ -48,6 +51,15 @@ protected:
|
||||
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::shared_ptr<MockDataSource> mockSource;
|
||||
};
|
||||
@ -167,4 +179,323 @@ TEST_F(DataCollectorTest, ErrorHandling) {
|
||||
|
||||
auto vehicles = collector->getVehicleData();
|
||||
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);
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include "network/HTTPDataSource.h"
|
||||
#include "utils/Logger.h"
|
||||
|
||||
class HTTPDataSourceTest : public ::testing::Test {
|
||||
protected:
|
||||
@ -9,9 +10,10 @@ protected:
|
||||
void SetUp() override {
|
||||
DataSourceConfig config;
|
||||
config.host = "localhost";
|
||||
config.port = 8080;
|
||||
config.port = 8081;
|
||||
config.aircraft_path = "/api/getCurrentFlightPositions";
|
||||
config.vehicle_path = "/api/getCurrentVehiclePositions";
|
||||
config.traffic_light_path = "/api/getTrafficLightSignals";
|
||||
source = std::make_unique<HTTPDataSource>(config);
|
||||
}
|
||||
|
||||
@ -35,23 +37,46 @@ TEST_F(HTTPDataSourceTest, DisconnectTest) {
|
||||
|
||||
TEST_F(HTTPDataSourceTest, FetchAircraftDataTest) {
|
||||
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) {
|
||||
std::vector<Vehicle> vehicles;
|
||||
source->connect();
|
||||
EXPECT_TRUE(source->connect()); // 确保连接成功
|
||||
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) {
|
||||
// 测试使用自定义路径创建数据源
|
||||
DataSourceConfig config;
|
||||
config.host = "localhost";
|
||||
config.port = 8080;
|
||||
config.port = 8081;
|
||||
config.aircraft_path = "/custom/path";
|
||||
auto customSource = std::make_unique<HTTPDataSource>(config);
|
||||
EXPECT_TRUE(customSource->connect());
|
||||
@ -61,8 +86,8 @@ TEST_F(HTTPDataSourceTest, CustomPathTest) {
|
||||
TEST_F(HTTPDataSourceTest, ConnectionFailureTest) {
|
||||
// 测试连接到不存在的服务器
|
||||
DataSourceConfig config;
|
||||
config.host = "invalid-host";
|
||||
config.port = 9999;
|
||||
config.host = "localhost"; // 使用本地地址
|
||||
config.port = 1; // 使用一个通常不会被使用的端口
|
||||
auto invalidSource = std::make_unique<HTTPDataSource>(config);
|
||||
EXPECT_FALSE(invalidSource->connect());
|
||||
EXPECT_FALSE(invalidSource->isAvailable());
|
||||
@ -72,9 +97,43 @@ TEST_F(HTTPDataSourceTest, FetchDataWithoutConnectionTest) {
|
||||
// 测试在未连接状态下获取数据
|
||||
std::vector<Aircraft> aircraft;
|
||||
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->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;
|
||||
// 假设服务器返回无效的 JSON 数据
|
||||
// EXPECT_FALSE(source->fetchAircraftData(aircraft));
|
||||
}
|
||||
|
||||
// 测试不同的超时配置
|
||||
TEST_F(HTTPDataSourceTest, DifferentTimeoutConfigTest) {
|
||||
// 测试读取超时小于连接超时
|
||||
{
|
||||
DataSourceConfig config;
|
||||
config.host = "localhost";
|
||||
config.port = 8081;
|
||||
config.timeout_ms = 5000; // 连接超时 5 秒
|
||||
config.read_timeout_ms = 2000; // 读取超时 2 秒
|
||||
|
||||
auto source = std::make_unique<HTTPDataSource>(config);
|
||||
EXPECT_FALSE(source->isAvailable()); // 只验证初始状态
|
||||
|
||||
std::vector<Aircraft> aircraft;
|
||||
EXPECT_FALSE(source->fetchAircraftData(aircraft));
|
||||
EXPECT_TRUE(aircraft.empty());
|
||||
}
|
||||
|
||||
// 测试读取超时等于连接超时
|
||||
{
|
||||
DataSourceConfig config;
|
||||
config.host = "localhost";
|
||||
config.port = 8081;
|
||||
config.timeout_ms = 5000; // 连接超时 5 秒
|
||||
config.read_timeout_ms = 5000; // 读取超时 5 秒
|
||||
|
||||
auto source = std::make_unique<HTTPDataSource>(config);
|
||||
EXPECT_FALSE(source->isAvailable()); // 只验证初始状态
|
||||
|
||||
std::vector<Aircraft> aircraft;
|
||||
EXPECT_FALSE(source->fetchAircraftData(aircraft));
|
||||
EXPECT_TRUE(aircraft.empty());
|
||||
}
|
||||
}
|
||||
|
||||
// 修改读取超时测试
|
||||
TEST_F(HTTPDataSourceTest, ReadTimeoutTest) {
|
||||
DataSourceConfig config;
|
||||
config.host = "localhost";
|
||||
config.port = 8081;
|
||||
config.timeout_ms = 5000; // 连接超时 5 秒
|
||||
config.read_timeout_ms = 2000; // 读取超时 2 秒
|
||||
|
||||
auto source = std::make_unique<HTTPDataSource>(config);
|
||||
EXPECT_FALSE(source->isAvailable()); // 验证初始状态
|
||||
|
||||
std::vector<Aircraft> aircraft;
|
||||
EXPECT_FALSE(source->fetchAircraftData(aircraft));
|
||||
EXPECT_TRUE(aircraft.empty());
|
||||
}
|
||||
|
||||
// 修改连接健康和超时测试
|
||||
TEST_F(HTTPDataSourceTest, ConnectionHealthAndTimeout) {
|
||||
DataSourceConfig config;
|
||||
config.host = "localhost";
|
||||
config.port = 8081;
|
||||
config.timeout_ms = 5000; // 连接超时 5 秒
|
||||
config.read_timeout_ms = 2000; // 读取超时 2 秒
|
||||
|
||||
auto source = std::make_unique<HTTPDataSource>(config);
|
||||
EXPECT_FALSE(source->isAvailable()); // 验证初始状态
|
||||
|
||||
std::vector<Aircraft> aircraft;
|
||||
EXPECT_FALSE(source->fetchAircraftData(aircraft));
|
||||
EXPECT_TRUE(aircraft.empty());
|
||||
}
|
||||
145
tests/TrafficLightDetectorTest.cpp
Normal file
145
tests/TrafficLightDetectorTest.cpp
Normal 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);
|
||||
}
|
||||
@ -4,96 +4,208 @@ import math
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# 基准坐标(青岛胶东机场)
|
||||
BASE_LAT = 36.361999
|
||||
BASE_LON = 120.088003
|
||||
# 坐标转换常数(粗略计算:1度约等于111km,所以1米约等于0.000009度)
|
||||
METERS_TO_DEG = 0.000009
|
||||
|
||||
# 模拟数据
|
||||
# 距离配置(米)
|
||||
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 = [
|
||||
{
|
||||
"flightNo": "CES2501", # 在跑道上
|
||||
"latitude": BASE_LAT,
|
||||
"longitude": BASE_LON + 0.001, # 在跑道中间位置
|
||||
"time": int(time.time()),
|
||||
"direction": 1 # 1表示向东,-1表示向西
|
||||
},
|
||||
{
|
||||
"flightNo": "CES2502", # 在滑行道上
|
||||
"latitude": BASE_LAT - 0.001,
|
||||
"longitude": BASE_LON,
|
||||
"time": int(time.time()),
|
||||
"direction": 1 # 1表示向东,-1表示向西
|
||||
"flightNo": "AC001", # 航空器
|
||||
"latitude": EAST_INTERSECTION["latitude"] - (DIST_150M * METERS_TO_DEG), # 东路口南150米
|
||||
"longitude": EAST_INTERSECTION["longitude"],
|
||||
"time": int(time.time() * 1000),
|
||||
"direction": 1, # 1表示向北
|
||||
"speed": 50.0 # 滑行速度 50km/h
|
||||
}
|
||||
]
|
||||
|
||||
# 车辆数据
|
||||
vehicle_data = [
|
||||
{
|
||||
"vehicleNo": "VEH001", # 南北方向移动
|
||||
"latitude": BASE_LAT - 0.0005,
|
||||
"longitude": BASE_LON + 0.001,
|
||||
"time": int(time.time()),
|
||||
"direction": 1, # 1表示向北,-1表示向南
|
||||
"phase": 0 # 用于控制循环运动
|
||||
"vehicleNo": "QN001", # 无人车1(西路口)
|
||||
"latitude": WEST_INTERSECTION["latitude"] + (DIST_50M * METERS_TO_DEG), # 西路口北50米
|
||||
"longitude": WEST_INTERSECTION["longitude"],
|
||||
"time": int(time.time() * 1000),
|
||||
"direction": 1, # 1表示向东
|
||||
"speed": 36.0 # 36km/h
|
||||
},
|
||||
{
|
||||
"vehicleNo": "VEH002", # 南北方向移动
|
||||
"latitude": BASE_LAT + 0.0005,
|
||||
"longitude": BASE_LON + 0.001,
|
||||
"time": int(time.time()),
|
||||
"direction": -1, # 1表示向北,-1表示向南
|
||||
"phase": math.pi # 与VEH001相位差180度
|
||||
"vehicleNo": "QN002", # 无人车2(东路口)
|
||||
"latitude": EAST_INTERSECTION["latitude"],
|
||||
"longitude": EAST_INTERSECTION["longitude"] - (DIST_150M * METERS_TO_DEG), # 东路口西150米
|
||||
"time": int(time.time() * 1000),
|
||||
"direction": 1, # 1表示向东
|
||||
"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
|
||||
}
|
||||
]
|
||||
|
||||
# 定义运动范围
|
||||
LAT_RANGE = 0.001 # 约110米
|
||||
LON_RANGE = 0.002 # 约220米
|
||||
VEHICLE_SPEED = 0.00005 # 每次更新的位置变化
|
||||
AIRCRAFT_SPEED = 0.00002 # 每次更新的位置变化
|
||||
def update_vehicle_position(vehicle, elapsed_time):
|
||||
"""更新车辆位置"""
|
||||
# 计算一次更新的移动距离(度)
|
||||
speed_mps = vehicle["speed"] * 1000 / 3600 # 速度转换为米/秒
|
||||
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')
|
||||
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:
|
||||
aircraft["time"] = current_time
|
||||
|
||||
# 更新经度位置
|
||||
new_lon = aircraft["longitude"] + (AIRCRAFT_SPEED * aircraft["direction"])
|
||||
|
||||
# 检查是否需要改变方向
|
||||
if new_lon > BASE_LON + LON_RANGE:
|
||||
aircraft["direction"] = -1 # 向西移动
|
||||
elif new_lon < BASE_LON - LON_RANGE:
|
||||
aircraft["direction"] = 1 # 向东移动
|
||||
|
||||
aircraft["longitude"] = new_lon
|
||||
# 只在达到更新间隔时更新位置
|
||||
if elapsed_time >= UPDATE_INTERVAL:
|
||||
for aircraft in aircraft_data:
|
||||
update_aircraft_position(aircraft, UPDATE_INTERVAL)
|
||||
aircraft["time"] = int(current_time * 1000)
|
||||
last_update_time = current_time
|
||||
|
||||
return jsonify(aircraft_data)
|
||||
|
||||
@app.route('/api/getCurrentVehiclePositions')
|
||||
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:
|
||||
vehicle["time"] = current_time
|
||||
|
||||
# 更新相位
|
||||
vehicle["phase"] += 0.1 # 调整此值可以改变运动速度
|
||||
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)
|
||||
# 只在达到更新间隔时更新位置
|
||||
if elapsed_time >= UPDATE_INTERVAL:
|
||||
for vehicle in vehicle_data:
|
||||
update_vehicle_position(vehicle, UPDATE_INTERVAL)
|
||||
vehicle["time"] = int(current_time * 1000)
|
||||
last_update_time = current_time
|
||||
|
||||
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
|
||||
def add_cors_headers(response):
|
||||
response.headers['Access-Control-Allow-Origin'] = '*'
|
||||
@ -102,4 +214,4 @@ def add_cors_headers(response):
|
||||
return response
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='localhost', port=8080, debug=True)
|
||||
app.run(host='localhost', port=8081, debug=True)
|
||||
@ -5,7 +5,7 @@
|
||||
<style>
|
||||
#messages {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
height: 800px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #ccc;
|
||||
margin-bottom: 10px;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user