safe_drive 0.4.3

safe_drive: Formally Specified Rust Bindings for ROS2
Documentation
# Timer

[Source code](https://github.com/tier4/safe_drive_tutorial/tree/main/timers).

This tutorial does not use `colcon` to build.
We use only `cargo`, which is a Rust's standard build system.

Don't forget loading ROS2's environment as follows.
If you already done so, you do not need this.

```text
$ . /opt/ros/iron/setup.bash
```

## Wall-timer

A wall-timer is a timer which periodically invoked.
This section describes how to use a wall-timer.

Let's create a package by using a `cargo` as follows.

```text
$ cargo new wall_timer
$ cd wall_timer
```

Then, add `safe_drive` to the dependencies of `Cargo.toml`.

```toml
[dependencies]
safe_drive = "0.4"
```

The following code is an example using a wall-timer.
The important method is `Selector::add_wall_timer()` which takes
a name, a duration, and a callback function.

```rust
use safe_drive::{
    context::Context, error::DynError, logger::Logger, msg::common_interfaces::std_msgs, pr_info,
};
use std::{rc::Rc, time::Duration};

fn main() -> Result<(), DynError> {
    // Create a context, a node, a subscriber, a publisher, and a selector.
    let ctx = Context::new()?;
    let node = ctx.create_node("my_node", None, Default::default())?;
    let subscriber = node.create_subscriber::<std_msgs::msg::UInt64>("my_topic", None)?;
    let publisher = node.create_publisher::<std_msgs::msg::UInt64>("my_topic", None)?;
    let mut selector = ctx.create_selector()?;

    // Create a logger.
    // To share this by multiple callback functions, use Rc.
    let logger = Rc::new(Logger::new("wall timer example"));

    // Add a wall timer to publish periodically.
    let mut cnt = Box::new(0);
    let mut msg = std_msgs::msg::UInt64::new().unwrap();
    let logger1 = logger.clone();

    selector.add_wall_timer(
        "publisher", // the name of timer
        Duration::from_secs(1),
        Box::new(move || {
            msg.data = *cnt;
            *cnt += 1;
            publisher.send(&msg).unwrap();
            pr_info!(logger1, "send: msg.data = {}", msg.data);
        }),
    );

    // Add a subscriber.
    selector.add_subscriber(
        subscriber,
        Box::new(move |msg| {
            pr_info!(logger, "received: msg.data = {}", msg.data);
        }),
    );

    // Spin.
    loop {
        selector.wait()?;
    }
}
```

Timers can be set by a method of selector as follows,
and the timers will be invoked when calling the `Selector::wait()` methods.

```rust
selector.add_wall_timer(
    "publisher", // the name of timer
    Duration::from_secs(1),
    Box::new(move || {
        msg.data = *cnt;
        *cnt += 1;
        publisher.send(&msg).unwrap();
        pr_info!(logger1, "send: msg.data = {}", msg.data);
    }),
);
```

- `"publisher"` is the name of this timer. The name is used for statistics. You can use any name.
- `Duration::from_secs(1)` is the duration for periodic invoking. This argument means the callback function is invoked every 1 second.
- `Box::new(move || ...)` is the callback function.

There is a publisher invoked by a timer, and a subscriber in this code.
When executing this, transmission and reception will be confirmed as follows.

```text
$ cargo run
[INFO] [1656557242.842509800] [wall timer example]: send: msg.data = 0
[INFO] [1656557242.842953300] [wall timer example]: received: msg.data = 0
[INFO] [1656557243.843103800] [wall timer example]: send: msg.data = 1
[INFO] [1656557243.843272900] [wall timer example]: received: msg.data = 1
[INFO] [1656557244.843574600] [wall timer example]: send: msg.data = 2
[INFO] [1656557244.844021200] [wall timer example]: received: msg.data = 2
[INFO] [1656557245.844349800] [wall timer example]: send: msg.data = 3
[INFO] [1656557245.844702900] [wall timer example]: received: msg.data = 3
```

## One-shot Timer

A wall-timer is invoked periodically,
but one-shot timer is invoked only once.
A one-shot can be set by the `Selector::add_timer()` method as follows.

```rust
use safe_drive::{context::Context, error::DynError, logger::Logger, pr_info};
use std::{cell::RefCell, collections::VecDeque, rc::Rc, time::Duration};

pub fn main() -> Result<(), DynError> {
    // Create a context, a publisher, and a logger.
    let ctx = Context::new()?;
    let mut selector = ctx.create_selector()?;
    let logger = Rc::new(Logger::new("one-shot timer example"));

    let queue = Rc::new(RefCell::new(VecDeque::new()));

    // Add a one-shot timer.
    let queue1 = queue.clone();
    selector.add_timer(
        Duration::from_secs(2),
        Box::new(move || {
            pr_info!(logger, "fired!");

            // Insert a timer to the queue.
            let mut q = queue1.borrow_mut();
            let logger1 = logger.clone();
            q.push_back((
                Duration::from_secs(2),
                (Box::new(move || pr_info!(logger1, "fired! again!"))),
            ));
        }),
    );

    // Spin.
    loop {
        {
            // Set timers.
            let mut q = queue.borrow_mut();
            while let Some((dur, f)) = q.pop_front() {
                selector.add_timer(dur, f);
            }
        }

        selector.wait()?;
    }
}
```

`Selector::add_timer()` does not take the name,
but other arguments are same as `Selector::add_wall_timer()`.

```rust
selector.add_timer(
    Duration::from_secs(2),
    Box::new(move || ...),
);
```

- `Duration::from_secs(2)` is a duration indicating when the timer will be invoked.
- `Box::new(move || ...)` is the callback function.

This code reenables a timer in the callback function.
To reenable, the callback takes a `queue` and
timers in the `queue` is reenabled in the spin as follows.

```rust
// Spin.
loop {
    {
        // Set timers.
        let mut q = queue.borrow_mut();
        while let Some((dur, f)) = q.pop_front() {
            selector.add_timer(dur, f);
        }
    }

    selector.wait()?;
}
```

The important thing is that the borrowed resources must be released.
To release definitely, the code fraction borrowing the `queue` is surrounded by braces.

The following is a execution result of this code.

```
$ cargo run
[INFO] [1657070943.324438900] [one-shot timer example]: fired!
[INFO] [1657070945.324675600] [one-shot timer example]: fired! again!
```