Skip to content
Open
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
2 changes: 2 additions & 0 deletions .github/workflows/generate-bindings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ jobs:
fi
git add rclrs/src/rcl_bindings_generated_${{ matrix.ros_distribution }}.rs
git commit -m "Regenerate bindings for ${{ matrix.ros_distribution }}"
git fetch origin update-bindings-${{ matrix.ros_distribution }}
git rebase origin/update-bindings-${{ matrix.ros_distribution }}
git push -u origin update-bindings-${{ matrix.ros_distribution }}
if [ $CREATE_PR -eq 1 ]; then
gh pr create --base main --head update-bindings-${{ matrix.ros_distribution }} --title "Regenerate bindings for ${{ matrix.ros_distribution }}" --body "This PR regenerates the bindings for ${{ matrix.ros_distribution }}."
Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/rust-minimal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
components: clippy, rustfmt

# Colcon can not be run in a venv which is required in Ubuntu Noble
# Removing the externally managed file
# Removing the externally managed file
- name: Install colcon-cargo and colcon-ros-cargo
run: |
sudo rm -f /usr/lib/python3.12/EXTERNALLY-MANAGED
Expand Down Expand Up @@ -117,9 +117,7 @@ jobs:
echo "Running cargo test in $path"
# Run cargo test for all features except use_ros_shim (needed for docs.rs)
if [ "$(basename $path)" = "rclrs" ]; then
cargo test -F default,dyn_msg
elif [ "$(basename $path)" = "rosidl_runtime_rs" ]; then
cargo test -F default
cargo test -F default,dyn_msg,serde
else
cargo test --all-features
fi
Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/rust-stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
components: clippy, rustfmt

# Colcon can not be run in a venv which is required in Ubuntu Noble
# Removing the externally managed file
# Removing the externally managed file
- name: Install colcon-cargo and colcon-ros-cargo
run: |
sudo rm -f /usr/lib/python3.12/EXTERNALLY-MANAGED
Expand Down Expand Up @@ -117,9 +117,7 @@ jobs:
echo "Running cargo test in $path"
# Run cargo test for all features except use_ros_shim (needed for docs.rs)
if [ "$(basename $path)" = "rclrs" ]; then
cargo test -F default,dyn_msg
elif [ "$(basename $path)" = "rosidl_runtime_rs" ]; then
cargo test -F default
cargo test -F default,dyn_msg,serde
else
cargo test --all-features
fi
Expand Down
3 changes: 1 addition & 2 deletions rclrs/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,8 +567,7 @@ unsafe impl Send for rcl_client_t {}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::*;
use crate::vendor::test_msgs;
use crate::{test_helpers::*, vendor::test_msgs};

#[test]
fn traits() {
Expand Down
5 changes: 5 additions & 0 deletions rclrs/src/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ impl Clock {
Self { kind, rcl_clock }
}

/// Returns the clock's `rcl_clock_t`.
pub(crate) fn get_rcl_clock(&self) -> &Arc<Mutex<rcl_clock_t>> {
&self.rcl_clock
}

/// Returns the clock's `ClockType`.
pub fn clock_type(&self) -> ClockType {
self.kind
Expand Down
4 changes: 4 additions & 0 deletions rclrs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ mod service;
mod subscription;
mod time;
mod time_source;
mod timer;
pub mod vendor;
mod wait_set;
mod worker;
Expand Down Expand Up @@ -221,5 +222,8 @@ pub use service::*;
pub use subscription::*;
pub use time::*;
use time_source::*;
pub use timer::*;
pub use wait_set::*;
pub use worker::*;

pub use rosidl_runtime_rs::{Message as MessageIDL, Service as ServiceIDL};
237 changes: 231 additions & 6 deletions rclrs/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ use async_std::future::timeout;
use rosidl_runtime_rs::Message;

use crate::{
rcl_bindings::*, Client, ClientOptions, ClientState, Clock, ContextHandle, ExecutorCommands,
IntoAsyncServiceCallback, IntoAsyncSubscriptionCallback, IntoNodeServiceCallback,
IntoNodeSubscriptionCallback, LogParams, Logger, ParameterBuilder, ParameterInterface,
ParameterVariant, Parameters, Promise, Publisher, PublisherOptions, PublisherState, RclrsError,
Service, ServiceOptions, ServiceState, Subscription, SubscriptionOptions, SubscriptionState,
TimeSource, ToLogParams, Worker, WorkerOptions, WorkerState, ENTITY_LIFECYCLE_MUTEX,
rcl_bindings::*, AnyTimerCallback, Client, ClientOptions, ClientState, Clock, ContextHandle,
ExecutorCommands, IntoAsyncServiceCallback, IntoAsyncSubscriptionCallback,
IntoNodeServiceCallback, IntoNodeSubscriptionCallback, IntoNodeTimerOneshotCallback,
IntoNodeTimerRepeatingCallback, IntoTimerOptions, LogParams, Logger, ParameterBuilder,
ParameterInterface, ParameterVariant, Parameters, Promise, Publisher, PublisherOptions,
PublisherState, RclrsError, Service, ServiceOptions, ServiceState, Subscription,
SubscriptionOptions, SubscriptionState, TimeSource, Timer, TimerState, ToLogParams, Worker,
WorkerOptions, WorkerState, ENTITY_LIFECYCLE_MUTEX,
};

/// A processing unit that can communicate with other nodes. See the API of
Expand Down Expand Up @@ -908,6 +910,229 @@ impl NodeState {
)
}

/// Create a [`Timer`] with a repeating callback.
///
/// This has similar behavior to `rclcpp::Node::create_timer` by periodically
/// triggering the callback of the timer. For a one-shot timer alternative,
/// see [`NodeState::create_timer_oneshot`].
///
/// See also:
/// * [`Self::create_timer_oneshot`]
/// * [`Self::create_timer_inert`]
///
/// # Behavior
///
/// While the callback of this timer is running, no other callbacks associated
/// with this node will be able to run. This is in contrast to callbacks given
/// to [`Self::create_subscription`] which can run multiple times in parallel.
///
/// Since the callback of this timer may block other callbacks from being able
/// to run, it is strongly recommended to ensure that the callback returns
/// quickly. If the callback needs to trigger long-running behavior then you
/// can consider using [`std::thread::spawn`], or for async behaviors you can
/// capture an [`ExecutorCommands`] in your callback and use [`ExecutorCommands::run`]
/// to issue a task for the executor to run in its async task pool.
///
/// Since these callbacks are blocking, you may use [`FnMut`] here instead of
/// being limited to [`Fn`].
///
/// # Timer Options
///
/// You can choose both
/// 1. a timer period (duration) which determines how often the callback is triggered
/// 2. a clock to measure the passage of time
///
/// Both of these choices are expressed by [`TimerOptions`][1].
///
/// By default the steady clock time will be used, but you could choose
/// node time instead if you want the timer to automatically use simulated
/// time when running as part of a simulation:
/// ```
/// # use rclrs::*;
/// # let executor = Context::default().create_basic_executor();
/// # let node = executor.create_node("my_node").unwrap();
/// use std::time::Duration;
///
/// let timer = node.create_timer_repeating(
/// TimerOptions::new(Duration::from_secs(1))
/// .node_time(),
/// || {
/// println!("Triggering once each simulated second");
/// },
/// )?;
/// # Ok::<(), RclrsError>(())
/// ```
///
/// If there is a specific manually-driven clock you want to use, you can
/// also select that:
/// ```
/// # use rclrs::*;
/// # let executor = Context::default().create_basic_executor();
/// # let node = executor.create_node("my_node").unwrap();
/// use std::time::Duration;
///
/// let (my_clock, my_source) = Clock::with_source();
///
/// let timer = node.create_timer_repeating(
/// TimerOptions::new(Duration::from_secs(1))
/// .clock(&my_clock),
/// || {
/// println!("Triggering once each simulated second");
/// },
/// )?;
///
/// my_source.set_ros_time_override(1_500_000_000);
/// # Ok::<(), RclrsError>(())
/// ```
///
/// If you are okay with the default choice of clock (steady clock) then you
/// can choose to simply pass a duration in as the options:
/// ```
/// # use rclrs::*;
/// # let executor = Context::default().create_basic_executor();
/// # let node = executor.create_node("my_node").unwrap();
/// use std::time::Duration;
///
/// let timer = node.create_timer_repeating(
/// Duration::from_secs(1),
/// || {
/// println!("Triggering per steady clock second");
/// },
/// )?;
/// # Ok::<(), RclrsError>(())
/// ```
///
/// # Node Timer Repeating Callbacks
///
/// Node Timer repeating callbacks support three signatures:
/// - <code>[FnMut] ()</code>
/// - <code>[FnMut] ([Time][2])</code>
/// - <code>[FnMut] (&[Timer])</code>
///
/// You can choose to receive the current time when the callback is being
/// triggered.
///
/// Or instead of the current time, you can get a borrow of the [`Timer`]
/// itself, that way if you need to access it from inside the callback, you
/// do not need to worry about capturing a [`Weak`][3] and then locking it.
/// This is useful if you need to change the callback of the timer from inside
/// the callback of the timer.
///
/// For an [`FnOnce`] instead of [`FnMut`], use [`Self::create_timer_oneshot`].
///
/// [1]: crate::TimerOptions
/// [2]: crate::Time
/// [3]: std::sync::Weak
pub fn create_timer_repeating<'a, Args>(
self: &Arc<Self>,
options: impl IntoTimerOptions<'a>,
callback: impl IntoNodeTimerRepeatingCallback<Args>,
) -> Result<Timer, RclrsError> {
self.create_timer_internal(options, callback.into_node_timer_repeating_callback())
}

/// Create a [`Timer`] whose callback will be triggered once after the period
/// of the timer has elapsed. After that you will need to use
/// [`TimerState::set_repeating`] or [`TimerState::set_oneshot`] or else
/// nothing will happen the following times that the `Timer` elapses.
///
/// This does not have an equivalent in `rclcpp`.
///
/// See also:
/// * [`Self::create_timer_repeating`]
/// * [`Self::create_timer_inert`]
///
/// # Behavior
///
/// While the callback of this timer is running, no other callbacks associated
/// with this node will be able to run. This is in contrast to callbacks given
/// to [`Self::create_subscription`] which can run multiple times in parallel.
///
/// Since the callback of this timer may block other callbacks from being able
/// to run, it is strongly recommended to ensure that the callback returns
/// quickly. If the callback needs to trigger long-running behavior then you
/// can consider using [`std::thread::spawn`], or for async behaviors you can
/// capture an [`ExecutorCommands`] in your callback and use [`ExecutorCommands::run`]
/// to issue a task for the executor to run in its async task pool.
///
/// Since these callbacks will only be triggered once, you may use [`FnOnce`] here.
///
/// # Timer Options
///
/// See [`NodeSate::create_timer_repeating`][3] for examples of setting the
/// timer options.
///
/// # Node Timer Oneshot Callbacks
///
/// Node Timer OneShot callbacks support three signatures:
/// - <code>[FnOnce] ()</code>
/// - <code>[FnOnce] ([Time][2])</code>
/// - <code>[FnOnce] (&[Timer])</code>
///
/// You can choose to receive the current time when the callback is being
/// triggered.
///
/// Or instead of the current time, you can get a borrow of the [`Timer`]
/// itself, that way if you need to access it from inside the callback, you
/// do not need to worry about capturing a [`Weak`][3] and then locking it.
/// This is useful if you need to change the callback of the timer from inside
/// the callback of the timer.
///
/// [2]: crate::Time
/// [3]: std::sync::Weak
pub fn create_timer_oneshot<'a, Args>(
self: &Arc<Self>,
options: impl IntoTimerOptions<'a>,
callback: impl IntoNodeTimerOneshotCallback<Args>,
) -> Result<Timer, RclrsError> {
self.create_timer_internal(options, callback.into_node_timer_oneshot_callback())
}

/// Create a [`Timer`] without a callback. Nothing will happen when this
/// `Timer` elapses until you use [`TimerState::set_repeating`] or
/// [`TimerState::set_oneshot`].
///
/// This function is not usually what you want. An inert timer is usually
/// just a follow-up state to a oneshot timer which is waiting to be given
/// a new callback to run. However, you could use this method to declare a
/// timer whose callbacks you will start to feed in at a later.
///
/// There is no equivalent to this function in `rclcpp`.
///
/// See also:
/// * [`Self::create_timer_repeating`]
/// * [`Self::create_timer_oneshot`]
pub fn create_timer_inert<'a>(
self: &Arc<Self>,
options: impl IntoTimerOptions<'a>,
) -> Result<Timer, RclrsError> {
self.create_timer_internal(options, AnyTimerCallback::Inert)
}

/// Used internally to create any kind of [`Timer`].
///
/// Downstream users should instead use:
/// * [`Self::create_timer_repeating`]
/// * [`Self::create_timer_oneshot`]
/// * [`Self::create_timer_inert`]
fn create_timer_internal<'a>(
self: &Arc<Self>,
options: impl IntoTimerOptions<'a>,
callback: AnyTimerCallback<Node>,
) -> Result<Timer, RclrsError> {
let options = options.into_timer_options();
let clock = options.clock.as_clock(self);
let node = options.clock.is_node_time().then(|| Arc::clone(self));
TimerState::create(
options.period,
clock,
callback,
self.commands.async_worker_commands(),
&self.handle.context_handle,
node,
)
}

/// Returns the ROS domain ID that the node is using.
///
/// The domain ID controls which nodes can send messages to each other, see the [ROS 2 concept article][1].
Expand Down
3 changes: 1 addition & 2 deletions rclrs/src/publisher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,7 @@ mod tests {

#[test]
fn test_publishers() -> Result<(), RclrsError> {
use crate::vendor::test_msgs::msg;
use crate::TopicEndpointInfo;
use crate::{vendor::test_msgs::msg, TopicEndpointInfo};

let namespace = "/test_publishers_graph";
let graph = construct_test_graph(namespace)?;
Expand Down
3 changes: 1 addition & 2 deletions rclrs/src/publisher/loaned_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ mod tests {

#[test]
fn traits() {
use crate::test_helpers::*;
use crate::vendor::test_msgs;
use crate::{test_helpers::*, vendor::test_msgs};

assert_send::<LoanedMessage<test_msgs::msg::rmw::BoundedSequences>>();
assert_sync::<LoanedMessage<test_msgs::msg::rmw::BoundedSequences>>();
Expand Down
2 changes: 1 addition & 1 deletion rclrs/src/rcl_bindings_generated_jazzy.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* automatically generated by rust-bindgen 0.72.0 */
/* automatically generated by rust-bindgen 0.72.1 */

pub type rcutils_ret_t = ::std::os::raw::c_int;
#[repr(C)]
Expand Down
Loading