denm: Show warning symbol
This commit is contained in:
parent
dc54b55230
commit
33a90864a2
10 changed files with 271 additions and 11 deletions
|
|
@ -1,13 +1,14 @@
|
|||
//! C-ITS DENM Evaluation
|
||||
|
||||
use c_its_parser::standards::cdd_2_2_1::etsi_its_cdd;
|
||||
use c_its_parser::standards::denm_2_2_1::denm_pdu_description;
|
||||
use log::info;
|
||||
|
||||
pub fn handle_denm(etsi: &denm_pdu_description::DENM) {
|
||||
pub fn handle_denm(etsi: &denm_pdu_description::DENM) -> Option<etsi_its_cdd::CauseCodeChoice> {
|
||||
let mgmt = &etsi.denm.management;
|
||||
|
||||
if mgmt.termination.is_some() {
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
if let (Some(sit), Some(loc)) = (&etsi.denm.situation, &etsi.denm.location) {
|
||||
|
|
@ -38,7 +39,11 @@ pub fn handle_denm(etsi: &denm_pdu_description::DENM) {
|
|||
"DENN: {station_id}/{seq_num} CC {cc}/{scc} at {lat_deg:.3}°N, {lon_deg:.3}°E until {end_time:?}"
|
||||
);
|
||||
}
|
||||
|
||||
Some(sit.event_type.cc_and_scc.clone())
|
||||
} else {
|
||||
info!("DENM: One of situation or location container not available");
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
|||
21
src/main.rs
21
src/main.rs
|
|
@ -401,7 +401,16 @@ async fn main(spawner: Spawner) -> ! {
|
|||
}
|
||||
|
||||
// update GNSS HB on screen
|
||||
let _ = screen.update_gnss(pvt);
|
||||
#[cfg(feature = "screen")]
|
||||
{
|
||||
let _ = screen.update_gnss(pvt);
|
||||
|
||||
// revert to waiting for DENMs, if SPAT is not enabled
|
||||
#[cfg(all(not(feature = "spat"), feature = "denm"))]
|
||||
let _ = screen
|
||||
.update(screen::ScreenState::DenmWaiting())
|
||||
.inspect_err(log_screen_error);
|
||||
}
|
||||
}
|
||||
ublox::proto27::PacketRef::AckNak(nak) => {
|
||||
error!("UBX: {nak:?}");
|
||||
|
|
@ -591,7 +600,15 @@ async fn main(spawner: Spawner) -> ! {
|
|||
transport: _,
|
||||
ref etsi,
|
||||
} => {
|
||||
applogic::denm::handle_denm(etsi);
|
||||
match applogic::denm::handle_denm(etsi) {
|
||||
#[cfg(feature = "screen")]
|
||||
Some(cc) => {
|
||||
let _ = screen
|
||||
.update(screen::ScreenState::DenmWarning(cc))
|
||||
.inspect_err(log_screen_error);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Send data to BLE thread
|
||||
#[cfg(feature = "ble")]
|
||||
|
|
|
|||
173
src/screen.rs
173
src/screen.rs
|
|
@ -1,11 +1,16 @@
|
|||
//! Screen Output
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
use c_its_parser::standards::cdd_2_2_1;
|
||||
use embedded_graphics::prelude::*;
|
||||
use embedded_graphics::{geometry, mono_font, pixelcolor, primitives, text};
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
use crate::applogic;
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
const DENM_MIN_DISPLAY_TIME: esp_hal::time::Duration = esp_hal::time::Duration::from_secs(3);
|
||||
|
||||
pub struct DisplaySize {
|
||||
width: u16,
|
||||
height: u16,
|
||||
|
|
@ -24,10 +29,20 @@ where
|
|||
prev_state: ScreenState,
|
||||
hb_state: bool,
|
||||
gnss_hb_state: bool,
|
||||
#[cfg(feature = "denm")]
|
||||
denm_display_start_time: esp_hal::time::Instant,
|
||||
|
||||
// assets
|
||||
#[cfg(feature = "spat")]
|
||||
bike_image: tinytga::Tga<'static, pixelcolor::Rgb565>,
|
||||
bike_image: tinytga::Tga<'static, pixelcolor::Rgb565>, // 45x25px
|
||||
// #[cfg(feature = "denm")]
|
||||
// warn_icon: tinytga::Tga<'static, pixelcolor::Rgb565>, // 200x153 px
|
||||
#[cfg(feature = "denm")]
|
||||
sv_insert: tinytga::Tga<'static, pixelcolor::Rgb565>, // 80px wide (original 140x70)
|
||||
#[cfg(feature = "denm")]
|
||||
ev_insert: tinytga::Tga<'static, pixelcolor::Rgb565>, // 60px wide (original 140x105)
|
||||
#[cfg(feature = "denm")]
|
||||
rww_insert: tinytga::Tga<'static, pixelcolor::Rgb565>, // 80px wide (original 140x95)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
|
|
@ -43,6 +58,10 @@ pub enum ScreenState {
|
|||
GlosaFound(applogic::GlosaFound),
|
||||
#[cfg(feature = "spat")]
|
||||
GlosaLocked(applogic::GlosaSignalInfo),
|
||||
#[cfg(all(not(feature = "spat"), feature = "denm"))]
|
||||
DenmWaiting(),
|
||||
#[cfg(feature = "denm")]
|
||||
DenmWarning(cdd_2_2_1::etsi_its_cdd::CauseCodeChoice),
|
||||
}
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
|
|
@ -101,9 +120,23 @@ where
|
|||
|
||||
pub fn new(target: D, display_size: DisplaySize) -> Self {
|
||||
#[cfg(feature = "spat")]
|
||||
let data = include_bytes!("../graphics/Sinnbild_Radfahrer_StVO_1992-25px-white.tga");
|
||||
#[cfg(feature = "spat")]
|
||||
let bike_image = tinytga::Tga::from_slice(data).expect("Failed to parse TGA image");
|
||||
let bike_image = tinytga::Tga::from_slice(include_bytes!(
|
||||
"../graphics/Sinnbild_Radfahrer_StVO_1992-25px-white.tga"
|
||||
))
|
||||
.expect("Failed to parse TGA image");
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
let sv_insert =
|
||||
tinytga::Tga::from_slice(include_bytes!("../graphics/Stationary_Vehicle-80px.tga"))
|
||||
.expect("Failed to parse TGA image");
|
||||
#[cfg(feature = "denm")]
|
||||
let ev_insert = tinytga::Tga::from_slice(include_bytes!(
|
||||
"../graphics/Stationary_Emergency_Vehicle-70px.tga"
|
||||
))
|
||||
.expect("Failed to parse TGA image");
|
||||
#[cfg(feature = "denm")]
|
||||
let rww_insert = tinytga::Tga::from_slice(include_bytes!("../graphics/Work_Zone-80px.tga"))
|
||||
.expect("Failed to parse TGA image");
|
||||
|
||||
Self {
|
||||
target,
|
||||
|
|
@ -111,15 +144,53 @@ where
|
|||
prev_state: ScreenState::Uninitialized,
|
||||
hb_state: false,
|
||||
gnss_hb_state: false,
|
||||
#[cfg(feature = "denm")]
|
||||
denm_display_start_time: esp_hal::time::Instant::EPOCH,
|
||||
#[cfg(feature = "spat")]
|
||||
bike_image,
|
||||
// #[cfg(feature = "denm")]
|
||||
// warn_icon,
|
||||
#[cfg(feature = "denm")]
|
||||
sv_insert,
|
||||
#[cfg(feature = "denm")]
|
||||
ev_insert,
|
||||
#[cfg(feature = "denm")]
|
||||
rww_insert,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, state: ScreenState) -> Result<(), D::Error> {
|
||||
#[cfg(feature = "denm")]
|
||||
let now = esp_hal::time::Instant::now();
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
// when a DENM was shown before, make sure it's shown for a minimum time
|
||||
if let ScreenState::DenmWarning(_) = self.prev_state {
|
||||
use core::ops::Sub;
|
||||
|
||||
// update DENM display, if there are updates
|
||||
if let ScreenState::DenmWarning(cause_code_choice) = &state {
|
||||
// TODO: find a way to immediately turn off the DENM display when the DENM is over, but keep it for a certain time initially
|
||||
self.denm_display_start_time = now;
|
||||
|
||||
if self.prev_state != state {
|
||||
self.draw_denm_warning(cause_code_choice)?;
|
||||
self.prev_state = state;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise early return when timer isn't expired yet
|
||||
if now.sub(self.denm_display_start_time) < DENM_MIN_DISPLAY_TIME {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if self.prev_state != state {
|
||||
match &state {
|
||||
ScreenState::Uninitialized | ScreenState::Initial => self.draw_slash_screen()?,
|
||||
|
||||
#[cfg(feature = "spat")]
|
||||
ScreenState::GlosaSearching(_glosa_searching) => self.draw_glosa_searching()?,
|
||||
#[cfg(feature = "spat")]
|
||||
|
|
@ -136,10 +207,18 @@ where
|
|||
self.draw_glosa(glosa_data, prev_glosa_data)?;
|
||||
}
|
||||
ScreenState::Error(msg) => self.draw_message(msg)?,
|
||||
#[cfg(all(not(feature = "spat"), feature = "denm"))]
|
||||
ScreenState::DenmWaiting() => self.draw_denm_waiting()?,
|
||||
#[cfg(feature = "denm")]
|
||||
ScreenState::DenmWarning(cause_code_choice) => {
|
||||
self.denm_display_start_time = now;
|
||||
self.draw_denm_warning(cause_code_choice)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update indicator/ heartbeat
|
||||
#[cfg(feature = "spat")]
|
||||
{
|
||||
let update_ind_dia = 10;
|
||||
let circle_x_pos =
|
||||
|
|
@ -477,6 +556,92 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "spat"), feature = "denm"))]
|
||||
fn draw_denm_waiting(&mut self) -> Result<(), D::Error> {
|
||||
// clear whole display and draw text
|
||||
self.clear()?;
|
||||
|
||||
text::Text::with_alignment(
|
||||
"Waiting\nfor DENM...",
|
||||
geometry::Point::new(i32::from(self.size.width) / 2, 130 + self.size.offset_top),
|
||||
Self::DEFAULT_TEXT_STYLE,
|
||||
text::Alignment::Center,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "denm")]
|
||||
pub fn draw_denm_warning(
|
||||
&mut self,
|
||||
cc: &cdd_2_2_1::etsi_its_cdd::CauseCodeChoice,
|
||||
) -> Result<(), D::Error> {
|
||||
// clear whole display and draw image
|
||||
self.clear()?;
|
||||
|
||||
let centerline = i32::from(self.size.width) / 2;
|
||||
let sign_top = self.size.offset_top + 40;
|
||||
|
||||
// build triangle shape
|
||||
{
|
||||
let point1 = geometry::Point::new(centerline, sign_top);
|
||||
let point2 = geometry::Point::new(centerline - 100, sign_top + 150);
|
||||
let point3 = geometry::Point::new(centerline + 100, sign_top + 150);
|
||||
let mut line_style =
|
||||
primitives::PrimitiveStyle::with_stroke(pixelcolor::Rgb565::CSS_RED, 10);
|
||||
line_style.fill_color = Some(pixelcolor::Rgb565::WHITE);
|
||||
|
||||
primitives::Triangle::new(point1, point2, point3)
|
||||
.into_styled(line_style)
|
||||
.draw(&mut self.target)?;
|
||||
}
|
||||
|
||||
// add icon inside the triangle
|
||||
match cc {
|
||||
cdd_2_2_1::etsi_its_cdd::CauseCodeChoice::slowVehicle26(_)
|
||||
| cdd_2_2_1::etsi_its_cdd::CauseCodeChoice::stationaryVehicle94(_) => {
|
||||
embedded_graphics::image::Image::new(
|
||||
&self.sv_insert,
|
||||
geometry::Point::new(centerline - (80 / 2), sign_top + 90),
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
}
|
||||
|
||||
cdd_2_2_1::etsi_its_cdd::CauseCodeChoice::rescueAndRecoveryWorkInProgress15(_)
|
||||
| cdd_2_2_1::etsi_its_cdd::CauseCodeChoice::emergencyVehicleApproaching95(_) => {
|
||||
embedded_graphics::image::Image::new(
|
||||
&self.ev_insert,
|
||||
geometry::Point::new(centerline - (70 / 2), sign_top + 85),
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
}
|
||||
|
||||
cdd_2_2_1::etsi_its_cdd::CauseCodeChoice::roadworks3(_) => {
|
||||
embedded_graphics::image::Image::new(
|
||||
&self.rww_insert,
|
||||
geometry::Point::new(centerline - (80 / 2), sign_top + 85),
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let (cc, scc) = cc.to_u8_tuple();
|
||||
text::Text::with_alignment(
|
||||
&alloc::format!("DENM CC {cc}/{scc}"),
|
||||
geometry::Point::new(
|
||||
i32::from(self.size.width) / 2,
|
||||
sign_top + 150 + (10 / 2) + 30,
|
||||
),
|
||||
Self::MID_TEXT_STYLE,
|
||||
text::Alignment::Center,
|
||||
)
|
||||
.draw(&mut self.target)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn draw_slash_screen(&mut self) -> Result<(), D::Error> {
|
||||
self.clear_all()?;
|
||||
|
|
|
|||
Loading…
Reference in a new issue