diff --git a/src/applogic/mod.rs b/src/applogic/mod.rs index 7e7ebc4..e9ce2d1 100644 --- a/src/applogic/mod.rs +++ b/src/applogic/mod.rs @@ -106,7 +106,54 @@ pub struct GlosaSignalInfo { pub struct SignalGroupData { pub phase: SignalPhase, pub maneuver: map::Maneuvers, - pub end_sec: Option, + pub timing: Option, +} + +#[cfg(feature = "spat")] +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct TimingData { + pub min_end_sec: i16, + pub likely_end_sec: Option, + pub max_end_sec: Option, +} + +impl TimingData { + pub fn new(value: &map::SignalGroup, now: chrono::DateTime) -> Option { + let min = value + .min_end_time() + .map(|val| Self::time_to_duration_sec(now, val)); + let likely_end_sec = value + .likely_time() + .map(|val| Self::time_to_duration_sec(now, val)); + let max_end_sec = value + .min_end_time() + .map(|val| Self::time_to_duration_sec(now, val)); + + if let Some(min_end_sec) = min { + Some(Self { + min_end_sec, + likely_end_sec, + max_end_sec, + }) + } else { + None + } + } + + fn time_to_duration_sec( + now: chrono::DateTime, + time_point: chrono::DateTime, + ) -> i16 { + use core::ops::Sub as _; + + use num_traits::Float as _; + + let dur = time_point.sub(now).as_seconds_f32(); + #[allow(clippy::cast_possible_truncation)] + let duration_sec = dur.round() as i16; + + duration_sec + } } #[cfg(feature = "spat")] @@ -357,33 +404,17 @@ impl State { let sig_grps = approach_sig_grps .iter() .filter_map(|sig| { - if let Some(manv) = sig.maneuvers() { - use core::ops::Sub; - - use num_traits::Float; - + if let Some(maneuver) = sig.maneuvers() { let phase = sig .phase() .map(core::convert::Into::into) .unwrap_or_default(); - - let end_sec = sig - .likely_time() - .or_else(|| sig.min_end_time()) - .map(|time| { - let dur = - time.sub(self.time.and_utc()).as_seconds_f32(); - - #[allow(clippy::cast_possible_truncation)] - let end_sec = dur.round() as i16; - - end_sec - }); + let timing = TimingData::new(*sig, self.time.and_utc()); Some(SignalGroupData { phase, - maneuver: manv, - end_sec, + maneuver, + timing, }) } else { None diff --git a/src/applogic/v2x/map.rs b/src/applogic/v2x/map.rs index bac8a69..d7e2195 100644 --- a/src/applogic/v2x/map.rs +++ b/src/applogic/v2x/map.rs @@ -173,6 +173,12 @@ impl Intersection { { sig_grp.likely_time = Some(likely.to_datetime_from_moy(moy, year)); } + if let Some(max) = &timing.max_end_time + && !max.is_out_of_range() + && !max.is_unknown() + { + sig_grp.max_end_time = Some(max.to_datetime_from_moy(moy, year)); + } } } } else { @@ -223,6 +229,7 @@ impl Intersection { target_lanes, phase: None, min_end_time: None, + max_end_time: None, likely_time: None, } }) @@ -410,7 +417,7 @@ pub struct SignalGroup { /// current signal phase phase: Option, min_end_time: Option>, - // max_end_time: Option>, + max_end_time: Option>, likely_time: Option>, } @@ -443,6 +450,9 @@ impl SignalGroup { pub fn likely_time(&self) -> Option> { self.likely_time } + pub fn max_end_time(&self) -> Option> { + self.max_end_time + } pub fn phase_to_str(&self) -> Option { self.phase.map(|v| { diff --git a/src/main.rs b/src/main.rs index 3e5ee4a..f42fe21 100644 --- a/src/main.rs +++ b/src/main.rs @@ -207,7 +207,33 @@ async fn main(spawner: Spawner) -> ! { staight_allowed: false, right_allowed: false }, - end_sec: Some(23) + timing: Some(applogic::TimingData { + min_end_sec: 23, + likely_end_sec: None, + max_end_sec: None + }) + }], + }, + )) + .inspect_err(log_screen_error); + embassy_time::Timer::after(embassy_time::Duration::from_secs(2)).await; + + let _ = screen + .update(screen::ScreenState::GlosaLocked( + applogic::GlosaSignalInfo { + intersection_id: 1234, + signal_groups: alloc::vec![applogic::SignalGroupData { + phase: applogic::SignalPhase::Red, + maneuver: applogic::v2x::map::Maneuvers { + left_allowed: true, + staight_allowed: false, + right_allowed: false + }, + timing: Some(applogic::TimingData { + min_end_sec: 23, + likely_end_sec: Some(24), + max_end_sec: Some(42) + }) }], }, )) @@ -508,8 +534,9 @@ async fn main(spawner: Spawner) -> ! { if let applogic::GlosaOutput::Locked(data) = &glosa_data { for sig in &data.signal_groups { let duration_str = sig - .end_sec - .map(|v| alloc::format!("for {v} s")) + .timing + .as_ref() + .map(|v| alloc::format!("for {} s", v.min_end_sec)) .unwrap_or_default(); println!("GLOSA: {} {} {duration_str}", sig.maneuver, sig.phase); } diff --git a/src/screen.rs b/src/screen.rs index 3895e57..0d1dab7 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -86,6 +86,8 @@ where const DEFAULT_TEXT_STYLE: mono_font::MonoTextStyle<'static, pixelcolor::Rgb565> = mono_font::MonoTextStyle::new(&profont::PROFONT_24_POINT, Self::COLOR_FG); + const MID_TEXT_STYLE: mono_font::MonoTextStyle<'static, pixelcolor::Rgb565> = + mono_font::MonoTextStyle::new(&profont::PROFONT_18_POINT, Self::COLOR_FG); const SMALL_TEXT_STYLE: mono_font::MonoTextStyle<'static, pixelcolor::Rgb565> = mono_font::MonoTextStyle::new(&profont::PROFONT_14_POINT, Self::COLOR_FG); @@ -169,7 +171,7 @@ where self.gnss_hb_state ); - // TODO: print on screen! + // TODO: print on screen? self.gnss_hb_state = !self.gnss_hb_state; @@ -324,18 +326,39 @@ where ) .draw(&mut self.target)?; - // draw timing indicator - if let Some(end_sec) = sig.end_sec { - text::Text::with_alignment( - &alloc::format!("{end_sec}s"), - geometry::Point::new(centerline, 160 + self.size.offset_top), - Self::DEFAULT_TEXT_STYLE, - text::Alignment::Center, - ) - .draw(&mut self.target)?; + // draw timing indicator(s) + if let Some(timing) = &sig.timing { + if let (Some(likely), Some(max)) = (timing.likely_end_sec, timing.max_end_sec) { + text::Text::with_alignment( + &alloc::format!("{likely}s"), + geometry::Point::new(centerline, 160 + self.size.offset_top), + Self::DEFAULT_TEXT_STYLE, + text::Alignment::Center, + ) + .draw(&mut self.target)?; + + // TODO: only on big screen!? + text::Text::with_alignment( + &alloc::format!("{}-{max}s", timing.min_end_sec), + geometry::Point::new(centerline, 185 + self.size.offset_top), + Self::MID_TEXT_STYLE, + text::Alignment::Center, + ) + .draw(&mut self.target)?; + } else { + text::Text::with_alignment( + &alloc::format!("{}s", timing.min_end_sec), + geometry::Point::new(centerline, 160 + self.size.offset_top), + Self::DEFAULT_TEXT_STYLE, + text::Alignment::Center, + ) + .draw(&mut self.target)?; + } } } + // TODO: Add current speed and ETA + Ok(()) }