pros_async/
lib.rs

1//! Tiny async runtime and robot traits for `pros-rs`.
2//! The async executor supports spawning tasks and blocking on futures.
3//! It has a reactor to improve the performance of some futures.
4//! It is recommended to use the `AsyncRobot` trait to run robot code.
5//! FreeRTOS tasks can still be used, but it is recommended to use only async tasks for performance.
6
7#![no_std]
8#![feature(negative_impls)]
9
10extern crate alloc;
11
12use core::{future::Future, task::Poll};
13
14use async_task::Task;
15use executor::EXECUTOR;
16use pros_core::error::Result;
17
18mod executor;
19mod reactor;
20
21/// Runs a future in the background without having to await it
22/// To get the the return value you can await a task.
23pub fn spawn<T>(future: impl Future<Output = T> + 'static) -> Task<T> {
24    executor::EXECUTOR.with(|e| e.spawn(future))
25}
26
27/// Blocks the current task untill a return value can be extracted from the provided future.
28/// Does not poll all futures to completion.
29pub fn block_on<F: Future + 'static>(future: F) -> F::Output {
30    executor::EXECUTOR.with(|e| e.block_on(spawn(future)))
31}
32
33/// A future that will complete after the given duration.
34/// Sleep futures that are closer to completion are prioritized to improve accuracy.
35#[derive(Debug)]
36pub struct SleepFuture {
37    target_millis: u32,
38}
39impl Future for SleepFuture {
40    type Output = ();
41
42    fn poll(
43        self: core::pin::Pin<&mut Self>,
44        cx: &mut core::task::Context<'_>,
45    ) -> core::task::Poll<Self::Output> {
46        if self.target_millis < unsafe { pros_sys::millis() } {
47            Poll::Ready(())
48        } else {
49            EXECUTOR.with(|e| {
50                e.reactor
51                    .borrow_mut()
52                    .sleepers
53                    .push(cx.waker().clone(), self.target_millis)
54            });
55            Poll::Pending
56        }
57    }
58}
59
60/// Returns a future that will complete after the given duration.
61pub fn sleep(duration: core::time::Duration) -> SleepFuture {
62    SleepFuture {
63        target_millis: unsafe { pros_sys::millis() + duration.as_millis() as u32 },
64    }
65}
66
67/// A trait for robot code that spins up the pros-rs async executor.
68/// This is the preferred trait to run robot code.
69pub trait AsyncRobot {
70    /// Runs during the operator control period.
71    /// This function may be called more than once.
72    /// For that reason, do not use `Peripherals::take`in this function.
73    fn opcontrol(&mut self) -> impl Future<Output = Result> {
74        async { Ok(()) }
75    }
76    /// Runs during the autonomous period.
77    fn auto(&mut self) -> impl Future<Output = Result> {
78        async { Ok(()) }
79    }
80    /// Runs continuously during the disabled period.
81    fn disabled(&mut self) -> impl Future<Output = Result> {
82        async { Ok(()) }
83    }
84    /// Runs once when the competition system is initialized.
85    fn comp_init(&mut self) -> impl Future<Output = Result> {
86        async { Ok(()) }
87    }
88}
89
90#[doc(hidden)]
91#[macro_export]
92macro_rules! __gen_async_exports {
93    ($rbt:ty) => {
94        pub static mut ROBOT: Option<$rbt> = None;
95
96        #[doc(hidden)]
97        #[no_mangle]
98        extern "C" fn opcontrol() {
99            $crate::block_on(<$rbt as $crate::AsyncRobot>::opcontrol(unsafe {
100                ROBOT
101                    .as_mut()
102                    .expect("Expected initialize to run before opcontrol")
103            }))
104            .unwrap();
105        }
106
107        #[doc(hidden)]
108        #[no_mangle]
109        extern "C" fn autonomous() {
110            $crate::block_on(<$rbt as $crate::AsyncRobot>::auto(unsafe {
111                ROBOT
112                    .as_mut()
113                    .expect("Expected initialize to run before auto")
114            }))
115            .unwrap();
116        }
117
118        #[doc(hidden)]
119        #[no_mangle]
120        extern "C" fn disabled() {
121            $crate::block_on(<$rbt as $crate::AsyncRobot>::disabled(unsafe {
122                ROBOT
123                    .as_mut()
124                    .expect("Expected initialize to run before disabled")
125            }))
126            .unwrap();
127        }
128
129        #[doc(hidden)]
130        #[no_mangle]
131        extern "C" fn competition_initialize() {
132            $crate::block_on(<$rbt as $crate::AsyncRobot>::comp_init(unsafe {
133                ROBOT
134                    .as_mut()
135                    .expect("Expected initialize to run before comp_init")
136            }))
137            .unwrap();
138        }
139    };
140}
141
142/// Allows your async robot code to be executed by the pros kernel.
143/// If your robot struct implements Default then you can just supply this macro with its type.
144/// If not, you can supply an expression that returns your robot type to initialize your robot struct.
145/// The code that runs to create your robot struct will run in the initialize function in PROS.
146///
147/// Example of using the macro with a struct that implements Default:
148/// ```rust
149/// use pros::prelude::*;
150/// #[derive(Default)]
151/// struct ExampleRobot;
152/// #[async_trait]
153/// impl AsyncRobot for ExampleRobot {
154///    asnyc fn opcontrol(&mut self) -> pros::Result {
155///       println!("Hello, world!");
156///      Ok(())
157///   }
158/// }
159/// async_robot!(ExampleRobot);
160/// ```
161///
162/// Example of using the macro with a struct that does not implement Default:
163/// ```rust
164/// use pros::prelude::*;
165/// struct ExampleRobot {
166///    x: i32,
167/// }
168/// #[async_trait]
169/// impl AsyncRobot for ExampleRobot {
170///     async fn opcontrol(&mut self) -> pros::Result {
171///         println!("Hello, world! {}", self.x);
172///         Ok(())
173///     }
174/// }
175/// impl ExampleRobot {
176///     pub fn new() -> Self {
177///        Self { x: 5 }
178///    }
179/// }
180/// async_robot!(ExampleRobot, ExampleRobot::new());
181#[macro_export]
182macro_rules! async_robot {
183    ($rbt:ty) => {
184        $crate::__gen_async_exports!($rbt);
185
186        #[no_mangle]
187        extern "C" fn initialize() {
188            let robot = Default::default();
189            unsafe {
190                ROBOT = Some(robot);
191            }
192        }
193    };
194    ($rbt:ty, $init:expr) => {
195        $crate::__gen_async_exports!($rbt);
196
197        #[no_mangle]
198        extern "C" fn initialize() {
199            let robot = $init;
200            unsafe {
201                ROBOT = Some(robot);
202            }
203        }
204    };
205}