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

Add methods related to node options to NodeBuilder #186

Merged
merged 26 commits into from
Jun 10, 2022
Merged
Changes from 11 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
85e7fea
Add methods related to node options to NodeBuilder
Soya-Onishi May 31, 2022
b04b17a
Format comment and move line to put near from related ones
Soya-Onishi Jun 1, 2022
a6ce30f
Remove domain_id because of erased field from galactic
Soya-Onishi Jun 2, 2022
a343aba
Fix SAFETY comments
Soya-Onishi Jun 2, 2022
e11f01e
Fix SAFETY docs for rcl_parse_arguments
Soya-Onishi Jun 2, 2022
3785db0
Separate procedure related to node_options in build()
Soya-Onishi Jun 2, 2022
d9416dd
Replace docs about arguments method
Soya-Onishi Jun 3, 2022
35cce29
Replace docs about use_global_arguments method
Soya-Onishi Jun 3, 2022
b5323a3
Add Drop impls
Soya-Onishi Jun 3, 2022
8090e80
merge from upstream
Soya-Onishi Jun 3, 2022
58d02fa
Add reference for details about command line arguments
Soya-Onishi Jun 3, 2022
612d7d5
remove wasted line
Soya-Onishi Jun 3, 2022
c2d9049
Add more detailed comments for enable_rosout method
Soya-Onishi Jun 3, 2022
bb09962
Add null check to prevent from calling post process twice
Soya-Onishi Jun 4, 2022
115ad6f
merge upstream
Soya-Onishi Jun 4, 2022
804bbb2
Fix valid string statements
Soya-Onishi Jun 4, 2022
1d46963
Exclude context lock from unsafe block
Soya-Onishi Jun 4, 2022
aded175
merge from upstream
Soya-Onishi Jun 9, 2022
39ca296
Remove Drop for rcl_arguments_t
Soya-Onishi Jun 9, 2022
82076ad
Merge branch 'master' into node_options
Soya-Onishi Jun 9, 2022
cc893ca
Add statement about rcl_arguments_t fininalization
Soya-Onishi Jun 9, 2022
fe5d05c
Merge branch 'node_options' of github.com:Soya-Onishi/ros2_rust into …
Soya-Onishi Jun 9, 2022
aad27b4
Fix comments
Soya-Onishi Jun 9, 2022
523ec5b
Add failsafe code like NulError checker in build
Soya-Onishi Jun 9, 2022
33e65d0
Fix to deal with erroneous case of cstring cast
Soya-Onishi Jun 10, 2022
7427701
Remove outdated safety comment
nnmm Jun 10, 2022
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
144 changes: 139 additions & 5 deletions rclrs/src/node/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ use std::sync::Arc;
///
/// The default values for optional fields are:
/// - `namespace: "/"`
/// - `use_global_arguments: true`
/// - `arguments: []`
/// - `enable_rosout: true`
///
/// # Example
/// ```
Expand Down Expand Up @@ -40,6 +43,9 @@ pub struct NodeBuilder {
context: Arc<Mutex<rcl_context_t>>,
name: String,
namespace: String,
use_global_arguments: bool,
arguments: Vec<String>,
enable_rosout: bool,
}

impl NodeBuilder {
Expand Down Expand Up @@ -84,6 +90,9 @@ impl NodeBuilder {
context: context.handle.clone(),
name: name.to_string(),
namespace: "/".to_string(),
use_global_arguments: true,
arguments: vec![],
enable_rosout: true,
}
}

Expand Down Expand Up @@ -142,28 +151,100 @@ impl NodeBuilder {
self
}

/// Enables or disables using global arguments.
///
/// The "global" arguments are those used in [creating the context][1].
///
/// # Example
/// ```
/// # use rclrs::{Context, Node, NodeBuilder, RclrsError};
/// let context_args = ["--ros-args", "--remap", "__node:=your_node"]
/// .map(String::from);
/// let context = Context::new(context_args)?;
/// // Ignore the global arguments:
/// let node_without_global_args = context
/// .create_node_builder("my_node")
/// .use_global_arguments(false)
/// .build()?;
/// assert_eq!(node_without_global_args.name(), "my_node");
/// // Do not ignore the global arguments:
/// let node_with_global_args = context
/// .create_node_builder("my_other_node")
/// .use_global_arguments(true)
/// .build()?;
/// assert_eq!(node_with_global_args.name(), "your_node");
/// # Ok::<(), RclrsError>(())
/// ```
///
/// [1]: crate::Context::new
pub fn use_global_arguments(mut self, enable: bool) -> Self {
self.use_global_arguments = enable;
self
}

/// Sets node-specific command line arguments.
///
/// These arguments are parsed the same way as those for [`Context::new()`][1].
/// However, the node-specific command line arguments have higher precedence than the arguments
/// used in creating the context.
///
/// For more details about command line arguments, see [here][2].
///
/// # Example
/// ```
/// # use rclrs::{Context, Node, NodeBuilder, RclrsError};
/// // Usually, this would change the name of "my_node" to "context_args_node":
/// let context_args = ["--ros-args", "--remap", "my_node:__node:=context_args_node"]
/// .map(String::from);
/// let context = Context::new(context_args)?;
/// // But the node arguments will change it to "node_args_node":
/// let node_args = ["--ros-args", "--remap", "my_node:__node:=node_args_node"]
/// .map(String::from);
/// let node = context
/// .create_node_builder("my_node")
/// .arguments(node_args)
/// .build()?;
/// assert_eq!(node.name(), "node_args_node");
/// # Ok::<(), RclrsError>(())
/// ```
///
/// [1]: crate::Context::new
/// [2]: https://design.ros2.org/articles/ros_command_line_arguments.html
pub fn arguments(mut self, arguments: impl IntoIterator<Item = String>) -> Self {
self.arguments = arguments.into_iter().collect();
self
}

/// Enables rosout.
/// If false, rosout logging is disabled.
pub fn enable_rosout(mut self, enable: bool) -> Self {
self.enable_rosout = enable;
self
}

/// Builds the node instance.
///
/// Node name and namespace validation is performed in this method.
///
/// For example usage, see the [`NodeBuilder`][1] docs.
///
/// # Panics
/// When the node name or namespace contain null bytes.
/// If any of below conditions are filled, panic occurs
/// - node name contains null byte
/// - namespace contains null byte
/// - any of node argument strings contain null byte.
///
/// [1]: crate::NodeBuilder
pub fn build(&self) -> Result<Node, RclrsError> {
let node_name = CString::new(self.name.as_str()).unwrap();
let node_namespace = CString::new(self.namespace.as_str()).unwrap();
let node_options = self.create_node_options()?;

// SAFETY: No preconditions for this function.
// SAFETY: Getting a zero-initialized value is always safe.
let mut node_handle = unsafe { rcl_get_zero_initialized_node() };

unsafe {
// SAFETY: No preconditions for this function.
let context_handle = &mut *self.context.lock();
// SAFETY: No preconditions for this function.
let node_options = rcl_node_get_default_options();

// SAFETY: The node handle is zero-initialized as expected by this function.
// The strings and node options are copied by this function, so we don't need
Expand All @@ -187,4 +268,57 @@ impl NodeBuilder {
subscriptions: std::vec![],
})
}

/// Creates node options.
/// options are overrided by field values of builder.
fn create_node_options(&self) -> Result<rcl_node_options_t, RclrsError> {
// SAFETY: No preconditions for this function.
let mut node_options = unsafe { rcl_node_get_default_options() };

let cstring_args = self
.arguments
.iter()
.map(|s| CString::new(s.as_str()).unwrap())
.collect::<Vec<_>>();
let cstring_arg_ptrs = cstring_args.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
// SAFETY: Getting a zero-initialized value is always safe.
let mut arguments = unsafe { rcl_get_zero_initialized_arguments() };
unsafe {
// SAFETY: This function does not store the ephemeral cstring_args_ptrs
// pointers. We are passing in a zero-initialized arguments struct as expected.
rcl_parse_arguments(
cstring_arg_ptrs.len() as i32,
cstring_arg_ptrs.as_ptr(),
rcutils_get_default_allocator(),
&mut arguments,
)
}
.ok()?;

node_options.arguments = arguments;
node_options.use_global_arguments = self.use_global_arguments;
node_options.enable_rosout = self.enable_rosout;
// SAFETY: No preconditions for this function.
node_options.allocator = unsafe { rcutils_get_default_allocator() };

Ok(node_options)
}
}

impl Drop for rcl_arguments_t {
fn drop(&mut self) {
// SAFETY: Do not finish this struct except here.
unsafe {
rcl_arguments_fini(self);
}
}
}

impl Drop for rcl_node_options_t {
fn drop(&mut self) {
// SAFETY: Do not finish this struct except here.
unsafe {
rcl_node_options_fini(self);
}
}
}