renderbug: first implementation of surface-based rendering
This commit is contained in:
		@@ -34,6 +34,9 @@ ws2812-esp32-rmt-driver = { version = "*", features = ["embedded-graphics-core"]
 | 
				
			|||||||
embedded-graphics = { version = "0.8.1", features = ["fixed_point", "defmt"] }
 | 
					embedded-graphics = { version = "0.8.1", features = ["fixed_point", "defmt"] }
 | 
				
			||||||
hsv = "0.1.1"
 | 
					hsv = "0.1.1"
 | 
				
			||||||
palette = { version = "0.7.6" }
 | 
					palette = { version = "0.7.6" }
 | 
				
			||||||
 | 
					embedded-canvas = "0.3.1"
 | 
				
			||||||
 | 
					embassy-executor = "0.6.0"
 | 
				
			||||||
 | 
					running-average = "0.1.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[build-dependencies]
 | 
					[build-dependencies]
 | 
				
			||||||
embuild = "0.32.0"
 | 
					embuild = "0.32.0"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										86
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -1,17 +1,58 @@
 | 
				
			|||||||
 | 
					#![feature(trait_upcasting)]
 | 
				
			||||||
#![allow(arithmetic_overflow)]
 | 
					#![allow(arithmetic_overflow)]
 | 
				
			||||||
use esp_idf_svc::hal::prelude::Peripherals;
 | 
					use esp_idf_svc::hal::prelude::Peripherals;
 | 
				
			||||||
use ws2812_esp32_rmt_driver::lib_embedded_graphics::{LedPixelStrip, Ws2812DrawTarget};
 | 
					use ws2812_esp32_rmt_driver::lib_embedded_graphics::{LedPixelStrip, Ws2812DrawTarget};
 | 
				
			||||||
use embedded_graphics::{
 | 
					use embedded_graphics::{
 | 
				
			||||||
    prelude::*,
 | 
					    prelude::*,
 | 
				
			||||||
    pixelcolor::Rgb888,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use palette::Hsv;
 | 
					use palette::Hsv;
 | 
				
			||||||
use palette::convert::IntoColorUnclamped;
 | 
					use palette::convert::IntoColorUnclamped;
 | 
				
			||||||
 | 
					use std::thread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod power;
 | 
					mod power;
 | 
				
			||||||
mod lib8;
 | 
					mod lib8;
 | 
				
			||||||
 | 
					mod render;
 | 
				
			||||||
 | 
					mod task;
 | 
				
			||||||
 | 
					mod time;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::time::Periodically;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct IdleTask {
 | 
				
			||||||
 | 
					    frame: u8,
 | 
				
			||||||
 | 
					    surface: render::Surface,
 | 
				
			||||||
 | 
					    updater: Periodically
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct IdleShader {
 | 
				
			||||||
 | 
					    frame: u8
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl render::Shader for IdleShader {
 | 
				
			||||||
 | 
					    fn draw(&self, coords: Point) -> lib8::RGB8 {
 | 
				
			||||||
 | 
					        Hsv::new_srgb(self.frame.wrapping_add(coords.x as u8), 255, 255).into_color_unclamped()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl IdleTask {
 | 
				
			||||||
 | 
					    fn new(render: &mut dyn render::Display) -> Self {
 | 
				
			||||||
 | 
					        IdleTask {
 | 
				
			||||||
 | 
					            frame: 0,
 | 
				
			||||||
 | 
					            surface: render.new_surface(),
 | 
				
			||||||
 | 
					            updater: Periodically::new_every_n_ms(16)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl task::Task for IdleTask {
 | 
				
			||||||
 | 
					    fn name(&self) -> &'static str { "Idle" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn tick(&mut self) {
 | 
				
			||||||
 | 
					        self.updater.run(|| {
 | 
				
			||||||
 | 
					            self.frame = self.frame.wrapping_add(1);
 | 
				
			||||||
 | 
					            self.surface.set_shader(Box::new(IdleShader { frame: self.frame }));
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn main() {
 | 
					fn main() {
 | 
				
			||||||
    // It is necessary to call this function once. Otherwise some patches to the runtime
 | 
					    // It is necessary to call this function once. Otherwise some patches to the runtime
 | 
				
			||||||
@@ -32,35 +73,18 @@ fn main() {
 | 
				
			|||||||
    const POWER_MA : u32 = 500;
 | 
					    const POWER_MA : u32 = 500;
 | 
				
			||||||
    const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA;
 | 
					    const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut draw = Ws2812DrawTarget::<LedPixelStrip<NUM_PIXELS>>::new(channel, led_pin).unwrap();
 | 
					    log::info!("Setting up display");
 | 
				
			||||||
    let mut hue : u8 = 0;
 | 
					    let target = Ws2812DrawTarget::<LedPixelStrip<NUM_PIXELS>>::new(channel, led_pin).unwrap();
 | 
				
			||||||
    let mut length : usize = NUM_PIXELS;
 | 
					    let mut display = render::EmbeddedDisplay::new(target, MAX_POWER_MW);
 | 
				
			||||||
    let mut forwards = false;
 | 
					 | 
				
			||||||
    loop {
 | 
					 | 
				
			||||||
        let mut totalMW = 0;
 | 
					 | 
				
			||||||
        draw.clear(Rgb888::BLACK);
 | 
					 | 
				
			||||||
        for i in 0..length {
 | 
					 | 
				
			||||||
            let hsvColor = Hsv::new_srgb(hue.wrapping_add(i as u8), 255, 255);
 | 
					 | 
				
			||||||
            let rgbColor : lib8::RGB8 = hsvColor.into_color_unclamped();
 | 
					 | 
				
			||||||
            let color = Rgb888::new(rgbColor.red, rgbColor.green, rgbColor.blue);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            totalMW += power::colorToMW(color);
 | 
					    log::info!("Creating runner");
 | 
				
			||||||
            Pixel(Point::new(i as i32, 0), color).draw(&mut draw).unwrap();
 | 
					    let mut runner = task::Scheduler::new(vec![
 | 
				
			||||||
        }
 | 
					        Box::new(IdleTask::new(&mut display)),
 | 
				
			||||||
        let brightness = power::brightnessForMW(totalMW, 255, MAX_POWER_MW);
 | 
					        Box::new(display),
 | 
				
			||||||
        draw.set_brightness(brightness);
 | 
					    ]);
 | 
				
			||||||
        draw.flush().unwrap();
 | 
					
 | 
				
			||||||
        log::info!("Frame hue={} power={} brightness={}", hue, totalMW, brightness);
 | 
					    log::info!("Ready to rock and roll");
 | 
				
			||||||
        hue  = hue.wrapping_add(1);
 | 
					    loop {
 | 
				
			||||||
        if forwards {
 | 
					        runner.tick();
 | 
				
			||||||
            length += 1
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            length -= 1
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if length <= 1 {
 | 
					 | 
				
			||||||
            forwards = true;
 | 
					 | 
				
			||||||
        } else if length >= NUM_PIXELS {
 | 
					 | 
				
			||||||
            forwards = false;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										26
									
								
								src/power.rs
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								src/power.rs
									
									
									
									
									
								
							@@ -1,23 +1,23 @@
 | 
				
			|||||||
use embedded_graphics::pixelcolor::RgbColor;
 | 
					use embedded_graphics::pixelcolor::RgbColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn colorToMW(color : impl RgbColor) -> u32 {
 | 
					pub fn color_to_mw<T: RgbColor>(color : &T) -> u32 {
 | 
				
			||||||
    const gRed_mW : u32   = 16 * 5; //< 16mA @ 5v = 80mW
 | 
					    const RED_MW : u32   = 16 * 5; //< 16mA @ 5v = 80mW
 | 
				
			||||||
    const gGreen_mW : u32 = 11 * 5; //< 11mA @ 5v = 55mW
 | 
					    const GREEN_MW : u32 = 11 * 5; //< 11mA @ 5v = 55mW
 | 
				
			||||||
    const gBlue_mW : u32  = 15 * 5; //< 15mA @ 5v = 75mW
 | 
					    const BLUE_MW : u32  = 15 * 5; //< 15mA @ 5v = 75mW
 | 
				
			||||||
    const gDark_mW : u32  =  1 * 5; //<  1mA @ 5v =  5mW
 | 
					    const DARK_MW : u32  =  1 * 5; //<  1mA @ 5v =  5mW
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let redMW = (color.r() as u32 * gRed_mW).wrapping_shr(8);
 | 
					    let red = (color.r() as u32 * RED_MW).wrapping_shr(8);
 | 
				
			||||||
    let greenMW = (color.g() as u32 * gGreen_mW).wrapping_shr(8);
 | 
					    let green = (color.g() as u32 * GREEN_MW).wrapping_shr(8);
 | 
				
			||||||
    let blueMW = (color.b() as u32 * gBlue_mW).wrapping_shr(8);
 | 
					    let blue = (color.b() as u32 * BLUE_MW).wrapping_shr(8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return redMW + greenMW + blueMW + gDark_mW;
 | 
					    return red + green + blue + DARK_MW;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn brightnessForMW(totalMW : u32, target : u8, maxPower: u32) -> u8 {
 | 
					pub fn brightness_for_mw(total_mw : u32, target : u8, max_power: u32) -> u8 {
 | 
				
			||||||
    let target32 = target as u32;
 | 
					    let target32 = target as u32;
 | 
				
			||||||
    let requestedMW = (totalMW * target32) / 256;
 | 
					    let requested_mw = (total_mw * target32) / 256;
 | 
				
			||||||
    if requestedMW > maxPower {
 | 
					    if requested_mw > max_power {
 | 
				
			||||||
        return ((target32 * maxPower) / requestedMW).try_into().unwrap();
 | 
					        return ((target32 * max_power) / requested_mw).try_into().unwrap();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return target;
 | 
					    return target;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										134
									
								
								src/render.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/render.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					use embedded_graphics::{
 | 
				
			||||||
 | 
					    prelude::*,
 | 
				
			||||||
 | 
					    pixelcolor::Rgb888
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use ws2812_esp32_rmt_driver::lib_embedded_graphics::{Ws2812DrawTarget, LedPixelShape};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::rc::Rc;
 | 
				
			||||||
 | 
					use std::cell::RefCell;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use running_average::RealTimeRunningAverage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::task;
 | 
				
			||||||
 | 
					use crate::lib8::RGB8;
 | 
				
			||||||
 | 
					use crate::power;
 | 
				
			||||||
 | 
					use crate::time::Periodically;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait Shader: Send {
 | 
				
			||||||
 | 
					    fn draw(&self, coords: Point) -> RGB8;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait Surfaces {
 | 
				
			||||||
 | 
					    fn new_surface(&mut self) -> Surface;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait Display: Surfaces {
 | 
				
			||||||
 | 
					    fn start_frame(&mut self) {}
 | 
				
			||||||
 | 
					    fn end_frame(&mut self) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn render_frame(&mut self) {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> task::Task for T
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					T: Display {
 | 
				
			||||||
 | 
					    fn name(&self) -> &'static str { "Renderer" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn tick(&mut self) {
 | 
				
			||||||
 | 
					        self.start_frame();
 | 
				
			||||||
 | 
					        self.render_frame();
 | 
				
			||||||
 | 
					        self.end_frame();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ShaderSlot {
 | 
				
			||||||
 | 
					    shader: Option<Box<dyn Shader>>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Surface {
 | 
				
			||||||
 | 
					    slot: Rc<RefCell<ShaderSlot>>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Surface {
 | 
				
			||||||
 | 
					    fn new(slot: Rc<RefCell<ShaderSlot>>) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            slot: slot
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_shader(&mut self, shader: Box<dyn Shader>) {
 | 
				
			||||||
 | 
					        self.slot.borrow_mut().shader = Some(shader);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct EmbeddedDisplay<T>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					T: DrawTarget {
 | 
				
			||||||
 | 
					    shaders : RefCell<Vec<Rc<RefCell<ShaderSlot>>>>,
 | 
				
			||||||
 | 
					    target: T,
 | 
				
			||||||
 | 
					    total_mw: u32,
 | 
				
			||||||
 | 
					    max_mw: u32,
 | 
				
			||||||
 | 
					    fps: RealTimeRunningAverage<u32>,
 | 
				
			||||||
 | 
					    frame: u32,
 | 
				
			||||||
 | 
					    fps_display: Periodically
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> EmbeddedDisplay<T>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					T: DrawTarget {
 | 
				
			||||||
 | 
					    pub fn new(target: T, max_mw: u32) -> Self {
 | 
				
			||||||
 | 
					        EmbeddedDisplay {
 | 
				
			||||||
 | 
					            shaders: RefCell::new(Vec::new()),
 | 
				
			||||||
 | 
					            target: target,
 | 
				
			||||||
 | 
					            max_mw: max_mw,
 | 
				
			||||||
 | 
					            total_mw: 0,
 | 
				
			||||||
 | 
					            fps: RealTimeRunningAverage::default(),
 | 
				
			||||||
 | 
					            frame: 0,
 | 
				
			||||||
 | 
					            fps_display: Periodically::new_every_n_seconds(5)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T> Surfaces for EmbeddedDisplay<T>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					T: DrawTarget {
 | 
				
			||||||
 | 
					    fn new_surface(&mut self) -> Surface {
 | 
				
			||||||
 | 
					        let slot = Rc::new(RefCell::new(ShaderSlot {
 | 
				
			||||||
 | 
					            shader: None
 | 
				
			||||||
 | 
					        }));
 | 
				
			||||||
 | 
					        let surface = Surface::new(slot.clone());
 | 
				
			||||||
 | 
					        self.shaders.borrow_mut().push(slot);
 | 
				
			||||||
 | 
					        return surface;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<T: LedPixelShape> Display for EmbeddedDisplay<Ws2812DrawTarget<'_, T>> {
 | 
				
			||||||
 | 
					    fn start_frame(&mut self) {
 | 
				
			||||||
 | 
					        self.target.clear(Rgb888::BLACK).unwrap();
 | 
				
			||||||
 | 
					        self.total_mw = 0;
 | 
				
			||||||
 | 
					        self.frame = self.frame.wrapping_add(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn end_frame(&mut self) {
 | 
				
			||||||
 | 
					        let brightness = power::brightness_for_mw(self.total_mw, 255, self.max_mw);
 | 
				
			||||||
 | 
					        self.target.set_brightness(brightness);
 | 
				
			||||||
 | 
					        self.target.flush().unwrap();
 | 
				
			||||||
 | 
					        self.fps.insert(1);
 | 
				
			||||||
 | 
					        self.fps_display.run(|| {
 | 
				
			||||||
 | 
					            log::info!("FPS: {} frame={} brightness={} mw={}", self.fps.measurement(), self.frame, brightness, self.total_mw);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn render_frame(&mut self) {
 | 
				
			||||||
 | 
					        for slot in self.shaders.borrow().iter() {
 | 
				
			||||||
 | 
					            if let Some(ref shader) = slot.borrow().shader {
 | 
				
			||||||
 | 
					                for i in 0..T::size().width {
 | 
				
			||||||
 | 
					                    let coords = Point::new(i as i32, 0);
 | 
				
			||||||
 | 
					                    let color = shader.draw(coords);
 | 
				
			||||||
 | 
					                    self.total_mw += power::color_to_mw(&color);
 | 
				
			||||||
 | 
					                    Pixel(coords, Rgb888::new(color.red, color.green, color.blue)).draw(&mut self.target).unwrap();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										145
									
								
								src/task.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								src/task.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,145 @@
 | 
				
			|||||||
 | 
					use std::fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait Task {
 | 
				
			||||||
 | 
					    fn tick(&mut self) {}
 | 
				
			||||||
 | 
					    fn start(&mut self) {}
 | 
				
			||||||
 | 
					    fn stop(&mut self) {}
 | 
				
			||||||
 | 
					    fn name(&self) -> &'static str;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					trait ScheduledState: std::fmt::Debug {
 | 
				
			||||||
 | 
					    fn start(self: Box<Self>) -> Box<dyn ScheduledState>;
 | 
				
			||||||
 | 
					    fn stop(self: Box<Self>) -> Box<dyn ScheduledState>;
 | 
				
			||||||
 | 
					    fn tick(self: Box<Self>, task: &mut dyn Task) -> Box<dyn ScheduledState>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct Starting {}
 | 
				
			||||||
 | 
					impl ScheduledState for Starting {
 | 
				
			||||||
 | 
					    fn start(self: Box<Self>) -> Box<dyn ScheduledState> {
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn stop(self: Box<Self>) -> Box<dyn ScheduledState> {
 | 
				
			||||||
 | 
					        Box::new(Stopped {})
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn tick(self: Box<Self>, task: &mut dyn Task) -> Box<dyn ScheduledState> {
 | 
				
			||||||
 | 
					        task.start();
 | 
				
			||||||
 | 
					        Box::new(Running{})
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct Running {}
 | 
				
			||||||
 | 
					impl ScheduledState for Running {
 | 
				
			||||||
 | 
					    fn start(self: Box<Self>) -> Box<dyn ScheduledState> {
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn stop(self: Box<Self>) -> Box<dyn ScheduledState> {
 | 
				
			||||||
 | 
					        Box::new(Stopping {})
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn tick(self: Box<Self>, task: &mut dyn Task) -> Box<dyn ScheduledState> {
 | 
				
			||||||
 | 
					        task.tick();
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct Stopping {}
 | 
				
			||||||
 | 
					impl ScheduledState for Stopping  {
 | 
				
			||||||
 | 
					    fn start(self: Box<Self>) -> Box<dyn ScheduledState> {
 | 
				
			||||||
 | 
					        Box::new(Running {})
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn stop(self: Box<Self>) -> Box<dyn ScheduledState> {
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn tick(self: Box<Self>, task: &mut dyn Task) -> Box<dyn ScheduledState> {
 | 
				
			||||||
 | 
					        task.stop();
 | 
				
			||||||
 | 
					        Box::new(Stopped {})
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					struct Stopped {}
 | 
				
			||||||
 | 
					impl ScheduledState for Stopped  {
 | 
				
			||||||
 | 
					    fn start(self: Box<Self>) -> Box<dyn ScheduledState> {
 | 
				
			||||||
 | 
					        Box::new(Starting {})
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn stop(self: Box<Self>) -> Box<dyn ScheduledState> {
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn tick(self: Box<Self>, _task: &mut dyn Task) -> Box<dyn ScheduledState> {
 | 
				
			||||||
 | 
					        self
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ScheduledTask {
 | 
				
			||||||
 | 
					    state: Option<Box<dyn ScheduledState>>,
 | 
				
			||||||
 | 
					    task: Box<dyn Task>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl std::fmt::Debug for ScheduledTask {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        f.debug_struct("ScheduledTask")
 | 
				
			||||||
 | 
					            .field("task", &self.task.name())
 | 
				
			||||||
 | 
					            .field("state", &self.state)
 | 
				
			||||||
 | 
					            .finish()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ScheduledTask {
 | 
				
			||||||
 | 
					    fn new(task: Box<dyn Task>) -> Self {
 | 
				
			||||||
 | 
					        ScheduledTask {
 | 
				
			||||||
 | 
					            state: Some(Box::new(Starting{})),
 | 
				
			||||||
 | 
					            task: task
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn start(&mut self) {
 | 
				
			||||||
 | 
					        if let Some(s) = self.state.take() {
 | 
				
			||||||
 | 
					            self.state = Some(s.start());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn stop(&mut self) {
 | 
				
			||||||
 | 
					        if let Some(s) = self.state.take() {
 | 
				
			||||||
 | 
					            self.state = Some(s.stop());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn tick(&mut self) {
 | 
				
			||||||
 | 
					        if let Some(s) = self.state.take() {
 | 
				
			||||||
 | 
					            self.state = Some(s.tick(self.task.as_mut()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Scheduler {
 | 
				
			||||||
 | 
					    tasks: Vec<ScheduledTask>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Scheduler {
 | 
				
			||||||
 | 
					    pub fn new(tasks: Vec<Box<dyn Task>>) -> Self {
 | 
				
			||||||
 | 
					        let mut scheduled = Vec::new();
 | 
				
			||||||
 | 
					        for task in tasks {
 | 
				
			||||||
 | 
					            log::info!("Scheduling task {:?}", task.name());
 | 
				
			||||||
 | 
					            scheduled.push(ScheduledTask::new(task));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Scheduler {
 | 
				
			||||||
 | 
					            tasks: scheduled
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn tick(&mut self) {
 | 
				
			||||||
 | 
					        for task in &mut self.tasks {
 | 
				
			||||||
 | 
					            task.tick();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/time.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/time.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					use std::time::{Instant, Duration};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Periodically {
 | 
				
			||||||
 | 
					    last_run: Instant,
 | 
				
			||||||
 | 
					    duration: Duration
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Periodically {
 | 
				
			||||||
 | 
					    pub fn new(duration: Duration) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            last_run: Instant::now(),
 | 
				
			||||||
 | 
					            duration: duration
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn new_every_n_seconds(seconds: u64) -> Self {
 | 
				
			||||||
 | 
					        Self::new(Duration::new(seconds, 0))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn new_every_n_ms(milliseconds: u32) -> Self {
 | 
				
			||||||
 | 
					        Self::new(Duration::new(0, milliseconds*1000))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn run<F>(&mut self, f: F) where F: FnOnce() {
 | 
				
			||||||
 | 
					        if self.last_run.elapsed() >= self.duration {
 | 
				
			||||||
 | 
					            f();
 | 
				
			||||||
 | 
					            self.last_run = Instant::now();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user