Skip to content

Implement functions to get parameters from global arguments #176

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

Merged
merged 6 commits into from
Jun 13, 2022
Merged
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
4 changes: 4 additions & 0 deletions rclrs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ parking_lot = "0.11.2"
# Needed for the Message trait, among others
rosidl_runtime_rs = "*"

[dev-dependencies]
# Needed for e.g. writing yaml files in tests
tempfile = "3.3.0"

[build-dependencies]
# Needed for FFI
bindgen = "0.59.1"
1 change: 1 addition & 0 deletions rclrs/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ fn main() {
}

println!("cargo:rustc-link-lib=dylib=rcl");
println!("cargo:rustc-link-lib=dylib=rcl_yaml_param_parser");
println!("cargo:rustc-link-lib=dylib=rcutils");
println!("cargo:rustc-link-lib=dylib=rmw");
println!("cargo:rustc-link-lib=dylib=rmw_implementation");
Expand Down
2 changes: 2 additions & 0 deletions rclrs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
mod context;
mod error;
mod node;
mod parameter;
mod qos;
mod wait;

Expand All @@ -16,6 +17,7 @@ mod rcl_bindings;
pub use context::*;
pub use error::*;
pub use node::*;
pub use parameter::*;
pub use qos::*;
pub use wait::*;

Expand Down
43 changes: 25 additions & 18 deletions rclrs/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ pub use self::publisher::*;
pub use self::subscription::*;

use crate::rcl_bindings::*;
use crate::{Context, QoSProfile, RclrsError, ToResult};
use std::ffi::CStr;
use crate::{Context, ParameterOverrideMap, QoSProfile, RclrsError, ToResult};

use std::cmp::PartialEq;
use std::ffi::CStr;
use std::fmt;
use std::sync::{Arc, Weak};
use std::vec::Vec;
Expand Down Expand Up @@ -69,6 +69,7 @@ pub struct Node {
rcl_node_mtx: Arc<Mutex<rcl_node_t>>,
pub(crate) rcl_context_mtx: Arc<Mutex<rcl_context_t>>,
pub(crate) subscriptions: Vec<Weak<dyn SubscriptionBase>>,
_parameter_map: ParameterOverrideMap,
}

impl Eq for Node {}
Expand Down Expand Up @@ -116,7 +117,7 @@ impl Node {
/// # Ok::<(), RclrsError>(())
/// ```
pub fn name(&self) -> String {
self.get_string(rcl_node_get_name)
self.call_string_getter(rcl_node_get_name)
}

/// Returns the namespace of the node.
Expand All @@ -142,7 +143,7 @@ impl Node {
/// # Ok::<(), RclrsError>(())
/// ```
pub fn namespace(&self) -> String {
self.get_string(rcl_node_get_namespace)
self.call_string_getter(rcl_node_get_namespace)
}

/// Returns the fully qualified name of the node.
Expand All @@ -162,26 +163,15 @@ impl Node {
/// # Ok::<(), RclrsError>(())
/// ```
pub fn fully_qualified_name(&self) -> String {
self.get_string(rcl_node_get_fully_qualified_name)
self.call_string_getter(rcl_node_get_fully_qualified_name)
}

// Helper for name(), namespace(), fully_qualified_name()
fn get_string(
fn call_string_getter(
&self,
getter: unsafe extern "C" fn(*const rcl_node_t) -> *const c_char,
) -> String {
let char_ptr = unsafe {
// SAFETY: The rcl_node is valid.
getter(&*self.rcl_node_mtx.lock())
};
debug_assert!(!char_ptr.is_null());
let cstr = unsafe {
// SAFETY: The returned CStr is immediately converted to an owned string,
// so the lifetime is no issue. The ptr is valid as per the documentation
// of rcl_node_get_name.
CStr::from_ptr(char_ptr)
};
cstr.to_string_lossy().into_owned()
unsafe { call_string_getter_with_handle(&*self.rcl_node_mtx.lock(), getter) }
}

/// Creates a [`Publisher`][1].
Expand Down Expand Up @@ -279,3 +269,20 @@ impl Node {
NodeBuilder::new(context, node_name)
}
}

// Helper used to implement call_string_getter(), but also used to get the FQN in the Node::new()
// function, which is why it's not merged into Node::call_string_getter().
// This function is unsafe since it's possible to pass in an rcl_node_t with dangling
// pointers etc.
unsafe fn call_string_getter_with_handle(
rcl_node: &rcl_node_t,
getter: unsafe extern "C" fn(*const rcl_node_t) -> *const c_char,
) -> String {
let char_ptr = getter(rcl_node);
debug_assert!(!char_ptr.is_null());
// SAFETY: The returned CStr is immediately converted to an owned string,
// so the lifetime is no issue. The ptr is valid as per the documentation
// of rcl_node_get_name.
let cstr = CStr::from_ptr(char_ptr);
cstr.to_string_lossy().into_owned()
}
58 changes: 34 additions & 24 deletions rclrs/src/node/builder.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use crate::rcl_bindings::*;
use crate::{Context, Node, RclrsError, ToResult};
use crate::{
node::call_string_getter_with_handle, resolve_parameter_overrides, Context, Node, RclrsError,
ToResult,
};

use std::ffi::CString;
use std::sync::Arc;

use parking_lot::Mutex;
use std::sync::Arc;

/// A builder for creating a [`Node`][1].
///
Expand Down Expand Up @@ -64,20 +67,19 @@ impl NodeBuilder {
///
/// # Example
/// ```
/// # use rclrs::{Context, NodeBuilder, RclrsError, RclReturnCode, NodeErrorCode};
/// # use rclrs::{Context, NodeBuilder, RclrsError, RclReturnCode};
/// let context = Context::new([])?;
/// // This is a valid node name
/// assert!(NodeBuilder::new(&context, "my_node").build().is_ok());
/// // This is another valid node name (although not a good one)
/// assert!(NodeBuilder::new(&context, "_______").build().is_ok());
/// // This is an invalid node name
/// assert_eq!(
/// assert!(matches!(
/// NodeBuilder::new(&context, "röböt")
/// .build()
/// .unwrap_err()
/// .code,
/// RclReturnCode::NodeError(NodeErrorCode::NodeInvalidName)
/// );
/// .unwrap_err(),
/// RclrsError::RclError { code: RclReturnCode::NodeInvalidName, .. }
/// ));
/// # Ok::<(), RclrsError>(())
/// ```
///
Expand Down Expand Up @@ -116,20 +118,19 @@ impl NodeBuilder {
///
/// # Example
/// ```
/// # use rclrs::{Context, Node, RclrsError, RclReturnCode, NodeErrorCode};
/// # use rclrs::{Context, Node, RclrsError, RclReturnCode};
/// let context = Context::new([])?;
/// // This is a valid namespace
/// let builder_ok_ns = Node::builder(&context, "my_node").namespace("/some/nested/namespace");
/// assert!(builder_ok_ns.build().is_ok());
/// // This is an invalid namespace
/// assert_eq!(
/// assert!(matches!(
/// Node::builder(&context, "my_node")
/// .namespace("/10_percent_luck/20_percent_skill")
/// .build()
/// .unwrap_err()
/// .code,
/// RclReturnCode::NodeError(NodeErrorCode::NodeInvalidNamespace)
/// );
/// .unwrap_err(),
/// RclrsError::RclError { code: RclReturnCode::NodeInvalidNamespace, .. }
/// ));
/// // A missing forward slash at the beginning is automatically added
/// assert_eq!(
/// Node::builder(&context, "my_node")
Expand Down Expand Up @@ -243,7 +244,7 @@ impl NodeBuilder {
err,
s: self.namespace.clone(),
})?;
let node_options = self.create_node_options()?;
let rcl_node_options = self.create_rcl_node_options()?;
let rcl_context = &mut *self.context.lock();

// SAFETY: Getting a zero-initialized value is always safe.
Expand All @@ -258,29 +259,38 @@ impl NodeBuilder {
node_name.as_ptr(),
node_namespace.as_ptr(),
rcl_context,
&node_options,
&rcl_node_options,
)
.ok()?;
};

let _parameter_map = unsafe {
let fqn = call_string_getter_with_handle(&rcl_node, rcl_node_get_fully_qualified_name);
resolve_parameter_overrides(
&fqn,
&rcl_node_options.arguments,
&rcl_context.global_arguments,
)?
};
let rcl_node_mtx = Arc::new(Mutex::new(rcl_node));

Ok(Node {
rcl_node_mtx,
rcl_context_mtx: self.context.clone(),
subscriptions: std::vec![],
_parameter_map,
})
}

/// Creates node options.
/// Creates a rcl_node_options_t struct from this builder.
///
/// Any fields not present in the builder will have their default value.
/// For detail about default values, see [`NodeBuilder`][1] docs.
///
/// [1]: crate::NodeBuilder
fn create_node_options(&self) -> Result<rcl_node_options_t, RclrsError> {
fn create_rcl_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 mut rcl_node_options = unsafe { rcl_node_get_default_options() };

let cstring_args = self
.arguments
Expand All @@ -299,17 +309,17 @@ impl NodeBuilder {
cstring_arg_ptrs.len() as i32,
cstring_arg_ptrs.as_ptr(),
rcutils_get_default_allocator(),
&mut node_options.arguments,
&mut rcl_node_options.arguments,
)
}
.ok()?;

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

Ok(node_options)
Ok(rcl_node_options)
}
}

Expand Down
5 changes: 5 additions & 0 deletions rclrs/src/parameter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod override_map;
mod value;

pub(crate) use override_map::*;
pub use value::*;
Loading