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

Options pattern (spin-off of #427) #429

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
14 changes: 4 additions & 10 deletions examples/message_demo/src/message_demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,25 +141,19 @@ fn demonstrate_pubsub() -> Result<(), Error> {
let context = rclrs::Context::new(env::args())?;
let 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_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",
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!")
},
Expand Down
3 changes: 1 addition & 2 deletions examples/minimal_pub_sub/src/minimal_publisher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ fn main() -> Result<(), Error> {

let node = rclrs::create_node(&context, "minimal_publisher")?;

let publisher =
node.create_publisher::<std_msgs::msg::String>("topic", rclrs::QOS_PROFILE_DEFAULT)?;
let publisher = node.create_publisher::<std_msgs::msg::String>("topic")?;

let mut message = std_msgs::msg::String::default();

Expand Down
1 change: 0 additions & 1 deletion examples/minimal_pub_sub/src/minimal_subscriber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ fn main() -> Result<(), Error> {

let _subscription = node.create_subscription::<std_msgs::msg::String, _>(
"topic",
rclrs::QOS_PROFILE_DEFAULT,
move |msg: std_msgs::msg::String| {
num_messages += 1;
println!("I heard: '{}'", msg.data);
Expand Down
4 changes: 1 addition & 3 deletions examples/minimal_pub_sub/src/minimal_two_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ impl MinimalSubscriber {
.node
.create_subscription::<std_msgs::msg::String, _>(
topic,
rclrs::QOS_PROFILE_DEFAULT,
move |msg: std_msgs::msg::String| {
minimal_subscriber_aux.callback(msg);
},
Expand All @@ -56,8 +55,7 @@ fn main() -> Result<(), Error> {
let subscriber_node_one = MinimalSubscriber::new("minimal_subscriber_one", "topic")?;
let subscriber_node_two = MinimalSubscriber::new("minimal_subscriber_two", "topic")?;

let publisher = publisher_node
.create_publisher::<std_msgs::msg::String>("topic", rclrs::QOS_PROFILE_DEFAULT)?;
let publisher = publisher_node.create_publisher::<std_msgs::msg::String>("topic")?;

std::thread::spawn(move || -> Result<(), rclrs::RclrsError> {
let mut message = std_msgs::msg::String::default();
Expand Down
3 changes: 1 addition & 2 deletions examples/minimal_pub_sub/src/zero_copy_publisher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ fn main() -> Result<(), Error> {

let node = rclrs::create_node(&context, "minimal_publisher")?;

let publisher =
node.create_publisher::<std_msgs::msg::rmw::UInt32>("topic", rclrs::QOS_PROFILE_DEFAULT)?;
let publisher = node.create_publisher::<std_msgs::msg::rmw::UInt32>("topic")?;

let mut publish_count: u32 = 1;

Expand Down
1 change: 0 additions & 1 deletion examples/minimal_pub_sub/src/zero_copy_subscriber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ fn main() -> Result<(), Error> {

let _subscription = node.create_subscription::<std_msgs::msg::UInt32, _>(
"topic",
rclrs::QOS_PROFILE_DEFAULT,
move |msg: rclrs::ReadOnlyLoanedMessage<'_, std_msgs::msg::UInt32>| {
num_messages += 1;
println!("I heard: '{}'", msg.data);
Expand Down
6 changes: 2 additions & 4 deletions examples/rust_pubsub/src/simple_publisher.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rclrs::{create_node, Context, Node, Publisher, RclrsError, QOS_PROFILE_DEFAULT};
use rclrs::{create_node, Context, Node, Publisher, RclrsError};
use std::{sync::Arc, thread, time::Duration};
use std_msgs::msg::String as StringMsg;
struct SimplePublisherNode {
Expand All @@ -8,9 +8,7 @@ struct SimplePublisherNode {
impl SimplePublisherNode {
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();
let _publisher = node.create_publisher("publish_hello").unwrap();
Ok(Self { node, _publisher })
}

Expand Down
12 changes: 4 additions & 8 deletions examples/rust_pubsub/src/simple_subscriber.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rclrs::{create_node, Context, Node, RclrsError, Subscription, QOS_PROFILE_DEFAULT};
use rclrs::{create_node, Context, Node, RclrsError, Subscription};
use std::{
env,
sync::{Arc, Mutex},
Expand All @@ -17,13 +17,9 @@ impl SimpleSubscriptionNode {
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);
},
)
.create_subscription::<StringMsg, _>("publish_hello", move |msg: StringMsg| {
*data_mut.lock().unwrap() = Some(msg);
})
.unwrap();
Ok(Self {
node,
Expand Down
52 changes: 45 additions & 7 deletions rclrs/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rosidl_runtime_rs::Message;
use crate::{
error::{RclReturnCode, ToResult},
rcl_bindings::*,
MessageCow, NodeHandle, RclrsError, ENTITY_LIFECYCLE_MUTEX,
IntoPrimitiveOptions, MessageCow, NodeHandle, QoSProfile, RclrsError, ENTITY_LIFECYCLE_MUTEX,
};

// SAFETY: The functions accessing this type, including drop(), shouldn't care about the thread
Expand Down Expand Up @@ -83,23 +83,29 @@ where
T: rosidl_runtime_rs::Service,
{
/// Creates a new client.
pub(crate) fn new(node_handle: Arc<NodeHandle>, topic: &str) -> Result<Self, RclrsError>
pub(crate) fn new<'a>(
node_handle: Arc<NodeHandle>,
options: impl Into<ClientOptions<'a>>,
) -> Result<Self, RclrsError>
// This uses pub(crate) visibility to avoid instantiating this struct outside
// [`Node::create_client`], see the struct's documentation for the rationale
where
T: rosidl_runtime_rs::Service,
{
let ClientOptions { service_name, qos } = options.into();
// SAFETY: Getting a zero-initialized value is always safe.
let mut rcl_client = unsafe { rcl_get_zero_initialized_client() };
let type_support = <T as rosidl_runtime_rs::Service>::get_type_support()
as *const rosidl_service_type_support_t;
let topic_c_string = CString::new(topic).map_err(|err| RclrsError::StringContainsNul {
err,
s: topic.into(),
})?;
let topic_c_string =
CString::new(service_name).map_err(|err| RclrsError::StringContainsNul {
err,
s: service_name.into(),
})?;

// SAFETY: No preconditions for this function.
let client_options = unsafe { rcl_client_get_default_options() };
let mut client_options = unsafe { rcl_client_get_default_options() };
client_options.qos = qos.into();

{
let rcl_node = node_handle.rcl_node.lock().unwrap();
Expand Down Expand Up @@ -275,6 +281,38 @@ where
}
}

/// `ClientOptions` are used by [`Node::create_client`][1] to initialize a
/// [`Client`] for a service.
///
/// [1]: crate::Node::create_client
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ClientOptions<'a> {
/// The name of the service that this client will send requests to
pub service_name: &'a str,
/// The quality of the service profile for this client
pub qos: QoSProfile,
}

impl<'a> ClientOptions<'a> {
/// Initialize a new [`ClientOptions`] with default settings.
pub fn new(service_name: &'a str) -> Self {
Self {
service_name,
qos: QoSProfile::services_default(),
}
}
}

impl<'a, T: IntoPrimitiveOptions<'a>> From<T> for ClientOptions<'a> {
fn from(value: T) -> Self {
let primitive = value.into_primitive_options();
let mut options = Self::new(primitive.name);
primitive.apply_to(&mut options.qos);
options
}
}

impl<T> ClientBase for Client<T>
where
T: rosidl_runtime_rs::Service,
Expand Down
Loading