From a0d524b82509e2b1da914f634aa346f0d45ec23a Mon Sep 17 00:00:00 2001 From: Victoria Fischer Date: Sat, 14 Dec 2024 14:53:24 +0100 Subject: [PATCH] task: rewrite event handling as a step towards event-based subscriptions --- src/animations.rs | 21 ++--- src/buffers.rs | 4 +- src/geometry.rs | 12 ++- src/main.rs | 35 +++++++- src/platform/esp32.rs | 180 +++++++++++++++++++----------------------- src/render.rs | 29 ++++--- src/scenes.rs | 111 ++++++++++++++++++++++++++ src/task.rs | 85 ++++++++++++++------ 8 files changed, 319 insertions(+), 158 deletions(-) create mode 100644 src/scenes.rs diff --git a/src/animations.rs b/src/animations.rs index 89a7897..45c2ba3 100644 --- a/src/animations.rs +++ b/src/animations.rs @@ -66,7 +66,9 @@ impl IdleTask { } impl Task for IdleTask { - fn start(&mut self) { + fn name(&self) -> &'static str { "Idle" } + + fn start(&mut self, _bus: &mut EventBus) { self.solid.set_shader(Box::new(SolidShader { })); self.surface.set_shader(Box::new(ThinkingShader { })); self.shimmer.set_shader(Box::new(ShimmerShader { })); @@ -76,9 +78,7 @@ impl Task for IdleTask { self.shimmer.set_opacity(64); } - fn tick(&mut self, event: &Event, bus: &mut EventBus) {} - - fn stop(&mut self) { + fn stop(&mut self, _bus: &mut EventBus) { self.solid.clear_shader(); self.surface.clear_shader(); self.shimmer.clear_shader(); @@ -169,16 +169,19 @@ impl TestPattern { } impl Task for TestPattern { - fn start(&mut self) { - self.surface.set_shader(Box::new(self.pattern.clone())); + fn name(&self) -> &'static str { "TestPattern" } + + fn start(&mut self, _bus: &mut EventBus) { + self.surface.set_shader(Box::new(self.pattern)); } - fn tick(&mut self, event: &Event, bus: &mut EventBus) { + fn on_tick(&mut self, bus: &mut EventBus) { self.updater.run(|| { self.pattern = self.pattern.next(); log::info!("Test pattern: {:?}", self.pattern); self.frame = 0; - self.surface.set_shader(Box::new(self.pattern.clone())); + self.surface.set_shader(Box::new(self.pattern)); + bus.push(Event::new_property_change("animations.test.pattern", format!("{:?}", self.pattern))); }); self.stepper.run(|| { self.frame = self.frame.wrapping_add(1); @@ -197,7 +200,7 @@ impl Task for TestPattern { }); } - fn stop(&mut self) { + fn stop(&mut self, _bus: &mut EventBus) { self.surface.clear_shader(); } } diff --git a/src/buffers.rs b/src/buffers.rs index ef920f9..c2690f7 100644 --- a/src/buffers.rs +++ b/src/buffers.rs @@ -1,4 +1,4 @@ -use crate::events::{Event, EventBus}; +use crate::events::EventBus; use crate::geometry::*; use crate::lib8::interpolate::Fract8Ops; use crate::power::AsMilliwatts; @@ -236,7 +236,7 @@ impl Surfaces for BufferedSurfacePool { impl Task for BufferedSurfacePool { - fn tick(&mut self, event: &Event, bus: &mut EventBus) { + fn on_tick(&mut self, _bus: &mut EventBus) { if self.pool.read().unwrap().is_dirty() { self.pool.write().unwrap().commit(); } diff --git a/src/geometry.rs b/src/geometry.rs index 2c78916..c7f349f 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -64,19 +64,19 @@ impl Coordinates { } } - fn top_left() -> Self { + const fn top_left() -> Self { Self::new(S::Data::MIN, S::Data::MIN) } - fn top_right() -> Self { + const fn top_right() -> Self { Self::new(S::Data::MAX, S::Data::MIN) } - fn bottom_left() -> Self { + const fn bottom_left() -> Self { Self::new(S::Data::MIN, S::Data::MAX) } - fn bottom_right() -> Self { + const fn bottom_right() -> Self { Self::new(S::Data::MAX, S::Data::MAX) } @@ -101,15 +101,13 @@ pub struct Rectangle { impl Rectangle { pub const fn new(top_left: Coordinates, bottom_right: Coordinates) -> Self { - //debug_assert!(top_left.x <= bottom_right.x); - //debug_assert!(top_left.y <= bottom_right.y); Self { top_left, bottom_right } } - pub fn everything() -> Self { + pub const fn everything() -> Self { Self { top_left: Coordinates::::top_left(), bottom_right: Coordinates::::bottom_right() diff --git a/src/main.rs b/src/main.rs index 5f89d97..cbc4d6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,8 +9,11 @@ mod animations; mod mappings; mod buffers; mod events; +mod scenes; use events::Event; +use rgb::Rgb; +use scenes::Sequencer; use crate::events::EventBus; use crate::platform::{DefaultBoard, Board}; @@ -40,7 +43,11 @@ fn main() { log::info!("🌌 Creating animations"); let mut animations = FixedSizeScheduler::new([ Box::new(animations::IdleTask::new(&mut surfaces)), - Box::new(animations::TestPattern::new(surfaces.new_surface(&Rectangle::everything()).unwrap())), + Box::new(animations::TestPattern::new(surfaces.new_surface(Rectangle::everything()).unwrap())), + ]); + + let mut inputs = FixedSizeScheduler::new([ + Box::new(Sequencer::new()), ]); let mut renderer = FixedSizeScheduler::new([Box::new(Renderer::new(output, surfaces))]); @@ -48,14 +55,36 @@ fn main() { log::info!("🚌 Starting event bus"); let mut bus = EventBus::new(); - log::info!("Ready to rock and roll"); + bus.push(Event::new_property_change("colors.primary", Rgb::new(255, 128, 128))); + bus.push(Event::new_property_change("system.board.chip_id", DefaultBoard::chip_id())); + bus.push(Event::new_property_change("system.network.online", false)); + + log::info!("Priming events..."); + let initial_tasks = [ + "Renderer", + "renderbug::scenes::Sequencer", + "renderbug::buffers::BufferedSurfacePool", + "renderbug::platform::esp32::WifiTask", + "renderbug::platform::esp32::MqttTask", + "renderbug::platform::esp32::CircadianRhythm" + ]; + for task_name in initial_tasks { + bus.push(Event::new_start_thing(task_name)); + log::info!("+ {}", task_name); + } bus.push(Event::new_ready_to_rock()); + + log::info!("🚀 Launching..."); loop { let next_event = bus.next(); match next_event { events::Event::Tick => (), - _ => log::info!("Event: {:?}", next_event) + Event::ReadyToRock => { + log::info!("🚀 Ready to rock and roll"); + } + _ => log::info!("⚡ Event: {:?}", next_event) } + inputs.tick(&next_event, &mut bus); animations.tick(&next_event, &mut bus); system.tick(&next_event, &mut bus); renderer.tick(&next_event, &mut bus); diff --git a/src/platform/esp32.rs b/src/platform/esp32.rs index d03bb98..6edd73e 100644 --- a/src/platform/esp32.rs +++ b/src/platform/esp32.rs @@ -1,9 +1,5 @@ -use core::borrow::BorrowMut; -use std::sync::Arc; use std::fmt::Debug; -use std::sync::Mutex; use std::thread::JoinHandle; -use std::thread::ScopedJoinHandle; use chrono::DateTime; use chrono::Timelike; @@ -16,11 +12,11 @@ use esp_idf_svc::hal::prelude::Peripherals; use esp_idf_svc::hal::rmt::RMT; use esp_idf_svc::hal::task::thread::ThreadSpawnConfiguration; use esp_idf_svc::mqtt::client::EspMqttClient; -use esp_idf_svc::mqtt::client::EspMqttConnection; use esp_idf_svc::mqtt::client::MqttClientConfiguration; use esp_idf_svc::netif::IpEvent; use esp_idf_svc::nvs::{EspDefaultNvsPartition, EspNvsPartition, NvsDefault}; use esp_idf_svc::sntp::EspSntp; +use esp_idf_svc::sntp::SyncStatus; use esp_idf_svc::sys::esp_efuse_mac_get_default; use esp_idf_svc::wifi::{AuthMethod, ClientConfiguration, Configuration, EspWifi, WifiEvent}; use rgb::Rgb; @@ -33,6 +29,7 @@ use crate::buffers::BufferedSurfacePool; use crate::buffers::Pixbuf; use crate::events::Event; use crate::events::EventBus; +use crate::events::Variant; use crate::lib8::interpolate::lerp8by8; use crate::mappings::StrideMapping; use crate::task::FixedSizeScheduler; @@ -59,7 +56,7 @@ impl Board for Esp32Board { } return u64::from_be_bytes(chip_id); } - + fn take() -> Self { // It is necessary to call this function once. Otherwise some patches to the runtime // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 @@ -73,7 +70,7 @@ impl Board for Esp32Board { Esp32Board { modem: Some(peripherals.modem), - sys_loop: sys_loop.clone(), + sys_loop: sys_loop, surfaces: BufferedSurfacePool::new(), pins: Some(peripherals.pins), rmt: Some(peripherals.rmt) @@ -178,7 +175,7 @@ impl Board for Esp32Board { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] struct ScheduleEntry { hour: u8, brightness: u8 @@ -192,7 +189,7 @@ struct CircadianRhythm { impl CircadianRhythm { fn new() -> Self { CircadianRhythm { - time_check: Periodically::new_every_n_seconds(5), + time_check: Periodically::new_every_n_seconds(60), schedule: [ ScheduleEntry { hour: 0, brightness: 0 }, ScheduleEntry { hour: 5, brightness: 0 }, @@ -208,11 +205,17 @@ impl CircadianRhythm { } } + fn update_brightness(&self, bus: &mut EventBus) { + let now: DateTime = std::time::SystemTime::now().into(); + let next_brightness = self.brightness_for_time(now.hour() as u8, now.minute() as u8); + bus.push(Event::new_property_change("output.brightness", next_brightness)); + } + fn brightness_for_time(&self, hour: u8, minute: u8) -> u8 { let mut start = self.schedule.last().unwrap(); let mut end = self.schedule.first().unwrap(); for cur in self.schedule.iter() { - if (cur.hour <= hour ) { + if cur.hour <= hour { start = cur; } else { end = cur; @@ -252,11 +255,20 @@ fn map_range(x: u16, in_min: u16, in_max: u16, out_min: u16, out_max: u16) -> u1 impl Task for CircadianRhythm { - fn tick(&mut self, event: &Event, bus: &mut EventBus) { - if self.time_check.tick() || event.eq(&Event::ReadyToRock) { - let now: DateTime = std::time::SystemTime::now().into(); - let next_brightness = self.brightness_for_time(now.hour() as u8, now.minute() as u8); - bus.push(Event::new_property_change("output.brightness", next_brightness)); + fn on_ready(&mut self, bus: &mut EventBus) { + self.update_brightness(bus); + } + + fn on_property_change(&mut self, key: &'static str, value: &Variant, bus: &mut EventBus) { + match (key, value) { + ("system.time.synchronized", Variant::Boolean(true)) => self.update_brightness(bus), + _ => () + } + } + + fn on_tick(&mut self, bus: &mut EventBus) { + if self.time_check.tick() { + self.update_brightness(bus); } } } @@ -270,7 +282,7 @@ impl MqttTask { fn new() -> Self { MqttTask { conn_thread: None, - client: None + client: None, } } @@ -292,39 +304,50 @@ impl MqttTask { }).unwrap()); self.client = Some(client); } + + fn topic_prefix(bus: &EventBus) -> String { + let chip_id: u64 = bus.property("system.board.chip_id").unwrap().into(); + + format!("homeassistant-test/renderbug/{:X}", chip_id) + } } impl Task for MqttTask { - fn tick(&mut self, event: &Event, bus: &mut EventBus) { - match event { - Event::Input(crate::events::InputEvent::NetworkOnline) => { + fn start(&mut self, bus: &mut EventBus) { + bus.push(Event::new_property_change("mqtt.online", false)); + } + + fn on_property_change(&mut self, key: &'static str, value: &Variant, bus: &mut EventBus) { + match (key, value) { + ("system.network.online", Variant::Boolean(true)) => { log::info!("Registering with MQTT"); self.start_mqtt(); if let Some(ref mut client) = self.client { client.enqueue( - "homeassistant-test/renderbug/rust", + Self::topic_prefix(bus).as_str(), esp_idf_svc::mqtt::client::QoS::AtLeastOnce, false, "hello, world".as_bytes() ).unwrap(); log::info!("MQTT should be online!"); + + bus.push(Event::new_property_change("mqtt.online", true)); } }, - Event::PropertyChange(name, value) => { + (name, value) => { if let Some(ref mut client) = self.client { - let payload = format!("name={} value={:?}", name, value); + let prefix = Self::topic_prefix(bus); + client.enqueue( - "homeassistant-test/renderbug/rust/property-change", + format!("{}/properties/{}", prefix, name).as_str(), esp_idf_svc::mqtt::client::QoS::AtLeastOnce, false, - payload.as_bytes() + format!("{}", value).as_bytes() ).unwrap(); - log::info!("property change bump: {}", payload); } } - _ => () } } } @@ -335,33 +358,36 @@ impl Debug for WifiTask { } } -#[derive(Debug, PartialEq, Clone, Copy)] -enum WifiState { - Stopped, - Disconnected, - Connecting, - Connected -} - struct WifiTask { wifi: EspWifi<'static>, ntp: EspSntp<'static>, connection_check: Periodically, - state: Arc>, - last_state: WifiState, sys_loop: EspSystemEventLoop, wifi_sub: Option>, - ip_sub: Option> + ip_sub: Option>, } impl WifiTask { fn new(modem: Modem, sys_loop: EspSystemEventLoop, nvs: &EspNvsPartition) -> Self { log::info!("Installing wifi driver"); - let mut wifi = EspWifi::new( + let wifi = EspWifi::new( modem, sys_loop.clone(), Some(nvs.clone()) ).unwrap(); + + WifiTask { + wifi, + ntp: EspSntp::new_default().unwrap(), + connection_check: Periodically::new_every_n_seconds(1), + sys_loop, + wifi_sub: None, + ip_sub: None, + } + } + + fn connect(&mut self) { + log::info!("Connecting wifi"); let wifi_config = Configuration::Client(ClientConfiguration { ssid: "The Frequency".try_into().unwrap(), bssid: None, @@ -370,22 +396,7 @@ impl WifiTask { channel: None, ..Default::default() }); - wifi.set_configuration(&wifi_config).unwrap(); - - WifiTask { - wifi, - ntp: EspSntp::new_default().unwrap(), - connection_check: Periodically::new_every_n_seconds(1), - state: Arc::new(Mutex::new(WifiState::Stopped)), - last_state: WifiState::Stopped, - sys_loop, - wifi_sub: None, - ip_sub: None - } - } - - fn connect(&mut self) { - log::info!("Connecting wifi"); + self.wifi.set_configuration(&wifi_config).unwrap(); self.wifi.start().unwrap(); self.wifi.connect().unwrap(); } @@ -398,71 +409,44 @@ impl WifiTask { } impl Task for WifiTask { - fn start(&mut self) { + fn start(&mut self, bus: &mut EventBus) { log::info!("Starting wifi!"); - - let wifi_state = self.state.clone(); + let mut wifi_bus = bus.clone(); self.wifi_sub = Some(self.sys_loop.subscribe::( move |evt| { - log::warn!("wifi event {:?}", evt); - let next_state = match evt { - WifiEvent::StaStopped => Some(WifiState::Disconnected), - WifiEvent::StaDisconnected => Some(WifiState::Disconnected), - WifiEvent::StaStarted => Some(WifiState::Connecting), - WifiEvent::StaConnected => Some(WifiState::Connecting), - _ => None - }; - - match next_state { - Some(s) => { - let mut state = wifi_state.lock().unwrap(); - *state = s - }, - None => () - } + log::debug!("wifi event {:?}", evt); + wifi_bus.push(Event::new_property_change("system.network.online", false)); }).unwrap()); - let ip_state = self.state.clone(); + let mut ip_bus = bus.clone(); self.ip_sub = Some(self.sys_loop.subscribe::(move |evt| { - log::warn!("ip event {:?}", evt); + log::debug!("ip event {:?}", evt); match evt { - IpEvent::DhcpIpAssigned(_) => { - let mut state = ip_state.lock().unwrap(); - *state = WifiState::Connected; + IpEvent::DhcpIpAssigned(addr) => { + ip_bus.push(Event::new_property_change("system.network.ip", addr.ip().to_string())); + ip_bus.push(Event::new_property_change("system.network.gateway", addr.gateway().to_string())); + ip_bus.push(Event::new_property_change("system.network.online", true)); }, _ => () } }).unwrap()); - self.connect(); } - fn tick(&mut self, event: &Event, bus: &mut EventBus) { + fn on_tick(&mut self, bus: &mut EventBus) { if self.connection_check.tick() { - let cur_state = *self.state.lock().unwrap(); - - if self.last_state != cur_state { - match cur_state { - WifiState::Connected => log::info!("Wifi connected!"), - WifiState::Connecting => log::info!("Connecting!"), - WifiState::Stopped => log::info!("Stopped!"), - WifiState::Disconnected => log::info!("Disconnected!") - } - - log::info!("online: {:?}", cur_state); - - self.last_state = cur_state; - - match cur_state { - WifiState::Connected => bus.push(Event::new_input_event(crate::events::InputEvent::NetworkOnline)), - _ => bus.push(Event::new_input_event(crate::events::InputEvent::NetworkOffline)) + if bus.property("system.network.online").unwrap() == Variant::Boolean(true) { + match self.ntp.get_sync_status() { + SyncStatus::Completed => bus.push(Event::new_property_change("system.time.synchronized", true)), + _ => bus.push(Event::new_property_change("system.time.synchronized", false)) } } } } - fn stop(&mut self) { + fn stop(&mut self, bus: &mut EventBus) { log::info!("Stopping wifi"); self.wifi_sub.take().unwrap(); self.ip_sub.take().unwrap(); self.disconnect(); + bus.push(Event::new_property_change("system.network.online", false)); } } diff --git a/src/render.rs b/src/render.rs index 2961084..f5285ba 100644 --- a/src/render.rs +++ b/src/render.rs @@ -74,24 +74,23 @@ impl Renderer { impl Task for Renderer { fn name(&self) -> &'static str { "Renderer" } - fn tick(&mut self, event: &Event, _bus: &mut EventBus) { - match event { - crate::events::Event::Tick => { - self.output.blank(); + fn on_property_change(&mut self, key: &'static str, value: &crate::events::Variant, _bus: &mut EventBus) { + self.output.on_event(&Event::new_property_change(key, value.clone())); + } - self.surfaces.render_to(&mut self.output, self.frame); - - self.output.commit(); - - self.frame += 1; - self.fps_display.run(|| { + fn on_tick(&mut self, bus: &mut EventBus) { + self.output.blank(); + + self.surfaces.render_to(&mut self.output, self.frame); + + self.output.commit(); + + self.frame += 1; + self.fps_display.run(|| { self.fps.insert((self.frame - self.frame_count) as u32); self.frame_count = self.frame; let fps = self.fps.measurement(); bus.push(Event::new_property_change("output.fps", fps.rate() as u32)); - }); - }, - _ => self.output.on_event(event) - } + }); } -} +} \ No newline at end of file diff --git a/src/scenes.rs b/src/scenes.rs new file mode 100644 index 0000000..40ba4e3 --- /dev/null +++ b/src/scenes.rs @@ -0,0 +1,111 @@ +use std::str::FromStr; + +use crate::task::Task; +use crate::events::*; + +#[derive(Debug, PartialEq, Eq, Clone)] +struct Scene { + name: &'static str, + patterns: Vec<&'static str>, + trigger: Trigger +} + +#[derive(Debug, PartialEq, Eq, Clone)] +enum Trigger { + Startup, + PropertyEquals(&'static str, Variant) +} + +pub struct Sequencer { + scenes: Vec, + cur_scene: String +} + +impl Sequencer { + pub fn new() -> Self { + Sequencer { + cur_scene: String::new(), + scenes: vec![ + Scene { + name: "Start", + patterns: vec!["Idle"], + trigger: Trigger::Startup + }, + Scene { + name: "Online", + patterns: vec!["Idle"], + trigger: Trigger::PropertyEquals("system.network.online", Variant::Boolean(true)) + } + ] + } + } + + fn get_scene(&self, name: &String) -> Option<&Scene> { + for scene in self.scenes.iter() { + if scene.name == name { + return Some(scene); + } + } + + return None; + } + + fn apply_scene(&mut self, name: &String, bus: &mut EventBus) { + if let Some(dst_tasks) = self.get_scene(name) { + if let Some(src_tasks) = self.get_scene(&self.cur_scene) { + let stop_queue = src_tasks.patterns.iter().filter(|i| !dst_tasks.patterns.contains(i)); + let start_queue = dst_tasks.patterns.iter().filter(|i| !src_tasks.patterns.contains(i)); + + log::info!("Switching scene from {} to {}", self.cur_scene, name); + + for stop in stop_queue { + bus.push(Event::new_stop_thing(stop)); + } + for start in start_queue { + bus.push(Event::new_start_thing(start)); + } + } else { + log::info!("Starting new scene {}", name); + log::info!("start={:?}", dst_tasks.patterns); + for start in dst_tasks.patterns.iter() { + bus.push(Event::new_start_thing(start)); + } + } + self.cur_scene = name.clone(); + } else { + panic!("Could not apply scene {:?} scenes={:?}", name, self.scenes); + } + } +} + +impl Task for Sequencer { + fn start(&mut self, bus: &mut EventBus) { + log::info!("Starting sequencer!!!"); + + let startup_scene = self.scenes.iter().filter(|i| { i.trigger == Trigger::Startup }).next().unwrap(); + bus.push(Event::new_property_change("scenes.current", startup_scene.name)); + } + + fn on_property_change(&mut self, key: &'static str, value: &Variant, bus: &mut EventBus) { + match (key, value) { + ("scenes.current", Variant::String(scene_name)) => { + log::info!("Applying scene"); + self.apply_scene(scene_name, bus); + }, + (key, value) => { + for scene in self.scenes.iter() { + match scene.trigger { + Trigger::PropertyEquals(trigger_key, ref trigger_value) => { + if trigger_key == key && trigger_value == value { + log::info!("Triggering scene {}", scene.name); + bus.push(Event::new_property_change("scenes.current", scene.name)) + } + }, + _ => () + } + } + } + _ => () + } + } +} \ No newline at end of file diff --git a/src/task.rs b/src/task.rs index 802f59a..87e2361 100644 --- a/src/task.rs +++ b/src/task.rs @@ -1,17 +1,20 @@ use core::fmt; -use crate::events::{Event, EventBus}; +use crate::events::{Event, EventBus, Variant}; pub trait Task: Send { - fn tick(&mut self, event: &Event, bus: &mut EventBus) {} - fn start(&mut self) {} - fn stop(&mut self) {} + fn on_ready(&mut self, bus: &mut EventBus) {} + fn on_tick(&mut self, bus: &mut EventBus) {} + fn on_property_change(&mut self, key: &'static str, value: &Variant, bus: &mut EventBus) {} + + fn start(&mut self, bus: &mut EventBus) {} + fn stop(&mut self, bus: &mut EventBus) {} fn name(&self) -> &'static str { core::any::type_name::() } } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] enum ScheduledState { Stopped, Start, @@ -36,7 +39,7 @@ impl core::fmt::Debug for ScheduledTask { impl ScheduledTask { fn new(task: Box) -> Self { ScheduledTask { - state: ScheduledState::Start, + state: ScheduledState::Stopped, task: task, } } @@ -58,53 +61,87 @@ impl ScheduledTask { } fn tick(&mut self, event: &Event, bus: &mut EventBus) { - self.state = match self.state { + match self.state { ScheduledState::Start => { log::info!("Starting task {}", self.task.name()); - self.task.start(); - ScheduledState::Running - }, - ScheduledState::Running => { - self.task.tick(event, bus); - ScheduledState::Running + self.task.start(bus); + self.state = ScheduledState::Running }, ScheduledState::Stop => { log::info!("Stopping task {}", self.task.name()); - self.task.stop(); - ScheduledState::Stopped + self.task.stop(bus); + self.state = ScheduledState::Stopped }, - ScheduledState::Stopped => ScheduledState::Stopped + _ => () + }; + + match self.state { + ScheduledState::Running => { + match event { + Event::Tick => self.task.on_tick(bus), + Event::ReadyToRock => self.task.on_ready(bus), + Event::PropertyChange(key, value) => self.task.on_property_change(key, value, bus), + _ => () + } + }, + _ => () } } } #[derive(Debug)] pub struct FixedSizeScheduler { - tasks: [Option; TASK_COUNT], + tasks: [ScheduledTask; TASK_COUNT], } impl FixedSizeScheduler { - pub fn new(tasks: [Box; TASK_COUNT]) -> Self { - let mut scheduled = [const { None }; TASK_COUNT]; +pub fn new(tasks: [Box; TASK_COUNT]) -> Self { + let mut scheduled: [ScheduledTask; TASK_COUNT] = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; let mut idx = 0; for task in tasks { - scheduled[idx] = Some(ScheduledTask::new(task)); + log::info!("Scheduling task {}", task.name()); + let slot = &mut scheduled[idx]; + unsafe { std::ptr::write(slot, ScheduledTask::new(task)) }; idx += 1; } FixedSizeScheduler { tasks: scheduled } } + + fn find_task(&mut self, name: &str) -> Option<&mut ScheduledTask> { + for slot in &mut self.tasks { + if slot.task.name() == name { + return Some(slot); + } + } + + None + } } impl Scheduler for FixedSizeScheduler { fn tick(&mut self, event: &Event, bus: &mut EventBus) { - for slot in &mut self.tasks { - match slot { - Some(task) => task.tick(event, bus), - _ => () + match event { + Event::StartThing(task_name) => { + if let Some(slot) = self.find_task(task_name) { + log::debug!("Starting {}", task_name); + slot.start(); + } + }, + Event::StopThing(task_name) => { + if let Some(slot) = self.find_task(task_name) { + log::debug!("Stopping {}", task_name); + slot.stop(); + } + }, + _ => { + for slot in &mut self.tasks { + slot.tick(event, bus); + } } } + } }