Several fixes on ros2 bridge and readme update#1253
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements several fixes and improvements to the ROS2 bridge functionality:
- Adds support for boolean literals
TrueandFalsein ROS2 message parsing - Extends dependency parsing to include
buildtool_dependandbuild_dependtags - Implements transitive dependency resolution for ROS2 messages using depth-first search
- Ensures proper cleanup of ROS nodes in example run scripts
- Updates documentation with clearer instructions and removes obsolete feature flags
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| libraries/extensions/ros2-bridge/msg-gen/src/parser/literal.rs | Adds capitalized boolean literal support (True/False) for compatibility with nav2_msgs |
| libraries/extensions/ros2-bridge/msg-gen/src/parser/package.rs | Implements transitive dependency resolution and adds support for additional dependency tags |
| libraries/extensions/ros2-bridge/Cargo.toml | Removes ros2-examples feature flag and moves dependencies to dev-dependencies |
| examples/ros2-bridge/rust/turtle/run.rs | Refactors cleanup logic to ensure ROS nodes are killed even if dataflow fails |
| examples/ros2-bridge/rust/topic-sub/run.rs | Refactors cleanup logic to ensure ROS nodes are killed even if dataflow fails |
| examples/ros2-bridge/rust/topic-pub/run.rs | Refactors cleanup logic to ensure ROS nodes are killed even if dataflow fails |
| examples/ros2-bridge/rust/service-server/run.rs | Removes error handling from run() call (error is now silently ignored) |
| examples/ros2-bridge/rust/service-client/run.rs | Refactors cleanup logic to ensure ROS nodes are killed even if dataflow fails |
| examples/ros2-bridge/python/turtle/run.rs | Refactors cleanup logic to ensure ROS nodes are killed even if dataflow fails |
| examples/ros2-bridge/c++/turtle/run.rs | Refactors cleanup logic to ensure ROS nodes are killed even if dataflow fails |
| examples/ros2-bridge/rust/turtle/README.md | Updates documentation with generic ROS distribution placeholder and simplified run command |
| examples/ros2-bridge/python/turtle/README.md | Updates documentation with generic ROS distribution placeholder and corrects file paths |
| examples/ros2-bridge/c++/turtle/README.md | Reorganizes documentation with clearer structure and updated run instructions |
| .github/workflows/ci.yml | Updates CI workflow to remove ros2-examples feature flag from cargo commands |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // flatten the dependencies | ||
| let dependencies = { | ||
| let mut flattened_dependencies = HashMap::<String, BTreeSet<String>>::new(); | ||
| let mut finished_set: BTreeSet<String> = dependencies.keys().cloned().into_iter().collect(); |
There was a problem hiding this comment.
The .into_iter() call is redundant here. The .cloned() iterator adapter already returns an iterator, so chaining .into_iter() after it has no effect.
Consider simplifying to:
let mut finished_set: BTreeSet<String> = dependencies.keys().cloned().collect();| let mut finished_set: BTreeSet<String> = dependencies.keys().cloned().into_iter().collect(); | |
| let mut finished_set: BTreeSet<String> = dependencies.keys().cloned().collect(); |
| .get(&package.name) | ||
| .unwrap() | ||
| .iter() | ||
| .filter_map(|dep| map.get(dep).and_then(|pkg| Some(pkg.clone()))) |
There was a problem hiding this comment.
The and_then with Some is redundant here. When using filter_map, you can directly return the value from map.get(dep) since it already returns Option<&Package>. The .and_then(|pkg| Some(pkg.clone())) is equivalent to just .map(|pkg| pkg.clone()).
Consider simplifying to:
.filter_map(|dep| map.get(dep).map(|pkg| pkg.clone()))| .filter_map(|dep| map.get(dep).and_then(|pkg| Some(pkg.clone()))) | |
| .filter_map(|dep| map.get(dep).map(|pkg| pkg.clone())) |
| let (finish_tx, finish_rx) = mpsc::channel(); | ||
| let dataflow_task = std::thread::spawn(move || { | ||
| run("dataflow.yml".to_string(), false).unwrap(); | ||
| run("dataflow.yml".to_string(), false); |
There was a problem hiding this comment.
The return value of the run function (which returns eyre::Result<()>) is being silently ignored. If the dataflow fails to run, this error will be lost and won't be propagated.
Consider handling the result:
let dataflow_task = std::thread::spawn(move || {
let result = run("dataflow.yml".to_string(), false);
finish_tx.send(());
result
});Then later, you could check the result with dataflow_task.join() if needed.
| } | ||
|
|
||
| fn dfs_collect( | ||
| name: &String, |
There was a problem hiding this comment.
The parameter type name: &String is non-idiomatic. It's generally better to use &str instead of &String for function parameters as it's more flexible (accepts both &String and &str) and follows Rust best practices.
Consider changing to:
fn dfs_collect(
name: &str,
// ... rest of parametersYou'll need to update the call sites accordingly (e.g., use name.as_str() or &name when calling).
| let direct_deps: BTreeSet<String> = dependencies.get(name).unwrap().iter().cloned().collect(); | ||
| for dep_name in &direct_deps { | ||
| let indirect_deps = dfs_collect( | ||
| &dep_name, | ||
| dependencies, | ||
| flattened_dependencies, | ||
| finished_set, | ||
| ); | ||
| add_deps.extend(indirect_deps); | ||
| } | ||
| add_deps.extend(direct_deps); |
There was a problem hiding this comment.
This line creates an unnecessary intermediate BTreeSet by cloning all the dependency names. The dependencies.get(name).unwrap() already returns a &BTreeSet<String>, which can be iterated directly.
Consider simplifying to:
for dep_name in dependencies.get(name).unwrap() {This avoids the allocation and cloning of all strings.
| let direct_deps: BTreeSet<String> = dependencies.get(name).unwrap().iter().cloned().collect(); | |
| for dep_name in &direct_deps { | |
| let indirect_deps = dfs_collect( | |
| &dep_name, | |
| dependencies, | |
| flattened_dependencies, | |
| finished_set, | |
| ); | |
| add_deps.extend(indirect_deps); | |
| } | |
| add_deps.extend(direct_deps); | |
| for dep_name in dependencies.get(name).unwrap() { | |
| let indirect_deps = dfs_collect( | |
| dep_name, | |
| dependencies, | |
| flattened_dependencies, | |
| finished_set, | |
| ); | |
| add_deps.extend(indirect_deps); | |
| } | |
| add_deps.extend(dependencies.get(name).unwrap().iter().cloned()); |
| ## Alternative(manual) | ||
| Not recommended, because manually build c++ is heavy job. |
There was a problem hiding this comment.
There are several grammar and spelling issues in this section:
- "Alternative(manual)" should have a space: "Alternative (manual)"
- "manually build c++ is heavy job" should be "manually building C++ is a heavy job"
| ## Alternative(manual) | |
| Not recommended, because manually build c++ is heavy job. | |
| ## Alternative (manual) | |
| Not recommended, because manually building C++ is a heavy job. |
|
|
||
| then start DORA service client from another terminal | ||
| ``` | ||
| 3. From terminal 3 in the folder of dora repository. Note the source command here is necessary as this allow ROS2 message types to be found and compile dynamically. |
There was a problem hiding this comment.
Grammar issue: "this allow" should be "this allows" (singular subject requires singular verb form).
| 3. From terminal 3 in the folder of dora repository. Note the source command here is necessary as this allow ROS2 message types to be found and compile dynamically. | |
| 3. From terminal 3 in the folder of dora repository. Note the source command here is necessary as this allows ROS2 message types to be found and compile dynamically. |
f6a9e60 to
bccee1f
Compare
haixuanTao
left a comment
There was a problem hiding this comment.
Looks great overall!
Made some comment and agree with copilot review m
Some ros2 message use the pascal case boolean literal. ref: https://github.com/ros-navigation/navigation2/blob/0fc9a10b9c2cdb28ac7a1ff8bd726650a4a9b8f1/nav2_msgs/action/DockRobot.action#L3 Signed-off-by: drindr <dreamchancn@qq.com>
Some ros2 msg like the `robot_localization` depends `builtin_interface` with the `builttool_depend`. Some ros2 msg like the `slam_toolbox` use the transitive depenedency through the `tf2_geometry_msgs` Signed-off-by: drindr <dreamchancn@qq.com>
Signed-off-by: drindr <dreamchancn@qq.com>
The examples of ROS2 have been placed under the crate `dora-ros2-bridge`, hence removing the `ros2-examples` feature-gate Signed-off-by: drindr <dreamchancn@qq.com>
…r's function name Signed-off-by: drindr <dreamchancn@qq.com>
bccee1f to
7a9cee2
Compare
|
This PR is updated. But I do not know why the CI is always timeout😭️. Should we configure the timeout with a longer timeout tolerance? |
I don't think that this will help. Looks like it hangs for some reason:
(note the timestamps) The command that hangs seems to be I just restarted the build to see whether it's reproducible. cc @haixuanTao |
|
Retrying seemed to "fix" it. Not sure what was causing this, we should look into it when we have time. But it seems to be unrelated to this PR. |
Maybe a timeout for the async waiting can solve it. |
There should be periodic timer ticks. Once the input is closed, the |
|
@haixuanTao Could you review this PR again. Flaws you mentioned above have been solved. |

TrueandFalseaccording to the nav2_msgs.buildtool_dependandbuild_dependtags as the dependencies while parsing the ROS2 package.run.rs.ros2-examplesfeature gate