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

Basic Timer implementation #440

Draft
wants to merge 27 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6f9543c
Basic Timer type compiling :).
agalbachicar Nov 28, 2024
7b42c02
Evaluates the Timer::new() againts different clock types.
agalbachicar Nov 29, 2024
a60d9f3
Implement time_until_next_call
agalbachicar Nov 29, 2024
d233b47
Added cancel behavior
JesusSilvaUtrera Nov 29, 2024
3de2bb9
Merge pull request #1 from JesusSilvaUtrera/jsilva/add_cancel
agalbachicar Nov 29, 2024
189606f
Implement rcl_timer_reset
agalbachicar Nov 29, 2024
59ed7e2
Adds Timer::call().
agalbachicar Nov 29, 2024
9af9dd9
Added timer_period_ns (#2)
JesusSilvaUtrera Nov 29, 2024
e46224f
Adds Timer::is_ready().
agalbachicar Nov 29, 2024
501439d
WIP Timer callback implementation.
agalbachicar Nov 29, 2024
132c9db
Preliminary callback.
agalbachicar Nov 29, 2024
1095351
Added comments to avoid warnings (#3)
JesusSilvaUtrera Nov 29, 2024
965ca22
Integrated the Timer into the WaitSet.
agalbachicar Nov 29, 2024
f503c84
Add create_timer to node (WIP) (#4)
JesusSilvaUtrera Nov 29, 2024
ed78b35
Makes it work with the integration demo.
agalbachicar Nov 29, 2024
214a991
Working E2E timer with node.
agalbachicar Nov 29, 2024
1eb1acc
Format fix.
agalbachicar Nov 29, 2024
91756ca
Fix a TODO for peace of mind.
agalbachicar Nov 29, 2024
fbb8629
Adds an example.
agalbachicar Dec 1, 2024
ab3d63a
Fix format for the example.
agalbachicar Dec 1, 2024
4515e9a
Adds tests, documentation and removes dead code in node.rs.
agalbachicar Dec 1, 2024
85930a3
Fix documentation style in clock.rs.
agalbachicar Dec 1, 2024
1563895
Removes duplicated test in node.rs
agalbachicar Dec 1, 2024
08acef5
Fix warnings while running tests in node.rs.
agalbachicar Dec 1, 2024
a4c1a97
Fix missing documentation in wait.rs.
agalbachicar Dec 1, 2024
655185d
Improvements to timer.
agalbachicar Dec 1, 2024
30a6717
Makes rustdoc pass in the examples.
agalbachicar Dec 1, 2024
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
12 changes: 12 additions & 0 deletions examples/rclrs_timer_demo/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "rclrs_timer_demo"
version = "0.1.0"
edition = "2021"

[[bin]]
name="rclrs_timer_demo"
path="src/rclrs_timer_demo.rs"


[dependencies]
rclrs = "*"
13 changes: 13 additions & 0 deletions examples/rclrs_timer_demo/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<package format="3">
<name>rclrs_timer_demo</name>
<version>0.1.0</version>
<description>Shows how to implement a timer within a Node using rclrs.</description>
<maintainer email="user@todo.todo">user</maintainer>
<license>TODO: License declaration.</license>

<depend>rclrs</depend>

<export>
<build_type>ament_cargo</build_type>
</export>
</package>
48 changes: 48 additions & 0 deletions examples/rclrs_timer_demo/src/rclrs_timer_demo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/// Creates a SimpleTimerNode, initializes a node and the timer with a callback
/// that prints the timer callback execution iteration. The callback is executed
/// thanks to the spin, which is in charge of executing the timer's events among
/// other entities' events.
use rclrs::{create_node, Context, Node, RclrsError, Timer};
use std::{
env,
sync::{Arc, Mutex},
};

/// Contains both the node and timer.
struct SimpleTimerNode {
node: Arc<Node>,
timer: Arc<Timer>,
}

impl SimpleTimerNode {
/// Creates a node and a timer with a callback.
///
/// The callback will simply print to stdout:
/// "Drinking 🧉 for the xth time every p nanoseconds."
/// where x is the iteration callback counter and p is the period of the timer.
fn new(context: &Context, timer_period_ns: i64) -> Result<Self, RclrsError> {
let node = create_node(context, "simple_timer_node")?;
let count: Arc<Mutex<i32>> = Arc::new(Mutex::new(0));
let timer = node.create_timer(
timer_period_ns,
context,
Some(Box::new(move |_| {
let x = *count.lock().unwrap();
println!(
"Drinking 🧉 for the {}th time every {} nanoseconds.",
x, timer_period_ns
);
*count.lock().unwrap() = x + 1;
})),
None,
)?;
Ok(Self { node, timer })
}
}

fn main() -> Result<(), RclrsError> {
let timer_period: i64 = 1e9 as i64; // 1 seconds.
let context = Context::new(env::args()).unwrap();
let simple_timer_node = Arc::new(SimpleTimerNode::new(&context, timer_period).unwrap());
rclrs::spin(simple_timer_node.node.clone())
}
5 changes: 5 additions & 0 deletions rclrs/src/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ impl 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
6 changes: 5 additions & 1 deletion rclrs/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ impl SingleThreadedExecutor {
})
{
let wait_set = WaitSet::new_for_node(&node)?;
let ready_entities = wait_set.wait(timeout)?;
let mut ready_entities = wait_set.wait(timeout)?;

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

for ready_subscription in ready_entities.subscriptions {
ready_subscription.execute()?;
Expand Down
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
49 changes: 48 additions & 1 deletion rclrs/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
rcl_bindings::*, Client, ClientBase, Clock, Context, ContextHandle, GuardCondition, LogParams,
Logger, ParameterBuilder, ParameterInterface, ParameterVariant, Parameters, Publisher,
QoSProfile, RclrsError, Service, ServiceBase, Subscription, SubscriptionBase,
SubscriptionCallback, TimeSource, ToLogParams, ENTITY_LIFECYCLE_MUTEX,
SubscriptionCallback, TimeSource, Timer, TimerCallback, ToLogParams, ENTITY_LIFECYCLE_MUTEX,
};

// SAFETY: The functions accessing this type, including drop(), shouldn't care about the thread
Expand Down Expand Up @@ -63,6 +63,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<Timer>>>,
time_source: TimeSource,
parameter: ParameterInterface,
pub(crate) handle: Arc<NodeHandle>,
Expand Down Expand Up @@ -340,6 +341,30 @@ impl Node {
Ok(subscription)
}

/// Creates a [`Timer`][1].
///
/// [1]: crate::Timer
/// TODO: make timer's lifetime depend on node's lifetime.
pub fn create_timer(
&self,
period_ns: i64,
context: &Context,
callback: Option<TimerCallback>,
clock: Option<Clock>,
) -> Result<Arc<Timer>, RclrsError> {
let clock_used = match clock {
Some(value) => value,
None => self.get_clock(),
};
let timer = Timer::new(&clock_used, &context, period_ns, callback)?;
let timer = Arc::new(timer);
self.timers_mtx
.lock()
.unwrap()
.push(Arc::downgrade(&timer) as Weak<Timer>);
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 @@ -369,6 +394,13 @@ impl Node {
.collect()
}

pub(crate) fn live_timers(&self) -> Vec<Arc<Timer>> {
{ 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 Expand Up @@ -551,6 +583,21 @@ mod tests {
Ok(())
}

#[test]
fn test_create_timer_without_clock_source() -> Result<(), RclrsError> {
let timer_period_ns: i64 = 1e6 as i64; // 1 millisecond.
let context = Context::new([])?;
let dut = NodeBuilder::new(&context, "node_with_timer")
.namespace("test_create_timer")
.build()?;

let _timer =
dut.create_timer(timer_period_ns, &context, Some(Box::new(move |_| {})), None)?;
assert_eq!(dut.live_timers().len(), 1);

Ok(())
}

#[test]
fn test_logger_name() -> Result<(), RclrsError> {
// Use helper to create 2 nodes for us
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 @@ -340,6 +340,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
4 changes: 4 additions & 0 deletions rclrs/src/rcl_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ cfg_if::cfg_if! {
#[derive(Debug)]
pub struct rcl_wait_set_t;

#[repr(C)]
#[derive(Debug)]
pub struct rcl_timer_t;

#[repr(C)]
#[derive(Debug)]
pub struct rcutils_string_array_t;
Expand Down
Loading
Loading