GithubHelp home page GithubHelp logo

rosrust's Introduction

rosrust

MIT Licensed Crates.io Build Status

rosrust is a pure Rust implementation of a ROS client library.

Usage

For all the key features, it is enough to depend on the crate itself. It's highly recommended to depend on rosrust_msg as well, as it provides bindings for message generation.

The following dependencies are recommended to use the crate:

[dependencies]
rosrust = "0.9"
rosrust_msg = "0.1"

If using Rust 2015 edition, just depend on the library with macro usage, using:

#[macro_use]
extern crate rosrust;

Examples are written using Rust 2018, as it's the expected edition to use now.

Implementation

rosrust is almost there with implementing all features of a ROS Client Library by fulfilling ROS requirements for implementing client libraries which are given in a more detailed form in the technical overview.

Mostly the missing features are related to extra tooling, like sensor_msgs/PointCloud2 and sensor_msgs/Image encoding/decoding, TF tree handling, and other things that are external libraries in roscpp and rospy as well.

The API is very close to the desired final look.

Integration with catkin will be handled once a satisfying set of features has been implemented.

Examples

The API is close to reaching its final form.

There are multiple examples in the examples folder. The publisher/subscriber and service/client examples are designed to closely imitate the roscpp tutorial.

Features

Message Generation

Message generation can be done automatically by depending on rosrust_msg, or manually using rosrust::rosmsg_include.

The preferred way is automatic, as it allows interop between dependencies that use messages and your crate.

If you do not have ROS installed, then the message generation utilizes the ROSRUST_MSG_PATH environment variable, which is a colon separated list of directories to search. These directories should have the structure <ROSRUST_MSG_PATH>/<anything>/<package>/msg/<message> or <ROSRUST_MSG_PATH>/<anything>/<package>/srv/<service>.

Automatic

For automatic message generation just depend on rosrust_msg, with the version specified at the top of this document.

After that you'll be able to generate a sensor_msgs/Imu message object by using rosrust_msg::sensor_msgs::Imu::default(). All fields are always public, so you can initialize structures as literals.

Manual

Message generation is done at build time. If you have ROS installed and sourced in your shell session, you will not need to do any extra setup for this to work.

To generate messages, create a module for messages. Using something like a msg.rs file in your project root results in importing similar to roscpp and rospy. The file only needs one line:

// If you wanted
// * messages: std_msgs/String, sensor_msgs/Imu
// * services: roscpp_tutorials/TwoInts
// * and all the message types used by them, like geometry_msgs/Vector3
rosrust::rosmsg_include!(std_msgs/String,sensor_msgs/Imu,roscpp_tutorials/TwoInts);

Just add this file to your project and you're done.

If you have put this in a src/msg.rs file, this will include all the generated structures, and add them to the msg namespace. Thus, to create a new sensor_msgs/Imu, you call msg::sensor_msgs::Imu::default(). All fields are always public, so you can initialize structures as literals.

Publishing to Topic

If we wanted to publish a defined message (let's use std_msgs/String) to topic chatter ten times a second, we can do it in the following way.

fn main() {
    // Initialize node
    rosrust::init("talker");

    // Create publisher
    let chatter_pub = rosrust::publish("chatter", 100).unwrap();

    let mut count = 0;

    // Create object that maintains 10Hz between sleep requests
    let rate = rosrust::rate(10.0);

    // Breaks when a shutdown signal is sent
    while rosrust::is_ok() {
        // Create string message
        let mut msg = rosrust_msg::std_msgs::String::default();
        msg.data = format!("hello world {}", count);

        // Send string message to topic via publisher
        chatter_pub.send(msg).unwrap();

        // Sleep to maintain 10Hz rate
        rate.sleep();

        count += 1;
    }
}

Subscribing to Topic

If we wanted to subscribe to an std_msgs/UInt64 topic some_topic, we just declare a callback. An alternative extra interface with iterators is being considered, but for now this is the only option.

The constructor creates an object, which represents the subscriber lifetime. Upon the destruction of this object, the topic is unsubscribed as well.

fn main() {
    // Initialize node
    rosrust::init("listener");

    // Create subscriber
    // The subscriber is stopped when the returned object is destroyed
    let _subscriber_raii = rosrust::subscribe("chatter", 100, |v: rosrust_msg::std_msgs::UInt64| {
        // Callback for handling received messages
        rosrust::ros_info!("Received: {}", v.data);
    }).unwrap();

    // Block the thread until a shutdown signal is received
    rosrust::spin();
}

Creating a Service

Creating a service is the easiest out of all the options. Just define a callback for each request. Let's use the roscpp_tutorials/AddTwoInts service on the topic /add_two_ints.

fn main() {
    // Initialize node
    rosrust::init("add_two_ints_server");

    // Create service
    // The service is stopped when the returned object is destroyed
    let _service_raii =
        rosrust::service::<rosrust_msg::roscpp_tutorials::TwoInts, _>("add_two_ints", move |req| {
            // Callback for handling requests
            let sum = req.a + req.b;

            // Log each request
            rosrust::ros_info!("{} + {} = {}", req.a, req.b, sum);

            Ok(rosrust_msg::roscpp_tutorials::TwoIntsRes { sum })
        }).unwrap();

    // Block the thread until a shutdown signal is received
    rosrust::spin();
}

Creating a Client

Clients can handle requests synchronously and asynchronously. The sync method behaves like a function, while the async approach is via reading data afterwards. The async consumes the passed parameter, since we're passing the parameter between threads. It's more common for users to pass and drop a parameter, so this being the default prevents needless cloning.

Let's call requests from the AddTwoInts service on the topic /add_two_ints. The numbers shall be provided as command line arguments.

We're also depending on env_logger here to log ros_info messages to the standard output.

use std::{env, time};

fn main() {
    env_logger::init();

    // Fetch args that are not meant for rosrust
    let args: Vec<_> = rosrust::args();

    if args.len() != 3 {
        eprintln!("usage: client X Y");
        return;
    }

    let a = args[1].parse::<i64>().unwrap();
    let b = args[2].parse::<i64>().unwrap();

    // Initialize node
    rosrust::init("add_two_ints_client");

    // Wait ten seconds for the service to appear
    rosrust::wait_for_service("add_two_ints", Some(time::Duration::from_secs(10))).unwrap();

    // Create client for the service
    let client = rosrust::client::<rosrust_msg::roscpp_tutorials::TwoInts>("add_two_ints").unwrap();

    // Synchronous call that blocks the thread until a response is received
    ros_info!(
        "{} + {} = {}",
        a,
        b,
        client
            .req(&rosrust_msg::roscpp_tutorials::TwoIntsReq { a, b })
            .unwrap()
            .unwrap()
            .sum
    );

    // Asynchronous call that can be resolved later on
    let retval = client.req_async(rosrust_msg::roscpp_tutorials::TwoIntsReq { a, b });
    rosrust::ros_info!("{} + {} = {}", a, b, retval.read().unwrap().unwrap().sum);
}

Parameters

There are a lot of methods provided, so we'll just give a taste of all of them here. Get requests return results, so you can use unwrap_or to handle defaults.

We're also depending on env_logger here to log ros_info messages to the standard output.

fn main() {
    env_logger::init();

    // Initialize node
    rosrust::init("param_test");

    // Create parameter, go through all methods, and delete it
    let param = rosrust::param("~foo").unwrap();
    rosrust::ros_info!("Handling ~foo:");
    rosrust::ros_info!("Exists? {:?}", param.exists()); // false
    param.set(&42u64).unwrap();
    rosrust::ros_info!("Get: {:?}", param.get::<u64>().unwrap());
    rosrust::ros_info!("Get raw: {:?}", param.get_raw().unwrap());
    rosrust::ros_info!("Search: {:?}", param.search().unwrap());
    rosrust::ros_info!("Exists? {}", param.exists().unwrap());
    param.delete().unwrap();
    rosrust::ros_info!("Get {:?}", param.get::<u64>().unwrap_err());
    rosrust::ros_info!("Get with default: {:?}", param.get::<u64>().unwrap_or(44u64));
    rosrust::ros_info!("Exists? {}", param.exists().unwrap());
}

Logging

Logging is provided through macros ros_debug!(), ros_info!(), ros_warn!(), ros_error!(), ros_fatal!().

Throttled logging options ara available too.

Command Line Remaps

Similar to rospy and roscpp, you can use the command line to remap topics and private parameters. Private parameters should be provided in a YAML format.

For more information, look at the official wiki, since the attempt was to 100% immitate this interface.

You can get a vector of the leftover command line argument strings with rosrust::args(), allowing easy argument parsing. This includes the first argument, the application name.

License

rosrust is distributed under the MIT license.

rosrust's People

Contributors

adnanademovic avatar alonblade avatar astraw avatar chachi avatar de-vri-es avatar gstavrinos avatar hikinggrass avatar lsr0 avatar lucasw avatar marcoesposito1988 avatar michaelgrupp avatar neachdainn avatar otl avatar photex avatar romainreignier avatar ssnover avatar taiki-e avatar zachgoins avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rosrust's Issues

the trait bound `[f64; 36]...` is not satisfied

Code causing the error

#[macro_use]
extern crate rosrust;
#[macro_use]
extern crate rosrust_codegen;
rosmsg_include!();
fn main() {
    rosrust::init("listener");
    let _subscriber_raii = rosrust::subscribe("odom", |v: msg::nav_msgs::Odometry| {
        ros_info!("Received: {}", v.pose.pose.position.x);
    }).unwrap();
    rosrust::spin();
}

Error output

error[E0277]: the trait bound `[f64; 36]: msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Serialize` is not satisfied
   --> /usr/local/home/kalshari/repositories/rust_ros_turtlebot/target/debug/build/rust_ros_turtlebot-3510f4c999606114/out/msg.rs:148:13
    |
148 |             pub covariance: [f64; 36],
    |             ^^^ the trait `msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Serialize` is not implemented for `[f64; 36]`
    |
    = help: the following implementations were found:
              <[T; 15] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Serialize>
              <[T; 23] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Serialize>
              <[T; 25] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Serialize>
              <[T; 6] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Serialize>
            and 30 others
    = note: required by `msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::ser::SerializeStruct::serialize_field`

error[E0277]: the trait bound `[f64; 36]: msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'_>` is not satisfied
   --> /usr/local/home/kalshari/repositories/rust_ros_turtlebot/target/debug/build/rust_ros_turtlebot-3510f4c999606114/out/msg.rs:148:13
    |
148 |             pub covariance: [f64; 36],
    |             ^^^ the trait `msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'_>` is not implemented for `[f64; 36]`
    |
    = help: the following implementations were found:
              <[T; 1] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
              <[T; 21] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
              <[T; 22] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
              <[T; 26] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
            and 30 others
    = note: required by `msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::de::SeqAccess::next_element`

error[E0277]: the trait bound `[f64; 36]: msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'_>` is not satisfied
   --> /usr/local/home/kalshari/repositories/rust_ros_turtlebot/target/debug/build/rust_ros_turtlebot-3510f4c999606114/out/msg.rs:148:13
    |
148 |             pub covariance: [f64; 36],
    |             ^^^ the trait `msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'_>` is not implemented for `[f64; 36]`
    |
    = help: the following implementations were found:
              <[T; 1] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
              <[T; 21] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
              <[T; 22] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
              <[T; 26] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
            and 30 others
    = note: required by `msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::de::MapAccess::next_value`

error[E0277]: `[f64; 36]` doesn't implement `std::fmt::Debug`
   --> /usr/local/home/kalshari/repositories/rust_ros_turtlebot/target/debug/build/rust_ros_turtlebot-3510f4c999606114/out/msg.rs:148:13
    |
148 |             pub covariance: [f64; 36],
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^ `[f64; 36]` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`
    |
    = help: the trait `std::fmt::Debug` is not implemented for `[f64; 36]`
    = note: required because of the requirements on the impl of `std::fmt::Debug` for `&[f64; 36]`
    = note: required for the cast to the object type `dyn std::fmt::Debug`

error[E0277]: the trait bound `[f64; 36]: std::default::Default` is not satisfied
   --> /usr/local/home/kalshari/repositories/rust_ros_turtlebot/target/debug/build/rust_ros_turtlebot-3510f4c999606114/out/msg.rs:148:13
    |
148 |             pub covariance: [f64; 36],
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::default::Default` is not implemented for `[f64; 36]`
    |
    = help: the following implementations were found:
              <[T; 15] as std::default::Default>
              <[T; 27] as std::default::Default>
              <[T; 8] as std::default::Default>
              <[T; 9] as std::default::Default>
            and 31 others
    = note: required by `std::default::Default::default`

error[E0277]: the trait bound `[f64; 36]: msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Serialize` is not satisfied
   --> /usr/local/home/kalshari/repositories/rust_ros_turtlebot/target/debug/build/rust_ros_turtlebot-3510f4c999606114/out/msg.rs:175:13
    |
175 |             pub covariance: [f64; 36],
    |             ^^^ the trait `msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Serialize` is not implemented for `[f64; 36]`
    |
    = help: the following implementations were found:
              <[T; 15] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Serialize>
              <[T; 23] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Serialize>
              <[T; 25] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Serialize>
              <[T; 6] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Serialize>
            and 30 others
    = note: required by `msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::ser::SerializeStruct::serialize_field`

error[E0277]: the trait bound `[f64; 36]: msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'_>` is not satisfied
   --> /usr/local/home/kalshari/repositories/rust_ros_turtlebot/target/debug/build/rust_ros_turtlebot-3510f4c999606114/out/msg.rs:175:13
    |
175 |             pub covariance: [f64; 36],
    |             ^^^ the trait `msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'_>` is not implemented for `[f64; 36]`
    |
    = help: the following implementations were found:
              <[T; 1] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
              <[T; 21] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
              <[T; 22] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
              <[T; 26] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
            and 30 others
    = note: required by `msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::de::SeqAccess::next_element`

error[E0277]: the trait bound `[f64; 36]: msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'_>` is not satisfied
   --> /usr/local/home/kalshari/repositories/rust_ros_turtlebot/target/debug/build/rust_ros_turtlebot-3510f4c999606114/out/msg.rs:175:13
    |
175 |             pub covariance: [f64; 36],
    |             ^^^ the trait `msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'_>` is not implemented for `[f64; 36]`
    |
    = help: the following implementations were found:
              <[T; 1] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
              <[T; 21] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
              <[T; 22] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
              <[T; 26] as msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::Deserialize<'de>>
            and 30 others
    = note: required by `msg::geometry_msgs::_IMPL_DESERIALIZE_FOR_Quaternion::_serde::de::MapAccess::next_value`

error[E0277]: `[f64; 36]` doesn't implement `std::fmt::Debug`
   --> /usr/local/home/kalshari/repositories/rust_ros_turtlebot/target/debug/build/rust_ros_turtlebot-3510f4c999606114/out/msg.rs:175:13
    |
175 |             pub covariance: [f64; 36],
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^ `[f64; 36]` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`
    |
    = help: the trait `std::fmt::Debug` is not implemented for `[f64; 36]`
    = note: required because of the requirements on the impl of `std::fmt::Debug` for `&[f64; 36]`
    = note: required for the cast to the object type `dyn std::fmt::Debug`

error[E0277]: the trait bound `[f64; 36]: std::default::Default` is not satisfied
   --> /usr/local/home/kalshari/repositories/rust_ros_turtlebot/target/debug/build/rust_ros_turtlebot-3510f4c999606114/out/msg.rs:175:13
    |
175 |             pub covariance: [f64; 36],
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::default::Default` is not implemented for `[f64; 36]`
    |
    = help: the following implementations were found:
              <[T; 15] as std::default::Default>
              <[T; 27] as std::default::Default>
              <[T; 8] as std::default::Default>
              <[T; 9] as std::default::Default>
            and 31 others
    = note: required by `std::default::Default::default`

error: aborting due to 10 previous errors

Environment

> rustc -V
rustc 1.30.0 (da5f414c2 2018-10-24)
> cat Cargo.toml 
[package]
name = "rust_ros_turtlebot"
version = "0.1.0"
authors = ["Khaled Sharif <[email protected]>"]

[dependencies]
rosrust = "0.6.5"
rosrust_codegen = "0.6.5"                                                                                                                                                   
serde = "1.0.25"                                                                                                                                                            
serde_derive = "1.0.25"                                                                                                                                                     
                                                                                                                                                                            
[build-dependencies]                                                                                                                                                        
rosrust_codegen = "0.6.5"   

Let me help you to add more messages

Hi, I'm working in a project that needs sensor_msgs/NavSatFix message, and currently is not implemented, so I want to help you to add more message types. I don't know how, so I need you to teach me.

Thanks.

Publisher type is not visible from outside crate

I cannot store a Publisher in a field in a struct because the type definition of Publisher is not visible outside the crate.

This can be easily fixed by making the api module public in src/lib.rs, but this seems like something you may have explicitly avoided.

Increase test coverage

Tests will be quite hard to do, since most of the code are TCP requests.
Any tips and suggestions are welcome, and greatly appreciated!

Support log verbosity levels

ROS allows setting verbosity levels for logging. I don't actually know how it's done behind the scenes, so info would be greatly appreciated.

I only found som info on the wiki here.

Status / purpose of rosrust_codegen crate is unclear

This repo is listed as the repository for both rosrust and rosrust_codegen crates and the README appears identical for both crates. It does not appear to be possible to use rosrust_codegen separately from rosrust. (If it is possible, I sincerely would appreciate some minimal docs on how to use it standalone.)

I don't know the reasons why the rosrust_codegen crate was published, but short of unpublishing it, perhaps the README for the rosrust_codegen crate could be updated to indicate that it's deprecated and rosrust should be used directly instead?

auto-deserialization of parameters broken

It seems the auto-deserialization of parameters is broken as documented in the README:

error[E0277]: the trait bound `msg::std_msgs::UInt64: rustc_serialize::serialize::Decodable` is not satisfied
  --> /home/astraw/src/mycrate/src/ros.rs:96:24
   |
96 |             param.get::<msg::std_msgs::UInt64>().unwrap().data
   |                   ^^^ the trait `rustc_serialize::serialize::Decodable` is not implemented for `msg::std_msgs::UInt64`

error: aborting due to previous error

error: Could not compile `mycrate`.

(This is on current master. Using param.get_raw() instead of param.get::<msg::std_msgs::UInt64>() works fine.)

Without digging in, my guess is that this broke in the transition to serde.

Relatedly, it would be good to get some tests, even simple ones, on the parameter api, as I didn't find any at the moment.

pub sub not works in `/use_sim_time` `true` and `/clock` is published

pub sub example does not work when

  • /use_sim_time param is true
  • /clock is published correctly

How to reproduce this problem

roscore
rosparam set /use_sim_time true
rostopic pub -s -r 1000 /clock rosgraph_msgs/Clock 'now'
./target/debug/publisher
./target/debug/subscriber

roscpp_tutorials works fine in this situation.

roscore
rosparam set /use_sim_time true
rostopic pub -s -r 1000 /clock rosgraph_msgs/Clock 'now'
rosrun roscpp_tutorials talker
rosrun roscpp_tutorials listener

published messages not seen by some tools

A very simple publisher, such as the chatter example in the readme, publishes messages that don't seem to be seen by all tools. rostopic echo /chatter sees them, but rostopic hz /chatter and rosbag record /chatter do not. I noticed this while investigating that a LabView node also does not observe the messages. Publishing the same with rostopic pub or a Python publisher and all tools observe the messages. Tried with Ubuntu 16 Mate/kinetic on a raspberry Pi, Ubuntu 18 Mate/melodic on x64, and even with RosOnWindows/melodic, same result everywhere.

Use procedural macros for message generation

Currently, messages are generated with a build script. This is a bit clumsy, but only because it requires some boilerplate (albeit a tiny amount) from the user.

This is not a high priority, as it doesn't add functionality, just makes the library more ergonomic to work with. But, if there is somebody who wants to fiddle with procedural macros, here is a neat problem.

A final goal would be functionality similar to schemas in diesel.

rospy.wait_for_service('/add_two_ints') causes example to log error

Running the service example in examples/serviceclient results in the following error: ERROR 2018-05-29T06:20:16Z: rosrust::tcpros::service: Failed to exchange headers for service '/add_two_ints': Broken pipe (os error 32)

To elicit this, run the service:

cd examples/serviceclient
cargo run --bin service

And run this Python test program:

import rospy

rospy.init_node('test_wait_for_service')
rospy.wait_for_service('/add_two_ints')

Unregister topic does not work

Unregister of topics does not seem to be working correctly.

You can check this when you kill publisher by Ctrl-c.

roscore
./target/debug/publisher #[Ctrl-c]
$ rostopic list
/chatter
/rosout
/rosout_agg

This should be

$ rostopic list
/rosout
/rosout_agg

Examples

Hi,

Could you also add some basic examples (subscribe, publish) for getting quickly going.

"Broken pipe" when using service defined in rust

My testing of rosrust 0.6.4 has most things working, but I'm hitting a couple issues with services. First is an error ("Broken pipe (os error 32)") printed in service calls. This happens when the service is called by the client example in examples/serviceclient.

Here is how I run the service:

rosrust/examples/serviceclient$ cargo run --bin service
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `/home/astraw/src/rosrust/target/debug/service`
[INFO @ examples/serviceclient/src/service.rs:23]: 4 + 5 = 9
ERROR:<unknown>: Broken pipe (os error 32)
[INFO @ examples/serviceclient/src/service.rs:23]: 4 + 5 = 9
ERROR:<unknown>: Broken pipe (os error 32)

Here is how I run the client:

rosrust/examples/serviceclient$ cargo run --bin client -- 4 5
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `/home/astraw/src/rosrust/target/debug/client 4 5`
[INFO @ examples/serviceclient/src/client.rs:35]: 4 + 5 = 9
[INFO @ examples/serviceclient/src/client.rs:48]: 4 + 5 = 9

Although the rust client example evidently successfully calls the service, I think my second issue may be related: the standard ROS tools cannot interact with the service without error: (UPDATE: This second issue is now #46.)

rosservice info add_two_ints
Node: /add_two_ints_server
URI: rosrpc://localhost:37153
ERROR: Unable to communicate with service [/add_two_ints], address [rosrpc://localhost:37153]

Or

rosservice call add_two_ints 4 5 
ERROR: Unable to communicate with service [/add_two_ints], address [rosrpc://localhost:37153]

Subscriber panics when malformed message comes in

When malformed message is received by a subscriber, the subscriber panics with the following message:
thread '<unnamed>' panicked at 'called 'Result::unwrap()' on an 'Err' value: Error(Msg("Failed to run the HTTP request within hyper."), State { next_error: Some(Io(Os { code: 104, kind: ConnectionReset, message: "Connection reset by peer" })), backtrace: Some(stack backtrace: ...

How to reproduce:
Use rostopic to publish an empty message to a topic, e.g. rostopic pub /topicname std_msgs/Float32

Rust backtrace shows that the following lines cause a panic

let (_code, _message, protocols): (i32, String, (String, String, i32)) = xml_rpc::Client::new()
.unwrap()
.call(
&publisher_uri.parse().unwrap(),
"requestTopic",
&(caller_id, topic, [["TCPROS"]]),
)
.unwrap()
.unwrap();

Contributing on TF implementation

I would like to help, maybe by starting working on TF tree handling. Before starting, I would like to ask if you already have some design in mind, or if you think an implementation similar to the roscpp one would be ok.

Integrate with the catkin build system

It would be greatly useful to build upon catkin_make invocations, and to be callable from rosrun <package> <node>. The latter would by extension allow running from launchfiles.

The current solution is creating shell scripts that run cargo run ... for the binary in question, and putting them in the /scripts subfolder. That's similar to how python scripts are already being run.

This isn't very urgent, since this hacky solution works for now.

Make error handling more sensible

Most of the error messages are just tacked on there, since the focus was getting functionality out there.

Now that functionality is there, the next step is being more clear what's going on when errors happen.

This includes getting rid of .unwrap() calls that are placed to kill a thread when we're done with it, since they are making noise on stderr.

Services shouldn't need Sync

I'm trying to write a service, but it's complaining that my (manually written...) ServicePair struct isn't Sync. I can certainly go add an Arc<Mutex<, but I really shouldn't have to, the function I send in to rosrust::service should be given ownership of the new message, so Send should be sufficient.

Edit: check that, I'm getting the error because I'm trying to use a mpsc::Sender in the service function, which makes it not Sync... which is harder to solve... and equally unnecessary. I'm giving the lambda to service, so again Send should be sufficient.

feature request: publisher latching

There is one feature we find useful in ROS not implemented in rosrust: publisher latching. A latched publisher keeps a copy of the last sent message and transmits it to any newly connecting subscribers. It is described a little more in the ROS 0.7 release announcement.

I looked at the publisher/subscriber code in rosrust, but this does not look entirely simple to add, at least not without being more familiar with the code base.

Write example cases like many other crates have

Many crates have example cases in an examples folder which can be compiled and run right out of the gate to give users something to work with. The examples in the README.md file should be translatable.

service fails on machine with IPv6 enabled

As explained in #41, which I originally thought was likely from a single bug, the rosrust service demo (started with rosrust/examples/serviceclient$ cargo run --bin service) can fail with standard ROS tools:

Node: /add_two_ints_server
URI: rosrpc://localhost:37153
ERROR: Unable to communicate with service [/add_two_ints], address [rosrpc://localhost:37153]

Or

rosservice call add_two_ints 4 5 
ERROR: Unable to communicate with service [/add_two_ints], address [rosrpc://localhost:37153]

I discovered that this does not happen when IPv6 is disabled on the machine in question. The tests were done with the env var ROS_HOSTNAME=localhost.

Make some integration tests with ROS itself

There's a whole matrix of scenarios that tend to break while fixing issues in others.

We need to be sure that Rust <-> Rust/Python/C++/ROS tools communication works, and that it works for publishers, subscribers, services, clients, and parameters.

That would prevent breaks like the one mentioned in #41.

Use associated constants for ROSMSG

The ROSMSG message declaration format allows us to define constants.

Currently they are written with namespaces and have a bit of a convoluted approach.

But, Rust 1.20 added associated constants to stable, allowing a more natural syntax for handling constants.

The usage of constants in messages is fairly rare, so this is not a high priority.

no messages received in c++ subscriber

Now it is possible to publish from C++, (#27) but c++ listener does not work.

I'm talking about below case.

  • Publisher: examples/publisher.rs
  • Subscriber: roscpp_tutorials/listener
roscore
rosrun roscpp_tutorials listener
cd rosrust
./target/debug/publisher

rostopic echo /chatter works fine, because it is python(?).

Subscriber needs a queue_size

Currently the Subscriber doesn't have a queue_size, resulting in unbounded memory growth when subscription processing slows down.

Add limited message queues for publishers

This is a standard feature within ROS clients, and is really there to prevent clogging up RAM and resources due to a slow reader.

The default would be an infinite queue, but setting or removing a limit would be possible after the publisher is created.

How is progress?

Hi adnanademovic.

I was wondering how development of rosrust was progressing? Is there anywhere that I can help? What are the plans from here?

rostopic echo says nothing

I succeeded to write publisher and subscriber using rosrust! That's great.
I can confirm that by rostopic info /some_topic, and subscriber says the message.

But when I try rostopic echo /some_topic , it says nothing.
Is this happen in your environment?

Special semantics for Header need to be implemented

The headers have a timestamp and sequential ID set if they are not specified.

Thus, if the timestamp equals zero, read the current time and set it to that.

If the sequential ID equals zero, read it from an atomic counter and then increment it.

More here.

Allow periodic and unique logging

Both rospy and roscpp allow various frequencies and ways of logging. In rospy there are most prominently logs that happen periodically or once, as described here.

In the C++ and Python solution, this is done by taking the line number and filename, and maintaining a singleton hash map of all the periodic and unique logs. So logic would be to use lazy_static to create:

  • hashmap periodicLog with key "file:line" and value Time representing the next fitting time.
  • hashset uniqueLog with same key structure.
  • the ros_*_throttle! and ros_*_once! macros would check time/presence, and execute the normal log if things fit, updating the time to (now() + 1s/rate) in the former case.

Info about export ROSRUST_MSG_PATH missing

I don't have ros installed on my machine (macos), so after cloning and running cargo build I encountered:

error: proc macro panicked
 --> rosrust/src/msg.rs:1:1
  |
1 | rosmsg_include!(rosgraph_msgs / Clock, rosgraph_msgs / Log, INTERNAL);
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = help: message: called `Result::unwrap()` on an `Err` value: Error(Msg("Could not find requested message in provided folders: rosgraph_msgs/Log"), State { next_error: None, backtrace: None })

So I had a look in the README especially the Message Generation section, but I couldn't figure it out at first. After looking in the travis.yml I found the relevant info to export ROSRUST_MSG_PATH=rosrust_codegen/src/msg_examples, at least I think it is, because afterwards it compiles.

Is this the right way to do it or did I miss something?

Perhaps this info should be in the README directly, maybe in a build instruction section like:

git clone [email protected]:adnanademovic/rosrust.git
cd rosrust
export ROSRUST_MSG_PATH=rosrust_codegen/src/msg_examples
cargo build

Since I don't know if this is a non issue, I didn't submit a PR for that, but if you're supposed to set the env variable and this info is beneficial I'm happy to do it.

rust version: 1.34.0-nightly

no messages received from c++publisher

Hi Adnan,

Our Goal is to publish a message in C++ and subscribe it in Rust. So our Steps we actually take are:

  • We are publishing in an c++ Node some Car infos. The message includes the header, position, speed and acceleration of the car.
  • Then we try to subscribe this Topic with our Rust Node.
    with "rostopic echo" we can see that there are messages published, but not our rust node.
  • On the other hand if we subscribe to the topic with an c++ oder python node we get the data. We also get the messages if we publish to this topic with a rust node.

If we add the env_logger we get the following messages:

ERROR:rosrust::api::slavehandler: Failed to connect to publisher 'http://Lenovo:34875/': Issue while reading XML-RPC data, for response
Caused by:Failed to parse parameters
Caused by:Failed to parse XML RPC parameter
Caused by:Failed to parse array's children
Caused by:Expected a node named 'value' with 1 child
ERROR:rosrust::api::ros: Failed to subscribe to all publishers of topic '/cars/a/car_connector/PositionUpdate': Issue while reading XML-RPC data, for response
ERROR:rosrust::api::slavehandler: Failed to connect to publisher 'http://Lenovo:34875/': Issue while reading XML-RPC data, for response
Caused by:Failed to parse parameters
Caused by:Failed to parse XML RPC parameter
Caused by:Failed to parse array's children
Caused by:Expected a node named 'value' with 1 child
ERROR:hyper::server: request error = TooLarge

If we change the branch in the Cargo.toml to
rosrust = { git = "https://github.com/adnanademovic/rosrust.git", branch = "xmlrpc-api-rework" }

We get the followed messages if we try to subscribe to the topic

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error(XmlFormat(Msg("Failed to parse XML-RPC response.")), State { next_error: Some(Expected token &XmlEvent::Characters(ref name) |
&XmlEvent::StartElement { name: OwnedName { local_name: ref name, .. }, .. }, found EndElement(value)), backtrace: None })', /checkout/src/libcore/result.rs:906:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.
benjamin@Lenovo:~/catkin_ws/src/anki/racing$ ./target/debug/racing_ki 
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error(XmlFormat(Msg("Failed to parse XML-RPC response.")), State { next_error: Some(Expected token &XmlEvent::Characters(ref name) |
&XmlEvent::StartElement { name: OwnedName { local_name: ref name, .. }, .. }, found EndElement(value)), backtrace: None })', /checkout/src/libcore/result.rs:906:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.

services and publishers not usable from remote machine

I have been testing rosrust in a multi-machine environment but I cannot get services or publishers to work from a remote machine. Here is my testing procedure:

machine 1 (IP addresss 10.4.66.28), terminal window 1

export ROS_MASTER_URI="http://10.4.66.28:11311"
roscore

machine 1 (IP addresss 10.4.66.28), terminal window 2

export ROS_MASTER_URI="http://10.4.66.28:11311"
cd examples/serviceclient
cargo run --bin service

As of current master (ffbf1ba), this works from machine 1:

machine 1 (IP addresss 10.4.66.28), terminal window 3

export ROS_MASTER_URI="http://10.4.66.28:11311"
rosservice call /add_two_ints 2 3
# prints `sum: 5`

However, this does not work:

machine 2 (IP addresss 10.4.66.24)

export ROS_MASTER_URI="http://10.4.66.28:11311"
rosservice call /add_two_ints 2 3
# prints `ERROR: Unable to communicate with service [/add_two_ints], address [rosrpc://machine1:41745]`

The situation is similar for the publisher. In that case, I have been testing with cargo run --bin publisher in examples/pubsub and rostopic echo /chatter.

very slow pub/sub

I'm sorry for the vagueness of this issue. Basically, I wrote something that I thought should be very simple (receive messages on a topic and publish them all to another topic) and it turned out really slow, much slower even than rospy.

Meat of the code in each language (gist follows):

let img_pub = RefCell::new(rosrust::publish("/out")?);
img_pub.borrow_mut().set_queue_size(Some(10));

let idx = AtomicUsize::new(0);
let _img_sub = rosrust::subscribe("/in",
                                  move |v: msg::sensor_msgs::Image| {
                                      let i = idx.load(Ordering::SeqCst) + 1;
                                      ros_info!("{}", i);
                                      idx.store(i, Ordering::SeqCst);

                                      img_pub.borrow_mut().send(v).unwrap();
                                  })?;

rosrust::spin();
class sub_cb:
    def __init__(self, pub):
        self.pub = pub
        self.i = 1

    def __call__(self, data):
        print self.i
        self.i += 1
        self.pub.publish(data)

def main():
    pub = rospy.Publisher('/out', Image, queue_size=10)
    sub = rospy.Subscriber('/in', Image, sub_cb(pub))
    
    rospy.spin()

Here is the full code.

I'm testing this using a bag that plays messages at a rate of 15 Hz. The Python node can keep up with this without a problem. However, the Rust node forwards messages with a delay of 1-2 minutes (!) and only manages 3 Hz.

I'm wondering if you have some ideas on why the Rust bindings are slower here, or what I'm doing differently in Rust vs Python.

Add full message generation at compile time

The release schedule for ROS packages is quite slow, and it would cause a dependency hell when interfacing with ROS itself. Besides that, there is no need for rosrust to even become part of standard distributed ROS, since you can compile almost everything without even having ROS on the machine.

The only exception so far are messages. I've made a genrs ROS package, and it's even on the ROS build farm right now, but in the end it basically utilized a bit of ROS's message generation to do a bit of preprocessing.

In the meantime, I've gotten enough knowledge about all the little details to reliably construct that code generation myself, so I'm planning to do so, and cut any dependency on ROS's release process.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.