Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions rclrs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ dyn_msg = ["ament_rs", "libloading"]
# This feature is solely for the purpose of being able to generate documetation without a ROS installation
# The only intended usage of this feature is for docs.rs builders to work, and is not intended to be used by end users
generate_docs = ["rosidl_runtime_rs/generate_docs"]
serde = []

[package.metadata.docs.rs]
features = ["generate_docs"]
2 changes: 1 addition & 1 deletion rclrs/src/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl From<ClockType> for rcl_clock_type_t {
#[derive(Clone, Debug)]
pub struct Clock {
kind: ClockType,
rcl_clock: Arc<Mutex<rcl_clock_t>>,
pub(crate) rcl_clock: Arc<Mutex<rcl_clock_t>>,
// TODO(luca) Implement jump callbacks
}

Expand Down
29 changes: 29 additions & 0 deletions rclrs/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ impl SingleThreadedExecutor {
for ready_service in ready_entities.services {
ready_service.execute()?;
}

for ready_timer in ready_entities.timers {
ready_timer.lock().unwrap().execute()?;
}
}

Ok(())
Expand All @@ -82,3 +86,28 @@ impl SingleThreadedExecutor {
Ok(())
}
}

#[cfg(test)]
mod tests {
use crate::{spin_once, Context};

use super::*;

#[test]
fn spin_once_fires_timer() -> Result<(), RclrsError> {
let context = Context::new([])?;
let node = Node::new(&context, "test_spin_timer")?;

let callback_triggered = Arc::new(Mutex::new(0));
let callback_flag = Arc::clone(&callback_triggered);

let _timer = node.create_timer(Duration::from_secs(0), move |_| {
*callback_flag.lock().unwrap() += 1;
})?;

spin_once(node, Some(Duration::ZERO))?;

assert_eq!(*callback_triggered.lock().unwrap(), 1);
Ok(())
}
}
2 changes: 2 additions & 0 deletions rclrs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod service;
mod subscription;
mod time;
mod time_source;
mod timer;
mod vendor;
mod wait;

Expand Down Expand Up @@ -49,6 +50,7 @@ pub use service::*;
pub use subscription::*;
pub use time::*;
use time_source::*;
pub use timer::*;
pub use wait::*;

/// Polls the node for new messages and executes the corresponding callbacks.
Expand Down
6 changes: 6 additions & 0 deletions rclrs/src/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ pub struct LogConditions {
pub log_if_true: bool,
}

impl Default for LogConditions {
fn default() -> Self {
Self::new()
}
}

impl LogConditions {
/// Default construct an instance
pub fn new() -> Self {
Expand Down
44 changes: 42 additions & 2 deletions rclrs/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::{
fmt,
os::raw::c_char,
sync::{atomic::AtomicBool, Arc, Mutex, Weak},
time::Duration,
vec::Vec,
};

Expand All @@ -16,7 +17,7 @@ use crate::{
rcl_bindings::*, Client, ClientBase, Clock, Context, ContextHandle, GuardCondition,
ParameterBuilder, ParameterInterface, ParameterVariant, Parameters, Publisher, QoSProfile,
RclrsError, Service, ServiceBase, Subscription, SubscriptionBase, SubscriptionCallback,
TimeSource, ENTITY_LIFECYCLE_MUTEX,
TimeSource, Timer, TimerBase, TimerCallback, ENTITY_LIFECYCLE_MUTEX,
};

// SAFETY: The functions accessing this type, including drop(), shouldn't care about the thread
Expand Down Expand Up @@ -47,7 +48,7 @@ unsafe impl Send for rcl_node_t {}
/// The namespace and name given when creating the node can be overridden through the command line.
/// In that sense, the parameters to the node creation functions are only the _default_ namespace and
/// name.
/// See also the [official tutorial][1] on the command line arguments for ROS nodes, and the
/// See also the [official tutorial][2] on the command line arguments for ROS nodes, and the
/// [`Node::namespace()`] and [`Node::name()`] functions for examples.
///
/// ## Rules for valid names
Expand All @@ -63,6 +64,7 @@ pub struct Node {
pub(crate) guard_conditions_mtx: Mutex<Vec<Weak<GuardCondition>>>,
pub(crate) services_mtx: Mutex<Vec<Weak<dyn ServiceBase>>>,
pub(crate) subscriptions_mtx: Mutex<Vec<Weak<dyn SubscriptionBase>>>,
pub(crate) timers_mtx: Mutex<Vec<Weak<Mutex<dyn TimerBase>>>>,
time_source: TimeSource,
parameter: ParameterInterface,
pub(crate) handle: Arc<NodeHandle>,
Expand Down Expand Up @@ -339,6 +341,35 @@ impl Node {
Ok(subscription)
}

/// Create a [`Timer`][1] that will use the node's clock.
///
/// A Timer may be modified via the `Arc` returned by this function or from
/// within its callback.
/// A weak reference counter to the `Timer` is stored within this node.
///
/// [1]: crate::Timer
pub fn create_timer<F>(
&self,
period: Duration,
callback: F,
) -> Result<Arc<Mutex<Timer>>, RclrsError>
where
F: TimerCallback + 'static,
{
let timer = Arc::new(Mutex::new(Timer::new_with_context_handle(
Arc::clone(&self.handle.context_handle),
self.get_clock(),
period,
callback,
)?));

self.timers_mtx
.lock()
.unwrap()
.push(Arc::downgrade(&timer) as Weak<Mutex<dyn TimerBase>>);
Ok(timer)
}

/// Returns the subscriptions that have not been dropped yet.
pub(crate) fn live_subscriptions(&self) -> Vec<Arc<dyn SubscriptionBase>> {
{ self.subscriptions_mtx.lock().unwrap() }
Expand Down Expand Up @@ -368,6 +399,15 @@ impl Node {
.collect()
}

pub(crate) fn live_timers(&self) -> Vec<Arc<Mutex<dyn TimerBase>>> {
self.timers_mtx
.lock()
.unwrap()
.iter()
.filter_map(Weak::upgrade)
.collect()
}

/// 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
1 change: 1 addition & 0 deletions rclrs/src/node/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ impl NodeBuilder {
guard_conditions_mtx: Mutex::new(vec![]),
services_mtx: Mutex::new(vec![]),
subscriptions_mtx: Mutex::new(vec![]),
timers_mtx: Mutex::new(vec![]),
time_source: TimeSource::builder(self.clock_type)
.clock_qos(self.clock_qos)
.build(),
Expand Down
2 changes: 1 addition & 1 deletion rclrs/src/publisher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ where
// * The rcl_node is kept alive by the NodeHandle because it is a dependency of the publisher.
// * The topic name and the options are copied by this function, so they can be dropped afterwards.
// * The entity lifecycle mutex is locked to protect against the risk of global
// variables in the rmw implementation being unsafely modified during cleanup.
// variables in the rmw implementation being unsafely modified during initialization.
rcl_publisher_init(
&mut rcl_publisher,
&*rcl_node,
Expand Down
2 changes: 1 addition & 1 deletion rclrs/src/subscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ where
// * The rcl_node is kept alive by the NodeHandle because it is a dependency of the subscription.
// * The topic name and the options are copied by this function, so they can be dropped afterwards.
// * The entity lifecycle mutex is locked to protect against the risk of global
// variables in the rmw implementation being unsafely modified during cleanup.
// variables in the rmw implementation being unsafely modified during initialization.
rcl_subscription_init(
&mut rcl_subscription,
&*rcl_node,
Expand Down
Loading
Loading