Merge branch 'master' into actions

This commit is contained in:
Martin Dahl 2021-05-15 11:24:09 +02:00
commit afb62321e5
25 changed files with 523 additions and 374 deletions

8
.gitignore vendored
View File

@ -2,10 +2,4 @@
Cargo.lock Cargo.lock
# code generation .envrc
msg_gen/src/introspection_functions.rs
msg_gen/src/msg_bindings.rs
msg_gen/src/msg_includes.h
rcl/src/rcl_bindings.rs
rcl/src/rcl_wrapper.h
src/generated_msgs.rs

View File

@ -1,7 +1,7 @@
[package] [package]
name = "r2r" name = "r2r"
version = "0.0.6" version = "0.0.8"
authors = ["Martin Dahl <martin.dahl@gmail.com>"] authors = ["Martin Dahl <martin.dahl@gmail.com>"]
description = "Minimal ros2 bindings." description = "Minimal ros2 bindings."
license = "Apache-2.0/MIT" license = "Apache-2.0/MIT"
@ -10,8 +10,7 @@ edition = "2018"
[dependencies] [dependencies]
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.123", features = ["derive"] }
serde_json = "1.0.62" serde_json = "1.0.62"
failure = "0.1.8" thiserror = "1.0"
failure_derive = "0.1.8"
lazy_static = "1.4.0" lazy_static = "1.4.0"
common = { path = "common", version = "0.0.3" } common = { path = "common", version = "0.0.3" }
rcl = { path = "rcl", version = "0.0.3" } rcl = { path = "rcl", version = "0.0.3" }

View File

@ -20,7 +20,10 @@ A couple of examples are included in examples/
. /opt/ros/foxy/setup.sh . /opt/ros/foxy/setup.sh
cargo build cargo build
cargo run --example subscriber_with_thread cargo run --example subscriber_with_thread
# In other shell
ros2 topic pub /hi std_msgs/msg/String "data: 'Hello, world!'"
``` ```
An example application can be found here <https://github.com/sequenceplanner/r2r-echo>, which can be installed by running cargo install --git https://github.com/sequenceplanner/r2r-echo. An example application can be found here <https://github.com/sequenceplanner/r2r-echo>, which can be installed by running cargo install --git https://github.com/sequenceplanner/r2r-echo.
What works? What works?

View File

@ -3,21 +3,29 @@ use msg_gen;
use std::env; use std::env;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::path::{Path,PathBuf}; use std::path::{Path, PathBuf};
fn main() { fn main() {
common::print_cargo_watches(); common::print_cargo_watches();
let msg_list = if let Some(cmake_includes) = env::var("CMAKE_INCLUDE_DIRS").ok() { let msg_list = if let Some(cmake_includes) = env::var("CMAKE_INCLUDE_DIRS").ok() {
let packages = cmake_includes.split(":").flat_map(|i| Path::new(i).parent()).collect::<Vec<_>>(); let packages = cmake_includes
.split(":")
.flat_map(|i| Path::new(i).parent())
.collect::<Vec<_>>();
let deps = env::var("CMAKE_IDL_PACKAGES").unwrap_or(String::default()); let deps = env::var("CMAKE_IDL_PACKAGES").unwrap_or(String::default());
let deps = deps.split(":").collect::<Vec<_>>(); let deps = deps.split(":").collect::<Vec<_>>();
let msgs = common::get_ros_msgs(&packages); let msgs = common::get_ros_msgs(&packages);
common::parse_msgs(&msgs).into_iter() common::parse_msgs(&msgs)
.filter(|msg| deps.contains(&msg.module.as_str())).collect::<Vec<_>>() .into_iter()
.filter(|msg| deps.contains(&msg.module.as_str()))
.collect::<Vec<_>>()
} else { } else {
let ament_prefix_var = env::var("AMENT_PREFIX_PATH").expect("Source your ROS!"); let ament_prefix_var = env::var("AMENT_PREFIX_PATH").expect("Source your ROS!");
let paths = ament_prefix_var.split(":").map(|i| Path::new(i)).collect::<Vec<_>>(); let paths = ament_prefix_var
.split(":")
.map(|i| Path::new(i))
.collect::<Vec<_>>();
let msgs = common::get_ros_msgs(&paths); let msgs = common::get_ros_msgs(&paths);
common::parse_msgs(&msgs) common::parse_msgs(&msgs)
}; };
@ -26,7 +34,11 @@ fn main() {
let mut modules = String::new(); let mut modules = String::new();
for (module, prefixes) in &msgs { for (module, prefixes) in &msgs {
modules.push_str(&format!(r#"pub mod {module}{{include!(concat!(env!("OUT_DIR"), "/{module}.rs"));}}{lf}"#, module=module, lf="\n")); modules.push_str(&format!(
r#"pub mod {module}{{include!(concat!(env!("OUT_DIR"), "/{module}.rs"));}}{lf}"#,
module = module,
lf = "\n"
));
let mut codegen = String::new(); let mut codegen = String::new();
for (prefix, msgs) in prefixes { for (prefix, msgs) in prefixes {

View File

@ -28,7 +28,6 @@ fn get_msgs_from_package(package: &Path) -> Vec<String> {
let mut msgs = vec![]; let mut msgs = vec![];
if let Ok(paths) = fs::read_dir(path) { if let Ok(paths) = fs::read_dir(path) {
for path in paths { for path in paths {
// println!("PATH Name: {}", path.unwrap().path().display()); // println!("PATH Name: {}", path.unwrap().path().display());
@ -45,14 +44,14 @@ fn get_msgs_from_package(package: &Path) -> Vec<String> {
lines.for_each(|l| { lines.for_each(|l| {
if l.starts_with("msg/") && (l.ends_with(".idl") || l.ends_with(".msg")) { if l.starts_with("msg/") && (l.ends_with(".idl") || l.ends_with(".msg")) {
if let Some(file_name_str) = file_name.to_str() { if let Some(file_name_str) = file_name.to_str() {
let substr = &l[4..l.len()-4]; let substr = &l[4..l.len() - 4];
let msg_name = format!("{}/msg/{}", file_name_str, substr); let msg_name = format!("{}/msg/{}", file_name_str, substr);
msgs.push(msg_name); msgs.push(msg_name);
} }
} }
if l.starts_with("srv/") && (l.ends_with(".idl") || l.ends_with(".srv")) { if l.starts_with("srv/") && (l.ends_with(".idl") || l.ends_with(".srv")) {
if let Some(file_name_str) = file_name.to_str() { if let Some(file_name_str) = file_name.to_str() {
let substr = &l[4..l.len()-4]; let substr = &l[4..l.len() - 4];
let srv_name = format!("{}/srv/{}", file_name_str, substr); let srv_name = format!("{}/srv/{}", file_name_str, substr);
msgs.push(srv_name); msgs.push(srv_name);
} }
@ -60,9 +59,9 @@ fn get_msgs_from_package(package: &Path) -> Vec<String> {
if l.starts_with("action/") && (l.ends_with(".idl") || l.ends_with(".action")) { if l.starts_with("action/") && (l.ends_with(".idl") || l.ends_with(".action")) {
if let Some(file_name_str) = file_name.to_str() { if let Some(file_name_str) = file_name.to_str() {
let substr = if l.ends_with(".action") { let substr = if l.ends_with(".action") {
&l[7..l.len()-7] &l[7..l.len() - 7]
} else { } else {
&l[7..l.len()-4] // .idl &l[7..l.len() - 4] // .idl
}; };
let action_name = format!("{}/action/{}", file_name_str, substr); let action_name = format!("{}/action/{}", file_name_str, substr);
println!("found action: {}", action_name); println!("found action: {}", action_name);
@ -93,23 +92,38 @@ pub fn get_ros_msgs(paths: &[&Path]) -> Vec<String> {
} }
pub fn parse_msgs(msgs: &Vec<String>) -> Vec<RosMsg> { pub fn parse_msgs(msgs: &Vec<String>) -> Vec<RosMsg> {
let v: Vec<Vec<&str>> = msgs.iter().map(|l| l.split("/").into_iter().take(3).collect()).collect(); let v: Vec<Vec<&str>> = msgs
let v: Vec<_> = v.iter().filter(|v|v.len() == 3). .iter()
map(|v| RosMsg { module: v[0].into(), prefix: v[1].into(), name: v[2].into()}).collect(); .map(|l| l.split("/").into_iter().take(3).collect())
.collect();
let v: Vec<_> = v
.iter()
.filter(|v| v.len() == 3)
.map(|v| RosMsg {
module: v[0].into(),
prefix: v[1].into(),
name: v[2].into(),
})
.collect();
// hack because I don't have time to find out the root cause of this at the moment. // hack because I don't have time to find out the root cause of this at the moment.
// for some reason the library files generated to this are called // for some reason the library files generated to this are called
// liblibstatistics_collector_test_msgs__..., but I don't know where test_msgs come from. // liblibstatistics_collector_test_msgs__..., but I don't know where test_msgs come from.
// (this seems to be a useless package anyway) // (this seems to be a useless package anyway)
// also affects message generation below. // also affects message generation below.
v.into_iter().filter(|v| v.module != "libstatistics_collector").collect() v.into_iter()
.filter(|v| v.module != "libstatistics_collector")
.collect()
} }
pub fn as_map(included_msgs: &[RosMsg]) -> HashMap<&str, HashMap<&str, Vec<&str>>> { pub fn as_map(included_msgs: &[RosMsg]) -> HashMap<&str, HashMap<&str, Vec<&str>>> {
let mut msgs = HashMap::new(); let mut msgs = HashMap::new();
for msg in included_msgs { for msg in included_msgs {
msgs.entry(msg.module.as_str()).or_insert(HashMap::new()).entry(msg.prefix.as_str()). msgs.entry(msg.module.as_str())
or_insert(Vec::new()).push(msg.name.as_str()); .or_insert(HashMap::new())
.entry(msg.prefix.as_str())
.or_insert(Vec::new())
.push(msg.name.as_str());
} }
msgs msgs
} }
@ -125,7 +139,7 @@ std_msgs/msg/Bool
x/y x/y
std_msgs/msg/String std_msgs/msg/String
"; ";
let msgs = msgs.lines().map(|l|l.to_string()).collect(); let msgs = msgs.lines().map(|l| l.to_string()).collect();
let parsed = parse_msgs(&msgs); let parsed = parse_msgs(&msgs);
assert_eq!(parsed[0].module, "std_msgs"); assert_eq!(parsed[0].module, "std_msgs");
assert_eq!(parsed[0].prefix, "msg"); assert_eq!(parsed[0].prefix, "msg");
@ -142,15 +156,14 @@ std_msgs/msg/Bool
x/y x/y
std_msgs/msg/String std_msgs/msg/String
"; ";
let msgs: Vec<String> = msgs.lines().map(|l|l.to_string()).collect(); let msgs: Vec<String> = msgs.lines().map(|l| l.to_string()).collect();
let parsed = parse_msgs(&msgs); let parsed = parse_msgs(&msgs);
let map = as_map(&parsed); let map = as_map(&parsed);
assert_eq!(map.get("std_msgs").unwrap().get("msg").unwrap()[0], "Bool"); assert_eq!(map.get("std_msgs").unwrap().get("msg").unwrap()[0], "Bool");
assert_eq!(map.get("std_msgs").unwrap().get("msg").unwrap()[1], "String"); assert_eq!(
map.get("std_msgs").unwrap().get("msg").unwrap()[1],
"String"
);
} }
} }

View File

@ -1,9 +1,8 @@
use r2r; use r2r;
use failure::Error;
use r2r::example_interfaces::srv::AddTwoInts; use r2r::example_interfaces::srv::AddTwoInts;
fn main() -> Result<(), Error> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "testnode", "")?; let mut node = r2r::Node::create(ctx, "testnode", "")?;
let client = node.create_client::<AddTwoInts::Service>("/add_two_ints")?; let client = node.create_client::<AddTwoInts::Service>("/add_two_ints")?;
@ -18,18 +17,18 @@ fn main() -> Result<(), Error> {
let mut c = 0; let mut c = 0;
loop { loop {
let req = AddTwoInts::Request { a: 10*c, b: c }; let req = AddTwoInts::Request { a: 10 * c, b: c };
let cb_req = req.clone(); let cb_req = req.clone();
let cb = Box::new(move |r: AddTwoInts::Response| let cb = Box::new(move |r: AddTwoInts::Response| {
println!("{} + {} = {}", cb_req.a, cb_req.b, r.sum)); println!("{} + {} = {}", cb_req.a, cb_req.b, r.sum)
});
client.request(&req, cb)?; client.request(&req, cb)?;
node.spin_once(std::time::Duration::from_millis(1000)); node.spin_once(std::time::Duration::from_millis(1000));
std::thread::sleep(std::time::Duration::from_millis(1000)); std::thread::sleep(std::time::Duration::from_millis(1000));
c+=1; c += 1;
} }
} }

View File

@ -1,8 +1,6 @@
use r2r; use r2r;
use failure::Error;
fn main() -> Result<(), Box<dyn std::error::Error>> {
fn main() -> Result<(), Error> {
{ {
let mut clock = r2r::Clock::create(r2r::ClockType::RosTime)?; let mut clock = r2r::Clock::create(r2r::ClockType::RosTime)?;
let now = clock.get_now()?; let now = clock.get_now()?;

View File

@ -1,11 +1,10 @@
use r2r; use r2r;
use failure::Error;
/// try to run like this /// try to run like this
/// cargo run --example logging -- --ros-args --log-level DEBUG /// cargo run --example logging -- --ros-args --log-level DEBUG
/// The logs produced with the node logger should show up in /rosout /// The logs produced with the node logger should show up in /rosout
fn main() -> Result<(), Error> { fn main() -> Result<(), Box<dyn std::error::Error>> {
r2r::log_debug!("before_init", "debug msg"); r2r::log_debug!("before_init", "debug msg");
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;
let node = r2r::Node::create(ctx, "logger_node", "")?; let node = r2r::Node::create(ctx, "logger_node", "")?;
@ -17,8 +16,7 @@ fn main() -> Result<(), Error> {
0 => r2r::log_debug!(nl, "debug msg: {}", i as f64 / 2.5), 0 => r2r::log_debug!(nl, "debug msg: {}", i as f64 / 2.5),
1 => r2r::log_info!(nl, "info msg {}", i % 2), 1 => r2r::log_info!(nl, "info msg {}", i % 2),
2 => r2r::log_warn!(nl, "warn msg {:?}", i.to_string()), 2 => r2r::log_warn!(nl, "warn msg {:?}", i.to_string()),
3 => r2r::log_error!(nl, "error msg {:?}", 3 => r2r::log_error!(nl, "error msg {:?}", i.to_string().as_bytes()),
i.to_string().as_bytes()),
_ => r2r::log_fatal!(nl, "fatal msg {:#X}", i), _ => r2r::log_fatal!(nl, "fatal msg {:#X}", i),
} }
@ -26,6 +24,6 @@ fn main() -> Result<(), Error> {
// non-node logger only outputs to stdout // non-node logger only outputs to stdout
r2r::log_debug!("other_logger", "i = {}", i); r2r::log_debug!("other_logger", "i = {}", i);
i+=1; i += 1;
} }
} }

View File

@ -1,19 +1,21 @@
use r2r; use r2r;
use failure::Error;
// try to run like this // try to run like this
// cargo run --example parameters -- --ros-args -p param_key:=[hej,hopp] -p key2:=5.5 key2=true -r __ns:=/demo -r __node:=my_node // cargo run --example parameters -- --ros-args -p param_key:=[hej,hopp] -p key2:=5.5 key2=true -r __ns:=/demo -r __node:=my_node
fn main() -> Result<(), Error> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;
let node = r2r::Node::create(ctx, "testnode", "")?; let node = r2r::Node::create(ctx, "testnode", "")?;
println!("node name: {}", node.name()?); println!("node name: {}", node.name()?);
println!("node fully qualified name: {}", node.fully_qualified_name()?); println!(
"node fully qualified name: {}",
node.fully_qualified_name()?
);
println!("node namespace: {}", node.namespace()?); println!("node namespace: {}", node.namespace()?);
println!("node parameters"); println!("node parameters");
node.params.iter().for_each(|(k,v)| { node.params.iter().for_each(|(k, v)| {
println!("{} - {:?}", k, v); println!("{} - {:?}", k, v);
}); });

View File

@ -1,10 +1,9 @@
use r2r; use r2r;
use r2r::builtin_interfaces::msg::Duration; use r2r::builtin_interfaces::msg::Duration;
use r2r::trajectory_msgs::msg::*;
use r2r::std_msgs::msg::Int32; use r2r::std_msgs::msg::Int32;
use failure::Error; use r2r::trajectory_msgs::msg::*;
fn main() -> Result<(), Error> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "testnode", "")?; let mut node = r2r::Node::create(ctx, "testnode", "")?;
let publisher = node.create_publisher::<JointTrajectoryPoint>("/hej")?; let publisher = node.create_publisher::<JointTrajectoryPoint>("/hej")?;
@ -12,13 +11,13 @@ fn main() -> Result<(), Error> {
let mut c = 0; let mut c = 0;
let mut positions: Vec<f64> = Vec::new(); let mut positions: Vec<f64> = Vec::new();
let cb = move |x:r2r::std_msgs::msg::String| { let cb = move |x: r2r::std_msgs::msg::String| {
println!("at count {} got: {}", c, x.data); println!("at count {} got: {}", c, x.data);
c = c + 1; c = c + 1;
positions.push(c as f64); positions.push(c as f64);
let to_send = JointTrajectoryPoint { let to_send = JointTrajectoryPoint {
positions: positions.clone(), positions: positions.clone(),
time_from_start : Duration { sec: c, nanosec: 0 }, time_from_start: Duration { sec: c, nanosec: 0 },
..Default::default() ..Default::default()
}; };
let mut native = r2r::WrappedNativeMsg::<Int32>::new(); let mut native = r2r::WrappedNativeMsg::<Int32>::new();
@ -28,12 +27,12 @@ fn main() -> Result<(), Error> {
publisher2.publish_native(&native).unwrap(); publisher2.publish_native(&native).unwrap();
}; };
let cb2 = move |x:JointTrajectoryPoint| { let cb2 = move |x: JointTrajectoryPoint| {
let serialized = serde_json::to_string(&x).unwrap(); let serialized = serde_json::to_string(&x).unwrap();
println!("JTP serialized as: {}", serialized); println!("JTP serialized as: {}", serialized);
}; };
let cb3 = move |raw_c:&r2r::WrappedNativeMsg<JointTrajectoryPoint>| { let cb3 = move |raw_c: &r2r::WrappedNativeMsg<JointTrajectoryPoint>| {
println!("Raw c data: {:?}", raw_c.positions); println!("Raw c data: {:?}", raw_c.positions);
}; };

View File

@ -1,10 +1,9 @@
use r2r; use r2r;
use std::thread;
use std::env;
use std::collections::HashMap; use std::collections::HashMap;
use failure::Error; use std::env;
use std::thread;
fn main() -> Result<(), Error> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "echo", "")?; let mut node = r2r::Node::create(ctx, "echo", "")?;
@ -17,17 +16,19 @@ fn main() -> Result<(), Error> {
while count < 50 { while count < 50 {
thread::sleep(std::time::Duration::from_millis(10)); thread::sleep(std::time::Duration::from_millis(10));
nt = node.get_topic_names_and_types()?; nt = node.get_topic_names_and_types()?;
if nt.contains_key(topic) { break; } if nt.contains_key(topic) {
break;
}
count += 1; count += 1;
} }
let type_name = nt.get(topic).and_then(|types|types.get(0)); let type_name = nt.get(topic).and_then(|types| types.get(0));
let type_name = match type_name { let type_name = match type_name {
Some(tn) => tn, Some(tn) => tn,
None => { None => {
eprintln!("Could not determine the type for the passed topic"); eprintln!("Could not determine the type for the passed topic");
return Ok(()); return Ok(());
}, }
}; };
println!("topic: {}, type: {}", topic, type_name); println!("topic: {}, type: {}", topic, type_name);
@ -36,8 +37,7 @@ fn main() -> Result<(), Error> {
let echo = &format!("{}_echo", topic); let echo = &format!("{}_echo", topic);
let echo_pub = node.create_publisher_untyped(echo, type_name)?; let echo_pub = node.create_publisher_untyped(echo, type_name)?;
let cb = move |msg: r2r::Result<serde_json::Value> | { let cb = move |msg: r2r::Result<serde_json::Value>| match msg {
match msg {
Ok(msg) => { Ok(msg) => {
let s = serde_json::to_string_pretty(&msg).unwrap(); let s = serde_json::to_string_pretty(&msg).unwrap();
println!("{}\n---\n", &s); println!("{}\n---\n", &s);
@ -46,7 +46,6 @@ fn main() -> Result<(), Error> {
Err(err) => { Err(err) => {
println!("Could not parse msg. {}", err); println!("Could not parse msg. {}", err);
} }
}
}; };
let _subref = node.subscribe_untyped(topic, type_name, Box::new(cb))?; let _subref = node.subscribe_untyped(topic, type_name, Box::new(cb))?;

View File

@ -1,9 +1,8 @@
use r2r; use r2r;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use failure::Error;
fn main() -> Result<(), Error> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;
let node = r2r::Node::create(ctx, "testnode", "")?; let node = r2r::Node::create(ctx, "testnode", "")?;
@ -16,5 +15,5 @@ fn main() -> Result<(), Error> {
println!("{}: {:?}", k, nt[k]); println!("{}: {:?}", k, nt[k]);
} }
thread::sleep(Duration::from_millis(500)); thread::sleep(Duration::from_millis(500));
}; }
} }

View File

@ -1,16 +1,14 @@
use r2r; use r2r;
use failure::Error;
use r2r::example_interfaces::srv::AddTwoInts; use r2r::example_interfaces::srv::AddTwoInts;
fn handle_service(request: AddTwoInts::Request) -> AddTwoInts::Response { fn handle_service(request: AddTwoInts::Request) -> AddTwoInts::Response {
println!("request: {} + {}", request.a, request.b); println!("request: {} + {}", request.a, request.b);
AddTwoInts::Response { AddTwoInts::Response {
sum: request.a + request.b sum: request.a + request.b,
} }
} }
fn main() -> Result<(), Error> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "testnode", "")?; let mut node = r2r::Node::create(ctx, "testnode", "")?;
node.create_service::<AddTwoInts::Service>("/add_two_ints", Box::new(handle_service))?; node.create_service::<AddTwoInts::Service>("/add_two_ints", Box::new(handle_service))?;

View File

@ -1,9 +1,8 @@
use r2r; use r2r;
use std::sync::mpsc; use std::sync::mpsc;
use std::thread; use std::thread;
use failure::Error;
fn main() -> Result<(), Error> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;
let th = { let th = {

View File

@ -1,25 +1,28 @@
use std::sync::mpsc; use std::sync::mpsc;
use std::thread; use std::thread;
use failure::Error;
use r2r; use r2r;
use r2r::std_msgs; use r2r::std_msgs;
fn main() -> Result<(), Error> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "testnode", "")?; let mut node = r2r::Node::create(ctx, "testnode", "")?;
let publisher = node.create_publisher::<std_msgs::msg::String>("/hej")?; let publisher = node.create_publisher::<std_msgs::msg::String>("/hello")?;
let pubint = node.create_publisher::<std_msgs::msg::Int32>("/count")?; let pubint = node.create_publisher::<std_msgs::msg::Int32>("/count")?;
let (tx, rx) = mpsc::channel::<String>(); let (tx, rx) = mpsc::channel::<String>();
thread::spawn(move || loop { thread::spawn(move || loop {
let msg = rx.recv().unwrap(); if let Ok(msg) = rx.recv() {
let deserialized: std_msgs::msg::String = serde_json::from_str(&msg).unwrap(); let deserialized: std_msgs::msg::String = serde_json::from_str(&msg).unwrap();
println!( println!(
"received: {}, deserialized ros msg = {:?}", "received: {}, deserialized ros msg = {:?}",
msg, deserialized msg, deserialized
); );
} else {
println!("stopping");
break;
}
}); });
let mut c = 0; let mut c = 0;
@ -34,7 +37,7 @@ fn main() -> Result<(), Error> {
pubint.publish(&to_send).unwrap(); pubint.publish(&to_send).unwrap();
}; };
let _ws2 = node.subscribe("/hopp", Box::new(cb))?; let _ws2 = node.subscribe("/hi", Box::new(cb))?;
// run for 10 seconds // run for 10 seconds
let mut count = 0; let mut count = 0;

View File

@ -1,14 +1,17 @@
use r2r; use r2r;
use failure::Error;
fn main() -> Result<(), Error> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "testnode", "")?; let mut node = r2r::Node::create(ctx, "testnode", "")?;
let mut x = 0; let mut x = 0;
let cb = move |elapsed: std::time::Duration| { let cb = move |elapsed: std::time::Duration| {
println!("timer called ({}), {}us since last call", x, elapsed.as_micros()); println!(
x+=1; "timer called ({}), {}us since last call",
x,
elapsed.as_micros()
);
x += 1;
}; };
node.create_wall_timer(std::time::Duration::from_millis(2000), Box::new(cb))?; node.create_wall_timer(std::time::Duration::from_millis(2000), Box::new(cb))?;

View File

@ -1,9 +1,9 @@
use bindgen;
use common;
use std::env; use std::env;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::path::{Path,PathBuf}; use std::path::{Path, PathBuf};
use bindgen;
use common;
fn main() { fn main() {
common::print_cargo_watches(); common::print_cargo_watches();
@ -11,21 +11,30 @@ fn main() {
let mut builder = bindgen::Builder::default(); let mut builder = bindgen::Builder::default();
let msg_list = if let Some(cmake_includes) = env::var("CMAKE_INCLUDE_DIRS").ok() { let msg_list = if let Some(cmake_includes) = env::var("CMAKE_INCLUDE_DIRS").ok() {
let packages = cmake_includes.split(":").flat_map(|i| Path::new(i).parent()).collect::<Vec<_>>(); let packages = cmake_includes
.split(":")
.flat_map(|i| Path::new(i).parent())
.collect::<Vec<_>>();
for p in cmake_includes.split(":") { for p in cmake_includes.split(":") {
builder = builder.clang_arg(format!("-I{}", p)); builder = builder.clang_arg(format!("-I{}", p));
} }
let deps = env::var("CMAKE_IDL_PACKAGES").unwrap_or(String::default()); let deps = env::var("CMAKE_IDL_PACKAGES").unwrap_or(String::default());
let deps = deps.split(":").collect::<Vec<_>>(); let deps = deps.split(":").collect::<Vec<_>>();
let msgs = common::get_ros_msgs(&packages); let msgs = common::get_ros_msgs(&packages);
common::parse_msgs(&msgs).into_iter() common::parse_msgs(&msgs)
.filter(|msg| deps.contains(&msg.module.as_str())).collect::<Vec<_>>() .into_iter()
.filter(|msg| deps.contains(&msg.module.as_str()))
.collect::<Vec<_>>()
} else { } else {
let ament_prefix_var = env::var("AMENT_PREFIX_PATH").expect("Source your ROS!"); let ament_prefix_var = env::var("AMENT_PREFIX_PATH").expect("Source your ROS!");
for p in ament_prefix_var.split(":") { for p in ament_prefix_var.split(":") {
builder = builder.clang_arg(format!("-I{}/include", p)); builder = builder.clang_arg(format!("-I{}/include", p));
} }
let paths = ament_prefix_var.split(":").map(|i| Path::new(i)).collect::<Vec<_>>(); let paths = ament_prefix_var
.split(":")
.map(|i| Path::new(i))
.collect::<Vec<_>>();
let msgs = common::get_ros_msgs(&paths); let msgs = common::get_ros_msgs(&paths);
common::parse_msgs(&msgs) common::parse_msgs(&msgs)
}; };
@ -78,8 +87,7 @@ fn main() {
let val = &format!("unsafe {{ rosidl_typesupport_introspection_c__get_message_type_support_handle__{}__{}__{}_{}() }} as *const i32 as usize", &msg.module, &msg.prefix, &msg.name, s); let val = &format!("unsafe {{ rosidl_typesupport_introspection_c__get_message_type_support_handle__{}__{}__{}_{}() }} as *const i32 as usize", &msg.module, &msg.prefix, &msg.name, s);
introspecion_map.push_str(&format!("m.insert(\"{}\", {});\n", key, val)); introspecion_map.push_str(&format!("m.insert(\"{}\", {});\n", key, val));
} }
} } else {
else {
let key = &format!("{}__{}__{}", &msg.module, &msg.prefix, &msg.name); let key = &format!("{}__{}__{}", &msg.module, &msg.prefix, &msg.name);
let val = &format!("unsafe {{ rosidl_typesupport_introspection_c__get_message_type_support_handle__{}__{}__{}() }} as *const i32 as usize", &msg.module, &msg.prefix, &msg.name); let val = &format!("unsafe {{ rosidl_typesupport_introspection_c__get_message_type_support_handle__{}__{}__{}() }} as *const i32 as usize", &msg.module, &msg.prefix, &msg.name);
introspecion_map.push_str(&format!("m.insert(\"{}\", {});\n", key, val)); introspecion_map.push_str(&format!("m.insert(\"{}\", {});\n", key, val));

View File

@ -116,7 +116,9 @@ pub fn generate_rust_service(module_: &str, prefix_: &str, name_: &str) -> Strin
}} }}
}} }}
", module_, prefix_, name_) ",
module_, prefix_, name_
)
} }
pub fn generate_rust_action(module_: &str, prefix_: &str, name_: &str) -> String { pub fn generate_rust_action(module_: &str, prefix_: &str, name_: &str) -> String {
@ -134,10 +136,11 @@ pub fn generate_rust_action(module_: &str, prefix_: &str, name_: &str) -> String
}} }}
}} }}
", module_, prefix_, name_) ",
module_, prefix_, name_
)
} }
// TODO: this is a terrible hack :) // TODO: this is a terrible hack :)
pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> String { pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> String {
let key = format!("{}__{}__{}", module_, prefix_, name_); let key = format!("{}__{}__{}", module_, prefix_, name_);
@ -157,8 +160,12 @@ pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> String {
// same for actions with _Goal, _Result, _Feedback // same for actions with _Goal, _Result, _Feedback
// TODO: refactor... // TODO: refactor...
let mut nn = name.splitn(2, "_"); let mut nn = name.splitn(2, "_");
let _mod_name = nn.next().expect(&format!("malformed service name {}", name)); let _mod_name = nn
let msg_name = nn.next().expect(&format!("malformed service name {}", name)); .next()
.expect(&format!("malformed service name {}", name));
let msg_name = nn
.next()
.expect(&format!("malformed service name {}", name));
name = msg_name.to_owned(); name = msg_name.to_owned();
} }
@ -282,14 +289,16 @@ pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> String {
copy_to_native.push_str(&format!("assert_eq!(self.{field_name}.len(), {array_size}, \"Field {{}} is fixed size of {{}}!\", \"{field_name}\", {array_size});\n", field_name = field_name, array_size = member.array_size_)); copy_to_native.push_str(&format!("assert_eq!(self.{field_name}.len(), {array_size}, \"Field {{}} is fixed size of {{}}!\", \"{field_name}\", {array_size});\n", field_name = field_name, array_size = member.array_size_));
if rust_field_type == "message" { if rust_field_type == "message" {
copy_to_native.push_str(&format!("for (t,s) in msg.{field_name}.iter_mut().zip(&self.{field_name}) {{ s.copy_to_native(t);}}\n", field_name=field_name)); copy_to_native.push_str(&format!("for (t,s) in msg.{field_name}.iter_mut().zip(&self.{field_name}) {{ s.copy_to_native(t);}}\n", field_name=field_name));
} } else if rust_field_type == "std::string::String" {
else if rust_field_type == "std::string::String" {
copy_to_native.push_str(&format!("for (t,s) in msg.{field_name}.iter_mut().zip(&self.{field_name}) {{ t.assign(&s);}}\n", field_name=field_name)); copy_to_native.push_str(&format!("for (t,s) in msg.{field_name}.iter_mut().zip(&self.{field_name}) {{ t.assign(&s);}}\n", field_name=field_name));
} else { } else {
copy_to_native.push_str(&format!("msg.{field_name}.copy_from_slice(&self.{field_name}[..{array_size}]);\n", field_name = field_name, array_size = member.array_size_)); copy_to_native.push_str(&format!(
"msg.{field_name}.copy_from_slice(&self.{field_name}[..{array_size}]);\n",
field_name = field_name,
array_size = member.array_size_
));
} }
} } else if member.is_array_ && (member.array_size_ == 0 || member.is_upper_bound_) {
else if member.is_array_ && (member.array_size_ == 0 || member.is_upper_bound_) {
// these are __Sequence:s // these are __Sequence:s
if rust_field_type == "message" { if rust_field_type == "message" {
let (_, _, _, c_struct, _) = introspection(member.members_); let (_, _, _, c_struct, _) = introspection(member.members_);
@ -359,7 +368,9 @@ pub fn generate_rust_msg(module_: &str, prefix_: &str, name_: &str) -> String {
{msgname}::from_native(&msg_native) {msgname}::from_native(&msg_native)
}} }}
}} }}
", msgname = name); ",
msgname = name
);
let module_str = format!( let module_str = format!(
" "
@ -401,7 +412,9 @@ impl WrappedNativeMsgUntyped {
let mut lines = String::new(); let mut lines = String::new();
for msg in msgs { for msg in msgs {
// for now don't generate untyped services or actions // for now don't generate untyped services or actions
if msg.prefix == "srv" || msg.prefix == "action" { continue; } if msg.prefix == "srv" || msg.prefix == "action" {
continue;
}
let typename = format!("{}/{}/{}", msg.module, msg.prefix, msg.name); let typename = format!("{}/{}/{}", msg.module, msg.prefix, msg.name);
let rustname = format!("{}::{}::{}", msg.module, msg.prefix, msg.name); let rustname = format!("{}::{}::{}", msg.module, msg.prefix, msg.name);

View File

@ -1,9 +1,9 @@
extern crate bindgen; extern crate bindgen;
use std::env;
use std::path::{Path,PathBuf};
use itertools::Itertools;
use common; use common;
use itertools::Itertools;
use std::env;
use std::path::{Path, PathBuf};
fn main() { fn main() {
common::print_cargo_watches(); common::print_cargo_watches();
@ -28,7 +28,9 @@ fn main() {
builder = builder.clang_arg(clang_arg); builder = builder.clang_arg(clang_arg);
} }
env::var("CMAKE_LIBRARIES").unwrap_or(String::new()).split(":") env::var("CMAKE_LIBRARIES")
.unwrap_or(String::new())
.split(":")
.into_iter() .into_iter()
.filter(|s| s.contains(".so") || s.contains(".dylib")) .filter(|s| s.contains(".so") || s.contains(".dylib"))
.flat_map(|l| Path::new(l).parent().and_then(|p| p.to_str())) .flat_map(|l| Path::new(l).parent().and_then(|p| p.to_str()))
@ -46,10 +48,12 @@ fn main() {
for ament_prefix_path in ament_prefix_var.split(":") { for ament_prefix_path in ament_prefix_var.split(":") {
builder = builder.clang_arg(format!("-I{}/include", ament_prefix_path)); builder = builder.clang_arg(format!("-I{}/include", ament_prefix_path));
println!("added include search dir: {}" , format!("-I{}/include", ament_prefix_path)); println!(
"added include search dir: {}",
format!("-I{}/include", ament_prefix_path)
);
println!("cargo:rustc-link-search=native={}/lib", ament_prefix_path); println!("cargo:rustc-link-search=native={}/lib", ament_prefix_path);
} }
} }
println!("cargo:rustc-link-lib=dylib=rcl"); println!("cargo:rustc-link-lib=dylib=rcl");
@ -62,7 +66,8 @@ fn main() {
let bindings = builder let bindings = builder
.no_debug("_OSUnaligned.*") .no_debug("_OSUnaligned.*")
.generate().expect("Unable to generate bindings"); .generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings bindings

View File

@ -17,24 +17,20 @@ impl Default for rmw_message_info_t {
impl Default for rmw_qos_profile_t { impl Default for rmw_qos_profile_t {
fn default() -> Self { fn default() -> Self {
let mut profile: rmw_qos_profile_t = unsafe { std::mem::zeroed() }; let mut profile: rmw_qos_profile_t = unsafe { std::mem::zeroed() };
profile.history = profile.history = rmw_qos_history_policy_t::RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT;
rmw_qos_history_policy_t::RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT;
profile.depth = 10; profile.depth = 10;
profile.reliability = profile.reliability =
rmw_qos_reliability_policy_t::RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT; rmw_qos_reliability_policy_t::RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT;
profile.durability = profile.durability = rmw_qos_durability_policy_t::RMW_QOS_POLICY_DURABILITY_SYSTEM_DEFAULT;
rmw_qos_durability_policy_t::RMW_QOS_POLICY_DURABILITY_SYSTEM_DEFAULT;
profile.avoid_ros_namespace_conventions = false; profile.avoid_ros_namespace_conventions = false;
profile.deadline = rmw_time_t { sec: 0, nsec: 0 }; profile.deadline = rmw_time_t { sec: 0, nsec: 0 };
profile.lifespan = rmw_time_t { sec: 0, nsec: 0 }; profile.lifespan = rmw_time_t { sec: 0, nsec: 0 };
profile.liveliness = profile.liveliness = rmw_qos_liveliness_policy_t::RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT;
rmw_qos_liveliness_policy_t::RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT;
profile.liveliness_lease_duration = rmw_time_t { sec: 0, nsec: 0 }; profile.liveliness_lease_duration = rmw_time_t { sec: 0, nsec: 0 };
profile profile
} }
} }
// special treatment to convert to/from rust strings. // special treatment to convert to/from rust strings.
// ros strings are owned by ros, assignment is a copy // ros strings are owned by ros, assignment is a copy
impl rosidl_runtime_c__String { impl rosidl_runtime_c__String {
@ -72,8 +68,12 @@ impl rosidl_runtime_c__U16String {
impl rosidl_runtime_c__U16String__Sequence { impl rosidl_runtime_c__U16String__Sequence {
pub fn update(&mut self, values: &[String]) { pub fn update(&mut self, values: &[String]) {
unsafe { rosidl_runtime_c__U16String__Sequence__fini(self as *mut _); } unsafe {
unsafe { rosidl_runtime_c__U16String__Sequence__init(self as *mut _, values.len()); } rosidl_runtime_c__U16String__Sequence__fini(self as *mut _);
}
unsafe {
rosidl_runtime_c__U16String__Sequence__init(self as *mut _, values.len());
}
let strs = unsafe { std::slice::from_raw_parts_mut(self.data, values.len()) }; let strs = unsafe { std::slice::from_raw_parts_mut(self.data, values.len()) };
for (target, source) in strs.iter_mut().zip(values) { for (target, source) in strs.iter_mut().zip(values) {
target.assign(&source); target.assign(&source);
@ -92,8 +92,12 @@ impl rosidl_runtime_c__U16String__Sequence {
impl rosidl_runtime_c__String__Sequence { impl rosidl_runtime_c__String__Sequence {
pub fn update(&mut self, values: &[String]) { pub fn update(&mut self, values: &[String]) {
unsafe { rosidl_runtime_c__String__Sequence__fini(self as *mut _); } unsafe {
unsafe { rosidl_runtime_c__String__Sequence__init(self as *mut _, values.len()); } rosidl_runtime_c__String__Sequence__fini(self as *mut _);
}
unsafe {
rosidl_runtime_c__String__Sequence__init(self as *mut _, values.len());
}
let strs = unsafe { std::slice::from_raw_parts_mut(self.data, values.len()) }; let strs = unsafe { std::slice::from_raw_parts_mut(self.data, values.len()) };
for (target, source) in strs.iter_mut().zip(values) { for (target, source) in strs.iter_mut().zip(values) {
target.assign(&source); target.assign(&source);
@ -130,7 +134,7 @@ macro_rules! primitive_sequence {
} }
} }
} }
} };
} }
primitive_sequence!(rosidl_runtime_c__float32, f32); primitive_sequence!(rosidl_runtime_c__float32, f32);

4
rustfmt.toml Normal file
View File

@ -0,0 +1,4 @@
hard_tabs = false
tab_spaces = 4
reorder_imports = true
newline_style = "Unix"

View File

@ -1,84 +1,85 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
use rcl::*; use rcl::*;
use thiserror::Error;
// TODO // TODO
#[derive(Debug, Fail)] #[derive(Error, Debug)]
pub enum Error { pub enum Error {
// Copied from the generated bindgen // Copied from the generated bindgen
#[fail(display = "RCL_RET_OK")] #[error("RCL_RET_OK")]
RCL_RET_OK, RCL_RET_OK,
#[fail(display = "RCL_RET_ERROR")] #[error("RCL_RET_ERROR")]
RCL_RET_ERROR, RCL_RET_ERROR,
#[fail(display = "RCL_RET_TIMEOUT")] #[error("RCL_RET_TIMEOUT")]
RCL_RET_TIMEOUT, RCL_RET_TIMEOUT,
#[fail(display = "RCL_RET_BAD_ALLOC")] #[error("RCL_RET_BAD_ALLOC")]
RCL_RET_BAD_ALLOC, RCL_RET_BAD_ALLOC,
#[fail(display = "RCL_RET_INVALID_ARGUMENT")] #[error("RCL_RET_INVALID_ARGUMENT")]
RCL_RET_INVALID_ARGUMENT, RCL_RET_INVALID_ARGUMENT,
#[fail(display = "RCL_RET_UNSUPPORTED")] #[error("RCL_RET_UNSUPPORTED")]
RCL_RET_UNSUPPORTED, RCL_RET_UNSUPPORTED,
#[fail(display = "RCL_RET_ALREADY_INIT")] #[error("RCL_RET_ALREADY_INIT")]
RCL_RET_ALREADY_INIT, RCL_RET_ALREADY_INIT,
#[fail(display = "RCL_RET_NOT_INIT")] #[error("RCL_RET_NOT_INIT")]
RCL_RET_NOT_INIT, RCL_RET_NOT_INIT,
#[fail(display = "RCL_RET_MISMATCHED_RMW_ID")] #[error("RCL_RET_MISMATCHED_RMW_ID")]
RCL_RET_MISMATCHED_RMW_ID, RCL_RET_MISMATCHED_RMW_ID,
#[fail(display = "RCL_RET_TOPIC_NAME_INVALID")] #[error("RCL_RET_TOPIC_NAME_INVALID")]
RCL_RET_TOPIC_NAME_INVALID, RCL_RET_TOPIC_NAME_INVALID,
#[fail(display = "RCL_RET_SERVICE_NAME_INVALID")] #[error("RCL_RET_SERVICE_NAME_INVALID")]
RCL_RET_SERVICE_NAME_INVALID, RCL_RET_SERVICE_NAME_INVALID,
#[fail(display = "RCL_RET_UNKNOWN_SUBSTITUTION")] #[error("RCL_RET_UNKNOWN_SUBSTITUTION")]
RCL_RET_UNKNOWN_SUBSTITUTION, RCL_RET_UNKNOWN_SUBSTITUTION,
#[fail(display = "RCL_RET_ALREADY_SHUTDOWN")] #[error("RCL_RET_ALREADY_SHUTDOWN")]
RCL_RET_ALREADY_SHUTDOWN, RCL_RET_ALREADY_SHUTDOWN,
#[fail(display = "RCL_RET_NODE_INVALID")] #[error("RCL_RET_NODE_INVALID")]
RCL_RET_NODE_INVALID, RCL_RET_NODE_INVALID,
#[fail(display = "RCL_RET_NODE_INVALID_NAME")] #[error("RCL_RET_NODE_INVALID_NAME")]
RCL_RET_NODE_INVALID_NAME, RCL_RET_NODE_INVALID_NAME,
#[fail(display = "RCL_RET_NODE_INVALID_NAMESPACE")] #[error("RCL_RET_NODE_INVALID_NAMESPACE")]
RCL_RET_NODE_INVALID_NAMESPACE, RCL_RET_NODE_INVALID_NAMESPACE,
#[fail(display = "RCL_RET_PUBLISHER_INVALID")] #[error("RCL_RET_PUBLISHER_INVALID")]
RCL_RET_PUBLISHER_INVALID, RCL_RET_PUBLISHER_INVALID,
#[fail(display = "RCL_RET_SUBSCRIPTION_INVALID")] #[error("RCL_RET_SUBSCRIPTION_INVALID")]
RCL_RET_SUBSCRIPTION_INVALID, RCL_RET_SUBSCRIPTION_INVALID,
#[fail(display = "RCL_RET_SUBSCRIPTION_TAKE_FAILED")] #[error("RCL_RET_SUBSCRIPTION_TAKE_FAILED")]
RCL_RET_SUBSCRIPTION_TAKE_FAILED, RCL_RET_SUBSCRIPTION_TAKE_FAILED,
#[fail(display = "RCL_RET_CLIENT_INVALID")] #[error("RCL_RET_CLIENT_INVALID")]
RCL_RET_CLIENT_INVALID, RCL_RET_CLIENT_INVALID,
#[fail(display = "RCL_RET_CLIENT_TAKE_FAILED")] #[error("RCL_RET_CLIENT_TAKE_FAILED")]
RCL_RET_CLIENT_TAKE_FAILED, RCL_RET_CLIENT_TAKE_FAILED,
#[fail(display = "RCL_RET_SERVICE_INVALID")] #[error("RCL_RET_SERVICE_INVALID")]
RCL_RET_SERVICE_INVALID, RCL_RET_SERVICE_INVALID,
#[fail(display = "RCL_RET_SERVICE_TAKE_FAILED")] #[error("RCL_RET_SERVICE_TAKE_FAILED")]
RCL_RET_SERVICE_TAKE_FAILED, RCL_RET_SERVICE_TAKE_FAILED,
#[fail(display = "RCL_RET_TIMER_INVALID")] #[error("RCL_RET_TIMER_INVALID")]
RCL_RET_TIMER_INVALID, RCL_RET_TIMER_INVALID,
#[fail(display = "RCL_RET_TIMER_CANCELED")] #[error("RCL_RET_TIMER_CANCELED")]
RCL_RET_TIMER_CANCELED, RCL_RET_TIMER_CANCELED,
#[fail(display = "RCL_RET_WAIT_SET_INVALID")] #[error("RCL_RET_WAIT_SET_INVALID")]
RCL_RET_WAIT_SET_INVALID, RCL_RET_WAIT_SET_INVALID,
#[fail(display = "RCL_RET_WAIT_SET_EMPTY")] #[error("RCL_RET_WAIT_SET_EMPTY")]
RCL_RET_WAIT_SET_EMPTY, RCL_RET_WAIT_SET_EMPTY,
#[fail(display = "RCL_RET_WAIT_SET_FULL")] #[error("RCL_RET_WAIT_SET_FULL")]
RCL_RET_WAIT_SET_FULL, RCL_RET_WAIT_SET_FULL,
#[fail(display = "RCL_RET_INVALID_REMAP_RULE")] #[error("RCL_RET_INVALID_REMAP_RULE")]
RCL_RET_INVALID_REMAP_RULE, RCL_RET_INVALID_REMAP_RULE,
#[fail(display = "RCL_RET_WRONG_LEXEME")] #[error("RCL_RET_WRONG_LEXEME")]
RCL_RET_WRONG_LEXEME, RCL_RET_WRONG_LEXEME,
#[fail(display = "RCL_RET_INVALID_PARAM_RULE")] #[error("RCL_RET_INVALID_PARAM_RULE")]
RCL_RET_INVALID_PARAM_RULE, RCL_RET_INVALID_PARAM_RULE,
#[fail(display = "RCL_RET_INVALID_LOG_LEVEL_RULE")] #[error("RCL_RET_INVALID_LOG_LEVEL_RULE")]
RCL_RET_INVALID_LOG_LEVEL_RULE, RCL_RET_INVALID_LOG_LEVEL_RULE,
#[fail(display = "RCL_RET_EVENT_INVALID")] #[error("RCL_RET_EVENT_INVALID")]
RCL_RET_EVENT_INVALID, RCL_RET_EVENT_INVALID,
#[fail(display = "RCL_RET_EVENT_TAKE_FAILED")] #[error("RCL_RET_EVENT_TAKE_FAILED")]
RCL_RET_EVENT_TAKE_FAILED, RCL_RET_EVENT_TAKE_FAILED,
// Our own errors // Our own errors
#[fail(display = "No typesupport build for the message type: {}", msgtype)] #[error("No typesupport built for the message type: {}", msgtype)]
InvalidMessageType { msgtype: String }, InvalidMessageType { msgtype: String },
#[fail(display = "Serde error: {}", err)] #[error("Serde error: {}", err)]
SerdeError { err: String }, SerdeError { err: String },
} }

View File

@ -1,14 +1,16 @@
include!(concat!(env!("OUT_DIR"), "/_r2r_generated_msgs.rs")); include!(concat!(env!("OUT_DIR"), "/_r2r_generated_msgs.rs"));
include!(concat!(env!("OUT_DIR"), "/_r2r_generated_untyped_helper.rs")); include!(concat!(
env!("OUT_DIR"),
"/_r2r_generated_untyped_helper.rs"
));
#[macro_use] extern crate failure_derive;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::ffi::{CString,CStr}; use std::collections::HashMap;
use std::mem::MaybeUninit; use std::ffi::{CStr, CString};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::time::Duration; use std::time::Duration;
use std::collections::HashMap;
use msg_gen::*; use msg_gen::*;
use rcl::*; use rcl::*;
@ -46,7 +48,6 @@ pub trait WrappedActionTypeSupport {
fn get_ts() -> &'static rosidl_action_type_support_t; fn get_ts() -> &'static rosidl_action_type_support_t;
} }
#[derive(Debug)] #[derive(Debug)]
pub struct WrappedNativeMsg<T> pub struct WrappedNativeMsg<T>
where where
@ -60,27 +61,33 @@ pub struct WrappedNativeMsgUntyped {
ts: &'static rosidl_message_type_support_t, ts: &'static rosidl_message_type_support_t,
msg: *mut std::os::raw::c_void, msg: *mut std::os::raw::c_void,
destroy: fn(*mut std::os::raw::c_void), destroy: fn(*mut std::os::raw::c_void),
msg_to_json: fn(native: *const std::os::raw::c_void) -> msg_to_json: fn(
std::result::Result<serde_json::Value, serde_json::error::Error>, native: *const std::os::raw::c_void,
msg_from_json: fn(native: *mut std::os::raw::c_void, json: serde_json::Value) -> ) -> std::result::Result<serde_json::Value, serde_json::error::Error>,
std::result::Result<(), serde_json::error::Error>, msg_from_json: fn(
native: *mut std::os::raw::c_void,
json: serde_json::Value,
) -> std::result::Result<(), serde_json::error::Error>,
} }
impl WrappedNativeMsgUntyped { impl WrappedNativeMsgUntyped {
fn new<T>() -> Self where T: WrappedTypesupport { fn new<T>() -> Self
let destroy = | native: *mut std::os::raw::c_void | { where
T: WrappedTypesupport,
{
let destroy = |native: *mut std::os::raw::c_void| {
let native_msg = native as *mut T::CStruct; let native_msg = native as *mut T::CStruct;
T::destroy_msg(native_msg); T::destroy_msg(native_msg);
}; };
let msg_to_json = | native: *const std::os::raw::c_void | { let msg_to_json = |native: *const std::os::raw::c_void| {
let msg = unsafe { T::from_native(&*(native as *const T::CStruct)) }; let msg = unsafe { T::from_native(&*(native as *const T::CStruct)) };
serde_json::to_value(&msg) serde_json::to_value(&msg)
}; };
let msg_from_json = | native: *mut std::os::raw::c_void, json: serde_json::Value | { let msg_from_json = |native: *mut std::os::raw::c_void, json: serde_json::Value| {
serde_json::from_value(json).map(|msg: T| { serde_json::from_value(json).map(|msg: T| unsafe {
unsafe { msg.copy_to_native(&mut *(native as *mut T::CStruct)); } msg.copy_to_native(&mut *(native as *mut T::CStruct));
}) })
}; };
@ -95,12 +102,15 @@ impl WrappedNativeMsgUntyped {
fn to_json(&self) -> Result<serde_json::Value> { fn to_json(&self) -> Result<serde_json::Value> {
let json = (self.msg_to_json)(self.msg); let json = (self.msg_to_json)(self.msg);
json.map_err(|serde_err|Error::SerdeError { err: serde_err.to_string() }) json.map_err(|serde_err| Error::SerdeError {
err: serde_err.to_string(),
})
} }
fn from_json(&mut self, json: serde_json::Value) -> Result<()> { fn from_json(&mut self, json: serde_json::Value) -> Result<()> {
(self.msg_from_json)(self.msg, json). (self.msg_from_json)(self.msg, json).map_err(|serde_err| Error::SerdeError {
map_err(|serde_err|Error::SerdeError { err: serde_err.to_string() }) err: serde_err.to_string(),
})
} }
pub fn void_ptr(&self) -> *const std::os::raw::c_void { pub fn void_ptr(&self) -> *const std::os::raw::c_void {
@ -253,8 +263,7 @@ where
} }
} }
impl Sub for WrappedSubUntyped impl Sub for WrappedSubUntyped {
{
fn handle(&self) -> &rcl_subscription_t { fn handle(&self) -> &rcl_subscription_t {
&self.rcl_handle &self.rcl_handle
} }
@ -275,7 +284,6 @@ impl Sub for WrappedSubUntyped
} }
} }
// services // services
struct WrappedService<T> struct WrappedService<T>
where where
@ -317,7 +325,11 @@ where
let response = (self.callback)(request); let response = (self.callback)(request);
let mut native_response = WrappedNativeMsg::<T::Response>::from(&response); let mut native_response = WrappedNativeMsg::<T::Response>::from(&response);
let res = unsafe { let res = unsafe {
rcl_send_response(&self.rcl_handle, &mut self.rcl_request, native_response.void_ptr_mut()) rcl_send_response(
&self.rcl_handle,
&mut self.rcl_request,
native_response.void_ptr_mut(),
)
}; };
// TODO // TODO
@ -379,10 +391,16 @@ where
} else { } else {
// I don't think this should be able to occur? Let's panic so we // I don't think this should be able to occur? Let's panic so we
// find out... // find out...
let we_have: String = self.callbacks.iter() let we_have: String = self
.callbacks
.iter()
.map(|(id, _)| id.to_string()) .map(|(id, _)| id.to_string())
.collect::<Vec<_>>().join(","); .collect::<Vec<_>>()
eprintln!("no such req id: {}, we have [{}], ignoring", req_id, we_have); .join(",");
eprintln!(
"no such req id: {}, we have [{}], ignoring",
req_id, we_have
);
} }
} }
@ -447,10 +465,9 @@ where
client_: Weak<Mutex<WrappedClient<T>>>, client_: Weak<Mutex<WrappedClient<T>>>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Context { pub struct Context {
context_handle: Arc<Mutex<Box<rcl_context_t>>>, context_handle: Arc<Mutex<ContextHandle>>,
} }
// Not 100% about this one. From our end the context is rarely used // Not 100% about this one. From our end the context is rarely used
@ -463,47 +480,76 @@ impl Context {
pub fn create() -> Result<Context> { pub fn create() -> Result<Context> {
let mut ctx: Box<rcl_context_t> = unsafe { Box::new(rcl_get_zero_initialized_context()) }; let mut ctx: Box<rcl_context_t> = unsafe { Box::new(rcl_get_zero_initialized_context()) };
// argc/v // argc/v
let args = std::env::args().map(|arg| CString::new(arg).unwrap() ).collect::<Vec<CString>>(); let args = std::env::args()
let mut c_args = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<*const ::std::os::raw::c_char>>(); .map(|arg| CString::new(arg).unwrap())
.collect::<Vec<CString>>();
let mut c_args = args
.iter()
.map(|arg| arg.as_ptr())
.collect::<Vec<*const ::std::os::raw::c_char>>();
c_args.push(std::ptr::null()); c_args.push(std::ptr::null());
let is_valid = unsafe { let is_valid = unsafe {
let allocator = rcutils_get_default_allocator(); let allocator = rcutils_get_default_allocator();
let mut init_options = rcl_get_zero_initialized_init_options(); let mut init_options = rcl_get_zero_initialized_init_options();
rcl_init_options_init(&mut init_options, allocator); rcl_init_options_init(&mut init_options, allocator);
rcl_init((c_args.len() - 1) as ::std::os::raw::c_int, c_args.as_ptr(), &init_options, ctx.as_mut()); rcl_init(
(c_args.len() - 1) as ::std::os::raw::c_int,
c_args.as_ptr(),
&init_options,
ctx.as_mut(),
);
rcl_init_options_fini(&mut init_options as *mut _); rcl_init_options_fini(&mut init_options as *mut _);
rcl_context_is_valid(ctx.as_mut()) rcl_context_is_valid(ctx.as_mut())
}; };
let logging_ok = unsafe { let logging_ok = unsafe {
let _guard = log_guard(); let _guard = log_guard();
let ret = rcl_logging_configure(&ctx.as_ref().global_arguments, let ret = rcl_logging_configure(
&rcutils_get_default_allocator()); &ctx.as_ref().global_arguments,
&rcutils_get_default_allocator(),
);
ret == RCL_RET_OK as i32 ret == RCL_RET_OK as i32
}; };
if is_valid && logging_ok { if is_valid && logging_ok {
Ok(Context { Ok(Context {
context_handle: Arc::new(Mutex::new(ctx)), context_handle: Arc::new(Mutex::new(ContextHandle(ctx))),
}) })
} else { } else {
Err(Error::RCL_RET_ERROR) // TODO Err(Error::RCL_RET_ERROR) // TODO
} }
} }
pub fn is_valid(&self) -> bool {
let mut ctx = self.context_handle.lock().unwrap();
unsafe { rcl_context_is_valid(ctx.as_mut()) }
}
} }
#[derive(Debug, Clone)] #[derive(Debug)]
pub struct ContextHandle(Arc<Mutex<Box<rcl_context_t>>>); pub struct ContextHandle(Box<rcl_context_t>);
impl Deref for ContextHandle {
type Target = Box<rcl_context_t>;
fn deref<'a>(&'a self) -> &'a Box<rcl_context_t> {
&self.0
}
}
impl DerefMut for ContextHandle {
fn deref_mut<'a>(&'a mut self) -> &'a mut Box<rcl_context_t> {
&mut self.0
}
}
impl Drop for ContextHandle { impl Drop for ContextHandle {
fn drop(&mut self) { fn drop(&mut self) {
println!("DROPPING CONTEXT HANDLE!");
let mut ctx_handle = self.0.lock().unwrap();
// TODO: error handling? atleast probably need rcl_reset_error // TODO: error handling? atleast probably need rcl_reset_error
unsafe { unsafe {
rcl::rcl_shutdown(ctx_handle.as_mut()); rcl::rcl_shutdown(self.0.as_mut());
rcl::rcl_context_fini(ctx_handle.as_mut()); rcl::rcl_context_fini(self.0.as_mut());
} }
} }
} }
@ -536,50 +582,46 @@ impl ParameterValue {
ParameterValue::String(string) ParameterValue::String(string)
} else if v.byte_array_value != std::ptr::null_mut() { } else if v.byte_array_value != std::ptr::null_mut() {
let vals = unsafe { let vals = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts((*v.byte_array_value).values, (*v.byte_array_value).size)
(*v.byte_array_value).values,
(*v.byte_array_value).size)
}; };
ParameterValue::ByteArray(vals.iter().cloned().collect()) ParameterValue::ByteArray(vals.iter().cloned().collect())
} } else if v.bool_array_value != std::ptr::null_mut() {
else if v.bool_array_value != std::ptr::null_mut() {
let vals = unsafe { let vals = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts((*v.bool_array_value).values, (*v.bool_array_value).size)
(*v.bool_array_value).values,
(*v.bool_array_value).size)
}; };
ParameterValue::BoolArray(vals.iter().cloned().collect()) ParameterValue::BoolArray(vals.iter().cloned().collect())
} } else if v.integer_array_value != std::ptr::null_mut() {
else if v.integer_array_value != std::ptr::null_mut() {
let vals = unsafe { let vals = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts(
(*v.integer_array_value).values, (*v.integer_array_value).values,
(*v.integer_array_value).size) (*v.integer_array_value).size,
)
}; };
ParameterValue::IntegerArray(vals.iter().cloned().collect()) ParameterValue::IntegerArray(vals.iter().cloned().collect())
} } else if v.double_array_value != std::ptr::null_mut() {
else if v.double_array_value != std::ptr::null_mut() {
let vals = unsafe { let vals = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts(
(*v.double_array_value).values, (*v.double_array_value).values,
(*v.double_array_value).size) (*v.double_array_value).size,
)
}; };
ParameterValue::DoubleArray(vals.iter().cloned().collect()) ParameterValue::DoubleArray(vals.iter().cloned().collect())
} } else if v.string_array_value != std::ptr::null_mut() {
else if v.string_array_value != std::ptr::null_mut() {
let vals = unsafe { let vals = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts(
(*v.string_array_value).data, (*v.string_array_value).data,
(*v.string_array_value).size) (*v.string_array_value).size,
)
}; };
let s = vals.iter().map(|cs| { let s = vals
.iter()
.map(|cs| {
let s = unsafe { CStr::from_ptr(*cs) }; let s = unsafe { CStr::from_ptr(*cs) };
s.to_str().unwrap_or("").to_owned() s.to_str().unwrap_or("").to_owned()
}).collect(); })
.collect();
ParameterValue::StringArray(s) ParameterValue::StringArray(s)
} } else {
else {
ParameterValue::NotSet ParameterValue::NotSet
} }
} }
@ -633,9 +675,8 @@ impl Node {
let ctx = self.context.context_handle.lock().unwrap(); let ctx = self.context.context_handle.lock().unwrap();
let mut params: Box<*mut rcl_params_t> = Box::new(std::ptr::null_mut()); let mut params: Box<*mut rcl_params_t> = Box::new(std::ptr::null_mut());
let ret = unsafe { let ret =
rcl_arguments_get_param_overrides(&ctx.global_arguments, params.as_mut()) unsafe { rcl_arguments_get_param_overrides(&ctx.global_arguments, params.as_mut()) };
};
if ret != RCL_RET_OK as i32 { if ret != RCL_RET_OK as i32 {
eprintln!("could not read parameters: {}", ret); eprintln!("could not read parameters: {}", ret);
return Err(Error::from_rcl_error(ret)); return Err(Error::from_rcl_error(ret));
@ -647,12 +688,16 @@ impl Node {
let node_names = unsafe { let node_names = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts(
(*(*params.as_ref())).node_names, (*(*params.as_ref())).num_nodes) (*(*params.as_ref())).node_names,
(*(*params.as_ref())).num_nodes,
)
}; };
let node_params = unsafe { let node_params = unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts(
(*(*params.as_ref())).params, (*(*params.as_ref())).num_nodes) (*(*params.as_ref())).params,
(*(*params.as_ref())).num_nodes,
)
}; };
let qualified_name = self.fully_qualified_name()?; let qualified_name = self.fully_qualified_name()?;
@ -665,25 +710,22 @@ impl Node {
// This is copied from rclcpp, but there is a comment there suggesting // This is copied from rclcpp, but there is a comment there suggesting
// that more wildcards will be added in the future. Take note and mimic // that more wildcards will be added in the future. Take note and mimic
// their behavior. // their behavior.
if !( if !(node_name == "/**"
node_name == "/**" || || node_name == "**"
node_name == "**" || || qualified_name == node_name
qualified_name == node_name || || name == node_name)
name == node_name {
) {
continue; continue;
} }
// make key value pairs. // make key value pairs.
let param_names = unsafe { let param_names =
std::slice::from_raw_parts(np.parameter_names,np.num_params) unsafe { std::slice::from_raw_parts(np.parameter_names, np.num_params) };
};
let param_values = unsafe { let param_values =
std::slice::from_raw_parts(np.parameter_values,np.num_params) unsafe { std::slice::from_raw_parts(np.parameter_values, np.num_params) };
};
for (s,v) in param_names.iter().zip(param_values) { for (s, v) in param_names.iter().zip(param_values) {
let s = unsafe { CStr::from_ptr(*s) }; let s = unsafe { CStr::from_ptr(*s) };
let key = s.to_str().unwrap_or(""); let key = s.to_str().unwrap_or("");
let val = ParameterValue::from_rcl(&*v); let val = ParameterValue::from_rcl(&*v);
@ -691,11 +733,10 @@ impl Node {
} }
} }
unsafe { rcl_yaml_node_struct_fini(*params) } ; unsafe { rcl_yaml_node_struct_fini(*params) };
Ok(()) Ok(())
} }
pub fn create(ctx: Context, name: &str, namespace: &str) -> Result<Node> { pub fn create(ctx: Context, name: &str, namespace: &str) -> Result<Node> {
// cleanup default options. // cleanup default options.
let (res, node_handle) = { let (res, node_handle) = {
@ -737,9 +778,13 @@ impl Node {
} }
} }
fn create_subscription_helper(&mut self, topic: &str, ts: *const rosidl_message_type_support_t) -> Result<rcl_subscription_t> { fn create_subscription_helper(
&mut self,
topic: &str,
ts: *const rosidl_message_type_support_t,
) -> Result<rcl_subscription_t> {
let mut subscription_handle = unsafe { rcl_get_zero_initialized_subscription() }; let mut subscription_handle = unsafe { rcl_get_zero_initialized_subscription() };
let topic_c_string = CString::new(topic).map_err(|_|Error::RCL_RET_INVALID_ARGUMENT)?; let topic_c_string = CString::new(topic).map_err(|_| Error::RCL_RET_INVALID_ARGUMENT)?;
let result = unsafe { let result = unsafe {
let mut subscription_options = rcl_subscription_get_default_options(); let mut subscription_options = rcl_subscription_get_default_options();
@ -814,17 +859,24 @@ impl Node {
Ok(self.subs.last().unwrap().handle()) // hmm... Ok(self.subs.last().unwrap().handle()) // hmm...
} }
pub fn create_service_helper(&mut self, service_name: &str, pub fn create_service_helper(
service_ts: *const rosidl_service_type_support_t) &mut self,
-> Result<rcl_service_t> { service_name: &str,
service_ts: *const rosidl_service_type_support_t,
) -> Result<rcl_service_t> {
let mut service_handle = unsafe { rcl_get_zero_initialized_service() }; let mut service_handle = unsafe { rcl_get_zero_initialized_service() };
let service_name_c_string = CString::new(service_name) let service_name_c_string =
.map_err(|_|Error::RCL_RET_INVALID_ARGUMENT)?; CString::new(service_name).map_err(|_| Error::RCL_RET_INVALID_ARGUMENT)?;
let result = unsafe { let result = unsafe {
let service_options = rcl_service_get_default_options(); let service_options = rcl_service_get_default_options();
rcl_service_init(&mut service_handle, self.node_handle.as_mut(), rcl_service_init(
service_ts, service_name_c_string.as_ptr(), &service_options) &mut service_handle,
self.node_handle.as_mut(),
service_ts,
service_name_c_string.as_ptr(),
&service_options,
)
}; };
if result == RCL_RET_OK as i32 { if result == RCL_RET_OK as i32 {
Ok(service_handle) Ok(service_handle)
@ -856,18 +908,24 @@ impl Node {
Ok(self.services.last().unwrap().handle()) // hmm... Ok(self.services.last().unwrap().handle()) // hmm...
} }
pub fn create_client_helper(
pub fn create_client_helper(&mut self, service_name: &str, &mut self,
service_ts: *const rosidl_service_type_support_t) service_name: &str,
-> Result<rcl_client_t> { service_ts: *const rosidl_service_type_support_t,
) -> Result<rcl_client_t> {
let mut client_handle = unsafe { rcl_get_zero_initialized_client() }; let mut client_handle = unsafe { rcl_get_zero_initialized_client() };
let service_name_c_string = CString::new(service_name) let service_name_c_string =
.map_err(|_|Error::RCL_RET_INVALID_ARGUMENT)?; CString::new(service_name).map_err(|_| Error::RCL_RET_INVALID_ARGUMENT)?;
let result = unsafe { let result = unsafe {
let client_options = rcl_client_get_default_options(); let client_options = rcl_client_get_default_options();
rcl_client_init(&mut client_handle, self.node_handle.as_mut(), rcl_client_init(
service_ts, service_name_c_string.as_ptr(), &client_options) &mut client_handle,
self.node_handle.as_mut(),
service_ts,
service_name_c_string.as_ptr(),
&client_options,
)
}; };
if result == RCL_RET_OK as i32 { if result == RCL_RET_OK as i32 {
Ok(client_handle) Ok(client_handle)
@ -876,16 +934,13 @@ impl Node {
} }
} }
pub fn create_client<T: 'static>( pub fn create_client<T: 'static>(&mut self, service_name: &str) -> Result<Client<T>>
&mut self,
service_name: &str,
) -> Result<Client<T>>
where where
T: WrappedServiceTypeSupport, T: WrappedServiceTypeSupport,
{ {
let client_handle = self.create_client_helper(service_name, T::get_ts())?; let client_handle = self.create_client_helper(service_name, T::get_ts())?;
let cloned_ch = rcl_client_t { let cloned_ch = rcl_client_t {
impl_: client_handle.impl_ impl_: client_handle.impl_,
}; };
let ws = WrappedClient::<T> { let ws = WrappedClient::<T> {
rcl_handle: cloned_ch, rcl_handle: cloned_ch,
@ -900,21 +955,22 @@ impl Node {
let arc = Arc::new(Mutex::new(ws)); let arc = Arc::new(Mutex::new(ws));
let client_ = Arc::downgrade(&arc); let client_ = Arc::downgrade(&arc);
self.clients.push((client_handle, arc)); self.clients.push((client_handle, arc));
let c = Client { let c = Client { client_ };
client_
};
Ok(c) Ok(c)
} }
pub fn service_available<T: WrappedServiceTypeSupport>(&self, client: &Client<T>) -> Result<bool> { pub fn service_available<T: WrappedServiceTypeSupport>(
let client = client.client_.upgrade().ok_or(Error::RCL_RET_CLIENT_INVALID)?; &self,
client: &Client<T>,
) -> Result<bool> {
let client = client
.client_
.upgrade()
.ok_or(Error::RCL_RET_CLIENT_INVALID)?;
let client = client.lock().unwrap(); let client = client.lock().unwrap();
let mut avail = false; let mut avail = false;
let result = unsafe { let result = unsafe {
rcl_service_server_is_available( rcl_service_server_is_available(self.node_handle.as_ref(), client.handle(), &mut avail)
self.node_handle.as_ref(),
client.handle(),
&mut avail)
}; };
if result == RCL_RET_OK as i32 { if result == RCL_RET_OK as i32 {
@ -925,11 +981,13 @@ impl Node {
} }
} }
pub fn create_publisher_helper(&mut self, topic: &str, pub fn create_publisher_helper(
typesupport: *const rosidl_message_type_support_t) -> Result<rcl_publisher_t> { &mut self,
topic: &str,
typesupport: *const rosidl_message_type_support_t,
) -> Result<rcl_publisher_t> {
let mut publisher_handle = unsafe { rcl_get_zero_initialized_publisher() }; let mut publisher_handle = unsafe { rcl_get_zero_initialized_publisher() };
let topic_c_string = CString::new(topic) let topic_c_string = CString::new(topic).map_err(|_| Error::RCL_RET_INVALID_ARGUMENT)?;
.map_err(|_|Error::RCL_RET_INVALID_ARGUMENT)?;
let result = unsafe { let result = unsafe {
let mut publisher_options = rcl_publisher_get_default_options(); let mut publisher_options = rcl_publisher_get_default_options();
@ -949,7 +1007,6 @@ impl Node {
} }
} }
pub fn create_publisher<T>(&mut self, topic: &str) -> Result<Publisher<T>> pub fn create_publisher<T>(&mut self, topic: &str) -> Result<Publisher<T>>
where where
T: WrappedTypesupport, T: WrappedTypesupport,
@ -963,7 +1020,11 @@ impl Node {
Ok(p) Ok(p)
} }
pub fn create_publisher_untyped(&mut self, topic: &str, topic_type: &str) -> Result<PublisherUntyped> { pub fn create_publisher_untyped(
&mut self,
topic: &str,
topic_type: &str,
) -> Result<PublisherUntyped> {
let dummy = WrappedNativeMsgUntyped::new_from(topic_type)?; // TODO, get ts without allocating msg let dummy = WrappedNativeMsgUntyped::new_from(topic_type)?; // TODO, get ts without allocating msg
let publisher_handle = self.create_publisher_helper(topic, dummy.ts)?; let publisher_handle = self.create_publisher_helper(topic, dummy.ts)?;
@ -1030,9 +1091,7 @@ impl Node {
} }
} }
let ret = unsafe { let ret = unsafe { rcl_wait(&mut ws, timeout) };
rcl_wait(&mut ws, timeout)
};
if ret == RCL_RET_TIMEOUT as i32 { if ret == RCL_RET_TIMEOUT as i32 {
unsafe { unsafe {
@ -1056,21 +1115,19 @@ impl Node {
} }
} }
let ws_timers = let ws_timers = unsafe { std::slice::from_raw_parts(ws.timers, ws.size_of_timers) };
unsafe { std::slice::from_raw_parts(ws.timers, ws.size_of_timers) };
assert_eq!(ws_timers.len(), self.timers.len()); assert_eq!(ws_timers.len(), self.timers.len());
for (s, ws_s) in self.timers.iter_mut().zip(ws_timers) { for (s, ws_s) in self.timers.iter_mut().zip(ws_timers) {
if ws_s != &std::ptr::null() { if ws_s != &std::ptr::null() {
let mut is_ready = false; let mut is_ready = false;
let ret = unsafe { let ret = unsafe { rcl_timer_is_ready(&s.timer_handle, &mut is_ready) };
rcl_timer_is_ready(&s.timer_handle, &mut is_ready)
};
if ret == RCL_RET_OK as i32 { if ret == RCL_RET_OK as i32 {
if is_ready { if is_ready {
let mut nanos = 0i64; let mut nanos = 0i64;
// todo: error handling // todo: error handling
let ret = unsafe { rcl_timer_get_time_since_last_call(&s.timer_handle, let ret = unsafe {
&mut nanos) }; rcl_timer_get_time_since_last_call(&s.timer_handle, &mut nanos)
};
if ret == RCL_RET_OK as i32 { if ret == RCL_RET_OK as i32 {
let ret = unsafe { rcl_timer_call(&mut s.timer_handle) }; let ret = unsafe { rcl_timer_call(&mut s.timer_handle) };
if ret == RCL_RET_OK as i32 { if ret == RCL_RET_OK as i32 {
@ -1082,8 +1139,7 @@ impl Node {
} }
} }
let ws_clients = let ws_clients = unsafe { std::slice::from_raw_parts(ws.clients, ws.size_of_clients) };
unsafe { std::slice::from_raw_parts(ws.clients, ws.size_of_clients) };
assert_eq!(ws_clients.len(), self.clients.len()); assert_eq!(ws_clients.len(), self.clients.len());
for ((_, s), ws_s) in self.clients.iter_mut().zip(ws_clients) { for ((_, s), ws_s) in self.clients.iter_mut().zip(ws_clients) {
if ws_s != &std::ptr::null() { if ws_s != &std::ptr::null() {
@ -1097,8 +1153,7 @@ impl Node {
} }
} }
let ws_services = let ws_services = unsafe { std::slice::from_raw_parts(ws.services, ws.size_of_services) };
unsafe { std::slice::from_raw_parts(ws.services, ws.size_of_services) };
assert_eq!(ws_services.len(), self.services.len()); assert_eq!(ws_services.len(), self.services.len());
for (s, ws_s) in self.services.iter_mut().zip(ws_services) { for (s, ws_s) in self.services.iter_mut().zip(ws_services) {
if ws_s != &std::ptr::null() { if ws_s != &std::ptr::null() {
@ -1116,11 +1171,15 @@ impl Node {
} }
} }
pub fn get_topic_names_and_types(&self) -> Result<HashMap<String, Vec<String>>> { pub fn get_topic_names_and_types(&self) -> Result<HashMap<String, Vec<String>>> {
let mut tnat = unsafe { rmw_get_zero_initialized_names_and_types() }; let mut tnat = unsafe { rmw_get_zero_initialized_names_and_types() };
let ret = unsafe { let ret = unsafe {
rcl_get_topic_names_and_types(self.node_handle.as_ref(), &mut rcutils_get_default_allocator(), false, &mut tnat) rcl_get_topic_names_and_types(
self.node_handle.as_ref(),
&mut rcutils_get_default_allocator(),
false,
&mut tnat,
)
}; };
if ret != RCL_RET_OK as i32 { if ret != RCL_RET_OK as i32 {
eprintln!("could not get topic names and types {}", ret); eprintln!("could not get topic names and types {}", ret);
@ -1131,27 +1190,35 @@ impl Node {
let types = unsafe { std::slice::from_raw_parts(tnat.types, tnat.names.size) }; let types = unsafe { std::slice::from_raw_parts(tnat.types, tnat.names.size) };
let mut res = HashMap::new(); let mut res = HashMap::new();
for (n,t) in names.iter().zip(types) { for (n, t) in names.iter().zip(types) {
let topic_name = unsafe {CStr::from_ptr(*n).to_str().unwrap().to_owned() }; let topic_name = unsafe { CStr::from_ptr(*n).to_str().unwrap().to_owned() };
let topic_types = unsafe { std::slice::from_raw_parts(t, t.size) }; let topic_types = unsafe { std::slice::from_raw_parts(t, t.size) };
let topic_types: Vec<String> = unsafe { let topic_types: Vec<String> = unsafe {
topic_types.iter().map(|t| CStr::from_ptr(*((*t).data)).to_str().unwrap().to_owned()).collect() topic_types
.iter()
.map(|t| CStr::from_ptr(*((*t).data)).to_str().unwrap().to_owned())
.collect()
}; };
res.insert(topic_name, topic_types); res.insert(topic_name, topic_types);
} }
unsafe { rmw_names_and_types_fini(&mut tnat); } // TODO: check return value unsafe {
rmw_names_and_types_fini(&mut tnat);
} // TODO: check return value
Ok(res) Ok(res)
} }
pub fn create_wall_timer( pub fn create_wall_timer(
&mut self, &mut self,
period: Duration, period: Duration,
callback: Box<dyn FnMut(Duration) -> ()>) -> Result<&Timer> { callback: Box<dyn FnMut(Duration) -> ()>,
) -> Result<&Timer> {
let mut clock_handle = MaybeUninit::<rcl_clock_t>::uninit(); let mut clock_handle = MaybeUninit::<rcl_clock_t>::uninit();
let ret = unsafe { let ret = unsafe {
rcl_steady_clock_init(clock_handle.as_mut_ptr(), &mut rcutils_get_default_allocator()) rcl_steady_clock_init(
clock_handle.as_mut_ptr(),
&mut rcutils_get_default_allocator(),
)
}; };
if ret != RCL_RET_OK as i32 { if ret != RCL_RET_OK as i32 {
eprintln!("could not create steady clock: {}", ret); eprintln!("could not create steady clock: {}", ret);
@ -1164,9 +1231,14 @@ impl Node {
{ {
let mut ctx = self.context.context_handle.lock().unwrap(); let mut ctx = self.context.context_handle.lock().unwrap();
let ret = unsafe { let ret = unsafe {
rcl_timer_init(&mut timer_handle, clock_handle.as_mut(), rcl_timer_init(
ctx.as_mut(), period.as_nanos() as i64, &mut timer_handle,
None, rcutils_get_default_allocator()) clock_handle.as_mut(),
ctx.as_mut(),
period.as_nanos() as i64,
None,
rcutils_get_default_allocator(),
)
}; };
if ret != RCL_RET_OK as i32 { if ret != RCL_RET_OK as i32 {
@ -1175,10 +1247,14 @@ impl Node {
} }
} }
let timer = Timer { timer_handle, clock_handle, callback }; let timer = Timer {
timer_handle,
clock_handle,
callback,
};
self.timers.push(timer); self.timers.push(timer);
Ok(&self.timers[self.timers.len()-1]) Ok(&self.timers[self.timers.len() - 1])
} }
pub fn logger<'a>(&'a self) -> &'a str { pub fn logger<'a>(&'a self) -> &'a str {
@ -1189,7 +1265,6 @@ impl Node {
let s = unsafe { CStr::from_ptr(ptr) }; let s = unsafe { CStr::from_ptr(ptr) };
s.to_str().unwrap_or("") s.to_str().unwrap_or("")
} }
} }
pub struct Timer { pub struct Timer {
@ -1252,7 +1327,10 @@ where
T: WrappedTypesupport, T: WrappedTypesupport,
{ {
// upgrade to actual ref. if still alive // upgrade to actual ref. if still alive
let publisher = self.handle.upgrade().ok_or(Error::RCL_RET_PUBLISHER_INVALID)?; let publisher = self
.handle
.upgrade()
.ok_or(Error::RCL_RET_PUBLISHER_INVALID)?;
// copy rust msg to native and publish it // copy rust msg to native and publish it
let native_msg: WrappedNativeMsg<T> = WrappedNativeMsg::<T>::from(msg); let native_msg: WrappedNativeMsg<T> = WrappedNativeMsg::<T>::from(msg);
let result = unsafe { let result = unsafe {
@ -1276,7 +1354,10 @@ where
T: WrappedTypesupport, T: WrappedTypesupport,
{ {
// upgrade to actual ref. if still alive // upgrade to actual ref. if still alive
let publisher = self.handle.upgrade().ok_or(Error::RCL_RET_PUBLISHER_INVALID)?; let publisher = self
.handle
.upgrade()
.ok_or(Error::RCL_RET_PUBLISHER_INVALID)?;
let result = let result =
unsafe { rcl_publish(publisher.as_ref(), msg.void_ptr(), std::ptr::null_mut()) }; unsafe { rcl_publish(publisher.as_ref(), msg.void_ptr(), std::ptr::null_mut()) };
@ -1289,7 +1370,6 @@ where
} }
} }
impl<T> Client<T> impl<T> Client<T>
where where
T: WrappedServiceTypeSupport, T: WrappedServiceTypeSupport,
@ -1299,18 +1379,16 @@ where
T: WrappedServiceTypeSupport, T: WrappedServiceTypeSupport,
{ {
// upgrade to actual ref. if still alive // upgrade to actual ref. if still alive
let client = self.client_.upgrade().ok_or(Error::RCL_RET_CLIENT_INVALID)?; let client = self
.client_
.upgrade()
.ok_or(Error::RCL_RET_CLIENT_INVALID)?;
let mut client = client.lock().unwrap(); let mut client = client.lock().unwrap();
// copy rust msg to native and publish it // copy rust msg to native and publish it
let native_msg: WrappedNativeMsg<T::Request> = WrappedNativeMsg::<T::Request>::from(msg); let native_msg: WrappedNativeMsg<T::Request> = WrappedNativeMsg::<T::Request>::from(msg);
let mut seq_no = 0i64; let mut seq_no = 0i64;
let result = unsafe { let result =
rcl_send_request( unsafe { rcl_send_request(&client.rcl_handle, native_msg.void_ptr(), &mut seq_no) };
&client.rcl_handle,
native_msg.void_ptr(),
&mut seq_no,
)
};
if result == RCL_RET_OK as i32 { if result == RCL_RET_OK as i32 {
client.callbacks.push((seq_no, cb)); client.callbacks.push((seq_no, cb));
@ -1322,11 +1400,13 @@ where
} }
} }
impl PublisherUntyped { impl PublisherUntyped {
pub fn publish(&self, msg: serde_json::Value) -> Result<()> { pub fn publish(&self, msg: serde_json::Value) -> Result<()> {
// upgrade to actual ref. if still alive // upgrade to actual ref. if still alive
let publisher = self.handle.upgrade().ok_or(Error::RCL_RET_PUBLISHER_INVALID)?; let publisher = self
.handle
.upgrade()
.ok_or(Error::RCL_RET_PUBLISHER_INVALID)?;
let mut native_msg = WrappedNativeMsgUntyped::new_from(&self.type_)?; let mut native_msg = WrappedNativeMsgUntyped::new_from(&self.type_)?;
native_msg.from_json(msg)?; native_msg.from_json(msg)?;
@ -1375,7 +1455,11 @@ impl Clock {
let rcl_ct = Clock::clock_type_to_rcl(&ct); let rcl_ct = Clock::clock_type_to_rcl(&ct);
let ret = unsafe { let ret = unsafe {
rcl_clock_init(rcl_ct, clock_handle.as_mut_ptr(), &mut rcutils_get_default_allocator()) rcl_clock_init(
rcl_ct,
clock_handle.as_mut_ptr(),
&mut rcutils_get_default_allocator(),
)
}; };
if ret != RCL_RET_OK as i32 { if ret != RCL_RET_OK as i32 {
eprintln!("could not create {:?} clock: {}", ct, ret); eprintln!("could not create {:?} clock: {}", ct, ret);
@ -1392,10 +1476,7 @@ impl Clock {
return Err(Error::from_rcl_error(RCL_RET_INVALID_ARGUMENT as i32)); return Err(Error::from_rcl_error(RCL_RET_INVALID_ARGUMENT as i32));
} }
let mut tp: rcutils_time_point_value_t = 0; let mut tp: rcutils_time_point_value_t = 0;
let ret = unsafe { let ret = unsafe { rcl_clock_get_now(&mut *self.clock_handle, &mut tp) };
rcl_clock_get_now(&mut *self.clock_handle,
&mut tp)
};
if ret != RCL_RET_OK as i32 { if ret != RCL_RET_OK as i32 {
eprintln!("could not create steady clock: {}", ret); eprintln!("could not create steady clock: {}", ret);
@ -1410,10 +1491,7 @@ impl Clock {
pub fn to_builtin_time(d: &Duration) -> builtin_interfaces::msg::Time { pub fn to_builtin_time(d: &Duration) -> builtin_interfaces::msg::Time {
let sec = d.as_secs() as i32; let sec = d.as_secs() as i32;
let nanosec = d.subsec_nanos(); let nanosec = d.subsec_nanos();
builtin_interfaces::msg::Time { builtin_interfaces::msg::Time { sec, nanosec }
sec,
nanosec,
}
} }
} }
@ -1429,6 +1507,18 @@ impl Drop for Clock {
mod tests { mod tests {
use super::*; use super::*;
#[test]
fn test_context_drop() -> () {
{
let ctx = Context::create().unwrap();
assert!(ctx.is_valid());
}
{
let ctx = Context::create().unwrap();
assert!(ctx.is_valid());
}
}
#[test] #[test]
fn test_ros_str() -> () { fn test_ros_str() -> () {
let hej = "hej hopp"; let hej = "hej hopp";
@ -1582,12 +1672,14 @@ mod tests {
msg.positions.push(34.0); msg.positions.push(34.0);
let json = serde_json::to_value(msg.clone()).unwrap(); let json = serde_json::to_value(msg.clone()).unwrap();
let mut native = WrappedNativeMsgUntyped::new_from("trajectory_msgs/msg/JointTrajectoryPoint").unwrap(); let mut native =
WrappedNativeMsgUntyped::new_from("trajectory_msgs/msg/JointTrajectoryPoint").unwrap();
native.from_json(json.clone()).unwrap(); native.from_json(json.clone()).unwrap();
let json2 = native.to_json().unwrap(); let json2 = native.to_json().unwrap();
assert_eq!(json, json2); assert_eq!(json, json2);
let msg2: trajectory_msgs::msg::JointTrajectoryPoint = serde_json::from_value(json2).unwrap(); let msg2: trajectory_msgs::msg::JointTrajectoryPoint =
serde_json::from_value(json2).unwrap();
assert_eq!(msg, msg2); assert_eq!(msg, msg2);
} }
@ -1596,9 +1688,7 @@ mod tests {
fn test_test_msgs_array() -> () { fn test_test_msgs_array() -> () {
let mut msg = test_msgs::msg::Arrays::default(); let mut msg = test_msgs::msg::Arrays::default();
println!("msg: {:?}", msg.string_values); println!("msg: {:?}", msg.string_values);
msg.string_values = vec![ msg.string_values = vec!["hej".to_string(), "hopp".to_string(), "stropp".to_string()];
"hej".to_string(), "hopp".to_string(), "stropp".to_string()
];
let msg_native = WrappedNativeMsg::<test_msgs::msg::Arrays>::from(&msg); let msg_native = WrappedNativeMsg::<test_msgs::msg::Arrays>::from(&msg);
let msg2 = test_msgs::msg::Arrays::from_native(&msg_native); let msg2 = test_msgs::msg::Arrays::from_native(&msg_native);
@ -1612,7 +1702,7 @@ mod tests {
fn test_test_msgs_array_too_few_elems() -> () { fn test_test_msgs_array_too_few_elems() -> () {
let mut msg = test_msgs::msg::Arrays::default(); let mut msg = test_msgs::msg::Arrays::default();
println!("msg: {:?}", msg.string_values); println!("msg: {:?}", msg.string_values);
msg.string_values = vec![ "hej".to_string(), "hopp".to_string() ]; msg.string_values = vec!["hej".to_string(), "hopp".to_string()];
let _msg_native = WrappedNativeMsg::<test_msgs::msg::Arrays>::from(&msg); let _msg_native = WrappedNativeMsg::<test_msgs::msg::Arrays>::from(&msg);
} }
@ -1659,14 +1749,14 @@ mod tests {
assert_eq!(goal, goal2); assert_eq!(goal, goal2);
let mut res = Fibonacci::Result::default(); let mut res = Fibonacci::Result::default();
res.sequence = vec![1,2,3]; res.sequence = vec![1, 2, 3];
let rn = WrappedNativeMsg::<_>::from(&res); let rn = WrappedNativeMsg::<_>::from(&res);
let res2 = Fibonacci::Result::from_native(&rn); let res2 = Fibonacci::Result::from_native(&rn);
println!("res2 {:?}", res2); println!("res2 {:?}", res2);
assert_eq!(res, res2); assert_eq!(res, res2);
let mut fb = Fibonacci::Feedback::default(); let mut fb = Fibonacci::Feedback::default();
fb.sequence = vec![4,3,6]; fb.sequence = vec![4, 3, 6];
let fbn = WrappedNativeMsg::<_>::from(&fb); let fbn = WrappedNativeMsg::<_>::from(&fb);
let fb2 = Fibonacci::Feedback::from_native(&fbn); let fb2 = Fibonacci::Feedback::from_native(&fbn);
println!("feedback2 {:?}", fb2); println!("feedback2 {:?}", fb2);

View File

@ -1,6 +1,6 @@
use std::ffi::{CString};
use std::sync::{Mutex,MutexGuard};
use rcl::*; use rcl::*;
use std::ffi::CString;
use std::sync::{Mutex, MutexGuard};
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -37,11 +37,13 @@ pub fn log(msg: &str, logger_name: &str, file: &str, line: u32, severity: LogSev
let message = CString::new(msg).unwrap(); let message = CString::new(msg).unwrap();
let severity = severity.to_native(); let severity = severity.to_native();
unsafe { unsafe {
rcutils_log(&location, rcutils_log(
&location,
severity as i32, severity as i32,
logger_name.as_ptr(), logger_name.as_ptr(),
format.as_ptr(), format.as_ptr(),
message.as_ptr()); message.as_ptr(),
);
} }
} }
@ -52,7 +54,7 @@ pub enum LogSeverity {
Info, Info,
Warn, Warn,
Error, Error,
Fatal Fatal,
} }
impl LogSeverity { impl LogSeverity {
@ -74,8 +76,14 @@ impl LogSeverity {
#[macro_export] #[macro_export]
macro_rules! __impl_log { macro_rules! __impl_log {
($logger_name:expr, $msg:expr, $file:expr, $line:expr, $severity:expr) => {{ ($logger_name:expr, $msg:expr, $file:expr, $line:expr, $severity:expr) => {{
$crate::log(&std::fmt::format($msg), $logger_name, $file, $line, $severity); $crate::log(
}} &std::fmt::format($msg),
$logger_name,
$file,
$line,
$severity,
);
}};
} }
#[macro_export] #[macro_export]
@ -118,7 +126,6 @@ macro_rules! log_fatal {
}} }}
} }
#[test] #[test]
fn test_log() { fn test_log() {
log_debug!("log_test", "debug msg"); log_debug!("log_test", "debug msg");

View File

@ -1,12 +1,11 @@
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use failure::Error;
use r2r; use r2r;
#[test] #[test]
// Let's create and drop a lot of node and publishers for a while to see that we can cope. // Let's create and drop a lot of node and publishers for a while to see that we can cope.
fn doesnt_crash() -> Result<(), Error> { fn doesnt_crash() -> Result<(), Box<dyn std::error::Error>> {
// a global shared context. // a global shared context.
let ctx = r2r::Context::create()?; let ctx = r2r::Context::create()?;