boomerang 0.3.0

Deterministic Middleware for Robotics
Documentation
use boomerang::{
    builder::*,
    runtime::{self},
    Reaction, Reactor,
};
use boomerang_util::timeout;

trait CountData:
    Copy + runtime::PortData + std::ops::AddAssign<i32> + std::cmp::PartialEq<i32>
{
}

impl CountData for i32 {}

#[derive(Reactor)]
#[reactor(state = "T", reaction = "ReactionT<T>", reaction = "ReactionShutdown")]
struct Count<T: CountData> {
    #[reactor(timer(period = "1 msec"))]
    t: TimerActionKey,
    c: TypedPortKey<T, Output>,
    #[reactor(child = runtime::Duration::from_secs(1))]
    _timeout: timeout::Timeout,
}

#[derive(Reaction)]
#[reaction(reactor = "Count::<T>", triggers(action = "t"))]
struct ReactionT<'a, T: CountData> {
    #[reaction(path = "c")]
    xyc: runtime::OutputRef<'a, T>,
}

impl<T: CountData> Trigger<Count<T>> for ReactionT<'_, T> {
    fn trigger(mut self, _ctx: &mut runtime::Context, state: &mut <Count<T> as Reactor>::State) {
        *state += 1;
        assert!(self.xyc.is_none());
        *self.xyc = Some(*state);
    }
}

#[derive(Reaction)]
#[reaction(reactor = "Count::<T>", bound = "T: CountData", triggers(shutdown))]
struct ReactionShutdown;

impl<T: CountData> Trigger<Count<T>> for ReactionShutdown {
    fn trigger(self, _ctx: &mut runtime::Context, state: &mut <Count<T> as Reactor>::State) {
        assert_eq!(*state, 1e3 as i32, "expected 1e3, got {state:?}");
        println!("ok");
    }
}

#[test]
fn count() {
    tracing_subscriber::fmt::init();
    let (_, sched) =
        boomerang_util::runner::build_and_test_reactor::<Count<i32>>("count", 0, true, false)
            .unwrap();
    let env = sched.into_env();
    let count = env
        .find_reactor_by_name("count")
        .and_then(|r| r.get_state::<i32>())
        .unwrap();
    assert_eq!(*count, 1e3 as i32);
}