Conversation
Replaced mission callbacks with new fleet manager action
As we are only having a centralized fleet manager, we don't need specific mission callbacks anymore.
Handlers to share with fleet manager and planners
Will be added instead into fleet manager
Following new mission request structure
Responding to the REST API client with the robot "points" from the requested mission. This feature is useful for the coverage planner mission, for the UI to display the points.
Possibility to add multiple obstacles at once, and possibility to keep obstacles when setting a new safety area border.
Added type and UUID useful for synchronization with the UI backend.
…r of borders and obstacles
Useful for mocking the action feedback for UI display using a rosbag
Subscribing and sending of telemetry data for sensor info
Added "robot_name" into SensorInfo telemetry topic
There was a problem hiding this comment.
Pull request overview
This PR implements a major refactoring of the IROC Bridge mission handling system to support a unified fleet manager architecture. The changes consolidate multiple mission-specific action clients into a single generic fleet manager client and introduce new getter endpoints for retrieving configuration data.
- Unified mission handling through a single
IROCFleetManagerActionclient replacing separate waypoint, coverage, and autonomy test clients - Added getter endpoints for world origin, safety borders, obstacles, and mission data
- Enhanced obstacle handling to support multiple obstacles in a single request
- Added sensor info parsing and telemetry support
Reviewed changes
Copilot reviewed 18 out of 20 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| src/iroc_bridge.cpp | Major refactoring consolidating action clients, adding getter endpoints, sensor info parsing, and multiple obstacle support |
| package.xml | Removed dependency on iroc_mission_handler |
| CMakeLists.txt | Removed iroc_mission_handler from catkin dependencies |
| launch/iroc_bridge.launch | Updated remappings to use unified mission action client and added new service remappings |
| config/config.yaml | Restructured configuration under 'iroc_bridge' namespace |
| json_examples/*.json | Updated JSON examples to match new mission format with type, uuid, and details fields |
| docs/README.md | Comprehensive documentation updates covering new endpoints, mission format, and subtask system |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| size_t point_index = 0; | ||
|
|
||
| for (size_t i = 0; i < number_of_obstacles; i++) { | ||
| int number_of_vertices = obstacles.rows.at(i); | ||
| double max_z = obstacles.max_z.at(i); | ||
| double min_z = obstacles.min_z.at(i); | ||
|
|
||
| // Extract the points for the obstacle | ||
| json points_list = json::list(); | ||
| for (int j = 0; j < number_of_vertices; j++) { | ||
| if (point_index < obstacles.data.size()) { | ||
| json point = {{"x", obstacles.data.at(point_index).x}, {"y", obstacles.data.at(point_index).y}}; | ||
| points_list[j] = std::move(point); | ||
| point_index++; // Move to next point | ||
| } | ||
| ref_points.push_back(waypoint); | ||
| } | ||
| mission_robot.points = ref_points; | ||
|
|
||
| // Debugging/logging | ||
| for (const auto& point : ref_points) { | ||
| ROS_INFO_STREAM("[IROCBridge]: Robot " << robot_name << " point: " << point.reference_point.position.x << ", " << point.reference_point.position.y << ", " << point.reference_point.position.z); | ||
| } |
There was a problem hiding this comment.
The variable 'point_index' could potentially exceed the size of 'obstacles.data' if the sum of all 'number_of_vertices' values is greater than the actual data size. Consider adding bounds checking to prevent out-of-bounds access.
| json_msg["message"] = get_safety_border_service.response.message; | ||
|
|
||
| json points = json::list(); | ||
| auto vector_points = get_safety_border_service.response.border.points; |
There was a problem hiding this comment.
There's a trailing whitespace at the end of line 1417. Please remove it for consistency.
| auto vector_points = get_safety_border_service.response.border.points; | |
| auto vector_points = get_safety_border_service.response.border.points; |
|
|
||
|
|
There was a problem hiding this comment.
There's a trailing whitespace at the end of line 55 in the launch file. Please remove it for consistency.
| std::string robot_name = robot_goals.at(i).name; | ||
| auto points = robot_goals.at(i).points; | ||
| int frame_id = robot_goals.at(i).frame_id; | ||
| int height_id = robot_goals.at(i).height_id; | ||
|
|
||
| // Extract the points | ||
| json points_list = json::list(); | ||
| for (int j = 0; j < points.size(); j++) { | ||
| mrs_msgs::Reference reference = points.at(j).reference; | ||
| json point = {{"x", reference.position.x}, {"y", reference.position.y}, {"z", reference.position.z}, {"heading", reference.heading}}; | ||
| points_list[j] = std::move(point); | ||
| } | ||
| json mission = {{"points", points_list}, {"frame_id", frame_id}, {"height_id", height_id}}; | ||
| json robot_data = {{"robot", robot_name}, {"success", true}, {"message", "Mission loaded successfully"}, {"mission", mission}}; | ||
| robots_data[i] = std::move(robot_data); |
There was a problem hiding this comment.
The loop at line 1129 uses 'points.size()' directly in the condition, but 'points' is obtained from 'robot_goals.at(i).points'. If this throws an exception or returns an invalid reference, the loop could fail. Consider adding validation that 'i' is within bounds of 'robot_goals' before accessing.
| std::string robot_name = robot_goals.at(i).name; | |
| auto points = robot_goals.at(i).points; | |
| int frame_id = robot_goals.at(i).frame_id; | |
| int height_id = robot_goals.at(i).height_id; | |
| // Extract the points | |
| json points_list = json::list(); | |
| for (int j = 0; j < points.size(); j++) { | |
| mrs_msgs::Reference reference = points.at(j).reference; | |
| json point = {{"x", reference.position.x}, {"y", reference.position.y}, {"z", reference.position.z}, {"heading", reference.heading}}; | |
| points_list[j] = std::move(point); | |
| } | |
| json mission = {{"points", points_list}, {"frame_id", frame_id}, {"height_id", height_id}}; | |
| json robot_data = {{"robot", robot_name}, {"success", true}, {"message", "Mission loaded successfully"}, {"mission", mission}}; | |
| robots_data[i] = std::move(robot_data); | |
| try { | |
| std::string robot_name = robot_goals.at(i).name; | |
| auto points = robot_goals.at(i).points; | |
| int frame_id = robot_goals.at(i).frame_id; | |
| int height_id = robot_goals.at(i).height_id; | |
| // Extract the points | |
| json points_list = json::list(); | |
| for (int j = 0; j < points.size(); j++) { | |
| mrs_msgs::Reference reference = points.at(j).reference; | |
| json point = {{"x", reference.position.x}, {"y", reference.position.y}, {"z", reference.position.z}, {"heading", reference.heading}}; | |
| points_list[j] = std::move(point); | |
| } | |
| json mission = {{"points", points_list}, {"frame_id", frame_id}, {"height_id", height_id}}; | |
| json robot_data = {{"robot", robot_name}, {"success", true}, {"message", "Mission loaded successfully"}, {"mission", mission}}; | |
| robots_data[i] = std::move(robot_data); | |
| } catch (const std::out_of_range& e) { | |
| // Handle out-of-range error gracefully | |
| // Optionally log the error or add an error entry | |
| continue; | |
| } |
| json telemetry_msg = { | ||
| {"robot_name", robot_name}, | ||
| {"type", sensor_info->type}, | ||
| {"details", json_msg}}; |
There was a problem hiding this comment.
The parseSensorInfo function attempts to create a wvalue JSON object from an rvalue, which may lead to issues. The 'json_msg' loaded from crow::json::load is an rvalue and cannot be directly assigned to a wvalue. Consider converting the rvalue to a string and then assigning it, or use the rvalue directly in the telemetry message construction.
| {"details", json_msg}}; | |
| {"details", json_msg.dump()}}; |
| const auto url = param_loader.loadParam2<std::string>("url"); | ||
| const auto client_port = param_loader.loadParam2<int>("client_port"); | ||
| const auto server_port = param_loader.loadParam2<int>("server_port"); | ||
|
|
There was a problem hiding this comment.
There's a trailing whitespace at the end of line 303. Please remove it for consistency.
| json_msg["message"] = get_obstacles_service.response.message; | ||
| int number_of_obstacles = get_obstacles_service.response.obstacles.rows.size(); | ||
| auto obstacles = get_obstacles_service.response.obstacles; | ||
| json obstacles_json_list = json::list(); |
There was a problem hiding this comment.
There's a trailing whitespace at the end of line 1568. Please remove it for consistency.
| json obstacles_json_list = json::list(); | |
| json obstacles_json_list = json::list(); |
| auto json = resultToJson(result); | ||
| const auto message = result->message; | ||
| auto json = resultToJson(result); | ||
| ROS_WARN("[IROCBridge]: %s", message.c_str()); | ||
| return crow::response(crow::status::BAD_REQUEST, json); | ||
| } | ||
| else { | ||
| ROS_INFO("[IROCBridge]: Mission received successfully"); | ||
| auto json = successMissionJson(mission_robots); | ||
| return crow::response(crow::status::CREATED, json); | ||
|
|
||
| // TODO to replace with proper action response in ROS2 | ||
| iroc_fleet_manager::GetMissionPointsSrv get_mission_data_service; | ||
| const auto resp = | ||
| callService<iroc_fleet_manager::GetMissionPointsSrv>(sc_get_mission_data_, get_mission_data_service.request, get_mission_data_service.response); | ||
|
|
||
| if (!resp.success) { | ||
| json error_response; | ||
| error_response["message"] = resp.message; | ||
| error_response["success"] = false; | ||
| error_response["robot_data"] = json::list(); | ||
| ROS_WARN_STREAM("[IROCBridge]: " << error_response["message"].dump()); | ||
| return crow::response(crow::status::INTERNAL_SERVER_ERROR, error_response.dump()); | ||
| } | ||
|
|
||
| auto mission_goal = get_mission_data_service.response.mission_goal; | ||
| auto json = missionGoalToJson(mission_goal); |
There was a problem hiding this comment.
The variable 'json' is declared twice in this function scope (lines 1681 and 1702), which causes a shadowing issue. The second declaration shadows the first one. Consider renaming one of them (e.g., 'error_json' and 'success_json' or 'result_json' and 'mission_json') to avoid confusion.
| if (!json_msg || !json_msg.has("type") || !json_msg.has("details")) | ||
| return crow::response(crow::status::BAD_REQUEST, "{\"message\": \"Bad request: Failed to parse JSON or missing 'type' and 'details' keys\"}"); | ||
|
|
||
| // Validate if the action client is connected and if the action is already running | ||
| if (!coverage_action_client_ptr_->isServerConnected()) { | ||
| if (!fleet_manager_action_client_->isServerConnected()) { | ||
| ROS_WARN_STREAM("[IROCBridge]: Action server is not connected. Check the iroc_fleet_manager node."); | ||
| std::string msg = "Action server is not connected. Check iroc_fleet_manager node.\n"; | ||
| return crow::response(crow::status::CONFLICT, "{\"message\": \"" + msg + "\"}"); | ||
| } | ||
| else if (!coverage_action_client_ptr_->getState().isDone()) { | ||
| } else if (!fleet_manager_action_client_->getState().isDone()) { | ||
| ROS_WARN_STREAM("[IROCBridge]: Mission is already running. Terminate the previous one, or wait until it is finished."); | ||
| std::string msg = "Mission is already running. Terminate the previous one, or wait until it is finished"; | ||
| return crow::response(crow::status::CONFLICT, "{\"message\": \"" + msg + "\"}"); | ||
| } | ||
|
|
||
| // Send the action goal to the fleet manager | ||
| coverageMissionActionServerGoal action_goal; | ||
| FleetManagerGoal action_goal; | ||
| std::string type = json_msg["type"].s(); | ||
| std::string uuid = json_msg["uuid"].s(); |
There was a problem hiding this comment.
Missing validation for the 'uuid' field in the JSON message. If the 'uuid' field is missing from the JSON, calling json_msg["uuid"].s() will throw an exception. Consider adding a check similar to the one for 'type' and 'details' fields.
| mrs_lib::SubscribeHandler<mrs_robot_diagnostics::StateEstimationInfo> sh_state_estimation_info; | ||
| mrs_lib::SubscribeHandler<mrs_robot_diagnostics::ControlInfo> sh_control_info; | ||
| // Fleet manager feedback subscriber | ||
| mrs_lib::SubscribeHandler<iroc_fleet_manager::IROCFleetManagerActionFeedback> sch_fleet_manager_feedback_; |
There was a problem hiding this comment.
The line 140 has a trailing whitespace after the subscriber initialization. This should be removed for consistency with the project's coding style.
| mrs_lib::SubscribeHandler<iroc_fleet_manager::IROCFleetManagerActionFeedback> sch_fleet_manager_feedback_; | |
| mrs_lib::SubscribeHandler<iroc_fleet_manager::IROCFleetManagerActionFeedback> sch_fleet_manager_feedback_; |
No description provided.