diff --git a/src/ui/panes.rs b/src/ui/panes.rs index d8e52d1e66f1ae3c67700db06202731c1d0e4b06..208b3ca6c23507c31e8442c67baac2aca5ea993a 100644 --- a/src/ui/panes.rs +++ b/src/ui/panes.rs @@ -1,6 +1,5 @@ mod default; mod messages_viewer; -mod pid; mod pid_drawing_tool; pub mod plot; @@ -50,11 +49,8 @@ pub enum PaneKind { #[strum(message = "Plot 2D")] Plot2D(plot::Plot2DPane), - #[strum(message = "PID Old")] + #[strum(message = "Pid")] PidOld(pid_drawing_tool::PidPane), - - #[strum(message = "PID New")] - Pid(pid::Pid), } impl Default for PaneKind { diff --git a/src/ui/panes/pid_drawing_tool.rs b/src/ui/panes/pid_drawing_tool.rs index 0b68be7608c1178dec86908d3a729a420c7870c9..26ca3ea3056035eb29826bb54806cb111e51d700 100644 --- a/src/ui/panes/pid_drawing_tool.rs +++ b/src/ui/panes/pid_drawing_tool.rs @@ -11,9 +11,8 @@ use elements::Element; use glam::Vec2; use grid::GridInfo; use serde::{Deserialize, Serialize}; -use std::f32::consts::PI; use strum::IntoEnumIterator; -use symbols::Symbol; +use symbols::{icons::Icon, Symbol}; use crate::ui::{composable_view::PaneResponse, utils::egui_to_glam}; @@ -207,22 +206,12 @@ impl PidPane { return; } - let elem_idx = self.hovers_element(pointer_pos); - if let Some(elem_idx) = elem_idx { + if let Some(elem_idx) = self.hovers_element(pointer_pos) { if ui.button("Connect").clicked() { self.action = Some(Action::Connect(elem_idx)); ui.close_menu(); } - if ui.button("Rotate 90° ⟲").clicked() { - self.elements[elem_idx].rotate(-PI / 2.0); - self.action.take(); - ui.close_menu(); - } - if ui.button("Rotate 90° ⟳").clicked() { - self.elements[elem_idx].rotate(PI / 2.0); - self.action.take(); - ui.close_menu(); - } + self.elements[elem_idx].context_menu(ui); if ui.button("Delete").clicked() { self.delete_element(elem_idx); self.action.take(); @@ -251,7 +240,20 @@ impl PidPane { } else { ui.menu_button("Symbols", |ui| { for symbol in Symbol::iter() { - if ui.button(symbol.to_string()).clicked() { + if let Symbol::Icon(_) = symbol { + ui.menu_button("Icons", |ui| { + for icon in Icon::iter() { + if ui.button(icon.to_string()).clicked() { + self.elements.push(Element::new( + self.grid.screen_to_grid(pointer_pos).round(), + Symbol::Icon(icon), + )); + self.action.take(); + ui.close_menu(); + } + } + }); + } else if ui.button(symbol.to_string()).clicked() { self.elements.push(Element::new( self.grid.screen_to_grid(pointer_pos).round(), symbol, @@ -322,8 +324,8 @@ impl PidPane { self.grid.apply_scroll_delta(scroll_delta, pointer_pos); // Invalidate the cache to redraw the images - for symbol in Symbol::iter() { - let img: egui::ImageSource = symbol.get_image(theme); + for icon in Icon::iter() { + let img: egui::ImageSource = icon.get_image(theme); ui.ctx().forget_image(img.uri().unwrap()); } } diff --git a/src/ui/panes/pid_drawing_tool/elements.rs b/src/ui/panes/pid_drawing_tool/elements.rs index db0c3154333913f315f573f46f4bc5d08b03161c..f64649842abc007f0dd81773b9737bc9757e2ca6 100644 --- a/src/ui/panes/pid_drawing_tool/elements.rs +++ b/src/ui/panes/pid_drawing_tool/elements.rs @@ -1,14 +1,16 @@ -use crate::{msg_broker, ui::utils::glam_to_egui}; +use std::f32::consts::PI; -use super::grid::GridInfo; -use super::symbols::Symbol; -use crate::error::ErrInstrument; -use egui::{Rect, Theme, Ui}; +use crate::{error::ErrInstrument, msg_broker, MSG_MANAGER}; + +use super::{ + grid::GridInfo, + symbols::{icons::Icon, Symbol, SymbolBehavior}, +}; +use egui::{Theme, Ui}; use glam::{Mat2, Vec2}; use serde::{Deserialize, Serialize}; #[derive(Clone, Serialize, Deserialize, Debug)] -#[serde(from = "SerialElement")] pub struct Element { /// Anchor postion in grid coordinates, top-left corner position: glam::Vec2, @@ -18,13 +20,6 @@ pub struct Element { /// Rotation in radiants rotation: f32, - - /// Anchor point in grid coordinates relative to the element's center - /// - /// These vectors include the current rotation of the element. - /// They are cached to avoid recomputing the rotation. - #[serde(skip)] - anchor_points: Vec<Vec2>, } impl PartialEq for Element { @@ -40,7 +35,6 @@ impl Element { Self { position: center - symbol.size() / 2.0, rotation: 0.0, - anchor_points: symbol.anchor_points(), symbol, } } @@ -54,6 +48,7 @@ impl Element { // The bounding box is just the size let min_e = Vec2::ZERO; let max_e = self.symbol.size(); + println!("size: {max_e:?}"); // Check if the point is in the bounding box min_e.x <= p_e.x && p_e.x <= max_e.x && min_e.y <= p_e.y && p_e.y <= max_e.y @@ -72,9 +67,28 @@ impl Element { pub fn change_symbol(&mut self, symbol: Symbol) { self.symbol = symbol; + } - // Anchor points can be different between symbols, realod the cache - self.reload_anchor_points(); + pub fn get_symbol(&self) -> Symbol { + self.symbol.clone() + } + + pub fn context_menu(&mut self, ui: &mut Ui) { + match &mut self.symbol { + Symbol::Icon(_) => { + if ui.button("Rotate 90° ⟲").clicked() { + self.rotate(-PI / 2.0); + ui.close_menu(); + } + if ui.button("Rotate 90° ⟳").clicked() { + self.rotate(PI / 2.0); + ui.close_menu(); + } + } + Symbol::Label(label) => { + label.context_menu(ui); + } + } } /// Rotate the element by its center @@ -87,31 +101,23 @@ impl Element { // Update absolute rotation self.rotation += rotation; - - // Recompute anchor points cache - self.reload_anchor_points(); - } - - fn reload_anchor_points(&mut self) { - // Rotation matrix from element's frame to grid's frame - let rotm_e_to_g = Mat2::from_angle(self.rotation); - - // Then rotate the anchor points - self.anchor_points = self - .symbol - .anchor_points() - .iter() - .map(|&p| rotm_e_to_g * p) - .collect(); } /// Returns the position of one anchor point in grid coordinates pub fn anchor_point(&self, idx: usize) -> Vec2 { - self.anchor_points[idx] + self.position + if let Some(anchor_points) = self.symbol.anchor_points() { + // Rotation matrix from element's frame to grid's frame + let rotm_e_to_g = Mat2::from_angle(self.rotation); + + // Then rotate and translate the anchor points + rotm_e_to_g * anchor_points[idx] + self.position + } else { + Vec2::ZERO + } } pub fn anchor_points_len(&self) -> usize { - self.anchor_points.len() + self.symbol.anchor_points().map_or(0, |v| v.len()) } /// Size in grid units @@ -125,35 +131,14 @@ impl Element { } pub fn draw(&mut self, grid: &GridInfo, ui: &Ui, theme: Theme) { - let center = glam_to_egui(grid.grid_to_screen(self.position)).to_pos2(); - let image_rect = Rect::from_min_size(center, glam_to_egui(self.size() * grid.size())); - - egui::Image::new(self.symbol.get_image(theme)) - .rotate(self.rotation, egui::Vec2::splat(0.0)) - .paint_at(ui, image_rect); + let pos = grid.grid_to_screen(self.position); + let size = grid.size(); + self.symbol.paint(ui, theme, pos, size, self.rotation); - if let Symbol::MotorValve(motor_valve) = &mut self.symbol { + if let Symbol::Icon(Icon::MotorValve(motor_valve)) = &mut self.symbol { msg_broker!().refresh_view(motor_valve).log_expect("bruh"); + } else if let Symbol::Label(label) = &mut self.symbol { + msg_broker!().refresh_view(label).log_expect("bruh"); } } } - -#[derive(Deserialize)] -pub struct SerialElement { - position: glam::Vec2, - symbol: Symbol, - rotation: f32, -} - -impl From<SerialElement> for Element { - fn from(value: SerialElement) -> Self { - let mut value = Self { - position: value.position, - symbol: value.symbol, - rotation: value.rotation, - anchor_points: Vec::new(), - }; - value.reload_anchor_points(); - value - } -} diff --git a/src/ui/panes/pid_drawing_tool/symbols.rs b/src/ui/panes/pid_drawing_tool/symbols.rs index d4b92f3ed0f2f811c2b7f6d6b31cf2f1d6d32454..4f8f4a8b26befc953a20d79578743b82893eb20e 100644 --- a/src/ui/panes/pid_drawing_tool/symbols.rs +++ b/src/ui/panes/pid_drawing_tool/symbols.rs @@ -1,169 +1,64 @@ -mod motor_valve; +pub mod icons; +mod labels; -use egui::{ImageSource, Theme}; +use crate::mavlink::ViewId; +use egui::{Theme, Ui}; +use enum_dispatch::enum_dispatch; use glam::Vec2; -use motor_valve::MotorValve; +use icons::Icon; +use labels::Label; use serde::{Deserialize, Serialize}; use strum_macros::{Display, EnumIter}; -use crate::mavlink::ViewId; - #[derive(Clone, Serialize, Deserialize, PartialEq, EnumIter, Display, Debug)] +#[enum_dispatch] pub enum Symbol { - Arrow, - BurstDisk, - CheckValve, - FlexibleConnection, - ManualValve, - MotorValve(MotorValve), - PressureGauge, - PressureRegulator, - PressureTransducer, - QuickConnector, - ReliefValve, - ThreeWayValve, - Vessel, + Icon(Icon), + Label(Label), } -impl Symbol { - pub fn get_image(&self, theme: Theme) -> ImageSource { - match (&self, theme) { - (Symbol::Arrow, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/arrow.svg") - } - (Symbol::Arrow, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/arrow.svg") - } - (Symbol::BurstDisk, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/burst_disk.svg") - } - (Symbol::BurstDisk, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/burst_disk.svg") - } - (Symbol::ManualValve, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/manual_valve.svg") - } - (Symbol::ManualValve, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/manual_valve.svg") - } - (Symbol::CheckValve, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/check_valve.svg") - } - (Symbol::CheckValve, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/check_valve.svg") - } - (Symbol::ReliefValve, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/relief_valve.svg") - } - (Symbol::ReliefValve, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/relief_valve.svg") - } - (Symbol::MotorValve(state), Theme::Light) => match state.last_value { - None => egui::include_image!("../../../../icons/pid_symbols/light/motor_valve.svg"), - Some(true) => { - egui::include_image!( - "../../../../icons/pid_symbols/light/motor_valve_green.svg" - ) - } - Some(false) => { - egui::include_image!("../../../../icons/pid_symbols/light/motor_valve_red.svg") - } - }, - (Symbol::MotorValve(state), Theme::Dark) => match state.last_value { - None => egui::include_image!("../../../../icons/pid_symbols/dark/motor_valve.svg"), - Some(true) => { - egui::include_image!("../../../../icons/pid_symbols/dark/motor_valve_green.svg") - } - Some(false) => { - egui::include_image!("../../../../icons/pid_symbols/dark/motor_valve_red.svg") - } - }, - (Symbol::ThreeWayValve, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/three_way_valve.svg") - } - (Symbol::ThreeWayValve, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/three_way_valve.svg") - } - (Symbol::PressureRegulator, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/pressure_regulator.svg") - } - (Symbol::PressureRegulator, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/pressure_regulator.svg") - } - (Symbol::QuickConnector, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/quick_connector.svg") - } - (Symbol::QuickConnector, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/quick_connector.svg") - } - (Symbol::PressureTransducer, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/pressure_transducer.svg") - } - (Symbol::PressureTransducer, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/pressure_transducer.svg") - } - (Symbol::PressureGauge, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/pressure_gauge.svg") - } - (Symbol::PressureGauge, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/pressure_gauge.svg") - } - (Symbol::FlexibleConnection, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/flexible_connection.svg") - } - (Symbol::FlexibleConnection, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/flexible_connection.svg") - } - (Symbol::Vessel, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/vessel.svg") - } - (Symbol::Vessel, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/vessel.svg") - } - } +impl Default for Symbol { + fn default() -> Self { + Symbol::Icon(Icon::default()) } +} + +#[enum_dispatch(Symbol)] +pub trait SymbolBehavior { + fn paint(&mut self, ui: &Ui, theme: Theme, pos: Vec2, size: f32, rotation: f32); + + /// Anchor point in grid coordinates relative to the element's center + /// + /// These vectors include the current rotation of the element. + /// They are cached to avoid recomputing the rotation. + fn anchor_points(&self) -> Option<Vec<Vec2>>; /// Symbol size in grid coordinates - pub fn size(&self) -> Vec2 { - match self { - Symbol::Arrow => (4.0, 4.0), - Symbol::BurstDisk => (4.0, 6.0), - Symbol::CheckValve => (10.0, 5.0), - Symbol::FlexibleConnection => (10.0, 6.0), - Symbol::ManualValve => (10.0, 5.0), - Symbol::MotorValve(_) => (10.0, 8.0), - Symbol::PressureGauge => (7.0, 7.0), - Symbol::PressureRegulator => (10.0, 10.0), - Symbol::PressureTransducer => (7.0, 7.0), - Symbol::QuickConnector => (6.0, 5.0), - Symbol::ReliefValve => (6.0, 10.0), - Symbol::ThreeWayValve => (10.0, 8.0), - Symbol::Vessel => (8.2, 15.2), - } - .into() - } + fn size(&self) -> Vec2; - /// Anchor point position relative to top right corner in grid units - pub fn anchor_points(&self) -> Vec<Vec2> { - match self { - Symbol::Arrow => vec![(0.0, 2.0), (4.0, 2.0)], - Symbol::BurstDisk => vec![(0.0, 3.0), (4.0, 3.0)], - Symbol::CheckValve => vec![(0.0, 2.5), (10.0, 2.5)], - Symbol::FlexibleConnection => vec![(0.0, 3.0), (10.0, 3.0)], - Symbol::ManualValve => vec![(0.0, 2.5), (10.0, 2.5)], - Symbol::MotorValve(_) => vec![(0.0, 5.0), (10.0, 5.0)], - Symbol::PressureGauge => vec![(3.5, 7.0)], - Symbol::PressureRegulator => vec![(0.0, 7.0), (10.0, 7.0)], - Symbol::PressureTransducer => vec![(3.5, 7.0)], - Symbol::QuickConnector => vec![(0.0, 2.5), (6.0, 2.5)], - Symbol::ReliefValve => vec![(3.0, 10.0)], - Symbol::ThreeWayValve => vec![(0.0, 3.0), (10.0, 3.0), (5.0, 8.0)], - Symbol::Vessel => vec![(0.0, 7.6), (8.2, 7.6), (4.1, 0.0), (4.1, 15.1)], - } - .iter() - .map(|&p| p.into()) - .collect() - } + // /// Anchor point position relative to top right corner in grid units + // pub fn anchor_points(&self) -> Vec<Vec2> { + // match self { + // Symbol::Arrow => vec![(0.0, 2.0), (4.0, 2.0)], + // Symbol::BurstDisk => vec![(0.0, 3.0), (4.0, 3.0)], + // Symbol::CheckValve => vec![(0.0, 2.5), (10.0, 2.5)], + // Symbol::FlexibleConnection => vec![(0.0, 3.0), (10.0, 3.0)], + // Symbol::ManualValve => vec![(0.0, 2.5), (10.0, 2.5)], + // Symbol::MotorValve(_) => vec![(0.0, 5.0), (10.0, 5.0)], + // Symbol::PressureGauge => vec![(3.5, 7.0)], + // Symbol::PressureRegulator => vec![(0.0, 7.0), (10.0, 7.0)], + // Symbol::PressureTransducer => vec![(3.5, 7.0)], + // Symbol::QuickConnector => vec![(0.0, 2.5), (6.0, 2.5)], + // Symbol::ReliefValve => vec![(3.0, 10.0)], + // Symbol::ThreeWayValve => vec![(0.0, 3.0), (10.0, 3.0), (5.0, 8.0)], + // Symbol::Vessel => vec![(0.0, 7.6), (8.2, 7.6), (4.1, 0.0), (4.1, 15.1)], + // } + // .iter() + // .map(|&p| p.into()) + // .collect() + // } + + fn context_menu(&mut self, ui: &mut Ui) {} } /// Single MavLink value source info diff --git a/src/ui/panes/pid_drawing_tool/symbols/icons.rs b/src/ui/panes/pid_drawing_tool/symbols/icons.rs index 66192cc3f40199b4f83ecec56c3592415186713c..5653b2635be6f62d10537c3be77b26c232051296 100644 --- a/src/ui/panes/pid_drawing_tool/symbols/icons.rs +++ b/src/ui/panes/pid_drawing_tool/symbols/icons.rs @@ -6,7 +6,11 @@ use motor_valve::MotorValve; use serde::{Deserialize, Serialize}; use strum_macros::{Display, EnumIter}; -use crate::ui::utils::glam_to_egui; +use crate::{ + mavlink::{MessageBroker, ViewId}, + msg_broker, + ui::utils::glam_to_egui, +}; use super::SymbolBehavior; @@ -214,7 +218,7 @@ struct MavlinkValue { field: String, #[serde(skip)] - view_id: egui::Id, + view_id: ViewId, } #[derive(Deserialize)] @@ -228,7 +232,7 @@ impl From<SerialMavlinkValue> for MavlinkValue { Self { msg_id: value.msg_id, field: value.field, - view_id: egui::Id::new(""), + view_id: ViewId::new(), } } } diff --git a/src/ui/panes/pid_drawing_tool/symbols/icons/motor_valve.rs b/src/ui/panes/pid_drawing_tool/symbols/icons/motor_valve.rs index af4f38f55f56eef934762ba41ab9218be32d8111..09819c6f1e26a087185386955b340e0e51338457 100644 --- a/src/ui/panes/pid_drawing_tool/symbols/icons/motor_valve.rs +++ b/src/ui/panes/pid_drawing_tool/symbols/icons/motor_valve.rs @@ -1,4 +1,4 @@ -use crate::mavlink::{extract_from_message, MavlinkResult, MessageView, TimedMessage}; +use crate::mavlink::{extract_from_message, MavlinkResult, MessageView, TimedMessage, ViewId}; use super::MavlinkValue; @@ -19,7 +19,7 @@ impl Default for MotorValve { source: MavlinkValue { msg_id: orion::GSE_TM_DATA::ID, field: "n2o_filling_valve_state".to_string(), - view_id: egui::Id::new(""), + view_id: ViewId::new(), }, last_value: None, } @@ -27,8 +27,8 @@ impl Default for MotorValve { } impl MessageView for MotorValve { - fn widget_id(&self) -> &egui::Id { - &self.source.view_id + fn view_id(&self) -> crate::mavlink::ViewId { + self.source.view_id } fn id_of_interest(&self) -> u32 { diff --git a/src/ui/panes/pid_drawing_tool/symbols/labels.rs b/src/ui/panes/pid_drawing_tool/symbols/labels.rs index 03c974fa193317cf2695ea64783c076477a012ee..7534969d4018fe799721a4b7e8486803eb4f26b9 100644 --- a/src/ui/panes/pid_drawing_tool/symbols/labels.rs +++ b/src/ui/panes/pid_drawing_tool/symbols/labels.rs @@ -1,17 +1,44 @@ use serde::{Deserialize, Serialize}; +use skyward_mavlink::{mavlink::MessageData, orion}; -use crate::ui::utils::glam_to_egui; +use crate::{ + mavlink::{extract_from_message, MavlinkResult, MessageView, ViewId}, + ui::utils::{egui_to_glam, glam_to_egui}, +}; -use super::SymbolBehavior; +use super::{MavlinkValue, SymbolBehavior}; use egui::{Align2, Color32, FontId, Rounding, Stroke, Theme, Ui}; use glam::Vec2; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] +const FONT_SIZE: f32 = 2.0; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct Label { - last_value: Option<f32>, - format_string: String, + text: String, + units: String, #[serde(skip)] show_window: bool, + + last_value: Option<f32>, + source: MavlinkValue, + size: Vec2, +} + +impl Default for Label { + fn default() -> Self { + Self { + text: "0.00".to_string(), + units: "".to_string(), + show_window: false, + source: MavlinkValue { + msg_id: orion::GSE_TM_DATA::ID, + field: "n2o_vessel_pressure".to_string(), + view_id: ViewId::new(), + }, + last_value: Some(0.0), + size: Vec2::new(FONT_SIZE * 0.6 * 4.0, FONT_SIZE), + } + } } impl SymbolBehavior for Label { @@ -22,13 +49,14 @@ impl SymbolBehavior for Label { Theme::Dark => Color32::WHITE, }; - painter.text( + let text_rect = painter.text( glam_to_egui(pos).to_pos2(), Align2::LEFT_TOP, - &self.format_string, - FontId::monospace(self.size().y * size), + &self.text, + FontId::monospace(FONT_SIZE * size), color, ); + self.size = egui_to_glam(text_rect.size()) / size; painter.rect( egui::Rect::from_min_size( glam_to_egui(pos).to_pos2(), @@ -48,7 +76,7 @@ impl SymbolBehavior for Label { .movable(true) .open(&mut show_window) .show(ui.ctx(), |ui| { - ui.text_edit_singleline(&mut self.format_string); + ui.text_edit_singleline(&mut self.units); }); self.show_window = show_window; } @@ -58,14 +86,54 @@ impl SymbolBehavior for Label { } fn size(&self) -> Vec2 { - let font_size = 2.0; - Vec2::new(font_size * 0.6 * self.format_string.len() as f32, font_size) + self.size } fn context_menu(&mut self, ui: &mut Ui) { + println!("Label context menu"); if ui.button("Edit").clicked() { self.show_window = true; ui.close_menu(); } } } + +impl MessageView for Label { + fn view_id(&self) -> ViewId { + self.source.view_id + } + + fn id_of_interest(&self) -> u32 { + self.source.msg_id + } + + fn is_valid(&self) -> bool { + self.last_value.is_some() + } + + fn populate_view( + &mut self, + msg_slice: &[crate::mavlink::TimedMessage], + ) -> crate::mavlink::MavlinkResult<()> { + self.update_view(msg_slice) + } + + fn update_view( + &mut self, + msg_slice: &[crate::mavlink::TimedMessage], + ) -> crate::mavlink::MavlinkResult<()> { + if let Some(msg) = msg_slice.last() { + let values: MavlinkResult<Vec<Option<f32>>> = + extract_from_message(&msg.message, [&self.source.field]); + if let Ok(values) = values { + if !values.is_empty() { + if let Some(value) = values[0] { + self.last_value = Some(value); + self.text = format!("{:.2}{}", value, self.units); + } + } + } + } + Ok(()) + } +}