Skip to content

Commit

Permalink
Added ROS2-like Rust Examples (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
imstevenpmwork authored Apr 22, 2024
1 parent ebd39c1 commit 6b68bfb
Show file tree
Hide file tree
Showing 12 changed files with 422 additions and 11 deletions.
16 changes: 8 additions & 8 deletions examples/python/README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
# Examples of Zenoh Python applications communicating with ROS 2 Nodes


## Messages Publication: [talker.py](talker.py)
## Messages Publication: [talker.py](src/talker.py)

This code mimics the ROS 2 [Topics "talker" demo](https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp/src/topics/talker.cpp). It's compatible with the ROS 2 [Topics "listener" demo](https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp/src/topics/listener.cpp) running those commands:
- `ros2 run demo_nodes_cpp listener`
- `zenho-bridge-ros2dds`
- `zenoh-bridge-ros2dds`
- `python ./talker.py`

## Messages Subscription: [listener.py](listener.py)
## Messages Subscription: [listener.py](src/listener.py)

This code mimics the ROS 2 [Topics "listener" demo](https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp/src/topics/listener.cpp). It's compatible with the ROS 2 [Topics "talker" demo](https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp/src/topics/talker.cpp) running those commands:
- `ros2 run demo_nodes_cpp talker`
- `zenho-bridge-ros2dds`
- `zenoh-bridge-ros2dds`
- `python ./listener.py`

## Services Client: [add_two_ints_client.py](add_two_ints_client.py)
## Services Client: [add_two_ints_client.py](src/add_two_ints_client.py)

This code mimics the ROS 2 [Services "add_two_ints_client" demo](https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp/src/services/add_two_ints_client.cpp). It's compatible with the ROS 2 [Services "add_two_ints_server" demo](https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp/src/services/add_two_ints_server.cpp) running those commands:
- `ros2 run demo_nodes_cpp add_two_ints_server`
- `zenho-bridge-ros2dds`
- `zenoh-bridge-ros2dds`
- `python ./add_two_ints_client.py`

## Actions Client: [fibonnacci_action_client.py](fibonnacci_action_client.py)
## Actions Client: [fibonnacci_action_client.py](src/fibonnacci_action_client.py)

This code mimics the ROS 2 [Actions "fibonnacci_action_client" demo](https://github.com/ros2/demos/blob/rolling/action_tutorials/action_tutorials_cpp/src/fibonacci_action_client.cpp). It's compatible with the ROS 2 [Actions "fibonnacci_action_server" demo](https://github.com/ros2/demos/blob/rolling/action_tutorials/action_tutorials_cpp/src/fibonacci_action_server.cpp) running those commands:
- `ros2 run action_tutorials_cpp fibonacci_action_server`
- `zenho-bridge-ros2dds`
- `zenoh-bridge-ros2dds`
- `python ./fibonnacci_action_client.py`
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class AddTwoInts_Request(IdlStruct, typename="AddTwoInts_Request"):

# Equivalent to AddTwoInts.Response class, but serializable by pycdr2
@dataclass
class AddTwoInts_Response(IdlStruct, typename="AddTwoInts_Request"):
class AddTwoInts_Response(IdlStruct, typename="AddTwoInts_Response"):
sum: pycdr2.types.int64

def main():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class Fibonacci_Feedback(IdlStruct, typename="Fibonacci_Feedback"):
def feedback_callback(sample: zenoh.Sample):
# Deserialize the message
feedback = Fibonacci_Feedback.deserialize(sample.payload)
print('Received feedback: {0}'.format(feedback.partial_sequence))
print('Next number in sequence received: {0}'.format(feedback.partial_sequence))


def main():
Expand All @@ -84,6 +84,7 @@ def main():
goal_id = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
req = Fibonacci_SendGoal_Request(goal_id, order=10)
# Send the query with the serialized request
print('Sending goal')
replies = session.get('fibonacci/_action/send_goal', zenoh.Queue(), value=req.serialize())
# Zenoh could get several replies for a request (e.g. from several "Service Servers" using the same name)
for reply in replies.receiver:
Expand All @@ -93,7 +94,7 @@ def main():
print('Goal rejected :(')
return

print('Goal accepted :)')
print('Goal accepted by server, waiting for result')

req = Fibonacci_GetResult_Request(goal_id)
# Send the query with the serialized request
Expand Down
File renamed without changes.
File renamed without changes.
17 changes: 17 additions & 0 deletions examples/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "rust_examples"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace]
[dependencies]
async-std = { version = "1.12.0" }
futures = { version = "0.3.28" }
zenoh = { version = "0.10.1-rc" }
clap = { version = "4.4.11", features = ["derive"] }
env_logger = { version = "0.10.0" }
serde = {version = "1" }
serde_derive = {version = "1"}
cdr = {version = "0.2.4"}
log = { version = "0.4.21"}
40 changes: 40 additions & 0 deletions examples/rust/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Examples of Zenoh Rust applications communicating with ROS 2 Nodes

## Building the examples
In order to build the examples you will need to:
* [Install Rust and Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html)
* [Clone the repository](https://github.com/eclipse-zenoh/zenoh-plugin-ros2dds)

Once this is done, compile by running the following:
```
cd examples/rust
cargo build
```

## Messages Publication: [talker.rs](src/bin/talker.rs)

This code mimics the ROS 2 [Topics "talker" demo](https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp/src/topics/talker.cpp). It's compatible with the ROS 2 [Topics "listener" demo](https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp/src/topics/listener.cpp) running those commands:
- `ros2 run demo_nodes_cpp listener`
- `zenoh-bridge-ros2dds`
- `cargo run --bin talker`

## Messages Subscription: [listener.rs](src/bin/listener.rs)

This code mimics the ROS 2 [Topics "listener" demo](https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp/src/topics/listener.cpp). It's compatible with the ROS 2 [Topics "talker" demo](https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp/src/topics/talker.cpp) running those commands:
- `ros2 run demo_nodes_cpp talker`
- `zenoh-bridge-ros2dds`
- `cargo run --bin listener`

## Services Client: [add_two_ints_client.rs](src/bin/add_two_ints_client.rs)

This code mimics the ROS 2 [Services "add_two_ints_client" demo](https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp/src/services/add_two_ints_client.cpp). It's compatible with the ROS 2 [Services "add_two_ints_server" demo](https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp/src/services/add_two_ints_server.cpp) running those commands:
- `ros2 run demo_nodes_cpp add_two_ints_server`
- `zenoh-bridge-ros2dds`
- `cargo run --bin add_two_ints_client`

## Actions Client: [fibonnacci_action_client.rs](src/bin/fibonnacci_action_client.rs)

This code mimics the ROS 2 [Actions "fibonnacci_action_client" demo](https://github.com/ros2/demos/blob/rolling/action_tutorials/action_tutorials_cpp/src/fibonacci_action_client.cpp). It's compatible with the ROS 2 [Actions "fibonnacci_action_server" demo](https://github.com/ros2/demos/blob/rolling/action_tutorials/action_tutorials_cpp/src/fibonacci_action_server.cpp) running those commands:
- `ros2 run action_tutorials_cpp fibonacci_action_server`
- `zenoh-bridge-ros2dds`
- `cargo run --bin fibonnacci_action_client`
2 changes: 2 additions & 0 deletions examples/rust/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[toolchain]
channel = "1.72.0"
75 changes: 75 additions & 0 deletions examples/rust/src/bin/add_two_ints_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// Copyright (c) 2023 ZettaScale Technology
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
//
// Contributors:
// ZettaScale Zenoh Team, <[email protected]>
//
use cdr::{CdrLe, Infinite};
use clap::{App, Arg};
use serde::{Deserialize, Serialize};
use zenoh::config::Config;
use zenoh::prelude::r#async::*;

#[derive(Serialize, PartialEq, Debug)]
struct AddTwoIntsRequest {
a: i64,
b: i64,
}

#[derive(Deserialize, PartialEq, Debug)]
struct AddTwoIntsResponse {
sum: i64,
}

#[async_std::main]
async fn main() {
env_logger::init();

let config = parse_args();

let session = zenoh::open(config).res().await.unwrap();

let req = AddTwoIntsRequest { a: 2, b: 3 };
let buf = cdr::serialize::<_, _, CdrLe>(&req, Infinite).unwrap();
let replies = session
.get("add_two_ints")
.with_value(buf)
.res()
.await
.unwrap();

while let Ok(reply) = replies.recv_async().await {
match cdr::deserialize_from::<_, AddTwoIntsResponse, _>(
reply.sample.unwrap().payload.reader(),
cdr::size::Infinite,
) {
Ok(res) => {
println!("Result of add_two_ints: {}", res.sum);
}
Err(e) => log::warn!("Error decoding message: {}", e),
}
}
}

fn parse_args() -> Config {
let args = App::new("zenoh sub example")
.arg(Arg::from_usage(
"-c, --config=[FILE] 'A configuration file.'",
))
.get_matches();

let config = if let Some(conf_file) = args.value_of("config") {
Config::from_file(conf_file).unwrap()
} else {
Config::default()
};

config
}
150 changes: 150 additions & 0 deletions examples/rust/src/bin/fibonnacci_action_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
//
// Copyright (c) 2023 ZettaScale Technology
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
//
// Contributors:
// ZettaScale Zenoh Team, <[email protected]>
//
use cdr::{CdrLe, Infinite};
use clap::{App, Arg};
use serde::{Deserialize, Serialize};
use zenoh::config::Config;
use zenoh::prelude::r#async::*;

#[derive(Deserialize, PartialEq, Debug)]
struct Time {
sec: u32,
nsec: u32,
}

#[derive(Serialize, PartialEq, Debug)]
struct FibonacciSendGoalRequest {
goal_id: [u8; 16],
order: i32,
}

#[derive(Deserialize, PartialEq, Debug)]
struct FibonacciSendGoalResponse {
accepted: bool,
stamp: Time,
}

#[derive(Serialize, PartialEq, Debug)]
struct FibonacciGetResultRequest {
goal_id: [u8; 16],
}

#[derive(Deserialize, PartialEq, Debug)]
struct FibonacciGetResultResponse {
status: i8,
sequence: Vec<i32>,
}

#[derive(Deserialize, PartialEq, Debug)]
struct FibonacciFeedback {
goal_id: [u8; 16],
partial_sequence: Vec<i32>,
}

#[async_std::main]
async fn main() {
env_logger::init();

let config = parse_args();

let session = zenoh::open(config).res().await.unwrap();

let _subscriber = session
.declare_subscriber("fibonacci/_action/feedback")
.callback(|sample| {
match cdr::deserialize_from::<_, FibonacciFeedback, _>(
sample.value.payload.reader(),
cdr::size::Infinite,
) {
Ok(msg) => {
println!(
"Next number in sequence received: {:?}",
msg.partial_sequence
);
}
Err(e) => log::warn!("Error decoding message: {}", e),
};
})
.res()
.await
.unwrap();

let goal_id: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let req = FibonacciSendGoalRequest {
goal_id: goal_id,
order: 10,
};

let buf = cdr::serialize::<_, _, CdrLe>(&req, Infinite).unwrap();
println!("Sending goal");
let replies = session
.get("fibonacci/_action/send_goal")
.with_value(buf)
.res()
.await
.unwrap();

while let Ok(reply) = replies.recv_async().await {
match cdr::deserialize_from::<_, FibonacciSendGoalResponse, _>(
reply.sample.unwrap().payload.reader(),
cdr::size::Infinite,
) {
Ok(res) => {
if res.accepted {
println!("Goal accepted by server, waiting for result");
} else {
println!("Goal rejected :(");
return;
}
}
Err(e) => log::warn!("Error decoding message: {}", e),
}
}

let req = FibonacciGetResultRequest { goal_id: goal_id };
let buf = cdr::serialize::<_, _, CdrLe>(&req, Infinite).unwrap();
let replies = session
.get("fibonacci/_action/get_result")
.with_value(buf)
.res()
.await
.unwrap();
while let Ok(reply) = replies.recv_async().await {
match cdr::deserialize_from::<_, FibonacciGetResultResponse, _>(
reply.sample.unwrap().payload.reader(),
cdr::size::Infinite,
) {
Ok(res) => {
println!("Result: {:?}", res.sequence);
}
Err(e) => log::warn!("Error decoding message: {}", e),
}
}
}

fn parse_args() -> Config {
let args = App::new("zenoh sub example")
.arg(Arg::from_usage(
"-c, --config=[FILE] 'A configuration file.'",
))
.get_matches();

let config = if let Some(conf_file) = args.value_of("config") {
Config::from_file(conf_file).unwrap()
} else {
Config::default()
};

config
}
Loading

0 comments on commit 6b68bfb

Please sign in to comment.