diff --git a/.gitignore b/.gitignore index 73a638b..7a6e4c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -/.vscode /.embuild /target -/Cargo.lock +/Cargo.lock \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5b9c67b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "probe-rs-debug", + "request": "launch", + "name": "probe-rs Test", + "cwd": "${workspaceFolder}", + "connectUnderReset": true, + "chip": "ESP32S3", + "flashingConfig": { + "flashingEnabled": true, + "haltAfterReset": true + }, + "coreConfigs": [ + { + "coreIndex": 0, + "programBinary": "./target/xtensa-esp32s3-espidf/debug/${workspaceFolderBasename}" + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..e6cfa12 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,29 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "cargo", + "command": "build", + "problemMatcher": [ + "$rustc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "label": "rust: cargo build" + }, + { + "type": "cargo", + "command": "espflash flash --no-stub --monitor", + "problemMatcher": [ + "$rustc" + ], + "group": { + "kind": "build", + "isDefault": false + }, + "label": "rust: cargo espflash" + } + ] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 11385c2..2f2ac0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,11 +11,13 @@ name = "renderbug" harness = false # do not use the built in cargo test harness -> resolve rust-analyzer errors [profile.release] -opt-level = "s" +opt-level = 3 +lto = true [profile.dev] debug = true # Symbols are nice and they don't increase the size on Flash opt-level = "z" +lto = true [features] default = ["std", "esp-idf-svc/native", "rmt", "smart-leds"] @@ -48,6 +50,7 @@ embedded-graphics = { version = "0.8.1", optional = true, features = ["fixed_poi ansi_term = "0.12.1" num = "0.4.3" chrono = "0.4.38" +fugit = "0.3.7" [build-dependencies] embuild = "0.32.0" 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/lib8/interpolate.rs b/src/lib8/interpolate.rs index 10f5d21..a338931 100644 --- a/src/lib8/interpolate.rs +++ b/src/lib8/interpolate.rs @@ -82,6 +82,25 @@ pub fn lerp7by8(a: i8, b: i8, frac: u8) -> i8 { } } +pub fn lerp8by8(a: u8, b: u8, frac: u8) -> u8 { + if b > a { + let delta = b - a; + let scaled = scale8(delta, frac); + return a + scaled; + } else { + let delta = a - b; + let scaled = scale8(delta, frac); + return a - scaled; + } +} + +pub fn map8(x: u8, range_start: u8, range_end: u8) -> u8 { + let range_width = range_end - range_start; + let mut out = scale8(x, range_width); + out += range_start; + return out; +} + pub fn ease8InOutQuad(i: u8) -> u8 { let j = if i & 0x80 != 0 { 255 - i diff --git a/src/main.rs b/src/main.rs index e3e1d57..f2932b5 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}; @@ -34,15 +38,24 @@ 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 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/mappings.rs b/src/mappings.rs index 1f4ac61..bb87868 100644 --- a/src/mappings.rs +++ b/src/mappings.rs @@ -101,6 +101,12 @@ impl StrideMapping { ]) } + pub fn new_fairylights() -> Self { + Self::from_json(&[ + (0, 0, 50, false) + ]) + } + pub fn new_jar() -> Self { Self::from_json(&[ (0, 0, 17, false), diff --git a/src/platform/esp32.rs b/src/platform/esp32.rs index 2f7b3eb..f2d6e0a 100644 --- a/src/platform/esp32.rs +++ b/src/platform/esp32.rs @@ -2,14 +2,20 @@ 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; use chrono::Utc; use esp_idf_svc::eventloop::{EspSubscription, EspSystemEventLoop, System}; use esp_idf_svc::hal::modem::Modem; use esp_idf_svc::hal::prelude::Peripherals; 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; @@ -23,6 +29,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 +64,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(); } @@ -89,7 +102,7 @@ pub struct Esp32Board { impl Board for Esp32Board { type Output = StrideOutput<[Rgb; 310], FastWs2812Esp32Rmt<'static>>; type Surfaces = BufferedSurfacePool; - type Scheduler = FixedSizeScheduler<2>; + type Scheduler = FixedSizeScheduler<4>; fn take() -> Self { // It is necessary to call this function once. Otherwise some patches to the runtime @@ -122,7 +135,7 @@ impl Board for Esp32Board { // But the implementation spawns a thread based on the core the driver was created in, // so we create the driver in another thread briefly. // Fun stuff. - let output = match chip_id { + let output = match chip_id { // panel test board [72, 202, 67, 89, 145, 204, 0, 0] => { StrideOutput::new( Pixbuf::new(), @@ -131,7 +144,7 @@ impl Board for Esp32Board { MAX_POWER_MW ) }, - [0x8C, 0xAA, 0xB5, 0x83, 0x5f, 0x74, 0x0, 0x0] => { + [0x8C, 0xAA, 0xB5, 0x83, 0x5f, 0x74, 0x0, 0x0] => { //ponderjar StrideOutput::new( Pixbuf::new(), StrideMapping::new_jar(), @@ -139,6 +152,30 @@ impl Board for Esp32Board { MAX_POWER_MW ) }, + [0x4a, 0xca, 0x43, 0x59, 0x85, 0x58, 0x0, 0x0] => { // Albus the tree + StrideOutput::new( + Pixbuf::new(), + StrideMapping::new_jar(), + std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), + MAX_POWER_MW + ) + }, + [0x48, 0xca, 0x43, 0x59, 0x9d, 0x48, 0x0, 0x0] => { // kitchen cabinets + StrideOutput::new( + Pixbuf::new(), + StrideMapping::new_fairylights(), + std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), + MAX_POWER_MW + ) + }, + [0x48, 0xca, 0x43, 0x59, 0x9e, 0xdc, 0x0, 0x0] => { // front window + StrideOutput::new( + Pixbuf::new(), + StrideMapping::new_fairylights(), + std::thread::spawn(move || { FastWs2812Esp32Rmt::new(peripherals.rmt.channel0, pins.gpio5).unwrap() }).join().unwrap(), + MAX_POWER_MW + ) + } _ => { StrideOutput::new( Pixbuf::new(), @@ -172,11 +209,164 @@ impl Board for Esp32Board { let nvs = EspDefaultNvsPartition::take().unwrap(); FixedSizeScheduler::new([ Box::new(WifiTask::new(self.modem.take().unwrap(), self.sys_loop.clone(), &nvs)), + Box::new(CircadianRhythm::new()), + Box::new(MqttTask::new()), Box::new(self.surfaces.clone()) ]) } } +#[derive(Debug, Clone, Copy)] +struct ScheduleEntry { + hour: u8, + brightness: u8 +} + +struct CircadianRhythm { + time_check: Periodically, + schedule: [ScheduleEntry;10] +} + +impl CircadianRhythm { + fn new() -> Self { + CircadianRhythm { + time_check: Periodically::new_every_n_seconds(5), + schedule: [ + ScheduleEntry { hour: 0, brightness: 0 }, + ScheduleEntry { hour: 5, brightness: 0 }, + ScheduleEntry { hour: 6, brightness: 10 }, + ScheduleEntry { hour: 7, brightness: 20 }, + ScheduleEntry { hour: 8, brightness: 80 }, + ScheduleEntry { hour: 11, brightness: 120 }, + ScheduleEntry { hour: 18, brightness: 200 }, + ScheduleEntry { hour: 19, brightness: 255 }, + ScheduleEntry { hour: 22, brightness: 120 }, + ScheduleEntry { hour: 23, brightness: 5 } + ] + } + } + + 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 ) { + start = cur; + } else { + end = cur; + break; + } + } + + log::info!("hour={:?} minute={:?} start={:?} end={:?}", hour, minute, start, end); + + let mut adjusted_end = end.clone(); + if start.hour > end.hour { + adjusted_end.hour += 24; + } + + let start_time = start.hour * 60; + let end_time = end.hour * 60; + let now_time = hour * 60 + minute; + + let duration = end_time - start_time; + let cur_duration = now_time - start_time; + + let frac = map_range(cur_duration.into(), 0, duration.into(), 0, 255) as u8; + + lerp8by8(start.brightness, end.brightness, frac) + } +} + +fn map_range(x: u16, in_min: u16, in_max: u16, out_min: u16, out_max: u16) -> u16 { + let run = in_max - in_min; + if run == 0 { + return 0; + } + let rise = out_max - out_min; + let delta = x - in_min; + return (delta * rise) / run + out_min; +} + + +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)); + } + } +} + +struct MqttTask { + client: Option>, + conn_thread: Option>, +} + +impl MqttTask { + fn new() -> Self { + MqttTask { + conn_thread: None, + client: None + } + } + + fn start_mqtt(&mut self) { + log::info!("Starting MQTT"); + let (client, mut conn) = EspMqttClient::new( + "mqtt://10.0.0.2:1883", + &MqttClientConfiguration { + client_id: Some("renderbug-rs"), + ..Default::default() + } + ).unwrap(); + log::info!("Connected!"); + + self.conn_thread = Some(std::thread::Builder::new() + .stack_size(6000) + .spawn(move || { + conn.next().unwrap(); + }).unwrap()); + self.client = Some(client); + } +} + +impl Task for MqttTask { + fn tick(&mut self, event: &Event, bus: &mut EventBus) { + match event { + Event::Input(crate::events::InputEvent::NetworkOnline) => { + log::info!("Registering with MQTT"); + + self.start_mqtt(); + + if let Some(ref mut client) = self.client { + client.enqueue( + "homeassistant-test/renderbug/rust", + esp_idf_svc::mqtt::client::QoS::AtLeastOnce, + false, + "hello, world".as_bytes() + ).unwrap(); + log::info!("MQTT should be online!"); + } + }, + Event::PropertyChange(name, value) => { + if let Some(ref mut client) = self.client { + let payload = format!("name={} value={:?}", name, value); + client.enqueue( + "homeassistant-test/renderbug/rust/property-change", + esp_idf_svc::mqtt::client::QoS::AtLeastOnce, + false, + payload.as_bytes() + ).unwrap(); + log::info!("property change bump: {}", payload); + } + } + _ => () + } + } +} + impl Debug for WifiTask { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("WifiTask").finish() @@ -283,7 +473,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(); @@ -298,10 +488,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/esp32_nostd.rs b/src/platform/esp32_nostd.rs new file mode 100644 index 0000000..6e4595b --- /dev/null +++ b/src/platform/esp32_nostd.rs @@ -0,0 +1,44 @@ +use rgb::Rgb; +use super::smart_leds_lib::rmt::FastWs2812Esp32Rmt; +use super::smart_leds_lib::StrideOutput; + +use crate::task::{FixedSizeScheduler, Task}; +use crate::buffers::StaticSurfacePool; + +use super::Board; + +pub struct Esp32Board<'a> { + output: Option<::Output>, + surfaces: Option, + tasks: Option<[&'a mut dyn Task; 1]> +} + +impl<'a> Board for Esp32Board<'a> { + type Output = StrideOutput<[Rgb; 310], FastWs2812Esp32Rmt<'a>>; + type Surfaces = StaticSurfacePool; + type Scheduler = FixedSizeScheduler<0>; + fn take() -> Self { + let peripherals = esp_hal::init(esp_hal::Config::default()); + //esp_alloc::heap_allocator!(72 * 1024); + + const POWER_VOLTS : u32 = 5; + const POWER_MA : u32 = 500; + const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA; + let pins = peripherals.pins; + + + Esp32Board { output: None, surfaces: None, tasks: None } + } + + fn output(&mut self) -> Self::Output { + self.output.take().unwrap() + } + + fn surfaces(&mut self) -> Self::Surfaces { + self.surfaces.take().unwrap() + } + + fn system_tasks(&mut self) -> Self::Scheduler { + FixedSizeScheduler::new([]) + } +} \ No newline at end of file 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