Skip to content

Commit 6b68bfb

Browse files
Added ROS2-like Rust Examples (#104)
1 parent ebd39c1 commit 6b68bfb

12 files changed

Lines changed: 422 additions & 11 deletions

examples/python/README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
# Examples of Zenoh Python applications communicating with ROS 2 Nodes
22

33

4-
## Messages Publication: [talker.py](talker.py)
4+
## Messages Publication: [talker.py](src/talker.py)
55

66
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:
77
- `ros2 run demo_nodes_cpp listener`
8-
- `zenho-bridge-ros2dds`
8+
- `zenoh-bridge-ros2dds`
99
- `python ./talker.py`
1010

11-
## Messages Subscription: [listener.py](listener.py)
11+
## Messages Subscription: [listener.py](src/listener.py)
1212

1313
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:
1414
- `ros2 run demo_nodes_cpp talker`
15-
- `zenho-bridge-ros2dds`
15+
- `zenoh-bridge-ros2dds`
1616
- `python ./listener.py`
1717

18-
## Services Client: [add_two_ints_client.py](add_two_ints_client.py)
18+
## Services Client: [add_two_ints_client.py](src/add_two_ints_client.py)
1919

2020
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:
2121
- `ros2 run demo_nodes_cpp add_two_ints_server`
22-
- `zenho-bridge-ros2dds`
22+
- `zenoh-bridge-ros2dds`
2323
- `python ./add_two_ints_client.py`
2424

25-
## Actions Client: [fibonnacci_action_client.py](fibonnacci_action_client.py)
25+
## Actions Client: [fibonnacci_action_client.py](src/fibonnacci_action_client.py)
2626

2727
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:
2828
- `ros2 run action_tutorials_cpp fibonacci_action_server`
29-
- `zenho-bridge-ros2dds`
29+
- `zenoh-bridge-ros2dds`
3030
- `python ./fibonnacci_action_client.py`

examples/python/add_two_ints_client.py renamed to examples/python/src/add_two_ints_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class AddTwoInts_Request(IdlStruct, typename="AddTwoInts_Request"):
2828

2929
# Equivalent to AddTwoInts.Response class, but serializable by pycdr2
3030
@dataclass
31-
class AddTwoInts_Response(IdlStruct, typename="AddTwoInts_Request"):
31+
class AddTwoInts_Response(IdlStruct, typename="AddTwoInts_Response"):
3232
sum: pycdr2.types.int64
3333

3434
def main():

examples/python/fibonnacci_action_client.py renamed to examples/python/src/fibonnacci_action_client.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class Fibonacci_Feedback(IdlStruct, typename="Fibonacci_Feedback"):
6060
def feedback_callback(sample: zenoh.Sample):
6161
# Deserialize the message
6262
feedback = Fibonacci_Feedback.deserialize(sample.payload)
63-
print('Received feedback: {0}'.format(feedback.partial_sequence))
63+
print('Next number in sequence received: {0}'.format(feedback.partial_sequence))
6464

6565

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

96-
print('Goal accepted :)')
97+
print('Goal accepted by server, waiting for result')
9798

9899
req = Fibonacci_GetResult_Request(goal_id)
99100
# Send the query with the serialized request

examples/rust/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "rust_examples"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
[workspace]
8+
[dependencies]
9+
async-std = { version = "1.12.0" }
10+
futures = { version = "0.3.28" }
11+
zenoh = { version = "0.10.1-rc" }
12+
clap = { version = "4.4.11", features = ["derive"] }
13+
env_logger = { version = "0.10.0" }
14+
serde = {version = "1" }
15+
serde_derive = {version = "1"}
16+
cdr = {version = "0.2.4"}
17+
log = { version = "0.4.21"}

examples/rust/README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Examples of Zenoh Rust applications communicating with ROS 2 Nodes
2+
3+
## Building the examples
4+
In order to build the examples you will need to:
5+
* [Install Rust and Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html)
6+
* [Clone the repository](https://github.com/eclipse-zenoh/zenoh-plugin-ros2dds)
7+
8+
Once this is done, compile by running the following:
9+
```
10+
cd examples/rust
11+
cargo build
12+
```
13+
14+
## Messages Publication: [talker.rs](src/bin/talker.rs)
15+
16+
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:
17+
- `ros2 run demo_nodes_cpp listener`
18+
- `zenoh-bridge-ros2dds`
19+
- `cargo run --bin talker`
20+
21+
## Messages Subscription: [listener.rs](src/bin/listener.rs)
22+
23+
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:
24+
- `ros2 run demo_nodes_cpp talker`
25+
- `zenoh-bridge-ros2dds`
26+
- `cargo run --bin listener`
27+
28+
## Services Client: [add_two_ints_client.rs](src/bin/add_two_ints_client.rs)
29+
30+
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:
31+
- `ros2 run demo_nodes_cpp add_two_ints_server`
32+
- `zenoh-bridge-ros2dds`
33+
- `cargo run --bin add_two_ints_client`
34+
35+
## Actions Client: [fibonnacci_action_client.rs](src/bin/fibonnacci_action_client.rs)
36+
37+
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:
38+
- `ros2 run action_tutorials_cpp fibonacci_action_server`
39+
- `zenoh-bridge-ros2dds`
40+
- `cargo run --bin fibonnacci_action_client`

examples/rust/rust-toolchain.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[toolchain]
2+
channel = "1.72.0"
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// Copyright (c) 2023 ZettaScale Technology
3+
//
4+
// This program and the accompanying materials are made available under the
5+
// terms of the Eclipse Public License 2.0 which is available at
6+
// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7+
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8+
//
9+
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10+
//
11+
// Contributors:
12+
// ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13+
//
14+
use cdr::{CdrLe, Infinite};
15+
use clap::{App, Arg};
16+
use serde::{Deserialize, Serialize};
17+
use zenoh::config::Config;
18+
use zenoh::prelude::r#async::*;
19+
20+
#[derive(Serialize, PartialEq, Debug)]
21+
struct AddTwoIntsRequest {
22+
a: i64,
23+
b: i64,
24+
}
25+
26+
#[derive(Deserialize, PartialEq, Debug)]
27+
struct AddTwoIntsResponse {
28+
sum: i64,
29+
}
30+
31+
#[async_std::main]
32+
async fn main() {
33+
env_logger::init();
34+
35+
let config = parse_args();
36+
37+
let session = zenoh::open(config).res().await.unwrap();
38+
39+
let req = AddTwoIntsRequest { a: 2, b: 3 };
40+
let buf = cdr::serialize::<_, _, CdrLe>(&req, Infinite).unwrap();
41+
let replies = session
42+
.get("add_two_ints")
43+
.with_value(buf)
44+
.res()
45+
.await
46+
.unwrap();
47+
48+
while let Ok(reply) = replies.recv_async().await {
49+
match cdr::deserialize_from::<_, AddTwoIntsResponse, _>(
50+
reply.sample.unwrap().payload.reader(),
51+
cdr::size::Infinite,
52+
) {
53+
Ok(res) => {
54+
println!("Result of add_two_ints: {}", res.sum);
55+
}
56+
Err(e) => log::warn!("Error decoding message: {}", e),
57+
}
58+
}
59+
}
60+
61+
fn parse_args() -> Config {
62+
let args = App::new("zenoh sub example")
63+
.arg(Arg::from_usage(
64+
"-c, --config=[FILE] 'A configuration file.'",
65+
))
66+
.get_matches();
67+
68+
let config = if let Some(conf_file) = args.value_of("config") {
69+
Config::from_file(conf_file).unwrap()
70+
} else {
71+
Config::default()
72+
};
73+
74+
config
75+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//
2+
// Copyright (c) 2023 ZettaScale Technology
3+
//
4+
// This program and the accompanying materials are made available under the
5+
// terms of the Eclipse Public License 2.0 which is available at
6+
// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7+
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
8+
//
9+
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10+
//
11+
// Contributors:
12+
// ZettaScale Zenoh Team, <zenoh@zettascale.tech>
13+
//
14+
use cdr::{CdrLe, Infinite};
15+
use clap::{App, Arg};
16+
use serde::{Deserialize, Serialize};
17+
use zenoh::config::Config;
18+
use zenoh::prelude::r#async::*;
19+
20+
#[derive(Deserialize, PartialEq, Debug)]
21+
struct Time {
22+
sec: u32,
23+
nsec: u32,
24+
}
25+
26+
#[derive(Serialize, PartialEq, Debug)]
27+
struct FibonacciSendGoalRequest {
28+
goal_id: [u8; 16],
29+
order: i32,
30+
}
31+
32+
#[derive(Deserialize, PartialEq, Debug)]
33+
struct FibonacciSendGoalResponse {
34+
accepted: bool,
35+
stamp: Time,
36+
}
37+
38+
#[derive(Serialize, PartialEq, Debug)]
39+
struct FibonacciGetResultRequest {
40+
goal_id: [u8; 16],
41+
}
42+
43+
#[derive(Deserialize, PartialEq, Debug)]
44+
struct FibonacciGetResultResponse {
45+
status: i8,
46+
sequence: Vec<i32>,
47+
}
48+
49+
#[derive(Deserialize, PartialEq, Debug)]
50+
struct FibonacciFeedback {
51+
goal_id: [u8; 16],
52+
partial_sequence: Vec<i32>,
53+
}
54+
55+
#[async_std::main]
56+
async fn main() {
57+
env_logger::init();
58+
59+
let config = parse_args();
60+
61+
let session = zenoh::open(config).res().await.unwrap();
62+
63+
let _subscriber = session
64+
.declare_subscriber("fibonacci/_action/feedback")
65+
.callback(|sample| {
66+
match cdr::deserialize_from::<_, FibonacciFeedback, _>(
67+
sample.value.payload.reader(),
68+
cdr::size::Infinite,
69+
) {
70+
Ok(msg) => {
71+
println!(
72+
"Next number in sequence received: {:?}",
73+
msg.partial_sequence
74+
);
75+
}
76+
Err(e) => log::warn!("Error decoding message: {}", e),
77+
};
78+
})
79+
.res()
80+
.await
81+
.unwrap();
82+
83+
let goal_id: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
84+
let req = FibonacciSendGoalRequest {
85+
goal_id: goal_id,
86+
order: 10,
87+
};
88+
89+
let buf = cdr::serialize::<_, _, CdrLe>(&req, Infinite).unwrap();
90+
println!("Sending goal");
91+
let replies = session
92+
.get("fibonacci/_action/send_goal")
93+
.with_value(buf)
94+
.res()
95+
.await
96+
.unwrap();
97+
98+
while let Ok(reply) = replies.recv_async().await {
99+
match cdr::deserialize_from::<_, FibonacciSendGoalResponse, _>(
100+
reply.sample.unwrap().payload.reader(),
101+
cdr::size::Infinite,
102+
) {
103+
Ok(res) => {
104+
if res.accepted {
105+
println!("Goal accepted by server, waiting for result");
106+
} else {
107+
println!("Goal rejected :(");
108+
return;
109+
}
110+
}
111+
Err(e) => log::warn!("Error decoding message: {}", e),
112+
}
113+
}
114+
115+
let req = FibonacciGetResultRequest { goal_id: goal_id };
116+
let buf = cdr::serialize::<_, _, CdrLe>(&req, Infinite).unwrap();
117+
let replies = session
118+
.get("fibonacci/_action/get_result")
119+
.with_value(buf)
120+
.res()
121+
.await
122+
.unwrap();
123+
while let Ok(reply) = replies.recv_async().await {
124+
match cdr::deserialize_from::<_, FibonacciGetResultResponse, _>(
125+
reply.sample.unwrap().payload.reader(),
126+
cdr::size::Infinite,
127+
) {
128+
Ok(res) => {
129+
println!("Result: {:?}", res.sequence);
130+
}
131+
Err(e) => log::warn!("Error decoding message: {}", e),
132+
}
133+
}
134+
}
135+
136+
fn parse_args() -> Config {
137+
let args = App::new("zenoh sub example")
138+
.arg(Arg::from_usage(
139+
"-c, --config=[FILE] 'A configuration file.'",
140+
))
141+
.get_matches();
142+
143+
let config = if let Some(conf_file) = args.value_of("config") {
144+
Config::from_file(conf_file).unwrap()
145+
} else {
146+
Config::default()
147+
};
148+
149+
config
150+
}

0 commit comments

Comments
 (0)