Skip to content

Commit 81c065e

Browse files
committed
Introduce InitOptions to allow manually setting domain ID
Signed-off-by: Michael X. Grey <[email protected]>
1 parent 10b2bcb commit 81c065e

File tree

1 file changed

+90
-7
lines changed

1 file changed

+90
-7
lines changed

rclrs/src/context.rs

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ pub(crate) struct ContextHandle {
5555
impl Context {
5656
/// Creates a new context.
5757
///
58-
/// Usually, this would be called with `std::env::args()`, analogously to `rclcpp::init()`.
58+
/// Usually this would be called with `std::env::args()`, analogously to `rclcpp::init()`.
5959
/// See also the official "Passing ROS arguments to nodes via the command-line" tutorial.
6060
///
61-
/// Creating a context can fail in case the args contain invalid ROS arguments.
61+
/// Creating a context will fail if the args contain invalid ROS arguments.
6262
///
6363
/// # Example
6464
/// ```
@@ -68,6 +68,21 @@ impl Context {
6868
/// assert!(Context::new(invalid_remapping).is_err());
6969
/// ```
7070
pub fn new(args: impl IntoIterator<Item = String>) -> Result<Self, RclrsError> {
71+
Self::new_with_options(args, InitOptions::new())
72+
}
73+
74+
/// Same as [`Context::new`] except you can additionally provide initialization options.
75+
///
76+
/// # Example
77+
/// ```
78+
/// use rclrs::{Context, InitOptions};
79+
/// let context = Context::new_with_options([], InitOptions::new().with_domain_id(Some(5))).unwrap();
80+
/// assert_eq!(context.domain_id(), 5);
81+
/// ````
82+
pub fn new_with_options(
83+
args: impl IntoIterator<Item=String>,
84+
options: InitOptions,
85+
) -> Result<Self, RclrsError> {
7186
// SAFETY: Getting a zero-initialized value is always safe
7287
let mut rcl_context = unsafe { rcl_get_zero_initialized_context() };
7388
let cstring_args: Vec<CString> = args
@@ -84,11 +99,7 @@ impl Context {
8499
unsafe {
85100
// SAFETY: No preconditions for this function.
86101
let allocator = rcutils_get_default_allocator();
87-
// SAFETY: Getting a zero-initialized value is always safe.
88-
let mut rcl_init_options = rcl_get_zero_initialized_init_options();
89-
// SAFETY: Passing in a zero-initialized value is expected.
90-
// In the case where this returns not ok, there's nothing to clean up.
91-
rcl_init_options_init(&mut rcl_init_options, allocator).ok()?;
102+
let mut rcl_init_options = options.into_rcl(allocator)?;
92103
// SAFETY: This function does not store the ephemeral init_options and c_args
93104
// pointers. Passing in a zero-initialized rcl_context is expected.
94105
let ret = rcl_init(
@@ -115,6 +126,25 @@ impl Context {
115126
})
116127
}
117128

129+
/// Returns the ROS domain ID that the context is using.
130+
///
131+
/// The domain ID controls which nodes can send messages to each other, see the [ROS 2 concept article][1].
132+
/// It can be set through the `ROS_DOMAIN_ID` environment variable.
133+
///
134+
/// [1]: https://docs.ros.org/en/rolling/Concepts/About-Domain-ID.html
135+
pub fn domain_id(&self) -> u8 {
136+
let mut domain_id: usize = 0;
137+
let ret = unsafe {
138+
rcl_context_get_domain_id(
139+
&mut *self.handle.rcl_context.lock().unwrap(),
140+
&mut domain_id
141+
)
142+
};
143+
144+
debug_assert_eq!(ret, 0);
145+
domain_id as u8
146+
}
147+
118148
/// Checks if the context is still valid.
119149
///
120150
/// This will return `false` when a signal has caused the context to shut down (currently
@@ -128,6 +158,59 @@ impl Context {
128158
}
129159
}
130160

161+
/// Additional options for initializing the Context.
162+
#[derive(Default, Clone)]
163+
pub struct InitOptions {
164+
/// The domain ID that should be used by the Context. Set to None to ask for
165+
/// the default behavior, which is to set the domain ID according to the
166+
/// [ROS_DOMAIN_ID][1] environment variable.
167+
///
168+
/// [1]: https://docs.ros.org/en/rolling/Concepts/Intermediate/About-Domain-ID.html#the-ros-domain-id
169+
domain_id: Option<u8>,
170+
}
171+
172+
impl InitOptions {
173+
/// Create a new InitOptions with all default values.
174+
pub fn new() -> InitOptions {
175+
Self::default()
176+
}
177+
178+
/// Transform an InitOptions into a new one with a certain domain_id
179+
pub fn with_domain_id(mut self, domain_id: Option<u8>) -> InitOptions {
180+
self.domain_id = domain_id;
181+
self
182+
}
183+
184+
/// Set the domain_id of an InitOptions, or reset it to the default behavior
185+
/// (determined by environment variables) by providing None.
186+
pub fn set_domain_id(&mut self, domain_id: Option<u8>) {
187+
self.domain_id = domain_id;
188+
}
189+
190+
/// Get the domain_id that will be provided by these InitOptions.
191+
pub fn domain_id(&self) -> Option<u8> {
192+
self.domain_id
193+
}
194+
195+
fn into_rcl(self, allocator: rcutils_allocator_s) -> Result<rcl_init_options_t, RclrsError> {
196+
unsafe {
197+
// SAFETY: Getting a zero-initialized value is always safe.
198+
let mut rcl_init_options = rcl_get_zero_initialized_init_options();
199+
// SAFETY: Passing in a zero-initialized value is expected.
200+
// In the case where this returns not ok, there's nothing to clean up.
201+
rcl_init_options_init(&mut rcl_init_options, allocator).ok()?;
202+
203+
// We only need to set the domain_id if the user asked for something
204+
// other than None. When the user asks for None, that is equivalent
205+
// to the default value in rcl_init_options.
206+
if let Some(domain_id) = self.domain_id {
207+
rcl_init_options_set_domain_id(&mut rcl_init_options, domain_id as usize);
208+
}
209+
return Ok(rcl_init_options);
210+
}
211+
}
212+
}
213+
131214
#[cfg(test)]
132215
mod tests {
133216
use super::*;

0 commit comments

Comments
 (0)