-
Notifications
You must be signed in to change notification settings - Fork 195
feat: add serialized pub/sub APIs #592
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,10 +44,12 @@ use crate::{ | |
| IntoNodeTimerOneshotCallback, IntoNodeTimerRepeatingCallback, IntoTimerOptions, LogParams, | ||
| Logger, MessageInfo, ParameterBuilder, ParameterInterface, ParameterVariant, Parameters, | ||
| Promise, Publisher, PublisherOptions, PublisherState, RclrsError, RequestedGoal, Service, | ||
| ServiceOptions, ServiceState, Subscription, SubscriptionOptions, SubscriptionState, | ||
| ServiceOptions, ServiceState, SerializedPublisher, SerializedSubscription, Subscription, | ||
| SubscriptionOptions, SubscriptionState, | ||
| TerminatedGoal, TimeSource, Timer, TimerState, ToLogParams, Worker, WorkerOptions, WorkerState, | ||
| ENTITY_LIFECYCLE_MUTEX, | ||
| }; | ||
| use crate::ToResult; | ||
|
|
||
| /// A processing unit that can communicate with other nodes. See the API of | ||
| /// [`NodeState`] to find out what methods you can call on a [`Node`]. | ||
|
|
@@ -1493,6 +1495,82 @@ impl NodeState { | |
| pub(crate) fn handle(&self) -> &Arc<NodeHandle> { | ||
| &self.handle | ||
| } | ||
|
|
||
| /// Creates a serialized subscription. | ||
| /// | ||
| /// This receives raw serialized (CDR) bytes, using `rcl_take_serialized_message`. | ||
| pub fn create_serialized_subscription<'a>( | ||
| &self, | ||
| topic_type: MessageTypeName, | ||
| options: impl Into<SubscriptionOptions<'a>>, | ||
| ) -> Result<SerializedSubscription, RclrsError> { | ||
|
Comment on lines
+1502
to
+1506
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This API is a bit odd for a subscription, instead of specifying a callback we return a subscription and then leave it to the user to call |
||
| let SubscriptionOptions { topic, qos } = options.into(); | ||
|
|
||
| // Use the same typesupport resolution as dynamic messages. | ||
| let metadata = crate::dynamic_message::DynamicMessageMetadata::new(topic_type)?; | ||
|
|
||
| let mut sub = unsafe { rcl_get_zero_initialized_subscription() }; | ||
| let topic_c = std::ffi::CString::new(topic).unwrap(); | ||
|
|
||
| let _context_lock = self.handle.context_handle.rcl_context.lock().unwrap(); | ||
| let node = self.handle.rcl_node.lock().unwrap(); | ||
| let _lifecycle_lock = ENTITY_LIFECYCLE_MUTEX.lock().unwrap(); | ||
|
|
||
| unsafe { | ||
| let mut opts = rcl_subscription_get_default_options(); | ||
| opts.qos = qos.into(); | ||
| rcl_subscription_init( | ||
| &mut sub, | ||
| &*node, | ||
| metadata.type_support_ptr(), | ||
| topic_c.as_ptr(), | ||
| &opts, | ||
| ) | ||
| .ok()?; | ||
| } | ||
|
|
||
| Ok(SerializedSubscription { | ||
| handle: Arc::clone(&self.handle), | ||
| sub, | ||
| }) | ||
| } | ||
|
|
||
| /// Creates a serialized publisher. | ||
| /// | ||
| /// This publishes raw serialized (CDR) bytes, using `rcl_publish_serialized_message`. | ||
| pub fn create_serialized_publisher<'a>( | ||
| &self, | ||
| topic_type: MessageTypeName, | ||
| options: impl Into<crate::PublisherOptions<'a>>, | ||
| ) -> Result<SerializedPublisher, RclrsError> { | ||
|
Comment on lines
+1541
to
+1545
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar comment to the dynamic subscription on whether we can streamline this with the dynamic message implementation.
Does this make sense? |
||
| let crate::PublisherOptions { topic, qos } = options.into(); | ||
|
|
||
| let metadata = crate::dynamic_message::DynamicMessageMetadata::new(topic_type)?; | ||
| let mut pub_ = unsafe { rcl_get_zero_initialized_publisher() }; | ||
| let topic_c = std::ffi::CString::new(topic).unwrap(); | ||
|
|
||
| let _context_lock = self.handle.context_handle.rcl_context.lock().unwrap(); | ||
| let node = self.handle.rcl_node.lock().unwrap(); | ||
| let _lifecycle_lock = ENTITY_LIFECYCLE_MUTEX.lock().unwrap(); | ||
|
|
||
| unsafe { | ||
| let mut opts = rcl_publisher_get_default_options(); | ||
| opts.qos = qos.into(); | ||
| rcl_publisher_init( | ||
| &mut pub_, | ||
| &*node, | ||
| metadata.type_support_ptr(), | ||
| topic_c.as_ptr(), | ||
| &opts, | ||
| ) | ||
| .ok()?; | ||
| } | ||
|
|
||
| Ok(SerializedPublisher { | ||
| handle: Arc::clone(&self.handle), | ||
| pub_, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| impl<'a> ToLogParams<'a> for &'a NodeState { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| use crate::{rcl_bindings::*, RclrsError, ToResult}; | ||
|
|
||
| /// A growable serialized message buffer. | ||
| /// | ||
| /// This wraps `rcl_serialized_message_t` (aka `rmw_serialized_message_t`). | ||
| pub struct SerializedMessage { | ||
| pub(crate) msg: rcl_serialized_message_t, | ||
| } | ||
|
|
||
| unsafe impl Send for SerializedMessage {} | ||
|
|
||
| impl SerializedMessage { | ||
| /// Create a new serialized message buffer with the given capacity in bytes. | ||
| pub fn new(capacity: usize) -> Result<Self, RclrsError> { | ||
| unsafe { | ||
| let mut msg = rcutils_get_zero_initialized_uint8_array(); | ||
| let allocator = rcutils_get_default_allocator(); | ||
| rcutils_uint8_array_init(&mut msg, capacity, &allocator).ok()?; | ||
| Ok(Self { msg }) | ||
| } | ||
| } | ||
|
|
||
| /// Return the current serialized payload. | ||
| pub fn as_bytes(&self) -> &[u8] { | ||
| unsafe { std::slice::from_raw_parts(self.msg.buffer, self.msg.buffer_length) } | ||
| } | ||
|
|
||
| /// Reset the length to 0 without changing capacity. | ||
| pub fn clear(&mut self) { | ||
| self.msg.buffer_length = 0; | ||
| } | ||
| } | ||
|
|
||
| impl Drop for SerializedMessage { | ||
| fn drop(&mut self) { | ||
| unsafe { | ||
| let _ = rcutils_uint8_array_fini(&mut self.msg); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| use crate::{node::NodeHandle, rcl_bindings::*, RclrsError, ToResult, ENTITY_LIFECYCLE_MUTEX}; | ||
| use std::{ptr, sync::Arc}; | ||
|
|
||
| use crate::serialized_message::SerializedMessage; | ||
|
|
||
| /// A publisher which publishes serialized ROS messages. | ||
| pub struct SerializedPublisher { | ||
| pub(crate) handle: Arc<NodeHandle>, | ||
| pub(crate) pub_: rcl_publisher_t, | ||
| } | ||
|
|
||
| unsafe impl Send for SerializedPublisher {} | ||
| unsafe impl Sync for SerializedPublisher {} | ||
|
|
||
| impl Drop for SerializedPublisher { | ||
| fn drop(&mut self) { | ||
| let _context_lock = self.handle.context_handle.rcl_context.lock().unwrap(); | ||
| let mut node = self.handle.rcl_node.lock().unwrap(); | ||
| let _lifecycle_lock = ENTITY_LIFECYCLE_MUTEX.lock().unwrap(); | ||
| unsafe { | ||
| let _ = rcl_publisher_fini(&mut self.pub_, &mut *node); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl SerializedPublisher { | ||
| /// Publish a serialized (CDR) message. | ||
| pub fn publish(&self, msg: &SerializedMessage) -> Result<(), RclrsError> { | ||
| unsafe { | ||
| rcl_publish_serialized_message(&self.pub_, &msg.msg, ptr::null_mut()).ok()?; | ||
| } | ||
| Ok(()) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| use crate::{node::NodeHandle, rcl_bindings::*, MessageInfo, RclrsError, ENTITY_LIFECYCLE_MUTEX}; | ||
| use std::{ptr, sync::Arc}; | ||
|
|
||
| use crate::serialized_message::SerializedMessage; | ||
|
|
||
| /// A subscription which receives serialized ROS messages. | ||
| pub struct SerializedSubscription { | ||
| pub(crate) handle: Arc<NodeHandle>, | ||
| pub(crate) sub: rcl_subscription_t, | ||
| } | ||
|
|
||
| unsafe impl Send for SerializedSubscription {} | ||
| unsafe impl Sync for SerializedSubscription {} | ||
|
|
||
| impl Drop for SerializedSubscription { | ||
| fn drop(&mut self) { | ||
| let _context_lock = self.handle.context_handle.rcl_context.lock().unwrap(); | ||
| let mut node = self.handle.rcl_node.lock().unwrap(); | ||
| let _lifecycle_lock = ENTITY_LIFECYCLE_MUTEX.lock().unwrap(); | ||
| unsafe { | ||
| let _ = rcl_subscription_fini(&mut self.sub, &mut *node); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl SerializedSubscription { | ||
| /// Take a serialized (CDR) message. | ||
| /// | ||
| /// Returns `Ok(None)` when no message is available. | ||
| pub fn take(&self, buf: &mut SerializedMessage) -> Result<Option<MessageInfo>, RclrsError> { | ||
| unsafe { | ||
| let mut info: rmw_message_info_t = std::mem::zeroed(); | ||
| let rc = | ||
| rcl_take_serialized_message(&self.sub, &mut buf.msg, &mut info, ptr::null_mut()); | ||
| if rc != 0 { | ||
| // No message available or error. The rmw/rcl API uses negative codes for "take failed". | ||
| return Ok(None); | ||
| } | ||
| Ok(Some(MessageInfo::from_rmw_message_info(&info))) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lib.rshas#![warn(missing_docs)], and CI runscargo rustdoc -- -D warnings/cargo clippy ... -D warnings. Makingrcl_bindingspublic without a doc comment will emit amissing_docswarning for this module item and fail CI. Add a///doc comment forpub mod rcl_bindings;(or annotate this item with#[allow(missing_docs)]/#[doc(hidden)]if you don't want it documented).