task: rewrite event handling as a step towards event-based subscriptions
This commit is contained in:
parent
f9a8b32d3e
commit
a0d524b825
@ -66,7 +66,9 @@ impl<T: Surface> IdleTask<T> {
|
||||
}
|
||||
|
||||
impl<T: Surface> Task for IdleTask<T> {
|
||||
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<T: Surface> Task for IdleTask<T> {
|
||||
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<T: Surface> TestPattern<T> {
|
||||
}
|
||||
|
||||
impl<T: Surface> Task for TestPattern<T> {
|
||||
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<T: Surface> Task for TestPattern<T> {
|
||||
});
|
||||
}
|
||||
|
||||
fn stop(&mut self) {
|
||||
fn stop(&mut self, _bus: &mut EventBus) {
|
||||
self.surface.clear_shader();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -64,19 +64,19 @@ impl<S: CoordinateSpace> Coordinates<S> {
|
||||
}
|
||||
}
|
||||
|
||||
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<Space: CoordinateSpace> {
|
||||
|
||||
impl<Space: CoordinateSpace> Rectangle<Space> {
|
||||
pub const fn new(top_left: Coordinates<Space>, bottom_right: Coordinates<Space>) -> 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::<Space>::top_left(),
|
||||
bottom_right: Coordinates::<Space>::bottom_right()
|
||||
|
35
src/main.rs
35
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);
|
||||
|
@ -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<Utc> = 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<Utc> = 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<Mutex<WifiState>>,
|
||||
last_state: WifiState,
|
||||
sys_loop: EspSystemEventLoop,
|
||||
wifi_sub: Option<EspSubscription<'static, System>>,
|
||||
ip_sub: Option<EspSubscription<'static, System>>
|
||||
ip_sub: Option<EspSubscription<'static, System>>,
|
||||
}
|
||||
|
||||
impl WifiTask {
|
||||
fn new(modem: Modem, sys_loop: EspSystemEventLoop, nvs: &EspNvsPartition<NvsDefault>) -> 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::<WifiEvent, _>( 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::<IpEvent, _>(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));
|
||||
}
|
||||
}
|
||||
|
@ -74,24 +74,23 @@ impl<T: Output, S: Surfaces> Renderer<T, S> {
|
||||
impl<T: Output, S: Surfaces> Task for Renderer<T, S> {
|
||||
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)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
111
src/scenes.rs
Normal file
111
src/scenes.rs
Normal file
@ -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<Scene>,
|
||||
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))
|
||||
}
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
85
src/task.rs
85
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::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
#[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<dyn Task>) -> 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<const TASK_COUNT: usize> {
|
||||
tasks: [Option<ScheduledTask>; TASK_COUNT],
|
||||
tasks: [ScheduledTask; TASK_COUNT],
|
||||
}
|
||||
|
||||
impl<const TASK_COUNT: usize> FixedSizeScheduler<TASK_COUNT> {
|
||||
pub fn new(tasks: [Box<dyn Task>; TASK_COUNT]) -> Self {
|
||||
let mut scheduled = [const { None }; TASK_COUNT];
|
||||
pub fn new(tasks: [Box<dyn Task>; 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<const TASK_COUNT: usize> Scheduler for FixedSizeScheduler<TASK_COUNT> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user