修改了无人车的控制 API
This commit is contained in:
parent
6cab347c36
commit
6ac931e802
@ -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
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}));
|
||||
|
||||
|
||||
@ -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):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user