Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
adf1b08
Move rclrs_examples to examples/minimal_pub_sub (#163)
nnmm May 16, 2022
5121d2d
Add Nikolai and Jacob to authors in Cargo.toml and maintainers in pac…
nnmm May 23, 2022
ba359ee
Add function to demonstrate serde integration (#179)
nnmm May 29, 2022
5579176
feat: add example launch (#187)
wep21 Jun 4, 2022
0600696
Move create_node and create_node_builder back to the rclrs module (#200)
esteve Jun 22, 2022
59e1080
Added support for clients and services (#146)
esteve Jul 8, 2022
5c3594b
Fix build errors in examples_rclrs_minimal_client_service (#220)
nnmm Jul 9, 2022
f2f00e9
Add support for loaned messages (#212)
nnmm Aug 16, 2022
46cbeaf
Extend SubscriptionCallback trait to ReadOnlyLoanedMessage (#266)
nnmm Sep 28, 2022
f3bf46b
Bump package versions to 0.3 (#274)
nnmm Oct 3, 2022
d765d7d
Format all code with group_imports = StdExternalCrate (#272)
nnmm Oct 3, 2022
dee354c
Version 0.3.1 (#285)
nnmm Oct 17, 2022
55071ab
Return Arc from the create_node function to match other create_X func…
esteve Jul 25, 2023
e67d089
Refactor spin and spin_once to mimic rclcpp's Executor API (#324)
esteve Aug 7, 2023
b863fc8
Add service_is_ready() (#339)
Carter12s Oct 26, 2023
9a46a52
Version 0.4.0 (#343)
esteve Nov 7, 2023
2718177
Revert "Version 0.4.0 (#343)" (#344)
esteve Nov 7, 2023
9039a89
Version 0.4.0 (#346)
esteve Nov 7, 2023
b1c0824
Version 0.4.1 (#353)
esteve Nov 28, 2023
1471dea
Add wchar support (#349)
maspe36 Jan 9, 2024
3e6ae3e
Moved the `rclrs_example_msgs` package to the examples folder. This p…
maspe36 Mar 31, 2024
f4e7c05
Use nightly for style check (#396)
mxgrey May 13, 2024
52b0d6a
Add simple talk/listener tutorial, matching the official ROS 2 docume…
Guelakais Jun 28, 2024
e66800d
Fix buffer overflow for bounded sequence example (#444)
mxgrey Dec 9, 2024
bfceba9
Execution structure (spin-off of #427) (#428)
mxgrey Feb 25, 2025
c048a8a
Options pattern (spin-off of #427) (#429)
mxgrey Mar 13, 2025
f1a0f41
Shared state pattern (spin-off of #427) (#430)
mxgrey May 12, 2025
1f27b13
Force backtrace version that is compatible with 1.75 (#481)
mxgrey May 14, 2025
a722c52
fix: add Cargo.lock to version control (#486)
esteve Jun 1, 2025
2f276e4
Async Workers (#446)
mxgrey Jun 8, 2025
f5ab55e
upgrade colcon-cargo in pixi.toml for windows CI (#495)
knmcguire Jul 1, 2025
c3d190d
Remove lockfiles from being tracked (#498)
mxgrey Aug 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions rclrs/logging_demo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "examples_logging_demo"
version = "0.1.0"
edition = "2021"

[dependencies]
rclrs = "0.4"
example_interfaces = "*"
21 changes: 21 additions & 0 deletions rclrs/logging_demo/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0"?>
<?xml-model
href="http://download.ros.org/schema/package_format3.xsd"
schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>examples_logging_demo</name>
<maintainer email="esteve@apache.org">Esteve Fernandez</maintainer>
<!-- This project is not military-sponsored, Jacob's employment contract just requires him to use this email address -->
<maintainer email="jacob.a.hassold.civ@army.mil">Jacob Hassold</maintainer>
<version>0.4.1</version>
<description>Package containing an example of how to use a worker in rclrs.</description>
<license>Apache License 2.0</license>

<depend>rclrs</depend>
<depend>rosidl_runtime_rs</depend>
<depend>example_interfaces</depend>

<export>
<build_type>ament_cargo</build_type>
</export>
</package>
39 changes: 39 additions & 0 deletions rclrs/logging_demo/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use rclrs::*;
use std::time::Duration;

fn main() -> Result<(), RclrsError> {
let mut executor = Context::default_from_env()?.create_basic_executor();
let node = executor.create_node("logging_demo")?;

let _subscription = node.clone().create_subscription(
"logging_demo",
move |msg: example_interfaces::msg::String| {
let data = msg.data;

// You can apply modifiers such as .once() to node.logger()
// to dictate how the logging behaves.
log!(node.logger().once(), "First message: {data}",);

log!(node.logger().skip_first(), "Subsequent message: {data}",);

// You can chain multiple modifiers together.
log_warn!(
node.logger().skip_first().throttle(Duration::from_secs(5)),
"Throttled message: {data}",
);
},
)?;

// Any &str can be used as the logger name and have
// logging modifiers applied to it.
log_info!(
"notice".once(),
"Ready to begin logging example_interfaces/msg/String messages published to 'logging_demo'.",
);
log_warn!(
"help",
"Try running\n \
$ ros2 topic pub logging_demo example_interfaces/msg/String \"data: message\"",
);
executor.spin(SpinOptions::default()).first_error()
}
19 changes: 19 additions & 0 deletions rclrs/message_demo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "examples_rclrs_message_demo"
version = "0.4.1"
authors = ["Nikolai Morin <nnmmgit@gmail.com>"]
edition = "2021"

[[bin]]
name = "message_demo"
path = "src/message_demo.rs"

[dependencies]
anyhow = {version = "1", features = ["backtrace"]}
rclrs = "0.4"
rosidl_runtime_rs = "0.4"
rclrs_example_msgs = { version = "0.4", features = ["serde"] }
serde_json = "1.0"

# This specific version is compatible with Rust 1.75
backtrace = "=0.3.74"
23 changes: 23 additions & 0 deletions rclrs/message_demo/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<?xml-model
href="http://download.ros.org/schema/package_format3.xsd"
schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>examples_rclrs_message_demo</name>
<version>0.4.1</version>
<description>Package containing an example of message-related functionality in rclrs.</description>
<maintainer email="nnmmgit@gmail.com">Nikolai Morin</maintainer>
<license>Apache License 2.0</license>

<build_depend>rclrs</build_depend>
<build_depend>rosidl_runtime_rs</build_depend>
<build_depend>rclrs_example_msgs</build_depend>

<exec_depend>rclrs</exec_depend>
<exec_depend>rosidl_runtime_rs</exec_depend>
<exec_depend>rclrs_example_msgs</exec_depend>

<export>
<build_type>ament_cargo</build_type>
</export>
</package>
181 changes: 181 additions & 0 deletions rclrs/message_demo/src/message_demo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
use std::convert::TryInto;

use anyhow::{Error, Result};
use rosidl_runtime_rs::{seq, BoundedSequence, Message, Sequence};

use rclrs::*;

fn check_default_values() {
let msg = rclrs_example_msgs::msg::rmw::VariousTypes::default();
assert!(msg.bool_member);
assert_eq!(msg.int8_member, 1i8);
assert_eq!(msg.uint8_member, 2u8);
assert_eq!(msg.byte_member, 3u8);
assert_eq!(msg.float32_member, 1e-2f32);
assert_eq!(msg.float_array, [1.0, 2.0, 3.0]);
assert_eq!(msg.float_seq_bounded, seq![3 # 4.0, 5.0]);
assert_eq!(msg.float_seq_unbounded, seq![6.0]);
assert_eq!(msg.string_member.to_string(), "Χαίρετε 你好");
assert_eq!(msg.wstring_member.to_string(), "αντίο σου 再见");
assert_eq!(msg.bounded_string_member.to_string(), "aou");
assert_eq!(msg.bounded_wstring_member.to_string(), "äöü");
assert_eq!(
msg.string_array.clone().map(|s| s.to_string()),
["R", "O", "S", "2"].map(String::from)
);
assert_eq!(
msg.string_seq_bounded,
seq![4 # "R".into(), "O".into(), "S".into(), "2".into()]
);
assert_eq!(
msg.string_seq_unbounded,
seq!["R".into(), "O".into(), "S".into(), "2".into()]
);
assert_eq!(
msg.bounded_string_array.clone().map(|s| s.to_string()),
["R", "O", "S", "2"].map(String::from)
);
assert_eq!(
msg.bounded_string_seq_bounded,
["R", "O", "S", "2"]
.into_iter()
.map(|s| s.try_into().unwrap())
.collect()
);
assert_eq!(
msg.bounded_string_seq_unbounded,
["R", "O", "S", "2"]
.into_iter()
.map(|s| s.try_into().unwrap())
.collect()
);
assert_eq!(msg.nested_member.effect.to_string(), "discombobulate");
assert_eq!(
msg.nested_array,
[msg.nested_member.clone(), msg.nested_member.clone()]
);
assert_eq!(msg.nested_seq_bounded, seq![3 #]);
assert_eq!(msg.nested_seq_unbounded, seq![]);

// The default instance for the idiomatic type also has the defaults set
let idiomatic_msg = rclrs_example_msgs::msg::VariousTypes::default();
assert_eq!(
rclrs_example_msgs::msg::VariousTypes::into_rmw_message(std::borrow::Cow::Owned(
idiomatic_msg
))
.into_owned(),
msg
);
}

fn check_default_idl_values() {
let idiomatic_msg = rclrs_example_msgs::msg::MyMessage::default();
let rmw_msg = rclrs_example_msgs::msg::rmw::MyMessage::default();

assert_eq!(idiomatic_msg.wchar_value, 0u16);
assert_eq!(rmw_msg.wchar_value, 0u16);
}

fn demonstrate_printing() {
let default_msg = rclrs_example_msgs::msg::VariousTypes::default();
println!("================== Compact debug representation ==================");
println!("{:?}", default_msg);
println!("================== Pretty debug representation ===================");
println!("{:#?}", default_msg);
// The RMW-native message type has the same output
let default_rmw_msg = rclrs_example_msgs::msg::rmw::VariousTypes::default();
assert_eq!(
format!("{:?}", default_msg),
format!("{:?}", default_rmw_msg)
);
assert_eq!(
format!("{:#?}", default_msg),
format!("{:#?}", default_rmw_msg)
);
}

fn demonstrate_serde() -> Result<(), Error> {
// When the serde feature is turned on, messages are able to be serialized
// to and deserialized from a variety of formats. Here JSON is used as an
// example.
// Works with RMW-native and idiomatic messages.
let idiomatic_msg = rclrs_example_msgs::msg::VariousTypes::default();
let rmw_msg = rclrs_example_msgs::msg::rmw::VariousTypes::default();
println!("================= JSON serialization with Serde ==================");
let idiomatic_serialized = serde_json::to_string_pretty(&idiomatic_msg)?;
let rmw_serialized = serde_json::to_string_pretty(&rmw_msg)?;
assert_eq!(idiomatic_serialized, rmw_serialized);
println!("{}", rmw_serialized);
let idiomatic_deserialized = serde_json::from_str(&idiomatic_serialized)?;
let rmw_deserialized = serde_json::from_str(&rmw_serialized)?;
assert_eq!(idiomatic_msg, idiomatic_deserialized);
assert_eq!(rmw_msg, rmw_deserialized);
Ok(())
}

fn demonstrate_sequences() {
// Convenient creation of (bounded) sequences with the seq! macro
// This one has three items and a length bound of 5
let mut float_seq_bounded = seq![5 # 1.0, 2.0, 3.0];
// Sequences and bounded sequences have iter(), iter_mut(), and into_iter()
float_seq_bounded
.iter_mut()
.for_each(|n: &mut f32| *n += 1.0);
let float_vec_1: Vec<_> = float_seq_bounded.iter().copied().collect();
let float_vec_2: Vec<_> = float_seq_bounded.into_iter().collect();
assert_eq!(float_vec_1, float_vec_2);
// Sequences also implement FromIterator.
let mut int_seq_unbounded: Sequence<i32> = [42; 4].into_iter().collect();
// Bounded sequences will ignore remaining items once the length bound is reached
let mut int_seq_bounded: BoundedSequence<i32, 3> = [42; 4].into_iter().collect();
// Sequences deref to slices
int_seq_bounded[2] = 24;
assert_eq!(int_seq_bounded.last(), Some(&24));
int_seq_unbounded[2..].copy_from_slice(&int_seq_bounded[1..]);
// New sequences will contain default values – and 0 for primitive types
let seq_with_default_values = Sequence::<rclrs_example_msgs::msg::rmw::NestedType>::new(1);
assert_eq!(seq_with_default_values[0].effect, "discombobulate".into());
}

fn demonstrate_pubsub() -> Result<(), Error> {
println!("================== Interoperability demo ==================");
// Demonstrate interoperability between idiomatic and RMW-native message types
let mut executor = Context::default_from_env()?.create_basic_executor();
let node = executor.create_node("message_demo")?;

let idiomatic_publisher =
node.create_publisher::<rclrs_example_msgs::msg::VariousTypes>("topic")?;
let direct_publisher =
node.create_publisher::<rclrs_example_msgs::msg::rmw::VariousTypes>("topic")?;

let _idiomatic_subscription = node
.create_subscription::<rclrs_example_msgs::msg::VariousTypes, _>(
"topic",
move |_msg: rclrs_example_msgs::msg::VariousTypes| println!("Got idiomatic message!"),
)?;
let _direct_subscription = node
.create_subscription::<rclrs_example_msgs::msg::rmw::VariousTypes, _>(
"topic",
move |_msg: rclrs_example_msgs::msg::rmw::VariousTypes| {
println!("Got RMW-native message!")
},
)?;
println!("Sending idiomatic message.");
idiomatic_publisher.publish(rclrs_example_msgs::msg::VariousTypes::default())?;
executor.spin(SpinOptions::spin_once()).first_error()?;
println!("Sending RMW-native message.");
direct_publisher.publish(rclrs_example_msgs::msg::rmw::VariousTypes::default())?;
executor.spin(SpinOptions::spin_once()).first_error()?;

Ok(())
}

fn main() -> Result<(), Error> {
check_default_values();
check_default_idl_values();
demonstrate_printing();
demonstrate_serde()?;
demonstrate_sequences();
demonstrate_pubsub()?;
Ok(())
}
27 changes: 27 additions & 0 deletions rclrs/minimal_client_service/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "examples_rclrs_minimal_client_service"
version = "0.4.1"
authors = ["Esteve Fernandez <esteve@apache.org>"]
edition = "2021"

[[bin]]
name = "minimal_client"
path = "src/minimal_client.rs"

[[bin]]
name = "minimal_client_async"
path = "src/minimal_client_async.rs"

[[bin]]
name = "minimal_service"
path = "src/minimal_service.rs"

[dependencies]
anyhow = {version = "1", features = ["backtrace"]}
tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread", "time"] }
rclrs = "0.4"
rosidl_runtime_rs = "0.4"
example_interfaces = "*"

# This specific version is compatible with Rust 1.75
backtrace = "=0.3.74"
23 changes: 23 additions & 0 deletions rclrs/minimal_client_service/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<?xml-model
href="http://download.ros.org/schema/package_format3.xsd"
schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>examples_rclrs_minimal_client_service</name>
<version>0.4.1</version>
<description>Package containing an example of the client-service mechanism in rclrs.</description>
<maintainer email="esteve@apache.org">Esteve Fernandez</maintainer>
<license>Apache License 2.0</license>

<build_depend>example_interfaces</build_depend>
<build_depend>rclrs</build_depend>
<build_depend>rosidl_runtime_rs</build_depend>

<exec_depend>example_interfaces</exec_depend>
<exec_depend>rclrs</exec_depend>
<exec_depend>rosidl_runtime_rs</exec_depend>

<export>
<build_type>ament_cargo</build_type>
</export>
</package>
31 changes: 31 additions & 0 deletions rclrs/minimal_client_service/src/minimal_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use anyhow::{Error, Result};
use example_interfaces::srv::*;
use rclrs::*;

fn main() -> Result<(), Error> {
let mut executor = Context::default_from_env()?.create_basic_executor();

let node = executor.create_node("minimal_client")?;

let client = node.create_client::<AddTwoInts>("add_two_ints")?;

let promise = executor.commands().run(async move {
println!("Waiting for service...");
client.notify_on_service_ready().await.unwrap();

let request = AddTwoInts_Request { a: 41, b: 1 };

println!("Waiting for response");
let response: AddTwoInts_Response = client.call(&request).unwrap().await.unwrap();

println!(
"Result of {} + {} is: {}",
request.a, request.b, response.sum,
);
});

executor
.spin(SpinOptions::new().until_promise_resolved(promise))
.first_error()?;
Ok(())
}
Loading