diff --git a/src/animations.rs b/src/animations.rs index f5baf8b..9e6c749 100644 --- a/src/animations.rs +++ b/src/animations.rs @@ -2,6 +2,7 @@ use palette::Hsv; use rgb::RGB8; +use crate::events::{Event, EventBus}; use crate::time::Periodically; use crate::geometry::*; use crate::render::{Shader, Surface, Surfaces}; @@ -75,7 +76,7 @@ impl Task for IdleTask { self.shimmer.set_opacity(64); } - fn tick(&mut self) {} + fn tick(&mut self, event: &Event, bus: &mut EventBus) {} fn stop(&mut self) { self.solid.clear_shader(); @@ -172,7 +173,7 @@ impl Task for TestPattern { self.surface.set_shader(Box::new(self.pattern.clone())); } - fn tick(&mut self) { + fn tick(&mut self, event: &Event, bus: &mut EventBus) { self.updater.run(|| { self.pattern = self.pattern.next(); log::info!("Test pattern: {:?}", self.pattern); diff --git a/src/buffers.rs b/src/buffers.rs index 57b3fd0..e21eee1 100644 --- a/src/buffers.rs +++ b/src/buffers.rs @@ -1,3 +1,4 @@ +use crate::events::{Event, EventBus}; use crate::geometry::*; use crate::lib8::interpolate::Fract8Ops; use crate::power::AsMilliwatts; @@ -235,7 +236,7 @@ impl Surfaces for BufferedSurfacePool { impl Task for BufferedSurfacePool { - fn tick(&mut self) { + fn tick(&mut self, event: &Event, bus: &mut EventBus) { if self.pool.read().unwrap().is_dirty() { self.pool.write().unwrap().commit(); } diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 0000000..bdaad0b --- /dev/null +++ b/src/events.rs @@ -0,0 +1,163 @@ +use core::fmt::Debug; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum InputEvent { + PowerOn, + PowerOff, + NetworkActivity, + NetworkOnline, + NetworkOffline +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Variant { + Byte(u8), + UInt(u32), + Int(i32), + BigUInt(u64), + BigInt(i64), + Boolean(bool) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Event { + ReadyToRock, + Tick, + StartThing(&'static str), + StopThing(&'static str), + Input(InputEvent), + PropertyChange(&'static str, Variant) +} + +pub struct SystemState { + key: &'static str, + value: Variant, + values: Vec::> +} + +impl SystemState { + pub fn new() -> Self { + SystemState { + key: "", + value: Variant::Byte(0), + values: Vec::new() + } + } + + fn get_key(&self, key: &'static str) -> Option<&Self> { + if key == self.key { + Some(self) + } else { + for next in self.values.iter() { + match next.get_key(key) { + None => (), + Some(next_val) => return Some(next_val) + } + } + return None + } + } + + fn get_key_mut(&mut self, key: &'static str) -> Option<&mut Self> { + if key == self.key { + Some(self) + } else { + for next in self.values.iter_mut() { + match next.get_key_mut(key) { + None => (), + Some(next_val) => return Some(next_val) + } + } + return None + } + } + + pub fn get(&self, key: &'static str) -> Option { + match self.get_key(key) { + None => None, + Some(v) => Some(v.value) + } + } + + pub fn set(&mut self, key: &'static str, value: V) where Variant: From { + match self.get_key_mut(key) { + None => self.values.push(Box::new(SystemState { + value: value.into(), + key: key, + values: Vec::new() + })), + Some(found_key) => { + found_key.value = value.into() + } + } + } +} + +impl Event { + pub fn new_tick() -> Self { + Event::Tick + } + + pub fn new_property_change(key: &'static str, data: T) -> Self where Variant: From { + Event::PropertyChange(key, Variant::from(data)) + } + + pub fn new_ready_to_rock() -> Self { + Event::ReadyToRock + } + + pub fn new_input_event(event: InputEvent) -> Self { + Event::Input(event) + } +} + +impl Into for Variant { + fn into(self) -> u8 { + match self { + Variant::Byte(b) => b, + _ => 0 + } + } +} + +impl From for Variant { + fn from(value: bool) -> Self { + Variant::Boolean(value) + } +} + +impl From for Variant { + fn from(value: i64) -> Self { + Variant::BigInt(value) + } +} + +impl From for Variant { + fn from(value: u8) -> Self { + Variant::Byte(value) + } +} + +pub struct EventBus { + pending: Vec +} + +impl EventBus { + pub fn new() -> Self { + EventBus { + pending: Vec::new() + } + } + + pub fn next(&mut self) -> Event { + if self.pending.len() == 0 { + Event::new_tick() + } else { + self.pending.pop().unwrap() + } + } + + pub fn push(&mut self, event: Event) { + self.pending.push(event); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e3e1d57..7c9a2a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,11 @@ mod platform; mod animations; mod mappings; mod buffers; +mod events; +use events::Event; + +use crate::events::EventBus; use crate::platform::{DefaultBoard, Board}; use crate::task::{FixedSizeScheduler, Scheduler}; use crate::render::{Surfaces, Renderer}; @@ -39,10 +43,19 @@ fn main() { let mut renderer = FixedSizeScheduler::new([Box::new(Renderer::new(output, surfaces))]); + log::info!("Starting event bus"); + let mut bus = EventBus::new(); + log::info!("Ready to rock and roll"); + bus.push(Event::new_ready_to_rock()); loop { - animations.tick(); - system.tick(); - renderer.tick(); + let next_event = bus.next(); + match next_event { + events::Event::Tick => (), + _ => log::info!("Event: {:?}", next_event) + } + 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 7b9b028..9683f2d 100644 --- a/src/platform/esp32.rs +++ b/src/platform/esp32.rs @@ -23,6 +23,9 @@ use super::Board; use crate::buffers::BufferedSurfacePool; use crate::buffers::Pixbuf; +use crate::events::Event; +use crate::events::EventBus; +use crate::lib8::interpolate::lerp8by8; use crate::mappings::StrideMapping; use crate::task::FixedSizeScheduler; use crate::task::Task; @@ -55,6 +58,10 @@ pub mod i2s { } impl<'d> Output for I2SOutput<'d> { + fn on_event(&mut self, event: &crate::events::Event) { + + } + fn blank(&mut self) { self.pixbuf.blank(); } @@ -307,7 +314,7 @@ impl Task for WifiTask { self.connect(); } - fn tick(&mut self ) { + fn tick(&mut self, event: &Event, bus: &mut EventBus) { if self.connection_check.tick() { let cur_state = *self.state.lock().unwrap(); @@ -322,10 +329,12 @@ impl Task for WifiTask { log::info!("online: {:?}", cur_state); self.last_state = cur_state; - } - let now: DateTime = std::time::SystemTime::now().into(); - log::info!("Current time: {} status={:?}", now.format("%d/%m/%Y %T"), self.ntp.get_sync_status()); + 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)) + } + } } } diff --git a/src/platform/smart_leds_lib.rs b/src/platform/smart_leds_lib.rs index e0c9ffc..caba483 100644 --- a/src/platform/smart_leds_lib.rs +++ b/src/platform/smart_leds_lib.rs @@ -1,6 +1,7 @@ use smart_leds_trait::SmartLedsWrite; use crate::buffers::Pixbuf; +use crate::events::Variant; use crate::render::{HardwarePixel, Output, PixelView, Sample}; use crate::power::brightness_for_mw; use crate::geometry::*; @@ -18,7 +19,8 @@ pub struct StrideOutput { pixbuf: P, stride_map: StrideMapping, target: T, - max_mw: u32 + max_mw: u32, + brightness: u8 } impl StrideOutput { @@ -28,7 +30,8 @@ impl StrideOutput { pixbuf, stride_map, target, - max_mw + max_mw, + brightness: 255 } } } @@ -46,11 +49,18 @@ impl, T: FastWrite> Output for StrideOutput { } fn commit(&mut self) { - let b = brightness_for_mw(self.pixbuf.as_milliwatts(), 255, self.max_mw); + let b = brightness_for_mw(self.pixbuf.as_milliwatts(), self.brightness, self.max_mw); if self.target.fast_write(self.pixbuf.iter_with_brightness(b)).is_err() { panic!("Could not write frame!"); }; } + + fn on_event(&mut self, event: &crate::events::Event) { + match event { + crate::events::Event::PropertyChange("output.brightness", new_brightness) => self.brightness = new_brightness.clone().into(), + _ => () + } + } } pub trait FastWrite: Send { diff --git a/src/render.rs b/src/render.rs index 658a1c8..7415a5f 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,5 +1,6 @@ use rgb::Rgb; +use crate::events::{Event, EventBus}; use crate::geometry::*; use crate::lib8::interpolate::Fract8Ops; use crate::power::AsMilliwatts; @@ -42,6 +43,7 @@ pub trait Surface: Send + Sync { } pub trait Output: Sample + Send { + fn on_event(&mut self, event: &Event); fn blank(&mut self); fn commit(&mut self); } @@ -70,17 +72,22 @@ impl Renderer { impl Task for Renderer { fn name(&self) -> &'static str { "Renderer" } - fn tick(&mut self) { - self.output.blank(); + fn tick(&mut self, event: &Event, _bus: &mut EventBus) { + match event { + crate::events::Event::Tick => { + self.output.blank(); - self.surfaces.render_to(&mut self.output, self.frame); - - self.output.commit(); - - self.fps.insert(1); - self.frame += 1; - self.fps_display.run(|| { - log::info!("FPS: {}", self.fps.measurement()); - }); + self.surfaces.render_to(&mut self.output, self.frame); + + self.output.commit(); + + self.fps.insert(1); + self.frame += 1; + self.fps_display.run(|| { + log::info!("FPS: {}", self.fps.measurement()); + }); + }, + _ => self.output.on_event(event) + } } } diff --git a/src/task.rs b/src/task.rs index 928abf3..591d711 100644 --- a/src/task.rs +++ b/src/task.rs @@ -1,7 +1,9 @@ use core::fmt; +use crate::events::{Event, EventBus}; + pub trait Task: Send { - fn tick(&mut self) {} + fn tick(&mut self, event: &Event, bus: &mut EventBus) {} fn start(&mut self) {} fn stop(&mut self) {} fn name(&self) -> &'static str { @@ -55,7 +57,7 @@ impl ScheduledTask { } } - fn tick(&mut self) { + fn tick(&mut self, event: &Event, bus: &mut EventBus) { self.state = match self.state { ScheduledState::Start => { log::info!("Starting task {}", self.task.name()); @@ -63,7 +65,7 @@ impl ScheduledTask { ScheduledState::Running }, ScheduledState::Running => { - self.task.tick(); + self.task.tick(event, bus); ScheduledState::Running }, ScheduledState::Stop => { @@ -96,10 +98,10 @@ impl FixedSizeScheduler { } impl Scheduler for FixedSizeScheduler { - fn tick(&mut self) { + fn tick(&mut self, event: &Event, bus: &mut EventBus) { for slot in &mut self.tasks { match slot { - Some(task) => task.tick(), + Some(task) => task.tick(event, bus), _ => () } } @@ -107,5 +109,5 @@ impl Scheduler for FixedSizeScheduler { } pub trait Scheduler { - fn tick(&mut self); + fn tick(&mut self, event: &Event, bus: &mut EventBus); } \ No newline at end of file