Skip to content

Move examples to separate repo #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
115 changes: 115 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: Rust

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

env:
CARGO_TERM_COLOR: always

jobs:
build:
strategy:
matrix:
ros_distribution:
- foxy
- galactic
- humble
- rolling
include:
# Foxy Fitzroy (June 2020 - May 2023)
- docker_image: rostooling/setup-ros-docker:ubuntu-focal-ros-foxy-ros-base-latest
ros_distribution: foxy
ros_version: 2
# Galactic Geochelone (May 2021 - November 2022)
- docker_image: rostooling/setup-ros-docker:ubuntu-focal-ros-galactic-ros-base-latest
ros_distribution: galactic
ros_version: 2
# Humble Hawksbill (May 2022 - May 2027)
- docker_image: rostooling/setup-ros-docker:ubuntu-jammy-ros-humble-ros-base-latest
ros_distribution: humble
ros_version: 2
# Rolling Ridley (June 2020 - Present)
- docker_image: rostooling/setup-ros-docker:ubuntu-jammy-ros-rolling-ros-base-latest
ros_distribution: rolling
ros_version: 2
runs-on: ubuntu-latest
container:
image: ${{ matrix.docker_image }}
steps:
- uses: actions/checkout@v2

- name: Search packages in this repository
id: list_packages
run: |
echo ::set-output name=package_list::$(colcon list --names-only)

- name: Setup ROS environment
uses: ros-tooling/[email protected]
with:
required-ros-distributions: ${{ matrix.ros_distribution }}

- name: Setup Rust
uses: dtolnay/[email protected]
with:
components: clippy, rustfmt

- name: Install colcon-cargo and colcon-ros-cargo
run: |
sudo pip3 install git+https://github.com/colcon/colcon-cargo.git
sudo pip3 install git+https://github.com/colcon/colcon-ros-cargo.git

- name: Check formatting of Rust packages
run: |
for path in $(colcon list | awk '$3 == "(ament_cargo)" { print $2 }'); do
cd $path
cargo fmt -- --check
cd -
done

- name: Install cargo-ament-build
run: |
cargo install --debug cargo-ament-build

- name: Build and test
id: build
uses: ros-tooling/[email protected]
with:
package-name: ${{ steps.list_packages.outputs.package_list }}
target-ros2-distro: ${{ matrix.ros_distribution }}
vcs-repo-file-url: ros2_rust_${{ matrix.ros_distribution }}.repos

- name: Run clippy on Rust packages
run: |
cd ${{ steps.build.outputs.ros-workspace-directory-name }}
. /opt/ros/${{ matrix.ros_distribution }}/setup.sh
for path in $(colcon list | awk '$3 == "(ament_cargo)" { print $2 }'); do
cd $path
echo "Running clippy in $path"
cargo clippy --all-targets --all-features -- -D warnings
cd -
done

- name: Run cargo test on Rust packages
run: |
cd ${{ steps.build.outputs.ros-workspace-directory-name }}
. install/setup.sh
for path in $(colcon list | awk '$3 == "(ament_cargo)" && $1 != "examples_rclrs_minimal_pub_sub" && $1 != "examples_rclrs_minimal_client_service" { print $2 }'); do
cd $path
echo "Running cargo test in $path"
cargo test --all-features
cd -
done

- name: Rustdoc check
run: |
cd ${{ steps.build.outputs.ros-workspace-directory-name }}
. /opt/ros/${{ matrix.ros_distribution }}/setup.sh
for path in $(colcon list | awk '$3 == "(ament_cargo)" && $1 != "examples_rclrs_minimal_pub_sub" && $1 != "examples_rclrs_minimal_client_service" { print $2 }'); do
cd $path
echo "Running rustdoc check in $path"
cargo rustdoc -- -D warnings
cd -
done
25 changes: 25 additions & 0 deletions message_demo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "examples_rclrs_message_demo"
version = "0.3.0"
authors = ["Nikolai Morin <[email protected]>"]
edition = "2021"

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

[dependencies]
anyhow = {version = "1", features = ["backtrace"]}

[dependencies.rclrs]
version = "0.3"

[dependencies.rosidl_runtime_rs]
version = "0.3"

[dependencies.rclrs_example_msgs]
version = "0.3"
features = ["serde"]

[dependencies.serde_json]
version = "1.0"
23 changes: 23 additions & 0 deletions 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.3.0</version>
<description>Package containing an example of message-related functionality in rclrs.</description>
<maintainer email="[email protected]">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>
177 changes: 177 additions & 0 deletions message_demo/src/message_demo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
use std::convert::TryInto;
use std::env;

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

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(), "äöü");
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 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 context = rclrs::Context::new(env::args())?;
let mut node = rclrs::create_node(&context, "message_demo")?;

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

let _idiomatic_subscription = node
.create_subscription::<rclrs_example_msgs::msg::VariousTypes, _>(
"topic",
rclrs::QOS_PROFILE_DEFAULT,
move |_msg: rclrs_example_msgs::msg::VariousTypes| println!("Got idiomatic message!"),
)?;
let _direct_subscription = node
.create_subscription::<rclrs_example_msgs::msg::rmw::VariousTypes, _>(
"topic",
rclrs::QOS_PROFILE_DEFAULT,
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())?;
rclrs::spin_once(&node, None)?;
println!("Sending RMW-native message.");
direct_publisher.publish(rclrs_example_msgs::msg::rmw::VariousTypes::default())?;
rclrs::spin_once(&node, None)?;

Ok(())
}

fn main() -> Result<(), Error> {
check_default_values();
demonstrate_printing();
demonstrate_serde()?;
demonstrate_sequences();
demonstrate_pubsub()?;
Ok(())
}
30 changes: 30 additions & 0 deletions minimal_client_service/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "examples_rclrs_minimal_client_service"
version = "0.3.0"
authors = ["Esteve Fernandez <[email protected]>"]
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"] }

[dependencies.rclrs]
version = "0.3"

[dependencies.rosidl_runtime_rs]
version = "0.3"

[dependencies.example_interfaces]
version = "*"
23 changes: 23 additions & 0 deletions 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.3.0</version>
<description>Package containing an example of the client-service mechanism in rclrs.</description>
<maintainer email="[email protected]">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>
Loading