events: implement a first attempt at an eventing system

This commit is contained in:
Torrie Fischer 2024-12-13 00:56:50 +01:00
parent 9a749c40a1
commit d7f312ffe4
8 changed files with 236 additions and 30 deletions

View File

@ -2,6 +2,7 @@ use palette::Hsv;
use rgb::RGB8; use rgb::RGB8;
use crate::events::{Event, EventBus};
use crate::time::Periodically; use crate::time::Periodically;
use crate::geometry::*; use crate::geometry::*;
use crate::render::{Shader, Surface, Surfaces}; use crate::render::{Shader, Surface, Surfaces};
@ -75,7 +76,7 @@ impl<T: Surface> Task for IdleTask<T> {
self.shimmer.set_opacity(64); self.shimmer.set_opacity(64);
} }
fn tick(&mut self) {} fn tick(&mut self, event: &Event, bus: &mut EventBus) {}
fn stop(&mut self) { fn stop(&mut self) {
self.solid.clear_shader(); self.solid.clear_shader();
@ -172,7 +173,7 @@ impl<T: Surface> Task for TestPattern<T> {
self.surface.set_shader(Box::new(self.pattern.clone())); 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.updater.run(|| {
self.pattern = self.pattern.next(); self.pattern = self.pattern.next();
log::info!("Test pattern: {:?}", self.pattern); log::info!("Test pattern: {:?}", self.pattern);

View File

@ -1,3 +1,4 @@
use crate::events::{Event, EventBus};
use crate::geometry::*; use crate::geometry::*;
use crate::lib8::interpolate::Fract8Ops; use crate::lib8::interpolate::Fract8Ops;
use crate::power::AsMilliwatts; use crate::power::AsMilliwatts;
@ -235,7 +236,7 @@ impl Surfaces for BufferedSurfacePool {
impl Task 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() { if self.pool.read().unwrap().is_dirty() {
self.pool.write().unwrap().commit(); self.pool.write().unwrap().commit();
} }

163
src/events.rs Normal file
View File

@ -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::<Box<SystemState>>
}
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<Variant> {
match self.get_key(key) {
None => None,
Some(v) => Some(v.value)
}
}
pub fn set<V>(&mut self, key: &'static str, value: V) where Variant: From<V> {
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<T>(key: &'static str, data: T) -> Self where Variant: From<T> {
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<u8> for Variant {
fn into(self) -> u8 {
match self {
Variant::Byte(b) => b,
_ => 0
}
}
}
impl From<bool> for Variant {
fn from(value: bool) -> Self {
Variant::Boolean(value)
}
}
impl From<i64> for Variant {
fn from(value: i64) -> Self {
Variant::BigInt(value)
}
}
impl From<u8> for Variant {
fn from(value: u8) -> Self {
Variant::Byte(value)
}
}
pub struct EventBus {
pending: Vec<Event>
}
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);
}
}

View File

@ -8,7 +8,11 @@ mod platform;
mod animations; mod animations;
mod mappings; mod mappings;
mod buffers; mod buffers;
mod events;
use events::Event;
use crate::events::EventBus;
use crate::platform::{DefaultBoard, Board}; use crate::platform::{DefaultBoard, Board};
use crate::task::{FixedSizeScheduler, Scheduler}; use crate::task::{FixedSizeScheduler, Scheduler};
use crate::render::{Surfaces, Renderer}; use crate::render::{Surfaces, Renderer};
@ -39,10 +43,19 @@ fn main() {
let mut renderer = FixedSizeScheduler::new([Box::new(Renderer::new(output, surfaces))]); 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"); log::info!("Ready to rock and roll");
bus.push(Event::new_ready_to_rock());
loop { loop {
animations.tick(); let next_event = bus.next();
system.tick(); match next_event {
renderer.tick(); 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);
} }
} }

View File

@ -23,6 +23,9 @@ use super::Board;
use crate::buffers::BufferedSurfacePool; use crate::buffers::BufferedSurfacePool;
use crate::buffers::Pixbuf; use crate::buffers::Pixbuf;
use crate::events::Event;
use crate::events::EventBus;
use crate::lib8::interpolate::lerp8by8;
use crate::mappings::StrideMapping; use crate::mappings::StrideMapping;
use crate::task::FixedSizeScheduler; use crate::task::FixedSizeScheduler;
use crate::task::Task; use crate::task::Task;
@ -55,6 +58,10 @@ pub mod i2s {
} }
impl<'d> Output for I2SOutput<'d> { impl<'d> Output for I2SOutput<'d> {
fn on_event(&mut self, event: &crate::events::Event) {
}
fn blank(&mut self) { fn blank(&mut self) {
self.pixbuf.blank(); self.pixbuf.blank();
} }
@ -307,7 +314,7 @@ impl Task for WifiTask {
self.connect(); self.connect();
} }
fn tick(&mut self ) { fn tick(&mut self, event: &Event, bus: &mut EventBus) {
if self.connection_check.tick() { if self.connection_check.tick() {
let cur_state = *self.state.lock().unwrap(); let cur_state = *self.state.lock().unwrap();
@ -322,10 +329,12 @@ impl Task for WifiTask {
log::info!("online: {:?}", cur_state); log::info!("online: {:?}", cur_state);
self.last_state = cur_state; self.last_state = cur_state;
}
let now: DateTime<Utc> = std::time::SystemTime::now().into(); match cur_state {
log::info!("Current time: {} status={:?}", now.format("%d/%m/%Y %T"), self.ntp.get_sync_status()); WifiState::Connected => bus.push(Event::new_input_event(crate::events::InputEvent::NetworkOnline)),
_ => bus.push(Event::new_input_event(crate::events::InputEvent::NetworkOffline))
}
}
} }
} }

View File

@ -1,6 +1,7 @@
use smart_leds_trait::SmartLedsWrite; use smart_leds_trait::SmartLedsWrite;
use crate::buffers::Pixbuf; use crate::buffers::Pixbuf;
use crate::events::Variant;
use crate::render::{HardwarePixel, Output, PixelView, Sample}; use crate::render::{HardwarePixel, Output, PixelView, Sample};
use crate::power::brightness_for_mw; use crate::power::brightness_for_mw;
use crate::geometry::*; use crate::geometry::*;
@ -18,7 +19,8 @@ pub struct StrideOutput<P: Pixbuf, T: FastWrite> {
pixbuf: P, pixbuf: P,
stride_map: StrideMapping, stride_map: StrideMapping,
target: T, target: T,
max_mw: u32 max_mw: u32,
brightness: u8
} }
impl<P: Pixbuf, T: FastWrite> StrideOutput<P, T> { impl<P: Pixbuf, T: FastWrite> StrideOutput<P, T> {
@ -28,7 +30,8 @@ impl<P: Pixbuf, T: FastWrite> StrideOutput<P, T> {
pixbuf, pixbuf,
stride_map, stride_map,
target, target,
max_mw max_mw,
brightness: 255
} }
} }
} }
@ -46,11 +49,18 @@ impl<P: Pixbuf<Pixel=T::Color>, T: FastWrite> Output for StrideOutput<P, T> {
} }
fn commit(&mut self) { 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() { if self.target.fast_write(self.pixbuf.iter_with_brightness(b)).is_err() {
panic!("Could not write frame!"); 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 { pub trait FastWrite: Send {

View File

@ -1,5 +1,6 @@
use rgb::Rgb; use rgb::Rgb;
use crate::events::{Event, EventBus};
use crate::geometry::*; use crate::geometry::*;
use crate::lib8::interpolate::Fract8Ops; use crate::lib8::interpolate::Fract8Ops;
use crate::power::AsMilliwatts; use crate::power::AsMilliwatts;
@ -42,6 +43,7 @@ pub trait Surface: Send + Sync {
} }
pub trait Output: Sample + Send { pub trait Output: Sample + Send {
fn on_event(&mut self, event: &Event);
fn blank(&mut self); fn blank(&mut self);
fn commit(&mut self); fn commit(&mut self);
} }
@ -70,7 +72,9 @@ impl<T: Output, S: Surfaces> Renderer<T, S> {
impl<T: Output, S: Surfaces> Task for Renderer<T, S> { impl<T: Output, S: Surfaces> Task for Renderer<T, S> {
fn name(&self) -> &'static str { "Renderer" } fn name(&self) -> &'static str { "Renderer" }
fn tick(&mut self) { fn tick(&mut self, event: &Event, _bus: &mut EventBus) {
match event {
crate::events::Event::Tick => {
self.output.blank(); self.output.blank();
self.surfaces.render_to(&mut self.output, self.frame); self.surfaces.render_to(&mut self.output, self.frame);
@ -82,5 +86,8 @@ impl<T: Output, S: Surfaces> Task for Renderer<T, S> {
self.fps_display.run(|| { self.fps_display.run(|| {
log::info!("FPS: {}", self.fps.measurement()); log::info!("FPS: {}", self.fps.measurement());
}); });
},
_ => self.output.on_event(event)
}
} }
} }

View File

@ -1,7 +1,9 @@
use core::fmt; use core::fmt;
use crate::events::{Event, EventBus};
pub trait Task: Send { pub trait Task: Send {
fn tick(&mut self) {} fn tick(&mut self, event: &Event, bus: &mut EventBus) {}
fn start(&mut self) {} fn start(&mut self) {}
fn stop(&mut self) {} fn stop(&mut self) {}
fn name(&self) -> &'static str { 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 { self.state = match self.state {
ScheduledState::Start => { ScheduledState::Start => {
log::info!("Starting task {}", self.task.name()); log::info!("Starting task {}", self.task.name());
@ -63,7 +65,7 @@ impl ScheduledTask {
ScheduledState::Running ScheduledState::Running
}, },
ScheduledState::Running => { ScheduledState::Running => {
self.task.tick(); self.task.tick(event, bus);
ScheduledState::Running ScheduledState::Running
}, },
ScheduledState::Stop => { ScheduledState::Stop => {
@ -96,10 +98,10 @@ impl<const TASK_COUNT: usize> FixedSizeScheduler<TASK_COUNT> {
} }
impl<const TASK_COUNT: usize> Scheduler for FixedSizeScheduler<TASK_COUNT> { impl<const TASK_COUNT: usize> Scheduler for FixedSizeScheduler<TASK_COUNT> {
fn tick(&mut self) { fn tick(&mut self, event: &Event, bus: &mut EventBus) {
for slot in &mut self.tasks { for slot in &mut self.tasks {
match slot { match slot {
Some(task) => task.tick(), Some(task) => task.tick(event, bus),
_ => () _ => ()
} }
} }
@ -107,5 +109,5 @@ impl<const TASK_COUNT: usize> Scheduler for FixedSizeScheduler<TASK_COUNT> {
} }
pub trait Scheduler { pub trait Scheduler {
fn tick(&mut self); fn tick(&mut self, event: &Event, bus: &mut EventBus);
} }