Skip to content

Commit 8973176

Browse files
committed
Use option struct for create_action_{client,server} arguments
This works similarly to the option struct used for the other endpoint types, except that it cannot use the PrimitiveOptions stuff since actions have multiple different QoS settings.
1 parent dbdebd1 commit 8973176

File tree

5 files changed

+145
-33
lines changed

5 files changed

+145
-33
lines changed

rclrs/src/action.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ mod server_goal_handle;
55
use crate::rcl_bindings::RCL_ACTION_UUID_SIZE;
66
use std::fmt;
77

8-
pub use client::{ActionClient, ActionClientBase, ActionClientState};
9-
pub use server::{ActionServer, ActionServerBase, ActionServerState};
8+
pub use client::{ActionClient, ActionClientBase, ActionClientOptions, ActionClientState};
9+
pub use server::{ActionServer, ActionServerBase, ActionServerOptions, ActionServerState};
1010
pub use server_goal_handle::ServerGoalHandle;
1111

1212
/// A unique identifier for a goal request.

rclrs/src/action/client.rs

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use crate::{
2-
error::ToResult, rcl_bindings::*, wait::WaitableNumEntities, Node, NodeHandle, RclrsError,
3-
ENTITY_LIFECYCLE_MUTEX,
2+
error::ToResult, rcl_bindings::*, wait::WaitableNumEntities, Node, NodeHandle, QoSProfile,
3+
RclrsError, ENTITY_LIFECYCLE_MUTEX,
44
};
55
use std::{
6+
borrow::Borrow,
67
ffi::CString,
78
marker::PhantomData,
89
sync::{atomic::AtomicBool, Arc, Mutex, MutexGuard},
@@ -101,17 +102,22 @@ where
101102
T: rosidl_runtime_rs::Action,
102103
{
103104
/// Creates a new action client.
104-
pub(crate) fn new(node: &Node, topic: &str) -> Result<Self, RclrsError>
105+
pub(crate) fn new<'a>(
106+
node: &Node,
107+
options: impl Into<ActionClientOptions<'a>>,
108+
) -> Result<Self, RclrsError>
105109
where
106110
T: rosidl_runtime_rs::Action,
107111
{
112+
let options = options.into();
108113
// SAFETY: Getting a zero-initialized value is always safe.
109114
let mut rcl_action_client = unsafe { rcl_action_get_zero_initialized_client() };
110115
let type_support = T::get_type_support() as *const rosidl_action_type_support_t;
111-
let topic_c_string = CString::new(topic).map_err(|err| RclrsError::StringContainsNul {
112-
err,
113-
s: topic.into(),
114-
})?;
116+
let action_name_c_string =
117+
CString::new(options.action_name).map_err(|err| RclrsError::StringContainsNul {
118+
err,
119+
s: options.action_name.into(),
120+
})?;
115121

116122
// SAFETY: No preconditions for this function.
117123
let action_client_options = unsafe { rcl_action_client_get_default_options() };
@@ -123,7 +129,7 @@ where
123129
// SAFETY:
124130
// * The rcl_action_client was zero-initialized as expected by this function.
125131
// * The rcl_node is kept alive by the NodeHandle because it is a dependency of the action client.
126-
// * The topic name and the options are copied by this function, so they can be dropped
132+
// * The action name and the options are copied by this function, so they can be dropped
127133
// afterwards.
128134
// * The entity lifecycle mutex is locked to protect against the risk of global
129135
// variables in the rmw implementation being unsafely modified during initialization.
@@ -132,7 +138,7 @@ where
132138
&mut rcl_action_client,
133139
&mut *rcl_node,
134140
type_support,
135-
topic_c_string.as_ptr(),
141+
action_name_c_string.as_ptr(),
136142
&action_client_options,
137143
)
138144
.ok()?;
@@ -209,3 +215,44 @@ where
209215
}
210216
}
211217
}
218+
219+
/// `ActionClientOptions` are used by [`Node::create_action_client`][1] to initialize an
220+
/// [`ActionClient`].
221+
///
222+
/// [1]: crate::Node::create_action_client
223+
#[derive(Debug, Clone)]
224+
#[non_exhaustive]
225+
pub struct ActionClientOptions<'a> {
226+
/// The name of the action that this client will send requests to
227+
pub action_name: &'a str,
228+
/// The quality of service profile for the goal service
229+
pub goal_service_qos: QoSProfile,
230+
/// The quality of service profile for the result service
231+
pub result_service_qos: QoSProfile,
232+
/// The quality of service profile for the cancel service
233+
pub cancel_service_qos: QoSProfile,
234+
/// The quality of service profile for the feedback topic
235+
pub feedback_topic_qos: QoSProfile,
236+
/// The quality of service profile for the status topic
237+
pub status_topic_qos: QoSProfile,
238+
}
239+
240+
impl<'a> ActionClientOptions<'a> {
241+
/// Initialize a new [`ActionClientOptions`] with default settings.
242+
pub fn new(action_name: &'a str) -> Self {
243+
Self {
244+
action_name,
245+
goal_service_qos: QoSProfile::services_default(),
246+
result_service_qos: QoSProfile::services_default(),
247+
cancel_service_qos: QoSProfile::services_default(),
248+
feedback_topic_qos: QoSProfile::topics_default(),
249+
status_topic_qos: QoSProfile::action_status_default(),
250+
}
251+
}
252+
}
253+
254+
impl<'a, T: Borrow<str> + ?Sized + 'a> From<&'a T> for ActionClientOptions<'a> {
255+
fn from(value: &'a T) -> Self {
256+
Self::new(value.borrow())
257+
}
258+
}

rclrs/src/action/server.rs

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ use crate::{
33
error::{RclReturnCode, ToResult},
44
rcl_bindings::*,
55
wait::WaitableNumEntities,
6-
Clock, DropGuard, Node, NodeHandle, RclrsError, ENTITY_LIFECYCLE_MUTEX,
6+
Clock, DropGuard, Node, NodeHandle, QoSProfile, RclrsError, ENTITY_LIFECYCLE_MUTEX,
77
};
88
use rosidl_runtime_rs::{Action, ActionImpl, Message, Service};
99
use std::{
10+
borrow::Borrow,
1011
collections::HashMap,
1112
ffi::CString,
1213
sync::{atomic::AtomicBool, Arc, Mutex, MutexGuard},
@@ -120,23 +121,25 @@ where
120121
T: rosidl_runtime_rs::Action + rosidl_runtime_rs::ActionImpl,
121122
{
122123
/// Creates a new action server.
123-
pub(crate) fn new(
124+
pub(crate) fn new<'a>(
124125
node: &Node,
125-
topic: &str,
126+
options: impl Into<ActionServerOptions<'a>>,
126127
goal_callback: impl Fn(GoalUuid, T::Goal) -> GoalResponse + 'static + Send + Sync,
127128
cancel_callback: impl Fn(Arc<ServerGoalHandle<T>>) -> CancelResponse + 'static + Send + Sync,
128129
accepted_callback: impl Fn(Arc<ServerGoalHandle<T>>) + 'static + Send + Sync,
129130
) -> Result<Self, RclrsError>
130131
where
131132
T: rosidl_runtime_rs::Action + rosidl_runtime_rs::ActionImpl,
132133
{
134+
let options = options.into();
133135
// SAFETY: Getting a zero-initialized value is always safe.
134136
let mut rcl_action_server = unsafe { rcl_action_get_zero_initialized_server() };
135137
let type_support = T::get_type_support() as *const rosidl_action_type_support_t;
136-
let topic_c_string = CString::new(topic).map_err(|err| RclrsError::StringContainsNul {
137-
err,
138-
s: topic.into(),
139-
})?;
138+
let action_name_c_string =
139+
CString::new(options.action_name).map_err(|err| RclrsError::StringContainsNul {
140+
err,
141+
s: options.action_name.into(),
142+
})?;
140143

141144
// SAFETY: No preconditions for this function.
142145
let action_server_options = unsafe { rcl_action_server_get_default_options() };
@@ -151,7 +154,7 @@ where
151154
// SAFETY:
152155
// * The rcl_action_server is zero-initialized as mandated by this function.
153156
// * The rcl_node is kept alive by the NodeHandle because it is a dependency of the action server.
154-
// * The topic name and the options are copied by this function, so they can be dropped
157+
// * The action name and the options are copied by this function, so they can be dropped
155158
// afterwards.
156159
// * The entity lifecycle mutex is locked to protect against the risk of global
157160
// variables in the rmw implementation being unsafely modified during initialization.
@@ -161,7 +164,7 @@ where
161164
&mut *rcl_node,
162165
&mut *rcl_clock,
163166
type_support,
164-
topic_c_string.as_ptr(),
167+
action_name_c_string.as_ptr(),
165168
&action_server_options,
166169
)
167170
.ok()?;
@@ -736,3 +739,45 @@ where
736739
}
737740
}
738741
}
742+
743+
/// `ActionServerOptions` are used by [`Node::create_action_server`][1] to initialize an
744+
/// [`ActionServer`].
745+
///
746+
/// [1]: crate::Node::create_action_server
747+
#[derive(Debug, Clone)]
748+
#[non_exhaustive]
749+
pub struct ActionServerOptions<'a> {
750+
/// The name of the action implemented by this server
751+
pub action_name: &'a str,
752+
/// The quality of service profile for the goal service
753+
pub goal_service_qos: QoSProfile,
754+
/// The quality of service profile for the result service
755+
pub result_service_qos: QoSProfile,
756+
/// The quality of service profile for the cancel service
757+
pub cancel_service_qos: QoSProfile,
758+
/// The quality of service profile for the feedback topic
759+
pub feedback_topic_qos: QoSProfile,
760+
/// The quality of service profile for the status topic
761+
pub status_topic_qos: QoSProfile,
762+
// TODO(nwn): result_timeout
763+
}
764+
765+
impl<'a> ActionServerOptions<'a> {
766+
/// Initialize a new [`ActionServerOptions`] with default settings.
767+
pub fn new(action_name: &'a str) -> Self {
768+
Self {
769+
action_name,
770+
goal_service_qos: QoSProfile::services_default(),
771+
result_service_qos: QoSProfile::services_default(),
772+
cancel_service_qos: QoSProfile::services_default(),
773+
feedback_topic_qos: QoSProfile::topics_default(),
774+
status_topic_qos: QoSProfile::action_status_default(),
775+
}
776+
}
777+
}
778+
779+
impl<'a, T: Borrow<str> + ?Sized + 'a> From<&'a T> for ActionServerOptions<'a> {
780+
fn from(value: &'a T) -> Self {
781+
Self::new(value.borrow())
782+
}
783+
}

rclrs/src/node.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ use std::{
1919
use rosidl_runtime_rs::Message;
2020

2121
use crate::{
22-
rcl_bindings::*, ActionClient, ActionClientBase, ActionClientState, ActionServer,
23-
ActionServerBase, ActionServerState, CancelResponse, Client, ClientBase, ClientOptions,
24-
ClientState, Clock, ContextHandle, GoalResponse, GoalUuid, GuardCondition, LogParams, Logger,
25-
ParameterBuilder, ParameterInterface, ParameterVariant, Parameters, Publisher,
26-
PublisherOptions, PublisherState, RclrsError, ServerGoalHandle, Service, ServiceBase,
27-
ServiceOptions, ServiceState, Subscription, SubscriptionBase, SubscriptionCallback,
28-
SubscriptionOptions, SubscriptionState, TimeSource, ToLogParams, ENTITY_LIFECYCLE_MUTEX,
22+
rcl_bindings::*, ActionClient, ActionClientBase, ActionClientOptions, ActionClientState,
23+
ActionServer, ActionServerBase, ActionServerOptions, ActionServerState, CancelResponse, Client,
24+
ClientBase, ClientOptions, ClientState, Clock, ContextHandle, GoalResponse, GoalUuid,
25+
GuardCondition, LogParams, Logger, ParameterBuilder, ParameterInterface, ParameterVariant,
26+
Parameters, Publisher, PublisherOptions, PublisherState, RclrsError, ServerGoalHandle, Service,
27+
ServiceBase, ServiceOptions, ServiceState, Subscription, SubscriptionBase,
28+
SubscriptionCallback, SubscriptionOptions, SubscriptionState, TimeSource, ToLogParams,
29+
ENTITY_LIFECYCLE_MUTEX,
2930
};
3031

3132
// SAFETY: The functions accessing this type, including drop(), shouldn't care about the thread
@@ -296,14 +297,14 @@ impl NodeState {
296297
///
297298
/// [1]: crate::ActionClient
298299
// TODO: make action client's lifetime depend on node's lifetime
299-
pub fn create_action_client<T>(
300+
pub fn create_action_client<'a, T>(
300301
self: &Arc<Self>,
301-
topic: &str,
302+
options: impl Into<ActionClientOptions<'a>>,
302303
) -> Result<ActionClient<T>, RclrsError>
303304
where
304305
T: rosidl_runtime_rs::Action,
305306
{
306-
let action_client = Arc::new(ActionClientState::<T>::new(self, topic)?);
307+
let action_client = Arc::new(ActionClientState::<T>::new(self, options)?);
307308
self.action_clients_mtx
308309
.lock()
309310
.unwrap()
@@ -315,9 +316,9 @@ impl NodeState {
315316
///
316317
/// [1]: crate::ActionServer
317318
// TODO: make action server's lifetime depend on node's lifetime
318-
pub fn create_action_server<ActionT, GoalCallback, CancelCallback, AcceptedCallback>(
319+
pub fn create_action_server<'a, ActionT, GoalCallback, CancelCallback, AcceptedCallback>(
319320
self: &Arc<Self>,
320-
topic: &str,
321+
options: impl Into<ActionServerOptions<'a>>,
321322
handle_goal: GoalCallback,
322323
handle_cancel: CancelCallback,
323324
handle_accepted: AcceptedCallback,
@@ -330,7 +331,7 @@ impl NodeState {
330331
{
331332
let action_server = Arc::new(ActionServerState::<ActionT>::new(
332333
self,
333-
topic,
334+
options,
334335
handle_goal,
335336
handle_cancel,
336337
handle_accepted,

rclrs/src/qos.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ impl QoSProfile {
295295
pub fn system_default() -> Self {
296296
QOS_PROFILE_SYSTEM_DEFAULT
297297
}
298+
299+
/// Get the default QoS profile for action status topics.
300+
pub fn action_status_default() -> Self {
301+
QOS_PROFILE_ACTION_STATUS_DEFAULT
302+
}
298303
}
299304

300305
impl From<QoSHistoryPolicy> for rmw_qos_history_policy_t {
@@ -478,3 +483,17 @@ pub const QOS_PROFILE_SYSTEM_DEFAULT: QoSProfile = QoSProfile {
478483
liveliness_lease: QoSDuration::SystemDefault,
479484
avoid_ros_namespace_conventions: false,
480485
};
486+
487+
/// Equivalent to `rcl_action_qos_profile_status_default` from the [`rcl_action` package][1].
488+
///
489+
/// [1]: https://github.com/ros2/rcl/blob/rolling/rcl_action/include/rcl_action/default_qos.h
490+
pub const QOS_PROFILE_ACTION_STATUS_DEFAULT: QoSProfile = QoSProfile {
491+
history: QoSHistoryPolicy::KeepLast { depth: 1 },
492+
reliability: QoSReliabilityPolicy::Reliable,
493+
durability: QoSDurabilityPolicy::TransientLocal,
494+
deadline: QoSDuration::SystemDefault,
495+
lifespan: QoSDuration::SystemDefault,
496+
liveliness: QoSLivelinessPolicy::SystemDefault,
497+
liveliness_lease: QoSDuration::SystemDefault,
498+
avoid_ros_namespace_conventions: false,
499+
};

0 commit comments

Comments
 (0)