修改了无人车的控制 API

This commit is contained in:
Tian jianyong 2025-01-10 11:56:30 +08:00
parent 6cab347c36
commit 6ac931e802
6 changed files with 141 additions and 95 deletions

View File

@ -61,66 +61,62 @@
### 2.1 无人车控制指令
2.1.1 接口地址: <http://IP:端口/openApi/emergency_control>
2.1.1 接口地址: <http://127.0.0.1:31140/api/VehicleCommandInfo>
2.1.2 请求方法POST
2.1.3 请求参数:
| 序号 | 字段名字 | 是否必选 | 类型 | 说明 |
| 字段名称 | 类型 | 是否必填 | 说明 |
|---------|------|----------|------|
| transId | string | 是 | 消息唯一 id消息的唯一标识符 |
| timestamp | long | 是 | 时间戳 |
| vehicleID | string | 是 | 车辆 ID |
| commandType | string | 是 | 指令类型ALERT告警指令SIGNAL信号灯指令WARNING预警指令RESUME恢复指令 |
| commandReason | string | 是 | 指令原因TRAFFIC_LIGHT红绿灯控制AIRCRAFT_CROSSING航空器交叉SPECIAL_VEHICLE特勤车辆AIRCRAFT_PUSH航空器推出RESUME_TRAFFIC恢复通行 |
| signalState | string | 否 | 信号灯状态(仅当 commandType 为 SIGNAL 时有效RED红灯GREEN绿灯 |
| intersectionId | string | 否 | 路口 ID仅当 commandType 为 SIGNAL 时有效) |
| latitude | double | 是 | 目标位置纬度(路口/航空器/特勤车) |
| longitude | double | 是 | 目标位置经度(路口/航空器/特勤车) |
| relativeSpeed | double | 否 | 相对速度(仅当 commandType 为 ALERT/WARNING 时有效) |
| relativeMotionX | double | 否 | 相对运动 X 分量(仅当 commandType 为 ALERT/WARNING 时有效) |
| relativeMotionY | double | 否 | 相对运动 Y 分量(仅当 commandType 为 ALERT/WARNING 时有效) |
| minDistance | double | 否 | 最小距离(仅当 commandType 为 ALERT/WARNING 时有效) |
|-----|------|------|----------|----------|
| 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"
示例:
requestData:
{
"messageUniqueId": "68f79d1a-e27f-11ed-b28c-2cf05d9c2649",
"timestamp": 1736175610000,
"vehicleID": "A001",
"commandType": "SIGNAL",
"commandReason": "TRAFFIC_LIGHT",
"signalState":"RED",
"intersectionId":"002",
"latitude": 343.23,
"longitude": 343.23,
"relativeSpeed": 3,
"relativeMotionX": 2002.12,
"relativeMotionY":100.12,
"minDistance":10.5
}
```
返回值:
2.1.5 返回格式:以 JSON 格式返回数据
| 字段名 | 类型 | 是否必须 | 描述 |
|---------|------|----------|------|
| transId | string | 是 | 消息唯一id消息的唯一标识符与请求id一致 |
| timestamp | long | 是 | 时间戳 |
| code | int | 是 | 接口返回的状态码200 请求成功400 请求失败并在msg内返回原因 |
| msg | string | 是 | 接口成功/失败的原因或者附加提示信息 |
``` http
response : {
"code" : 200,
"msg" : "success",
"error" : ""
示例:
responseData:
{
"code": 200,
"msg": "success",
"transId": "68f79d1a-e27f-11ed-b28c-2cf05d9c2649",
"timestamp": 1736175610
}
```

View File

@ -399,7 +399,8 @@ void System::processCollisions(const std::vector<CollisionRisk>& risks) {
cmd.reason = otherId.substr(0, 2) == "AC" ?
CommandReason::AIRCRAFT_CROSSING :
CommandReason::SPECIAL_VEHICLE;
cmd.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
cmd.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
// 设置目标位置(冲突对象的位置)
const MovingObject* target = findVehicle(otherId);
@ -429,7 +430,8 @@ void System::processCollisions(const std::vector<CollisionRisk>& risks) {
cmd.reason = otherId.substr(0, 2) == "AC" ?
CommandReason::AIRCRAFT_CROSSING :
CommandReason::SPECIAL_VEHICLE;
cmd.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
cmd.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
// 设置目标位置
const MovingObject* target = findVehicle(otherId);
@ -486,7 +488,8 @@ void System::processCollisions(const std::vector<CollisionRisk>& risks) {
cmd.vehicleId = vehicleId;
cmd.type = CommandType::RESUME;
cmd.reason = CommandReason::RESUME_TRAFFIC;
cmd.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
cmd.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
broadcastVehicleCommand(cmd);
controllableVehicles_->sendCommand(vehicleId, cmd);
@ -605,7 +608,8 @@ void System::broadcastCollisionWarning(const CollisionRisk& risk) {
{"distance", risk.distance},
{"relativeSpeed", risk.relativeSpeed},
{"warningLevel", getRiskLevelString(risk.level)},
{"timestamp", std::chrono::system_clock::now().time_since_epoch().count()}
{"timestamp", std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count()}
};
// 广播冲突预警消息
@ -797,7 +801,8 @@ void System::checkUnmannedVehicleSafetyZones(const std::vector<Vehicle>& vehicle
cmd.vehicleId = vehicle.id;
cmd.type = CommandType::RESUME;
cmd.reason = CommandReason::RESUME_TRAFFIC;
cmd.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
cmd.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
broadcastVehicleCommand(cmd);
controllableVehicles_->sendCommand(vehicle.id, cmd);
@ -828,7 +833,8 @@ bool System::handleSafetyZoneRisk(const Vehicle& vehicle,
cmd.reason = zone->getType() == SafetyZoneType::AIRCRAFT ?
CommandReason::AIRCRAFT_CROSSING :
CommandReason::SPECIAL_VEHICLE;
cmd.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
cmd.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
// 查找触发安全区的目标物体
const MovingObject* target = nullptr;

View File

@ -49,7 +49,8 @@ void TrafficLightDetector::sendStopCommand(const std::string& vehicleNo) {
cmd.type = CommandType::SIGNAL;
cmd.reason = CommandReason::TRAFFIC_LIGHT;
cmd.signalState = SignalState::RED;
cmd.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
cmd.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
// 添加路口信息
const Intersection* intersection = intersection_config_.findByTrafficLightId(current_signal_.trafficLightId);
@ -70,7 +71,8 @@ void TrafficLightDetector::sendContinueCommand(const std::string& vehicleNo) {
cmd.type = CommandType::SIGNAL;
cmd.reason = CommandReason::TRAFFIC_LIGHT;
cmd.signalState = SignalState::GREEN;
cmd.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
cmd.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
// 添加路口信息
const Intersection* intersection = intersection_config_.findByTrafficLightId(current_signal_.trafficLightId);

View File

@ -32,12 +32,18 @@ bool HTTPClient::sendCommand(const std::string& ip, int port, const VehicleComma
// 构造请求URL
std::stringstream url;
url << "http://" << ip << ":" << port << "/api/vehicle/command";
url << "http://" << ip << ":" << port << "/api/VehicleCommandInfo";
// 构造请求体
// 生成消息唯一 id
std::stringstream transId;
transId << std::hex << std::chrono::system_clock::now().time_since_epoch().count();
// 构造请求体(必填字段)
nlohmann::json request = {
{"vehicleId", command.vehicleId},
{"type", [&]() {
{"transId", transId.str()},
{"timestamp", command.timestamp},
{"vehicleID", command.vehicleId},
{"commandType", [&]() {
switch (command.type) {
case CommandType::SIGNAL: return "SIGNAL";
case CommandType::ALERT: return "ALERT";
@ -46,7 +52,7 @@ bool HTTPClient::sendCommand(const std::string& ip, int port, const VehicleComma
default: return "UNKNOWN";
}
}()},
{"reason", [&]() {
{"commandReason", [&]() {
switch (command.reason) {
case CommandReason::TRAFFIC_LIGHT: return "TRAFFIC_LIGHT";
case CommandReason::AIRCRAFT_CROSSING: return "AIRCRAFT_CROSSING";
@ -56,19 +62,23 @@ bool HTTPClient::sendCommand(const std::string& ip, int port, const VehicleComma
default: return "UNKNOWN";
}
}()},
{"timestamp", command.timestamp}
{"latitude", command.latitude},
{"longitude", command.longitude}
};
// 添加可选字段
if (command.type == CommandType::SIGNAL) {
// 信号灯指令的可选字段
request["signalState"] = command.signalState == SignalState::RED ? "RED" : "GREEN";
request["intersectionId"] = command.intersectionId;
}
if (command.type != CommandType::RESUME) {
request["targetPosition"] = {
{"latitude", command.latitude},
{"longitude", command.longitude}
};
if (command.type == CommandType::ALERT || command.type == CommandType::WARNING) {
// 告警/预警指令的可选字段
request["relativeSpeed"] = command.relativeSpeed;
request["relativeMotionX"] = command.relativeMotionX;
request["relativeMotionY"] = command.relativeMotionY;
request["minDistance"] = command.minDistance;
}
std::string request_body = request.dump();
@ -100,8 +110,23 @@ bool HTTPClient::sendCommand(const std::string& ip, int port, const VehicleComma
curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &http_code);
if (http_code == 200) {
Logger::debug("Command sent successfully: ", request_body);
return true;
try {
// 解析响应
nlohmann::json response = nlohmann::json::parse(response_buffer_);
int code = response["code"].get<int>();
std::string msg = response["msg"].get<std::string>();
if (code == 200) {
Logger::debug("Command sent successfully: ", request_body);
return true;
} else {
Logger::error("Command failed with code: ", code, " message: ", msg);
return false;
}
} catch (const std::exception& e) {
Logger::error("Failed to parse response: ", e.what());
return false;
}
} else {
Logger::error("Command failed with HTTP code: ", http_code, " response: ", response_buffer_);
return false;

View File

@ -37,7 +37,8 @@ protected:
a.geo.latitude = lat;
a.geo.longitude = lon;
a.altitude = 5.0;
a.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
a.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
return a;
}
@ -47,7 +48,8 @@ protected:
v.vehicleNo = id;
v.geo.latitude = lat;
v.geo.longitude = lon;
v.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
v.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
return v;
}
@ -56,7 +58,8 @@ protected:
TrafficLightSignal signal;
signal.trafficLightId = id;
signal.status = static_cast<SignalStatus>(state);
signal.timestamp = std::chrono::system_clock::now().time_since_epoch().count();
signal.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
return signal;
}
@ -483,7 +486,7 @@ TEST_F(DataCollectorTest, ReadTimeoutMechanism) {
// 模拟读取超时
EXPECT_CALL(*mockSource, fetchAircraftData(::testing::_))
.WillRepeatedly(::testing::Invoke([](std::vector<Aircraft>& aircraft) {
std::this_thread::sleep_for(std::chrono::milliseconds(60)); // 睡眠超过读取超时时间但小于连<EFBFBD><EFBFBD><EFBFBD>超时
std::this_thread::sleep_for(std::chrono::milliseconds(60)); // 睡眠超过读取超时时间但小于连超时
return false;
}));

View File

@ -283,13 +283,13 @@ def calculate_distance_to_target(vehicle, target_lat, target_lon):
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
return R * c
@app.route('/api/vehicle/command', methods=['POST'])
@app.route('/api/VehicleCommandInfo', methods=['POST'])
def handle_vehicle_command():
try:
data = request.json
vehicle_id = data.get("vehicleId")
command_type = data.get("type", "").upper()
reason = data.get("reason", "").upper()
vehicle_id = data.get("vehicleID")
command_type = data.get("commandType", "").upper()
reason = data.get("commandReason", "").upper()
signal_state = data.get("signalState", "").upper()
target_lat = data.get("latitude", None)
target_lon = data.get("longitude", None)
@ -307,8 +307,10 @@ def handle_vehicle_command():
if not vehicle_state:
print(f"未找到车辆: {vehicle_id}")
return jsonify({
"status": "error",
"message": f"Vehicle {vehicle_id} not found"
"code": 404,
"msg": f"Vehicle {vehicle_id} not found",
"transId": data.get("transId"),
"timestamp": int(time.time() * 1000)
}), 404
# 打印当前车辆状态
@ -320,15 +322,19 @@ def handle_vehicle_command():
if command_type not in ["RED", "GREEN"]:
print(f"特勤车辆忽略非红绿灯指令: {vehicle_id}")
return jsonify({
"status": "ok",
"message": "Special vehicle only responds to traffic light signals"
"code": 200,
"msg": "Special vehicle only responds to traffic light signals",
"transId": data.get("transId"),
"timestamp": int(time.time() * 1000)
})
# 更新红绿灯状态和指令状态
vehicle_state.update_command(command_type, target_lat, target_lon)
print(f"特勤车 {vehicle_id} 更新状态: command={command_type}, traffic_light={vehicle_state.traffic_light_state}")
return jsonify({
"status": "ok",
"message": "Traffic light state updated"
"code": 200,
"msg": "Traffic light state updated",
"transId": data.get("transId"),
"timestamp": int(time.time() * 1000)
})
# 检查指令优先级并添加详细日志
@ -340,8 +346,10 @@ def handle_vehicle_command():
print(f"指令优先级过低: vehicle={vehicle_id}, current_priority={vehicle_state.command_priority}, "
f"command={command_type}")
return jsonify({
"status": "warning",
"message": "Command priority too low"
"code": 400,
"msg": "Command priority too low",
"transId": data.get("transId"),
"timestamp": int(time.time() * 1000)
})
# 处理不同类型的指令
@ -364,7 +372,7 @@ def handle_vehicle_command():
vehicle_state.target_lon = target_lon
# 立即更新车辆速度
current_vehicle["speed"] = 0
elif command_type in ["RED", "GREEN"]:
print(f"执行红绿灯指令: vehicle_id={vehicle_id}, state={command_type}")
# 红绿灯状态的更新由 update_command 处理
@ -406,10 +414,12 @@ def handle_vehicle_command():
f"target_speed={vehicle_state.target_speed}, brake_mode={vehicle_state.brake_mode}")
return jsonify({
"status": "ok",
"message": "Command executed successfully"
"code": 200,
"msg": "Command executed successfully",
"transId": data.get("transId"),
"timestamp": int(time.time() * 1000)
})
# 更新车辆状态
vehicle_state.update_command(command_type, target_lat, target_lon)
vehicle_state.command_reason = reason
@ -426,15 +436,19 @@ def handle_vehicle_command():
f"target_speed={vehicle_state.target_speed}, brake_mode={vehicle_state.brake_mode}")
return jsonify({
"status": "ok",
"message": "Command executed successfully"
"code": 200,
"msg": "Command executed successfully",
"transId": data.get("transId"),
"timestamp": int(time.time() * 1000)
})
except Exception as e:
print(f"Error handling vehicle command: {str(e)}")
return jsonify({
"status": "error",
"message": str(e)
"code": 500,
"msg": str(e),
"transId": data.get("transId", ""),
"timestamp": int(time.time() * 1000)
}), 500
def calculate_distance_to_intersection(vehicle, intersection):