Skip to content
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

Pub sub example #2

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
2 changes: 2 additions & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
imports_granularity = "Crate"

15 changes: 15 additions & 0 deletions your_package_name/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "your_package_name"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name="simple_publisher"
path="src/simple_publisher.rs"
[[bin]]
name="simple_subscriber"
path="src/simple_subscriber.rs"
[dependencies]
rclrs = "*"
std_msgs = "*"
14 changes: 14 additions & 0 deletions your_package_name/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<package format="3">
<name>your_project_name</name>
<version>0.0.0</version>
<description>TODO: Package description. Seriously. Please make a Package description. We all will thank for it.</description>
<maintainer email="[email protected]">user</maintainer>
<license>TODO: License declaration. Licenses are Great. Please add a Licence</license>

<depend>rclrs</depend>
<depend>std_msgs</depend>

<export>
<build_type>ament_cargo</build_type>
</export>
</package>
69 changes: 69 additions & 0 deletions your_package_name/src/simple_publisher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT};
/// Creates a SimplePublisherNode, initializes a node and publisher, and provides
/// methods to publish a simple "Hello World" message on a loop in separate threads.

/// Imports the Arc type from std::sync, used for thread-safe reference counting pointers,
/// and the StringMsg message type from std_msgs for publishing string messages.
use std::{sync::Arc, thread, time::Duration};
use std_msgs::msg::String as StringMsg;
// / SimplePublisherNode struct contains node and publisher members.
// / Used to initialize a ROS 2 node and publisher, and publish messages.
struct SimplePublisherNode {
node: Arc<Node>,
_publisher: Arc<Publisher<StringMsg>>,
}
/// The `new` function takes a context and returns a Result containing the
/// initialized SimplePublisherNode or an error. It creates a node with the
/// given name and creates a publisher on the "publish_hello" topic.
///
/// The SimplePublisherNode contains the node and publisher members.
impl SimplePublisherNode {
/// Creates a new SimplePublisherNode by initializing a node and publisher.
///
/// This function takes a context and returns a Result containing the
/// initialized SimplePublisherNode or an error. It creates a node with the
/// given name and creates a publisher on the "publish_hello" topic.
///
/// The SimplePublisherNode contains the node and publisher members.
fn new(context: &Context) -> Result<Self, RclrsError> {
let node = create_node(context, "simple_publisher").unwrap();
let _publisher = node
.create_publisher("publish_hello", QOS_PROFILE_DEFAULT)
.unwrap();
Ok(Self { node, _publisher })
}

/// Publishes a "Hello World" message on the publisher.
///
/// Creates a StringMsg with "Hello World" as the data, publishes it on
/// the `_publisher`, and returns a Result. This allows regularly publishing
/// a simple message on a loop.
fn publish_data(&self, increment: i32) -> Result<i32, RclrsError> {
let msg: StringMsg = StringMsg {
data: format!("Hello World {}", increment),
};
self._publisher.publish(msg).unwrap();
Ok(increment + 1_i32)
}
}

/// The main function initializes a ROS 2 context, node and publisher,
/// spawns a thread to publish messages repeatedly, and spins the node
/// to receive callbacks.
///
/// It creates a context, initializes a SimplePublisherNode which creates
/// a node and publisher, clones the publisher to pass to the thread,
/// spawns a thread to publish "Hello World" messages repeatedly, and
/// calls spin() on the node to receive callbacks. This allows publishing
/// messages asynchronously while spinning the node.
fn main() -> Result<(), RclrsError> {
let context = Context::new(std::env::args()).unwrap();
let publisher = Arc::new(SimplePublisherNode::new(&context).unwrap());
let publisher_other_thread = Arc::clone(&publisher);
let mut count: i32 = 0;
thread::spawn(move || loop {
thread::sleep(Duration::from_millis(1000));
count = publisher_other_thread.publish_data(count).unwrap();
});
rclrs::spin(publisher.node.clone())
}
63 changes: 63 additions & 0 deletions your_package_name/src/simple_subscriber.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT};
use std::{
env,
sync::{Arc, Mutex},
thread,
time::Duration,
};
use std_msgs::msg::String as StringMsg;
/// A simple ROS2 subscriber node that receives and prints "hello" messages.
///
/// This node creates a subscription to the "publish_hello" topic and prints the
/// received messages to the console. It runs the subscription in a separate
/// thread, while the main thread calls `rclrs::spin()` to keep the node running.
pub struct SimpleSubscriptionNode {
node: Arc<Node>,
_subscriber: Arc<Subscription<StringMsg>>,
data: Arc<Mutex<Option<StringMsg>>>,
}
/// Implements a simple ROS2 subscriber node that receives and prints "hello" messages.
/// The `new` function creates the node and the subscription, and returns a `SimpleSubscriptionNode`
/// instance. The `data_callback` function can be used to access the latest received message.
impl SimpleSubscriptionNode {
fn new(context: &Context) -> Result<Self, RclrsError> {
let node = create_node(context, "simple_subscription").unwrap();
let data: Arc<Mutex<Option<StringMsg>>> = Arc::new(Mutex::new(None));
let data_mut: Arc<Mutex<Option<StringMsg>>> = Arc::clone(&data);
let _subscriber = node
.create_subscription::<StringMsg, _>(
"publish_hello",
QOS_PROFILE_DEFAULT,
move |msg: StringMsg| {
*data_mut.lock().unwrap() = Some(msg);
},
)
.unwrap();
Ok(Self {
node,
_subscriber,
data,
})
}
fn data_callback(&self) -> Result<(), RclrsError> {
if let Some(data) = self.data.lock().unwrap().as_ref() {
println!("{}", data.data);
} else {
println!("No message available yet.");
}
Ok(())
}
}
/// The `main` function creates a new ROS2 context, a `SimpleSubscriptionNode` instance, and starts a separate thread to periodically call the `data_callback` method on the subscription. The main thread then calls `rclrs::spin()` to keep the node running and receive messages.
///
/// The separate thread is used to ensure that the `data_callback` method is called regularly, even if the main thread is blocked in `rclrs::spin()`. This allows the subscriber to continuously process and print the received "hello" messages.
fn main() -> Result<(), RclrsError> {
let context = Context::new(env::args()).unwrap();
let subscription = Arc::new(SimpleSubscriptionNode::new(&context).unwrap());
let subscription_other_thread = Arc::clone(&subscription);
thread::spawn(move || loop {
thread::sleep(Duration::from_millis(1000));
subscription_other_thread.data_callback().unwrap()
});
rclrs::spin(subscription.node.clone())
}