diff --git a/README.md b/README.md index 074e99a..6661e54 100644 --- a/README.md +++ b/README.md @@ -5,23 +5,28 @@ Minimal bindings for ROS2 that does *not* require hooking in to the ROS2 build i How to use ------------ -You need to source your ROS2 installation before building/running. A couple of examples are included in examples/ +1. Depend on this package: r2r = { git = "https://github.com/sequenceplanner/r2r" }. +2. You need to source your ROS2 installation before building/running. +3. The bindings will rebuild automatically if/when you source your workspaces. + +A couple of examples are included in examples/ ``` . /opt/ros/dashing/setup.sh cargo build cargo run --example subscriber_with_thread ``` -In order to avoid building everything, put the message types you need in `msgs.txt` before building. (Or just `ros2 msg list > msgs.txt` if you want everything). + What works? -------- -- Only tested with ROS2 Dashing -- Simple publish/subscribe, see examples. +- Up to date with ROS2 Dashing +- Building Rust types +- Publish/subscribe TODO ------------ - The code generation is currently just a big hack. Needs cleanup and refactoring. - Implement error handling. Now all methods just return Err(()). -- Expose more of the RCL. - - +- Expose more of the RCL. +- Services and action types are currently ignored. +- QoS settings etc. diff --git a/build.rs b/build.rs index 337e1d5..fe9008b 100644 --- a/build.rs +++ b/build.rs @@ -6,8 +6,11 @@ use std::io::Write; use std::path::PathBuf; fn main() { - let msgs_str = read_file("./msgs.txt").expect("You need to create msgs.txt"); - let msgs_list = parse_msgs(&msgs_str); + println!("cargo:rerun-if-env-changed=AMENT_PREFIX_PATH"); + + let msgs = get_all_ros_msgs(); + let msgs_list = parse_msgs(&msgs); + let msgs = as_map(&msgs_list); let mut codegen = String::new(); diff --git a/common/src/lib.rs b/common/src/lib.rs index a06f357..ee4c16a 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,4 +1,8 @@ use std::collections::HashMap; +use std::env; +use std::fs::{self, File}; +use std::io::{self, Read}; +use std::path::PathBuf; #[derive(Debug)] pub struct RosMsg { @@ -7,8 +11,72 @@ pub struct RosMsg { pub name: String, // e.g. "String" } -pub fn parse_msgs(msgs: &str) -> Vec { - let v: Vec> = msgs.lines().map(|l| l.split("/").into_iter().take(3).collect()).collect(); +// TODO: actions and srv are similiar +pub fn get_all_ros_msgs() -> Vec { + let resource_index_subfolder = "share/ament_index/resource_index"; + let resource_type = "rosidl_interfaces"; + let ament_prefix_var_name = "AMENT_PREFIX_PATH"; + let ament_prefix_var = env::var(ament_prefix_var_name).expect("Source your ROS!"); + + let mut msgs: Vec = Vec::new(); + + for ament_prefix_path in ament_prefix_var.split(":") { + // println!("prefix: {}", ament_prefix_path); + + let path = PathBuf::from(ament_prefix_path); + let path = path.join(resource_index_subfolder); + let path = path.join(resource_type); + + if let Ok(paths) = fs::read_dir(path) { + + for path in paths { + // println!("PATH Name: {}", path.unwrap().path().display()); + + let path = path.unwrap().path(); + let path2 = path.clone(); + let file_name = path2.file_name().unwrap(); + + // println!("Messages for: {:?}", file_name); + if let Ok(mut file) = File::open(path) { + let mut s = String::new(); + file.read_to_string(&mut s).unwrap(); + let lines = s.lines(); + + lines.for_each(|l| { + if l.starts_with("msg/") && (l.ends_with(".idl") || l.ends_with(".msg")) { + if let Some(file_name_str) = file_name.to_str() { + let substr = &l[4..l.len()-4]; + let msg_name = format!("{}/msg/{}", file_name_str, substr); + msgs.push(msg_name); + } + } + }); + } + } + } + } + + msgs.sort(); + msgs.dedup(); + + return msgs; +} + +#[test] +fn test_msg_list() { + + let msgs = get_all_ros_msgs(); + for m in &msgs { + println!("{}", m); + } + + assert!(msgs.contains(&"std_msgs/msg/String".to_string())); + assert!(msgs.contains(&"builtin_interfaces/msg/Time".to_string())); + +} + +pub fn parse_msgs(msgs: &Vec) -> Vec { + let v: Vec> = msgs.iter().map(|l| l.split("/").into_iter().take(3).collect()).collect(); v.iter().filter(|v|v.len() == 3). map(|v| RosMsg { module: v[0].into(), prefix: v[1].into(), name: v[2].into()}).collect() @@ -23,16 +91,6 @@ pub fn as_map(included_msgs: &[RosMsg]) -> HashMap<&str, HashMap<&str, Vec<&str> msgs } -use std::io::{self, Read}; -use std::fs::File; - -pub fn read_file(filename: &str) -> io::Result { - let mut file = File::open(filename)?; - let mut s = String::new(); - file.read_to_string(&mut s)?; - Ok(s) -} - #[cfg(test)] mod tests { use super::*; @@ -65,10 +123,9 @@ std_msgs/msg/String assert_eq!(map.get("std_msgs").unwrap().get("msg").unwrap()[0], "Bool"); assert_eq!(map.get("std_msgs").unwrap().get("msg").unwrap()[1], "String"); - - } - + + } + } - diff --git a/msg_gen/build.rs b/msg_gen/build.rs index 207e699..de2cc5a 100644 --- a/msg_gen/build.rs +++ b/msg_gen/build.rs @@ -8,9 +8,9 @@ use std::path::PathBuf; use common::*; fn main() { - println!("cargo:rerun-if-changed=../msgs.txt"); + println!("cargo:rerun-if-env-changed=AMENT_PREFIX_PATH"); - let msgs = read_file("../msgs.txt").expect("You need to create msgs.txt"); + let msgs = get_all_ros_msgs(); let msg_list = parse_msgs(&msgs); let msg_map = as_map(&msg_list); diff --git a/msgs.txt b/msgs.txt deleted file mode 100644 index ca73369..0000000 --- a/msgs.txt +++ /dev/null @@ -1,15 +0,0 @@ -builtin_interfaces/msg/Duration -builtin_interfaces/msg/Time -geometry_msgs/msg/Vector3 -geometry_msgs/msg/AccelWithCovariance -geometry_msgs/msg/Accel -std_msgs/msg/String -std_msgs/msg/Bool -std_msgs/msg/Int32 -std_msgs/msg/UInt32 -std_msgs/msg/Header -trajectory_msgs/msg/JointTrajectory -trajectory_msgs/msg/JointTrajectoryPoint -shape_msgs/msg/SolidPrimitive - - diff --git a/rcl/build.rs b/rcl/build.rs index 5d711fe..7f90e6b 100644 --- a/rcl/build.rs +++ b/rcl/build.rs @@ -4,6 +4,8 @@ use std::env; use std::path::PathBuf; fn main() { + println!("cargo:rerun-if-env-changed=AMENT_PREFIX_PATH"); + let mut builder = bindgen::Builder::default() .header("src/rcl_wrapper.h") .derive_copy(false)