use core::future::pending;
use embassy_time::{Duration, Timer};
use qmh_actor::*;
#[embassy_executor::main]
async fn main(spawner: embassy_executor::Spawner) -> ! {
let config = Config {
starting_count: 0,
schedule: Duration::from_secs(2),
};
let inbox_1 = spawn_actor(spawner, config).expect("ActorQMH failed to start");
Timer::after_secs(1).await;
inbox_1.send(Message::Act(1)).await;
Timer::after_secs(1).await;
inbox_1.send(Message::Echo("Hello".to_string())).await;
Timer::after_secs(1).await;
inbox_1.send(Message::Echo("Hello".to_string())).await;
Timer::after_secs(5).await;
inbox_1.send(Message::Stop).await;
pending().await
}
pub mod qmh_actor {
use actor_internals::*;
use core::future::pending;
use ector::{mutex::NoopRawMutex, *};
use embassy_executor::SpawnError;
use embassy_futures::select::{Either, select};
use embassy_sync::channel::Sender;
use embassy_time::{Duration, Instant, Timer};
pub type ActorInbox<M> = Sender<'static, NoopRawMutex, M, 1>;
pub enum Message {
Act(usize),
Stop,
Echo(String),
}
pub struct Config {
pub starting_count: usize,
pub schedule: Duration,
}
pub fn spawn_actor(
spawner: embassy_executor::Spawner,
config: Config,
) -> Result<ActorInbox<Message>, SpawnError> {
static CONTEXT: ActorContext<ActorQMH> = ActorContext::new();
let inbox = CONTEXT.address();
spawner.spawn(actor_task(&CONTEXT, ActorQMH::new(spawner, config, inbox)))?;
Ok(inbox)
}
mod actor_internals {
use super::*;
struct Scheduler {
timer: Timer,
start: Instant,
}
pub(super) struct ActorQMH {
scheduler: Option<Scheduler>,
count: usize,
period: Duration,
}
impl Actor for ActorQMH {
type Message = Message;
async fn on_mount<M>(&mut self, _: DynamicAddress<Message>, mut inbox: M) -> !
where
M: Inbox<Self::Message>,
{
println!("QMH started!");
loop {
let deadline = async {
match self.scheduler.as_mut() {
Some(Scheduler {
timer: next_timer, ..
}) => next_timer.await,
None => pending().await,
}
};
match select(inbox.next(), deadline).await {
Either::First(action) => self.act(action).await,
Either::Second(_) => self.next().await,
}
}
}
}
impl ActorQMH {
pub(super) fn new(
_spawner: embassy_executor::Spawner,
config: Config,
_inbox: ActorInbox<Message>,
) -> Self {
Self {
scheduler: None,
count: config.starting_count,
period: config.schedule,
}
}
async fn act(&mut self, msg: Message) {
match msg {
Message::Act(n) => {
self.count += n;
self.scheduler = Some(Scheduler {
timer: Timer::after(self.period),
start: Instant::now(),
});
println!("Acting: {}", n);
}
Message::Stop => {
println!("Stopping");
self.scheduler = None;
}
Message::Echo(s) => {
println!("Echoing: {}", s);
}
}
}
async fn next(&mut self) {
self.count += 1;
if let Some(Scheduler { timer, start }) = self.scheduler.as_mut() {
println!("Next: {} @ {:?}ms", self.count, start.elapsed().as_millis());
*timer = Timer::after(self.period);
}
}
}
#[embassy_executor::task]
pub(super) async fn actor_task(context: &'static ActorContext<ActorQMH>, actor: ActorQMH) {
context.mount(actor).await;
}
}
}