Giter Site home page Giter Site logo

carter12s / roslibrust Goto Github PK

View Code? Open in Web Editor NEW
39.0 5.0 7.0 1.32 MB

A rust client for working with ROS's rosbridge.

Home Page: https://docs.rs/roslibrust

License: MIT License

Rust 94.30% Dockerfile 0.19% Shell 0.18% CMake 0.07% Jinja 0.82% C++ 4.44%
ros ros2 rust

roslibrust's Introduction

RosLibRust

Noetic Galactic Humble Iron License:MIT

This package aims to provide a convenient "async first" library for interacting with ROS.

Currently this packaged provides support for both ROS1 native communication (TCPROS) and rosbridge's protocol which provides support for both ROS1 and ROS2 albeit with some overhead.

Information about the protocol can be found here.

Note on documentation: All information about the crate itself (examples, documentation, tutorials, etc.) lives in the source code and can be viewed on docs.rs. This readme is for "Meta" information about developing for the crate.

Fully Supported via rosbridge: Noetic, Galactic, Humble, Iron,

Code Generation of ROS Messages

The crates roslibrust_codegen and roslibrust_codegen_macro support code generation in Rust for ROS1 and ROS2 message, service, and action files. Many of the examples use the macro for convenience. find_and_generate_ros_messages is a macro which accepts an optional list of paths relative to the project workspace directory and will additionally check the ROS_PACKAGE_PATH environment variable for paths to ROS packages.

It's used like this:

roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces/std_msgs");

Code generation can also be done with a build.rs script using the same code generation backend called by the macro. See the contents of example_package for a detailed example of how this can be done. While the proc_macros are extremely convenient for getting started there is currently no (good) way for a proc_macro to inform the compiler that it needs to be re-generated when an external file changes. Using a build script requires more setup, but can correctly handling re-building when message files are edited.

Generated message types are compatible with both the ROS1 native and RosBridge backends.

Roadmap

Feature rosbridge ROS1 ROS2
examples x
message_gen
advertise / publish x
unadvertise x
subscribe x
unsubscribe x
services x
actions (codgen of message types only)
rosapi x x
TLS / wss:// Should be working, untested N/A N/A

Upcoming features in rough order:

  • Ability to write generic clients via ROS trait
  • In memory backend that can be used for testing
  • Support for parameter server

Contributing

Contribution through reporting of issues encountered and implementation in PRs is welcome! Before landing a large PR with lots of code implemented, please open an issue if there isn't a relevant one already available and chat with a maintainer to make sure the design fits well with all supported platforms and any in-progress implementation efforts.

Minimum Supported Rust Version / MSRV

We don't have an official MSRV yet.

Due to cargo 1.72 enabling "doctest-in-workspace" by default it is recommended to use Rust 1.72+ for development. Previous rust versions are support but will require some incantations when executing doctests.

The experimental topic_provider feature currently relies on async fn in traits from Rust 1.75. When that feature standardizes that will likely become our MSRV.

Running Tests

There are various unit tests and integration tests behind feature flags. For tests with ROS1, both through rosbridge and native clients, you'll need a locally running rosbridge_websocket node and rosmaster. Then run with cargo test --features "ros1_test ros1". For tests with ROS2, you'll need a running rosbridge server, then run with cargo test --features "ros2_test". You can find relevant Dockerfiles and docker compose configurations udner the docker directory.

roslibrust's People

Contributors

carter12s avatar entire avatar jondo2010 avatar lucasw avatar ssnover 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

Watchers

 avatar  avatar  avatar  avatar  avatar

roslibrust's Issues

Use of std_msgs for rust "help: use struct literal syntax instead: `MultiArrayLayout {}`"

Hi, I want to know is there any respository that I can use for std_msgs like https://github.com/ros/std_msgs. As I am trying to use this with the roslibrust package but, with data types like Float64MultiArray.msg I am getting the follwong error


  let msg = std_msgs::Float64MultiArray {layout: MultiArrayLayout, data: Vec};
   |                                                          ^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `MultiArrayLayout {}`



32 |         let msg = std_msgs::Float64MultiArray {layout: MultiArrayLayout {}, data: Vec {}};
   |                                                                                   ^^^^^^ expected struct `std::vec::Vec`, found struct `Vec`
   |

the name `ApplyPlanningScene` is defined multiple times `ApplyPlanningScene` must be defined only once in the type namespace of this modulerustcClick for full compiler diagnostic

Hi, I am using roslibrust to send data from a system that is having ROS install with moveit and gazebo. Following is the example I am following, however, I am getting the following error

the name `ApplyPlanningScene` is defined multiple times
`ApplyPlanningScene` must be defined only once in the type namespace of this modulerustc[Click for full compiler diagnostic](rust-analyzer-diagnostics-view:/diagnostic%20message%20%5B1%5D?1#file%3A%2F%2F%2Fhome%2Fakumar%2FTargetArm_Project%2Ftargetarm%2Fmessage_broadcaster%2Fsrc%2Fmain.rs)
the name `ChangeControlDimensions` is defined multiple times
`ChangeControlDimensions` must be defined only once in the type namespace of this modulerustc[Click for full compiler diagnostic](rust-analyzer-diagnostics-view:/diagnostic%20message%20%5B2%5D?2#file%3A%2F%2F%2Fhome%2Fakumar%2FTargetArm_Project%2Ftargetarm%2Fmessage_broadcaster%2Fsrc%2Fmain.rs)
the name `ChangeDriftDimensions` is defined multiple times
`ChangeDriftDimensions` must be defined only once in the type namespace of this modulerustc[Click for full compiler diagnostic](rust-analyzer-diagnostics-view:/diagnostic%20message%20%5B3%5D?3#file%3A%2F%2F%2Fhome%2Fakumar%2FTargetArm_Project%2Ftargetarm%2Fmessage_broadcaster%2Fsrc%2Fmain.rs)


use log::*;
use roslibrust::ClientHandle;

roslibrust_codegen_macro::find_and_generate_ros_messages!("/opt/ros/noetic/share/std_msgs");


#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    simple_logger::SimpleLogger::new()
        .with_level(log::LevelFilter::Debug)
        .without_timestamps() // required for running wsl2
        .init()
        .unwrap();
let client = ClientHandle::new("ws://localhost:9090").await?;
let publisher = client.advertise::<std_msgs::String>("talker").await?;
let val = "Hello World"; // sending example string to ros node working on another computer 
loop {
    let msg = std_msgs::String {data: val.to_string()};
    info!("About to publish");
    publisher.publish(msg).await.unwrap();
    info!("Published msg...");
    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}

}

Improved Integration Testing Utilizing rosapi

Goal is to improve robustness of our testing by leveraging the rosapi node to check the secondary side effects are executing correctly. To do this we need to get a CI docker image which includes both rosbridge and rosapi node and modify CI script to also start rosapi along with rosbridge ahead of integration tests.

This slightly builds on the issue for building a wrapper for rosapi.

Actual tests I'd like to build:

  • Confirm publisher unadvertise on Drop actually works by calling rosapi to get list of topics after it is dropped
  • Confirm service unadvertise on Drop actually works by calling rosapi to get list of topics after it is dropped
  • Actually execute the examples in CI and confirm they pass

CI for ROS2 / Full ROS2 support

I'm pretty dang sure this library is ROS2 compatible / compliant because rosbridge looks basically identical, but I'd like to confirm that.

Build a 2nd CI docker image that has rosbridge (also checkout #41 and probably stick rosapi node in as well), and make a second github action to run CI for both ros1 and ros2.

While doing this, think about how maybe to support different versions of ros2 and what generating that CI docker image would look like for Rolling vs. other versions. Is there anyway to auto update the image / kick-off CI as rolling is updated? I think a problem we're going to run into here is finding the right base images to build off of? If there is a "good" ROS2_rolling:latest image we can use this might not be terrible, but we likely need to strip 95% of the contents out of that image.

Bonus: update the auto-badges in the readme to show CI status for ROS1 and ROS2 (or even better various ROS2 versions).

WARN [roslibrust::rosbridge::client] Failed to reconnect: CommFailure(Io(Os { code: 111, kind: ConnectionRefused, message: "Connection refused" }))

Hi, I created my won package using example . I am getting the following

[roslibrust::rosbridge::client] Failed to reconnect: CommFailure(Io(Os { code: 111, kind: ConnectionRefused, message: "Connection refused" }))

Code I am using is

use log::*;
use roslibrust::ClientHandle;

roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces/std_msgs");

/// This example creates a client, and publishes a message to the topic "talker"
/// Running this example at the same time as subscribe_and_log will have the two examples
/// pass messages between each other.
/// To run this example a rosbridge websocket server should be running at the deafult port (9090).
#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    simple_logger::SimpleLogger::new()
        .with_level(log::LevelFilter::Debug)
        .without_timestamps() // required for running wsl2
        .init()
        .unwrap();

    let client = ClientHandle::new("ws://localhost:9080").await?;
    let publisher = client.advertise::<std_msgs::Header>("talker").await?;

    loop {
        let msg = std_msgs::Header::default();
        info!("About to publish");
        publisher.publish(msg).await.unwrap();
        info!("Published msg...");
        tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
    }
}

Support Message Generation Without ROS Installed

Usecase to be able to generate messages on a system without ROS installed.

Possible Solutions:

A) Store copies of std_msgs/ within lib to use for generation. Fallback to these if ROS_ env vars aren't found

B) Allow users to specify where to find message libraries explicitly instead of via environment variables (not ros canonical so probably not ideal)

I think I'll go with A) I don't think it will break too much to do that. We may need to store seperate versions for different ros releases, but let's start with noetic and see where we end up

Duplicate service struct definitions are constructed if the same service appears more than once

Copy the contents of assets/ros1_common_interfaces/common_msgs/diagnostic_msgs to another directory, maybe /tmp. Add this to your search paths somehow (ROS_PACKAGE_PATH=/tmp) and run RUST_LOG=debug cargo run --bin roslibrust_test.

The conflict is correctly noticed and msg files are not duplicated. However, service files are duplicated! Given the example, search roslibrust_test/src/ros1.rs and you'll find two entries for the AddDiagnostic service!

Flaky failure of Integration Test: bad_message_recv

https://github.com/Carter12s/roslibrust/actions/runs/4556418853/jobs/8036747405?pr=91

test rosbridge::integration_tests::integration_tests::bad_message_recv ... FAILED
test rosbridge::integration_tests::integration_tests::error_on_non_existent_service ... ok
test rosbridge::integration_tests::integration_tests::parallel_construction ... ok
test rosbridge::integration_tests::integration_tests::self_publish ... ok
test rosbridge::integration_tests::integration_tests::test_disconnect_returns_error ... ok
test rosbridge::integration_tests::integration_tests::test_strong_and_weak_client_counts ... ok
test rosbridge::integration_tests::integration_tests::timeouts_new ... ok
test rosbridge::integration_tests::integration_tests::unadvertise ... ignored
test rosbridge::integration_tests::integration_tests::working_with_char ... ok

failures:

---- rosbridge::integration_tests::integration_tests::bad_message_recv stdout ----
Error: Operation timed out: deadline has elapsed

Caused by:
    deadline has elapsed


failures:
    rosbridge::integration_tests::integration_tests::bad_message_recv

test result: FAILED. 7 passed; 1 failed; 1 ignored; 0 measured; 0 filtered out; finished in 1.27s

Resolved itself on retry

Sending data to another computer ROS

I would like to know how I can send data to different computer, I initiated with sending data into the same machine using the code. However, getting the following error message, is there a specific way on entering the IP address or any other requirement that needs to be taken care of before starting sending the message. Like do I need to install rosbridge and run it before sending the message.

Error
[roslibrust::rosbridge::client] Failed to reconnect: CommFailure(Io(Os { code: 111, kind: ConnectionRefused, message: "Connection refused" }))

CODE


use log::*;
use roslibrust::ClientHandle;

roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces/std_msgs");

/// This example creates a client, and publishes a message to the topic "talker"
/// Running this example at the same time as subscribe_and_log will have the two examples
/// pass messages between each other.
/// To run this example a rosbridge websocket server should be running at the deafult port (9090).
#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    simple_logger::SimpleLogger::new()
        .with_level(log::LevelFilter::Debug)
        .without_timestamps() // required for running wsl2
        .init()
        .unwrap();

    let client = ClientHandle::new("ws://localhost:9080").await?;
    let publisher = client.advertise::<std_msgs::Header>("talker").await?;

    loop {
        let msg = std_msgs::Header::default();
        info!("About to publish");
        publisher.publish(msg).await.unwrap();
        info!("Published msg...");
        tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
    }
}

Error when trying to implement turtlesim node

On ubuntu 20.4 with ROS noetic install.

Tried making the following changes to basic_publisher.rs example

+ use geometry_msgs::{Twist, Vector3};
- roslibrust_codegen_macro::find_and_generate_ros_messages!("roslibrust/local_msgs");
+ roslibrust_codegen_macro::find_and_generate_ros_messages!();

-     let publisher = client.advertise::<std_msgs::Header>("talker").await?;
+    let publisher = client.advertise::<Twist>("/turtle1/cmd_vel").await?;
    loop {
-         let msg = std_msgs::Header::default();
+        let msg = Twist {
+            linear: Vector3 {
+                x: 0.0,
+                y: 0.0,
+                z: 0.0,
+            },
+            angular: Vector3 {
+               x: 1.0,
+                y: 0.0,
+                z: 0.0,
+            },
+        };

This was the error output from the console after running the following

cargo run --example basic_publisher

error[E0412]: cannot find type `DurationI` in module `std_msgs`
 --> roslibrust/examples/basic_publisher.rs:4:1
  |
4 | roslibrust_codegen_macro::find_and_generate_ros_messages!();
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in `std_msgs`
  |
  = note: this error originates in the macro `roslibrust_codegen_macro::find_and_generate_ros_messages` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0412]: cannot find type `TimeI` in module `std_msgs`
 --> roslibrust/examples/basic_publisher.rs:4:1
  |
4 | roslibrust_codegen_macro::find_and_generate_ros_messages!();
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in `std_msgs`
  |
  = note: this error originates in the macro `roslibrust_codegen_macro::find_and_generate_ros_messages` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0412]: cannot find type `DurationI` in module `self`
 --> roslibrust/examples/basic_publisher.rs:4:1
  |
4 | roslibrust_codegen_macro::find_and_generate_ros_messages!();
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in `self`
  |
  = note: this error originates in the macro `roslibrust_codegen_macro::find_and_generate_ros_messages` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0412]: cannot find type `TimeI` in module `self`
 --> roslibrust/examples/basic_publisher.rs:4:1
  |
4 | roslibrust_codegen_macro::find_and_generate_ros_messages!();
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in `self`
  |
  = note: this error originates in the macro `roslibrust_codegen_macro::find_and_generate_ros_messages` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0283]: type annotations needed
 --> roslibrust/examples/basic_publisher.rs:4:1
  |
4 | roslibrust_codegen_macro::find_and_generate_ros_messages!();
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
  |
  = note: cannot satisfy `_: Default`
  = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info)

Some errors have detailed explanations: E0283, E0412.
For more information about an error, try `rustc --explain E0283`.

The text file would have been a .rs if let me paste that in here, but output.txt is the printed output of the variable "source" from roslibrust_test/main.rs

output.txt

Support cbor-raw encoding for messages

Currently roslibrust assumes and only supports default json-encoding.

It looks in the two affected places are Client::handle_publish where messages are decoded (and there's an unwrap assuming the encoding is json) and Writer::publish which assumes the msg is a RosMessageType. It probably makes sense to construct the publisher with an encoding mechanism defined in order to prevent changing the publish/subscribe APIs.

The format of these messages is not extremely well documented in rosbridge_suite's protocol document, nor is it even implemented in roslibpy, but from what I can tell it should produce messages like:

"msg": {
    "bytes": <bytearray>
}

We can make use of serde_rosmsg which implements the correct encoding mechanism, as despite the name it has no relation with cbor other than that their both in binary...

CI support for generating CI docker images

DevOps building off of #42 potentially. Right now the docker image that CI generates has to be manually updated when a developer chooses to. This isn't the worst thing in the world right now as noetic is effectively frozen, but as we go to support ROS2 / Rolling it would be great if a CI job can be kick-off / triggered somehow to generate new docker images when the underlying dependencies change...

conflicting implementation for `realsense2_camera::DeviceInfo`

Hi, I am trying to write a publisher to publish data to another computer where ROS will be running. However, I am getting the following error. I am sharing the code with you. I am getting the following error

error[E0428]: the name `DeviceInfo` is defined multiple times
 --> src/main.rs:6:1
  |
6 | roslibrust_codegen_macro::find_and_generate_ros_messages!();
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  | |
  | `DeviceInfo` redefined here
  | previous definition of the type `DeviceInfo` here
  |
  = note: `DeviceInfo` must be defined only once in the type namespace of this module
  = note: this error originates in the macro `roslibrust_codegen_macro::find_and_generate_ros_messages` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0433]: failed to resolve: could not find `serde` in the list of imported crates
 --> src/main.rs:6:1
  |
6 | roslibrust_codegen_macro::find_and_generate_ros_messages!();
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not find `serde` in the list of imported crates
  |
  = note: this error originates in the macro `roslibrust_codegen_macro::find_and_generate_ros_messages` (in Nightly builds, run with -Z macro-backtrace for more info)

warning: unused import: `roslibrust_codegen_macro::find_and_generate_ros_messages`
 --> src/main.rs:3:5
  |
3 | use roslibrust_codegen_macro::find_and_generate_ros_messages;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error[E0119]: conflicting implementations of trait `roslibrust::RosServiceType` for type `realsense2_camera::DeviceInfo`
 --> src/main.rs:6:1
  |
6 | roslibrust_codegen_macro::find_and_generate_ros_messages!();
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  | |
  | first implementation here
  | conflicting implementation for `realsense2_camera::DeviceInfo`
  |
  = note: this error originates in the macro `roslibrust_codegen_macro::find_and_generate_ros_messages` (in Nightly builds, run with -Z macro-backtrace for more info)

Some errors have detailed explanations: E0119, E0428, E0433.
For more information about an error, try `rustc --explain E0119`.
warning: `ros_rust_bridge` (bin "ros_rust_bridge") generated 1 warning
error: could not compile `ros_rust_bridge` due to 3 previous errors; 1 warning emitted



CODE

use log::*;
use roslibrust::ClientHandle;
use roslibrust_codegen_macro::find_and_generate_ros_messages;


roslibrust_codegen_macro::find_and_generate_ros_messages!();

/// This example creates a client, and publishes a message to the topic "talker"
/// Running this example at the same time as subscribe_and_log will have the two examples
/// pass messages between each other.
/// To run this example a rosbridge websocket server should be running at the deafult port (9090).
#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    simple_logger::SimpleLogger::new()
        .with_level(log::LevelFilter::Debug)
        .without_timestamps() // required for running wsl2
        .init()
        .unwrap();

    let client = ClientHandle::new("ws://localhost:9090").await?;
    let publisher = client.advertise::<std_msgs::Header>("talker").await?;

    loop {
        let msg = std_msgs::Header::default();
        info!("About to publish");
        publisher.publish(msg).await.unwrap();
        info!("Published msg...");
        tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
    }
}

Fix Codegen Dependency so we can Publish

    Updating crates.io index
error: all dependencies must have a version specified when publishing.
dependency `codegen` does not specify a version
Note: The published dependency will use the version from crates.io,
the `git` specification will be removed from the dependency declaration.```

Add "Stamped" trait to code generation

Goal is to automatically detect that a given type contains a field Header header and if so implement a trait named Stamped for it that provides access to the header.

This is intended to allow things like auto incrementing the seq number during each publish by detecting Header... TODO look into rust type system more for how best to do this.

Unadvertise Service Support

  • Handle returned by advertise service should auto un-advertise the service
  • Re-registering a service should produce a warning and have the previous callback be dropped

bond/Constants.msg breaks the code generation

A friend of mine tried running in a ROS environment and noticed that the macro was failing to run. I asked for the generated output and found that the const string parameter was malformed in the output from this message: https://github.com/ros/bond_core/blob/kinetic-devel/bond/msg/Constants.msg

This is what it tried to generate:

#[allow(non_snake_case)]
#[derive(:: serde :: Deserialize, :: serde :: Serialize, Debug, Default, Clone, PartialEq)]
pub struct Constants {}
impl ::roslibrust::RosMessageType for Constants {
    const ROS_TYPE_NAME: &'static str = "bond/Constants";
}
impl Constants {
    pub const DEAD_PUBLISH_PERIOD: f32 = 0.05;
    pub const DEFAULT_CONNECT_TIMEOUT: f32 = 10.0;
    pub const DEFAULT_HEARTBEAT_TIMEOUT: f32 = 4.0;
    pub const DEFAULT_DISCONNECT_TIMEOUT: f32 = 2.0;
    pub const DEFAULT_HEARTBEAT_PERIOD: f32 = 1.0;
    pub const DISABLE_HEARTBEAT_TIMEOUT_PARAM: &'static str = / bond_disable_heartbeat_timeout;
}

The str got malformed somehow.

use of undeclared crate or module `std_msgs`

Hi, I am using this package to send data from raspberrypi where I don't want to install ROS, however, I am not sure how can I define the std::msg by my own, I have seen some examples in https://github.com/Carter12s/roslibrust/tree/master/assets/test_msgs/msg . I also tried copying assert folder to my package.

My code looks , something like this

use log::*;
use roslibrust::ClientHandle;

roslibrust_codegen_macro::find_and_generate_ros_messages!("assets/ros1_common_interfaces/std_msgs");

/// This example creates a client, and publishes a message to the topic "talker"
/// Running this example at the same time as subscribe_and_log will have the two examples
/// pass messages between each other.
/// To run this example a rosbridge websocket server should be running at the deafult port (9090).
#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    simple_logger::SimpleLogger::new()
        .with_level(log::LevelFilter::Debug)
        .without_timestamps() // required for running wsl2
        .init()
        .unwrap();

    let client = ClientHandle::new("ws://192.169.XX.XX:9090").await?;
    let publisher = client.advertise::<std_msgs::Header>("talker").await?;

    loop {
        let msg = std_msgs::Header::default();
        info!("About to publish");
        publisher.publish(msg).await.unwrap();
        info!("Published msg...");
        tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
    }
}

Misleading message when calling inactive service

    let result = client
        .call_service::<(), rosapi::GetTimeResponse>("/not_real_service", ())
        .await
        .expect("Error while calling get_time service");

This will panic with the following error instead of gracefully failing:

thread 'main' panicked at 'Error while calling get_time service: InvalidMessage(Error("invalid type: string "Service /not_real_service does not exist", expected struct GetTimeResponse", line: 0, column: 0))'

Recommendation: ros msg files as submodules instead of copies

Currently all of the ROS msg files that are frequently distributed with ROS are contained in the local_msgs directory of this repo. I think it'd make sense to include these via submodules, not because they are subject to change at some point, but just to direct back to a single source of truth.

Recreate Ros Tutorials

One thing I'd advocate for (maybe prior to publishing on crates.io) is to recreate the ROS tutorial in a mdbook and then host it with Jekyll from a GitHub repo. This will make it easier to show how to get started.

UNIX Socket to roslibrist

Hi, I would like to know has anyone tried data transfer from UNIX Socket to roslibrust , like making a subscriber using roslibrust ?
I want to transfer data from an application to roslibrust in the same machine and then make a publisher to transfer to another system. Any suggestion would be much appreciated. My basic Idea now is to create a UNIX Socket and transfer data to a subscriber in roslibrust within the same system and then transfer the data to another system making a publisher.

Variable names or other tokens may be Rust keywords and need to be escaped with r#

ROS users will not be considering if certain variables names like override. We need to detect these and escape them like r#override.

Error:

error: expected identifier, found reserved keyword `override`
 --> <PATH_TO_SOURCE>/src/msgs.rs:3:21493
  |
3 | ...nRequest { pub action : u8 , pub robot_id : u8 , pub override : bool , pub data : std :: string :: String , } impl :: roslibrust :: RosMessageType for Calibrat...
  |                                                         ^^^^^^^^ expected identifier, found reserved keyword
  |
help: escape `override` to use it as an identifier
  |

Using roslibrust without ros installation

Hi, I know I can use roslibrust library to send messages without ros installation, however I would like to know is there any example that can help me to create my own message and give path to that. Basically my question is how to create ros msg package? Thank you

Tracking issue for v0.X

Creating this super-issue to make a list of things we want to get into the crate prior to releasing v0.3.0.

Feel free to add more, trying to get some semblance of when we're targeting what, what features we're prioritizing, etc.

Support for rosapi

Would be very nice for this library to support:

  • rostopic list
  • rostopic info
  • roservice list
  • rosservice info

These calls are available if the rosapi node is being run, we could build a nice wrapper API to abstract away the underlying service calls and nicely wrap the functionality.

Improve reporting of lack of ROS_PACKAGE_PATH

Currently using eprintln! to indicate when there is no ROS_PACKAGE_PATH defined. This should probably be a log::warn in roslibrust_rospack and any output could potentially be printed or reported by users of the log ecosystem. This could perhaps be used in the proc macro, but I need to do more investigation into what the best practices are for printing messages in proc-macros.

Example may be broken

In the subscribe_and_log example, there's the following loop:

loop {
        let _ = rx.changed().await;
        let msg = rx.borrow();
        info!("Got msg: {:?}", msg);
    }

The reference for tokio::sync::watch::Receiver<T>::borrow says:

This method does not mark the returned value as seen, so future calls to changed may return immediately even if you have already seen the value with a call to borrow.

So this example may be reading the same message over and over.

rx.borrow_and_update(); might make more sense.

ROS1: Work needed to support native ROS1 nodes

Did a little spelunking through ros/ros_comm some time ago and I think the things needed to achieve basic pubsub on ROS1 are:

  • Start generating md5sums for message types and add a method to get that md5sum
  • For publishers:
    • Host an XMLRPC on a TCP port
    • Call registerPublisher method on rosmaster XMLRPC API.
    • Handle connection headers from subscribers which want to listen by responding with header response
    • Then send rosmsg encoded data on the stream above
  • For subscribers
    • Request the XMLRPC URIs from rosmaster for a given topic
    • Listen for new publishers registered on an XMLRPC URI
    • Make TCPROS connections to publishers (first with a connection header), wait for connection header response
    • Handle incoming rosmsg encoded data.

Relevant docs:

No readme

Two can play this game, a game where everyone wins.

Service files with comments containing 3 or more consecutive dashes break parsing

We currently separate service files into two message files by searching for a triple dash through the whole file contents. However, --- is legal to have in the file as a comment and will break parsing.

For example:

# beginner_tutorials/AddTwoInts
# A file can have ------- if it would like, but will break parsing
int64 a
int64 b
---
int64 sum

Kill stubborn_spin on drop of client

Right now when the client is dropped it doesn't actually go away because in its constructor it is cloned and passed into the stubborn_spin() task.

We should figure out the right way to make sure the client actually goes away when dropped.

How to deal with disconnection?

Since this library is targeting communication with ROS assets remotely I think disconnection / re-connection should be a very core feature of the API with effective and clean error handling.

Current state:

  • Client::new() is stubborn and completely blocks until connection is established, kinda breaks timeout support, but it does start the "stubborn connect" which I do like
  • Subscriptions should survive disconnection, but impossible to know if they are disconnected? Should we push a disconnected error through them? We probably have to re-subscribe after we lose comms.
  • Publishers should have individual publishes fail, but once connection is re-established trying them again should be fine. However, do we need to re-advertise?
  • Service calls that experience a disconnect mid-call will likely hang forever and be effectively a memory leak. Likely need to be able to push an error back here and flush in-flight service calls when we disconnect.

Overall the library is in a lot of ways pretty brittle to disconnection right now for moderately complex usages.

The ideal outcome of this issue is a significant improvement to how we approach disconnections including systematic testing of disconnections (how do we create a disconnect in a test?)

Replace Reciever with Non-tokio type

While the ergonomics of tokio::sync::watch::Reciever make sense for a Subscription we should do much better.

What specifically should change:

  1. It should be our own type
  2. It should cause unsubscribe automatically on drop
  3. It should have error handling exposure

Re-organize Crates Again...

Okay I'm continuing to learn about crate dependencies in rust, how they chain, and the specific intricacies of proc-macros.

Here is my current problem...

I want to start working with rosapi in roslibrust. Partially I want an API for rosapi as a nice to have (#18), but I also want to use the rosapi node as a method for more robust testing. For example, confirming we advertised a service by getting the information for that service through rosapi.

However, I cannot invoke the proc_macro for codegen from a test written in roslibrust (even thou it is a dev-dependency) because of some weird import rules bullshit.

error[E0433]: failed to resolve: could not find `roslibrust` in the list of imported crates
   --> roslibrust/src/rosbridge/integration_tests.rs:333:9
    |
333 | /         roslibrust_codegen_macro::find_and_generate_ros_messages!(
334 | |             "assets/ros1_common_interfaces/rosapi"
335 | |         );
    | |_________^ could not find `roslibrust` in the list of imported crates
    |
    = note: this error originates in the macro `roslibrust_codegen_macro::find_and_generate_ros_messages` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0433`.
error: could not compile `roslibrust` due to previous error
warning: build failed, waiting for other jobs to finish...

It may be possible to work around this, but I'm not sure I want to.

Upon reflection I have a different design in mind for how this set of crates can (should) be internally structured:

  1. roslibrust_codegen: includes the current codegen module + the proc macro
  2. roslibrust: what currently exists
  3. roslibrust_test: can be eliminated and its tests directly built into roslibrust

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.