diff --git a/docs/official_api.md b/docs/official_api.md index 0b49735..5e0e2f2 100644 --- a/docs/official_api.md +++ b/docs/official_api.md @@ -61,66 +61,62 @@ ### 2.1 无人车控制指令 -2.1.1 接口地址: +2.1.1 接口地址: 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 } - -``` diff --git a/src/core/System.cpp b/src/core/System.cpp index ba4b76c..94f0808 100644 --- a/src/core/System.cpp +++ b/src/core/System.cpp @@ -399,7 +399,8 @@ void System::processCollisions(const std::vector& 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::system_clock::now().time_since_epoch()).count(); // 设置目标位置(冲突对象的位置) const MovingObject* target = findVehicle(otherId); @@ -429,7 +430,8 @@ void System::processCollisions(const std::vector& 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::system_clock::now().time_since_epoch()).count(); // 设置目标位置 const MovingObject* target = findVehicle(otherId); @@ -486,7 +488,8 @@ void System::processCollisions(const std::vector& 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::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::system_clock::now().time_since_epoch()).count()} }; // 广播冲突预警消息 @@ -797,7 +801,8 @@ void System::checkUnmannedVehicleSafetyZones(const std::vector& 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::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::system_clock::now().time_since_epoch()).count(); // 查找触发安全区的目标物体 const MovingObject* target = nullptr; diff --git a/src/detector/TrafficLightDetector.cpp b/src/detector/TrafficLightDetector.cpp index d2d0e07..d007603 100644 --- a/src/detector/TrafficLightDetector.cpp +++ b/src/detector/TrafficLightDetector.cpp @@ -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::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::system_clock::now().time_since_epoch()).count(); // 添加路口信息 const Intersection* intersection = intersection_config_.findByTrafficLightId(current_signal_.trafficLightId); diff --git a/src/network/HTTPClient.cpp b/src/network/HTTPClient.cpp index 46293e1..0935887 100644 --- a/src/network/HTTPClient.cpp +++ b/src/network/HTTPClient.cpp @@ -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(); + std::string msg = response["msg"].get(); + + 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; diff --git a/tests/DataCollectorTest.cpp b/tests/DataCollectorTest.cpp index f9bbc54..beebd76 100644 --- a/tests/DataCollectorTest.cpp +++ b/tests/DataCollectorTest.cpp @@ -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::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::system_clock::now().time_since_epoch()).count(); return v; } @@ -56,7 +58,8 @@ protected: TrafficLightSignal signal; signal.trafficLightId = id; signal.status = static_cast(state); - signal.timestamp = std::chrono::system_clock::now().time_since_epoch().count(); + signal.timestamp = std::chrono::duration_cast( + 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) { - std::this_thread::sleep_for(std::chrono::milliseconds(60)); // 睡眠超过读取超时时间但小于连���超时 + std::this_thread::sleep_for(std::chrono::milliseconds(60)); // 睡眠超过读取超时时间但小于连接超时 return false; })); diff --git a/tools/mock_server.py b/tools/mock_server.py index 5cd6b8b..def421a 100644 --- a/tools/mock_server.py +++ b/tools/mock_server.py @@ -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):