ui: split out safety ui into its own dedicated set of layers
This commit is contained in:
@@ -25,6 +25,7 @@ wokwi = ["max-usb-power"]
|
||||
headless = []
|
||||
mpu = ["dep:mpu6050-dmp"]
|
||||
gps = ["dep:nmea"]
|
||||
oled = []
|
||||
demo = []
|
||||
|
||||
[dependencies]
|
||||
@@ -137,3 +138,4 @@ overflow-checks = false
|
||||
|
||||
[build-dependencies]
|
||||
image = "0.25.8"
|
||||
rmp = "0.8"
|
||||
8
build.rs
8
build.rs
@@ -3,6 +3,7 @@ use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use image::GenericImageView;
|
||||
use rmp;
|
||||
|
||||
fn main() {
|
||||
linker_be_nice();
|
||||
@@ -54,6 +55,13 @@ fn main() {
|
||||
println!("cargo::rerun-if-changed={fname_str}");
|
||||
}
|
||||
}
|
||||
|
||||
/*let test_data_path = Path::new("test-data");
|
||||
let gps_data = File::open(test_data_path.join("LocationGps.csv")).unwrap();
|
||||
let accel_data = File::open(test_data_path.join("AccelerometerUncalibrated.csv")).unwrap();
|
||||
let gyro_data = File::open(test_data_path.join("GyroscopeUncalibrated.csv")).unwrap();
|
||||
let mut test_data_output = File::create(Path::new("target/test_data.rs")).unwrap();*/
|
||||
|
||||
}
|
||||
|
||||
fn linker_be_nice() {
|
||||
|
||||
@@ -11,6 +11,7 @@ use core::ptr::addr_of_mut;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Instant, Timer};
|
||||
|
||||
use esp_hal::{gpio::{Output, OutputConfig}, peripherals, time::Rate};
|
||||
#[allow(unused_imports)]
|
||||
use esp_hal::{
|
||||
clock::CpuClock, interrupt::software::SoftwareInterruptControl, system::{AppCoreGuard, CpuControl, Stack}, timer::{systimer::SystemTimer, timg::{TimerGroup, Wdt}},
|
||||
@@ -19,7 +20,7 @@ use esp_hal::{
|
||||
|
||||
use esp_hal_embassy::{Executor, InterruptExecutor};
|
||||
use log::*;
|
||||
use renderbug_embassy::{logging::RenderbugLogger, tasks::ui::UiSurfacePool};
|
||||
use renderbug_embassy::{logging::RenderbugLogger, tasks::{safetyui::{safety_ui_main, SafetyUi}, ui::UiSurfacePool}};
|
||||
use renderbug_embassy::events::BusGarage;
|
||||
use static_cell::StaticCell;
|
||||
use esp_backtrace as _;
|
||||
@@ -45,7 +46,6 @@ static CORE_HANDLE: StaticCell<AppCoreGuard> = StaticCell::new();
|
||||
async fn main(spawner: Spawner) {
|
||||
critical_section::with(|_| {
|
||||
RenderbugLogger::init_logger();
|
||||
//esp_println::logger::init_logger_from_env();
|
||||
});
|
||||
|
||||
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
|
||||
@@ -62,13 +62,15 @@ async fn main(spawner: Spawner) {
|
||||
let timer1 = TimerGroup::new(peripherals.TIMG1);
|
||||
let mut ui_wdt = timer1.wdt;
|
||||
ui_wdt.set_timeout(esp_hal::timer::timg::MwdtStage::Stage0, esp_hal::time::Duration::from_secs(10));
|
||||
//ui_wdt.enable(); //FIXME: Re-enable UI watchdog once we have a brain task running
|
||||
ui_wdt.enable(); //FIXME: Re-enable UI watchdog once we have a brain task running
|
||||
|
||||
let garage = BUS_GARAGE.init(Default::default());
|
||||
|
||||
info!("Setting up rendering pipeline");
|
||||
let mut surfaces = UiSurfacePool::default();
|
||||
let ui = Ui::new(&mut surfaces, garage.display.clone());
|
||||
let ui = Ui::new(&mut surfaces);
|
||||
let mut safety_surfaces = UiSurfacePool::default();
|
||||
let safety_ui = SafetyUi::new(&mut safety_surfaces, garage.display.clone());
|
||||
|
||||
let mut wdt = timer0.wdt;
|
||||
wdt.set_timeout(esp_hal::timer::timg::MwdtStage::Stage0, esp_hal::time::Duration::from_secs(3));
|
||||
@@ -80,7 +82,7 @@ async fn main(spawner: Spawner) {
|
||||
#[cfg(not(feature="headless"))]
|
||||
{
|
||||
wdt.enable();
|
||||
hi_spawn.must_spawn(renderbug_embassy::tasks::render::render(peripherals.RMT, peripherals.GPIO5.degrade(), surfaces, garage.display.clone(), wdt));
|
||||
hi_spawn.must_spawn(renderbug_embassy::tasks::render::render(peripherals.RMT, peripherals.GPIO5.degrade(), surfaces, safety_surfaces, garage.display.clone(), wdt));
|
||||
}
|
||||
#[cfg(feature="headless")]
|
||||
garage.display.notify_render_is_running(true);
|
||||
@@ -104,6 +106,26 @@ async fn main(spawner: Spawner) {
|
||||
spawner.must_spawn(renderbug_embassy::tasks::gps::gps_task(garage.motion.dyn_sender(), I2cDevice::new(i2c_bus)));
|
||||
}
|
||||
|
||||
#[cfg(feature="oled")]
|
||||
{
|
||||
use esp_hal::i2c::master::{Config, I2c};
|
||||
|
||||
let mut rst = Output::new(peripherals.GPIO21, esp_hal::gpio::Level::Low, OutputConfig::default());
|
||||
Timer::after_millis(10).await;
|
||||
rst.set_high();
|
||||
Timer::after_millis(10).await;
|
||||
rst.set_low();
|
||||
Timer::after_millis(10).await;
|
||||
rst.set_high();
|
||||
|
||||
let i2c = I2c::new(
|
||||
peripherals.I2C0,
|
||||
Config::default().with_frequency(Rate::from_khz(400))
|
||||
).unwrap().with_scl(peripherals.GPIO18).with_sda(peripherals.GPIO17).into_async();
|
||||
|
||||
spawner.must_spawn(renderbug_embassy::tasks::oled::oled_task(i2c, garage.telemetry.dyn_subscriber().unwrap()));
|
||||
}
|
||||
|
||||
#[cfg(feature="simulation")]
|
||||
{
|
||||
spawner.must_spawn(renderbug_embassy::tasks::simulation::motion_simulation_task(garage.motion.dyn_sender()));
|
||||
@@ -111,7 +133,7 @@ async fn main(spawner: Spawner) {
|
||||
}
|
||||
|
||||
info!("Launching motion engine");
|
||||
spawner.must_spawn(motion_task(garage.motion.dyn_receiver(), garage.notify.dyn_sender(), garage.predict.dyn_sender()));
|
||||
spawner.must_spawn(motion_task(garage.motion.dyn_receiver(), garage.notify.dyn_publisher().unwrap(), garage.predict.dyn_sender()));
|
||||
|
||||
info!("Starting core 2");
|
||||
let mut cpu_control = CpuControl::new(peripherals.CPU_CTRL);
|
||||
@@ -122,19 +144,21 @@ async fn main(spawner: Spawner) {
|
||||
#[cfg(feature="radio")]
|
||||
{
|
||||
info!("Launching wifi");
|
||||
spawner.must_spawn(renderbug_embassy::tasks::wifi::wireless_task(garage.notify.dyn_receiver(), timer0.timer0.into(), peripherals.RNG, peripherals.WIFI, peripherals.BT));
|
||||
//spawner.must_spawn(renderbug_embassy::tasks::wifi::wireless_task(garage.notify.dyn_receiver().unwrap(), timer0.timer0.into(), peripherals.RNG, peripherals.WIFI, peripherals.BT));
|
||||
}
|
||||
info!("Launching Safety UI");
|
||||
spawner.must_spawn(safety_ui_main(garage.notify.dyn_subscriber().unwrap(), safety_ui));
|
||||
info!("Launching UI");
|
||||
spawner.must_spawn(ui_main(garage.notify.dyn_receiver(), ui));
|
||||
spawner.must_spawn(ui_main(garage.notify.dyn_subscriber().unwrap(), garage.telemetry.dyn_publisher().unwrap(), ui));
|
||||
#[cfg(feature="demo")]
|
||||
{
|
||||
warn!("Launching with demo sequencer");
|
||||
spawner.must_spawn(renderbug_embassy::tasks::demo::demo_task(garage.notify.dyn_sender()));
|
||||
spawner.must_spawn(renderbug_embassy::tasks::demo::demo_task(garage.notify.dyn_publisher().unwrap()));
|
||||
}
|
||||
#[cfg(not(feature="demo"))]
|
||||
{
|
||||
info!("Launching prediction engine");
|
||||
spawner.must_spawn(renderbug_embassy::tasks::predict::prediction_task(garage.predict.dyn_receiver(), garage.notify.dyn_sender()));
|
||||
spawner.must_spawn(renderbug_embassy::tasks::predict::prediction_task(garage.predict.dyn_receiver(), garage.notify.dyn_publisher().unwrap(), garage.telemetry.dyn_publisher().unwrap()));
|
||||
}
|
||||
info!("Launching core 2 watchdog");
|
||||
spawner.must_spawn(wdt_task(ui_wdt));
|
||||
|
||||
@@ -10,4 +10,6 @@ pub mod wifi;
|
||||
#[cfg(feature="simulation")]
|
||||
pub mod simulation;
|
||||
pub mod predict;
|
||||
pub mod demo;
|
||||
pub mod demo;
|
||||
pub mod safetyui;
|
||||
pub mod oled;
|
||||
136
src/tasks/safetyui.rs
Normal file
136
src/tasks/safetyui.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
use embassy_sync::{channel::DynamicReceiver, pubsub::DynSubscriber};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use figments::prelude::*;
|
||||
use figments_render::output::Brightness;
|
||||
use rgb::{Rgb, Rgba};
|
||||
use log::*;
|
||||
use alloc::sync::Arc;
|
||||
use core::fmt::Debug;
|
||||
use futures::join;
|
||||
|
||||
use crate::{animation::{AnimDisplay, AnimatedSurface, Animation}, display::{DisplayControls, SegmentSpace, Uniforms}, events::{Notification, Scene, SensorSource}, shaders::*, tasks::ui::UiSurfacePool};
|
||||
|
||||
pub struct SafetyUi<S: Surface> {
|
||||
// Headlight and brakelight layers can only be overpainted by the bootsplash overlay layer
|
||||
headlight: AnimatedSurface<S>,
|
||||
brakelight: AnimatedSurface<S>,
|
||||
|
||||
// The overlay covers everything and is used to implement a power-on and power-off animation.
|
||||
overlay: AnimatedSurface<S>,
|
||||
display: DisplayControls
|
||||
}
|
||||
|
||||
impl<S: Debug + Surface<Uniforms = Uniforms, CoordinateSpace = SegmentSpace, Pixel = Rgba<u8>>> SafetyUi<S> {
|
||||
pub fn new<SS: Surfaces<SegmentSpace, Surface = S>>(surfaces: &mut SS, display: DisplayControls) -> Self where SS::Error: Debug {
|
||||
Self {
|
||||
overlay: SurfaceBuilder::build(surfaces)
|
||||
.rect(Rectangle::everything())
|
||||
.shader(Thinking::default())
|
||||
.visible(false)
|
||||
.finish().unwrap().into(),
|
||||
headlight: SurfaceBuilder::build(surfaces)
|
||||
.rect(Rectangle::new_from_coordinates(0, 0, 255, 0))
|
||||
.shader(Headlight::default())
|
||||
.visible(false)
|
||||
.opacity(0)
|
||||
.finish().unwrap().into(),
|
||||
brakelight: SurfaceBuilder::build(surfaces)
|
||||
.rect(Brakelight::rectangle())
|
||||
.shader(Brakelight::default())
|
||||
.visible(false)
|
||||
.opacity(0)
|
||||
.finish().unwrap().into(),
|
||||
display
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn sleep(&mut self) {
|
||||
info!("Running sleep sequence");
|
||||
let fade_out = Animation::default().duration(Duration::from_secs(1)).from(255).to(0);
|
||||
let mut disp_anim = AnimDisplay(&mut self.display);
|
||||
fade_out.apply(&mut disp_anim).await;
|
||||
|
||||
warn!("Resetting safety lights");
|
||||
self.brakelight.set_visible(false);
|
||||
self.headlight.set_visible(false);
|
||||
|
||||
warn!("Turning off display");
|
||||
self.display.set_on(false);
|
||||
// Wait for the display hardware to actually turn off, before we return to process the next event, which could cause funky behaviors.
|
||||
// FIXME: also deadlocks :(
|
||||
//self.display.render_is_running.wait().await;
|
||||
}
|
||||
|
||||
pub async fn wake(&mut self) {
|
||||
info!("Running startup fade sequence");
|
||||
|
||||
info!("Turning on display");
|
||||
// Turn on the display hardware
|
||||
self.display.set_brightness(0);
|
||||
self.display.set_on(true);
|
||||
// Wait for the renderer to start running again
|
||||
// FIXME: This deadlocks :(
|
||||
//self.display.render_is_running.wait().await;
|
||||
|
||||
let fade_in = Animation::default().duration(Duration::from_secs(3)).from(0).to(255);
|
||||
info!("Fading in brightness with overlay");
|
||||
self.overlay.set_opacity(255);
|
||||
self.overlay.set_visible(true);
|
||||
fade_in.apply(&mut AnimDisplay(&mut self.display)).await;
|
||||
|
||||
warn!("Turning on safety lights");
|
||||
self.headlight.set_opacity(0);
|
||||
self.headlight.set_visible(true);
|
||||
self.brakelight.set_opacity(0);
|
||||
self.brakelight.set_visible(true);
|
||||
join!(
|
||||
fade_in.apply(&mut self.headlight),
|
||||
fade_in.apply(&mut self.brakelight)
|
||||
);
|
||||
|
||||
let fade_out = Animation::default().duration(Duration::from_secs(1)).from(255).to(0);
|
||||
info!("Fade out overlay");
|
||||
fade_out.apply(&mut self.overlay).await;
|
||||
self.overlay.set_visible(false);
|
||||
Timer::after_secs(3).await;
|
||||
warn!("Turning off safety lights");
|
||||
join!(
|
||||
fade_out.apply(&mut self.headlight),
|
||||
fade_out.apply(&mut self.brakelight)
|
||||
);
|
||||
info!("Wakeup complete!");
|
||||
}
|
||||
|
||||
pub async fn on_event(&mut self, event: Notification) {
|
||||
match event {
|
||||
Notification::SceneChange(_) => (), // We already log this inside apply_scene()
|
||||
evt => info!("SafetyUI event: {evt:?}")
|
||||
}
|
||||
|
||||
match event {
|
||||
// Toggling head and brake lights
|
||||
// FIXME: These should be a Off/Low/High enum, so the stopping brake looks different from the dayrunning brake.
|
||||
Notification::SetBrakelight(is_on) => self.brakelight.set_on(is_on).await,
|
||||
Notification::SetHeadlight(is_on) => self.headlight.set_on(is_on).await,
|
||||
|
||||
Notification::Sleep => self.sleep().await,
|
||||
Notification::WakeUp => self.wake().await,
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn safety_ui_main(mut events: DynSubscriber<'static, Notification>, mut ui: SafetyUi<<UiSurfacePool as Surfaces<SegmentSpace>>::Surface>) {
|
||||
// Wait for the renderer to start running
|
||||
//ui.display.render_is_running.wait().await;
|
||||
ui.display.wait_until_render_is_running().await;
|
||||
|
||||
// Run the wake sequence, and turn on the lights
|
||||
ui.wake().await;
|
||||
|
||||
// Enter the event loop
|
||||
loop {
|
||||
ui.on_event(events.next_message_pure().await).await;
|
||||
}
|
||||
}
|
||||
155
src/tasks/ui.rs
155
src/tasks/ui.rs
@@ -1,5 +1,5 @@
|
||||
use embassy_sync::channel::DynamicReceiver;
|
||||
use embassy_time::Duration;
|
||||
use embassy_sync::{channel::{DynamicReceiver, DynamicSender}, pubsub::{DynPublisher, DynSubscriber}};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use figments::prelude::*;
|
||||
use rgb::{Rgb, Rgba};
|
||||
use log::*;
|
||||
@@ -7,7 +7,7 @@ use alloc::sync::Arc;
|
||||
use core::fmt::Debug;
|
||||
use futures::join;
|
||||
|
||||
use crate::{animation::{AnimDisplay, AnimatedSurface, Animation}, display::{SegmentSpace, Uniforms}, events::{DisplayControls, Notification, Scene, SensorSource}, shaders::*};
|
||||
use crate::{animation::{AnimDisplay, AnimatedSurface, Animation}, display::{SegmentSpace, Uniforms}, events::{Notification, Scene, SensorSource, Telemetry}, shaders::*};
|
||||
|
||||
pub struct Ui<S: Surface> {
|
||||
// Background layer provides an always-running background for everything to draw on
|
||||
@@ -21,18 +21,10 @@ pub struct Ui<S: Surface> {
|
||||
|
||||
// Notification layer sits on top of the content, and is used for transient event notifications (gps lost, wifi found, etc)
|
||||
notification: AnimatedSurface<S>,
|
||||
|
||||
// Headlight and brakelight layers can only be overpainted by the bootsplash overlay layer
|
||||
headlight: AnimatedSurface<S>,
|
||||
brakelight: AnimatedSurface<S>,
|
||||
|
||||
// The overlay covers everything and is used to implement a power-on and power-off animation.
|
||||
overlay: AnimatedSurface<S>,
|
||||
display: Arc<DisplayControls>
|
||||
}
|
||||
|
||||
impl<S: Debug + Surface<Uniforms = Uniforms, CoordinateSpace = SegmentSpace, Pixel = Rgba<u8>>> Ui<S> {
|
||||
pub fn new<SS: Surfaces<SegmentSpace, Surface = S>>(surfaces: &mut SS, display: Arc<DisplayControls>) -> Self where SS::Error: Debug {
|
||||
pub fn new<SS: Surfaces<SegmentSpace, Surface = S>>(surfaces: &mut SS) -> Self where SS::Error: Debug {
|
||||
Self {
|
||||
background: SurfaceBuilder::build(surfaces)
|
||||
.rect(Rectangle::everything())
|
||||
@@ -58,24 +50,7 @@ impl<S: Debug + Surface<Uniforms = Uniforms, CoordinateSpace = SegmentSpace, Pix
|
||||
.rect(Rectangle::everything())
|
||||
.shader(Background::default())
|
||||
.visible(false)
|
||||
.finish().unwrap().into(),
|
||||
// FIXME: Headlight, brakelight, and the overlay should all be handled as part of the safety UI
|
||||
headlight: SurfaceBuilder::build(surfaces)
|
||||
.rect(Rectangle::new_from_coordinates(0, 0, 255, 0))
|
||||
.shader(Headlight::default())
|
||||
.visible(false)
|
||||
.finish().unwrap().into(),
|
||||
brakelight: SurfaceBuilder::build(surfaces)
|
||||
.rect(Brakelight::rectangle())
|
||||
.shader(Brakelight::default())
|
||||
.visible(false)
|
||||
.finish().unwrap().into(),
|
||||
overlay: SurfaceBuilder::build(surfaces)
|
||||
.rect(Rectangle::everything())
|
||||
.shader(Thinking::default())
|
||||
.visible(false)
|
||||
.finish().unwrap().into(),
|
||||
display
|
||||
.finish().unwrap().into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,71 +77,18 @@ impl<S: Debug + Surface<Uniforms = Uniforms, CoordinateSpace = SegmentSpace, Pix
|
||||
self.notification.set_visible(false);
|
||||
}
|
||||
|
||||
pub async fn sleep(&mut self) {
|
||||
info!("Running sleep sequence");
|
||||
let fade_out = Animation::default().duration(Duration::from_secs(1)).from(255).to(0);
|
||||
let mut disp_anim = AnimDisplay(&self.display);
|
||||
fade_out.apply(&mut disp_anim).await;
|
||||
|
||||
// Reset layers to the initial state now that the display should be off
|
||||
pub fn as_slice(&mut self) -> [&mut S; 4] {
|
||||
[
|
||||
&mut *self.tail,
|
||||
&mut *self.panels,
|
||||
&mut *self.background,
|
||||
&mut *self.motion
|
||||
].set_opacity(0);
|
||||
[
|
||||
&mut *self.tail,
|
||||
&mut *self.panels,
|
||||
&mut *self.background,
|
||||
&mut *self.motion
|
||||
].set_visible(false);
|
||||
|
||||
info!("Turning off display");
|
||||
self.display.set_on(false);
|
||||
// Wait for the display hardware to actually turn off, before we return to process the next event, which could cause funky behaviors.
|
||||
// FIXME: also deadlocks :(
|
||||
//self.display.render_is_running.wait().await;
|
||||
info!("Display is now sleeping.");
|
||||
]
|
||||
}
|
||||
|
||||
pub async fn wakeup(&mut self) {
|
||||
info!("Running startup fade sequence");
|
||||
|
||||
info!("Turning on display");
|
||||
// Turn on the display hardware
|
||||
self.display.set_brightness(0);
|
||||
self.display.set_on(true);
|
||||
// Wait for the renderer to start running again
|
||||
// FIXME: This deadlocks :(
|
||||
//self.display.render_is_running.wait().await;
|
||||
|
||||
let fade_in = Animation::default().duration(Duration::from_secs(3)).from(0).to(255);
|
||||
let fade_out = Animation::default().duration(Duration::from_secs(1)).from(255).to(0);
|
||||
let mut disp_anim = AnimDisplay(&self.display);
|
||||
info!("Fade in overlay");
|
||||
self.overlay.set_visible(true);
|
||||
join!(
|
||||
fade_in.apply(&mut self.overlay),
|
||||
fade_in.apply(&mut disp_anim)
|
||||
);
|
||||
|
||||
pub async fn show(&mut self) {
|
||||
info!("Flipping on surfaces");
|
||||
// Flip on all the layers
|
||||
[
|
||||
&mut *self.panels,
|
||||
&mut *self.tail,
|
||||
&mut *self.background,
|
||||
&mut *self.motion
|
||||
].set_visible(true);
|
||||
|
||||
// Enter the startup scene
|
||||
self.apply_scene(Scene::Ready).await;
|
||||
|
||||
info!("Fade out overlay");
|
||||
fade_out.apply(&mut self.overlay).await;
|
||||
self.overlay.set_visible(false);
|
||||
info!("Wakeup complete!");
|
||||
self.as_slice().set_visible(true);
|
||||
}
|
||||
|
||||
// TODO: Brakelight should only be toggled when actually braking or stationary
|
||||
@@ -225,18 +147,13 @@ impl<S: Debug + Surface<Uniforms = Uniforms, CoordinateSpace = SegmentSpace, Pix
|
||||
// Scene change
|
||||
Notification::SceneChange(scene) => self.apply_scene(scene).await,
|
||||
|
||||
// Toggling head and brake lights
|
||||
Notification::SetBrakelight(is_on) => self.brakelight.set_on(is_on).await,
|
||||
Notification::SetHeadlight(is_on) => self.headlight.set_on(is_on).await,
|
||||
|
||||
Notification::Sleep => self.sleep().await,
|
||||
Notification::WakeUp => self.wakeup().await,
|
||||
|
||||
Notification::SensorsOffline => {
|
||||
self.flash_notification_color(Rgb::new(255, 0, 0)).await;
|
||||
self.flash_notification_color(Rgb::new(0, 255, 0)).await;
|
||||
self.flash_notification_color(Rgb::new(0, 0, 255)).await;
|
||||
}
|
||||
|
||||
Notification::WakeUp => self.show().await,
|
||||
_ => ()
|
||||
|
||||
// Other event ideas:
|
||||
@@ -252,24 +169,56 @@ impl<S: Debug + Surface<Uniforms = Uniforms, CoordinateSpace = SegmentSpace, Pix
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Debug + Surface<Uniforms = Uniforms, CoordinateSpace = SegmentSpace, Pixel = Rgba<u8>>> Surface for Ui<S> {
|
||||
type Uniforms = S::Uniforms;
|
||||
|
||||
type CoordinateSpace = S::CoordinateSpace;
|
||||
|
||||
type Pixel = S::Pixel;
|
||||
|
||||
fn set_shader<T: Shader<Self::Uniforms, Self::CoordinateSpace, Self::Pixel> + 'static>(&mut self, shader: T) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn clear_shader(&mut self) {
|
||||
self.as_slice().clear_shader();
|
||||
}
|
||||
|
||||
fn set_rect(&mut self, rect: Rectangle<Self::CoordinateSpace>) {
|
||||
self.as_slice().set_rect(rect);
|
||||
}
|
||||
|
||||
fn set_opacity(&mut self, opacity: u8) {
|
||||
self.as_slice().set_opacity(opacity);
|
||||
}
|
||||
|
||||
fn set_visible(&mut self, visible: bool) {
|
||||
self.as_slice().set_visible(visible);
|
||||
}
|
||||
|
||||
fn set_offset(&mut self, offset: Coordinates<Self::CoordinateSpace>) {
|
||||
self.as_slice().set_offset(offset);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="headless")]
|
||||
pub type UiSurfacePool = NullBufferPool<NullSurface<Uniforms, SegmentSpace, Rgba<u8>>>;
|
||||
#[cfg(not(feature="headless"))]
|
||||
pub type UiSurfacePool = BufferedSurfacePool<Uniforms, SegmentSpace, Rgba<u8>>;
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn ui_main(events: DynamicReceiver<'static, Notification>, mut ui: Ui<<UiSurfacePool as Surfaces<SegmentSpace>>::Surface>) {
|
||||
// Wait for the renderer to start running
|
||||
//ui.display.render_is_running.wait().await;
|
||||
pub async fn ui_main(mut events: DynSubscriber<'static, Notification>, telemetery: DynPublisher<'static, Telemetry>, mut ui: Ui<<UiSurfacePool as Surfaces<SegmentSpace>>::Surface>) {
|
||||
// FIXME: This should instead wait on some kind of flag set by the safety UI, or else we risk painting before we even have a display up and running
|
||||
Timer::after_secs(3).await;
|
||||
ui.show().await;
|
||||
|
||||
// Run the wake sequence, and turn on the lights
|
||||
ui.wakeup().await;
|
||||
// FIXME: Moving the safety lights into another task will let us turn them both on in parallel with the wakeup sequence
|
||||
ui.headlight.set_on(true).await;
|
||||
ui.brakelight.set_on(true).await;
|
||||
// Enter the startup scene
|
||||
ui.apply_scene(Scene::Ready).await;
|
||||
|
||||
// Enter the event loop
|
||||
loop {
|
||||
ui.on_event(events.receive().await).await;
|
||||
let evt = events.next_message_pure().await;
|
||||
ui.on_event(evt).await;
|
||||
telemetery.publish(Telemetry::Notification(evt)).await;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user