diff --git a/icons/pid_symbols/dark/motor_valve_green.svg b/icons/pid_symbols/dark/motor_valve_green.svg new file mode 100644 index 0000000000000000000000000000000000000000..c5e62636f99c5e7e9c9f0cb42563f167fc4c7d48 --- /dev/null +++ b/icons/pid_symbols/dark/motor_valve_green.svg @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="10" + height="8" + version="1.1" + id="svg1" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs1" /> + <path + fill="none" + stroke="#ffffff" + d="M 1,3 V 7 L 9,3 v 4 z" + id="path1" + style="fill:#00ff00;stroke-width:0.2;stroke-linejoin:round" /> + <path + fill="none" + stroke="#ffffff" + d="M 0,5 H 1" + id="path2" + style="stroke-width:0.2" /> + <path + fill="none" + stroke="#ffffff" + d="M 5,2.5 V 5" + id="path3" + style="stroke-width:0.2" /> + <path + fill="none" + stroke="#ffffff" + d="m 9,5 h 1" + id="path4" + style="stroke-width:0.2" /> + <circle + style="fill:none;stroke:#ffffff;stroke-width:0.2" + id="circle1" + cx="5" + cy="1.5" + r="1" /> + <path + style="fill:#ffffff;stroke:#ffffff;stroke-opacity:1;stroke-width:0.05;stroke-dasharray:none;stroke-linejoin:round" + d="M 4.5,2 L 4.5,1 L 4.7,1 L 5,1.9 L 5.3,1 L 5.5,1 L 5.5,2 L 5.4,2 L 5.4,1.1 L 5.1,2 L 4.9,2 L 4.6,1.1 L 4.6,2 Z" + id="text1" /> +</svg> diff --git a/icons/pid_symbols/dark/motor_valve_red.svg b/icons/pid_symbols/dark/motor_valve_red.svg new file mode 100644 index 0000000000000000000000000000000000000000..cf1404caf8354abab263d067909b3073768fb5d8 --- /dev/null +++ b/icons/pid_symbols/dark/motor_valve_red.svg @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="10" + height="8" + version="1.1" + id="svg1" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs1" /> + <path + fill="none" + stroke="#ffffff" + d="M 1,3 V 7 L 9,3 v 4 z" + id="path1" + style="fill:#ff0000;stroke-width:0.2;stroke-linejoin:round" /> + <path + fill="none" + stroke="#ffffff" + d="M 0,5 H 1" + id="path2" + style="stroke-width:0.2" /> + <path + fill="none" + stroke="#ffffff" + d="M 5,2.5 V 5" + id="path3" + style="stroke-width:0.2" /> + <path + fill="none" + stroke="#ffffff" + d="m 9,5 h 1" + id="path4" + style="stroke-width:0.2" /> + <circle + style="fill:none;stroke:#ffffff;stroke-width:0.2" + id="circle1" + cx="5" + cy="1.5" + r="1" /> + <path + style="fill:#ffffff;stroke:#ffffff;stroke-opacity:1;stroke-width:0.05;stroke-dasharray:none;stroke-linejoin:round" + d="M 4.5,2 L 4.5,1 L 4.7,1 L 5,1.9 L 5.3,1 L 5.5,1 L 5.5,2 L 5.4,2 L 5.4,1.1 L 5.1,2 L 4.9,2 L 4.6,1.1 L 4.6,2 Z" + id="text1" /> +</svg> diff --git a/icons/pid_symbols/light/motor_valve_green.svg b/icons/pid_symbols/light/motor_valve_green.svg new file mode 100644 index 0000000000000000000000000000000000000000..ef8cea865f8d42cf205e78aad98e3e95a3cf582e --- /dev/null +++ b/icons/pid_symbols/light/motor_valve_green.svg @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="10" + height="8" + version="1.1" + id="svg1" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs1" /> + <path + fill="none" + stroke="#000000" + d="M 1,3 V 7 L 9,3 v 4 z" + id="path1" + style="fill:#00ff00;stroke-width:0.2;stroke-linejoin:round" /> + <path + fill="none" + stroke="#000000" + d="M 0,5 H 1" + id="path2" + style="stroke-width:0.2" /> + <path + fill="none" + stroke="#000000" + d="M 5,2.5 V 5" + id="path3" + style="stroke-width:0.2" /> + <path + fill="none" + stroke="#000000" + d="m 9,5 h 1" + id="path4" + style="stroke-width:0.2" /> + <circle + style="fill:none;stroke:#000000;stroke-width:0.2" + id="circle1" + cx="5" + cy="1.5" + r="1" /> + <path + style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:0.05;stroke-dasharray:none;stroke-linejoin:round" + d="M 4.5,2 L 4.5,1 L 4.7,1 L 5,1.9 L 5.3,1 L 5.5,1 L 5.5,2 L 5.4,2 L 5.4,1.1 L 5.1,2 L 4.9,2 L 4.6,1.1 L 4.6,2 Z" + id="text1" /> +</svg> diff --git a/icons/pid_symbols/light/motor_valve_red.svg b/icons/pid_symbols/light/motor_valve_red.svg new file mode 100644 index 0000000000000000000000000000000000000000..7e947e9ea86e240dd37554369a789bc28e996529 --- /dev/null +++ b/icons/pid_symbols/light/motor_valve_red.svg @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="10" + height="8" + version="1.1" + id="svg1" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs1" /> + <path + fill="none" + stroke="#000000" + d="M 1,3 V 7 L 9,3 v 4 z" + id="path1" + style="fill:#ff0000;stroke-width:0.2;stroke-linejoin:round" /> + <path + fill="none" + stroke="#000000" + d="M 0,5 H 1" + id="path2" + style="stroke-width:0.2" /> + <path + fill="none" + stroke="#000000" + d="M 5,2.5 V 5" + id="path3" + style="stroke-width:0.2" /> + <path + fill="none" + stroke="#000000" + d="m 9,5 h 1" + id="path4" + style="stroke-width:0.2" /> + <circle + style="fill:none;stroke:#000000;stroke-width:0.2" + id="circle1" + cx="5" + cy="1.5" + r="1" /> + <path + style="fill:#000000;stroke:#000000;stroke-opacity:1;stroke-width:0.05;stroke-dasharray:none;stroke-linejoin:round" + d="M 4.5,2 L 4.5,1 L 4.7,1 L 5,1.9 L 5.3,1 L 5.5,1 L 5.5,2 L 5.4,2 L 5.4,1.1 L 5.1,2 L 4.9,2 L 4.6,1.1 L 4.6,2 Z" + id="text1" /> +</svg> diff --git a/src/ui/panes/pid_drawing_tool.rs b/src/ui/panes/pid_drawing_tool.rs index 0ee1b861e177f8369cf753b9140422b2cc4011cf..b7b5bbaada800354191af830d4917a2ecc78f0ca 100644 --- a/src/ui/panes/pid_drawing_tool.rs +++ b/src/ui/panes/pid_drawing_tool.rs @@ -18,7 +18,7 @@ use crate::ui::{composable_view::PaneResponse, utils::egui_to_glam}; use super::PaneBehavior; -#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] +#[derive(Clone, Debug)] enum Action { Connect(usize), ContextMenu(Vec2), @@ -28,7 +28,7 @@ enum Action { } /// Piping and instrumentation diagram -#[derive(Clone, Serialize, Deserialize, PartialEq, Default, Debug)] +#[derive(Clone, Serialize, Deserialize, Default, Debug)] pub struct PidPane { elements: Vec<Element>, connections: Vec<Connection>, @@ -44,6 +44,15 @@ pub struct PidPane { center_content: bool, } +impl PartialEq for PidPane { + fn eq(&self, other: &Self) -> bool { + self.elements == other.elements + && self.connections == other.connections + && self.grid == other.grid + && self.center_content == other.center_content + } +} + impl PaneBehavior for PidPane { fn ui(&mut self, ui: &mut egui::Ui) -> PaneResponse { let theme = PidPane::find_theme(ui.ctx()); @@ -178,8 +187,8 @@ impl PidPane { } } - fn draw_elements(&self, ui: &Ui, theme: Theme) { - for element in &self.elements { + fn draw_elements(&mut self, ui: &Ui, theme: Theme) { + for element in &mut self.elements { element.draw(&self.grid, ui, theme); } } diff --git a/src/ui/panes/pid_drawing_tool/elements.rs b/src/ui/panes/pid_drawing_tool/elements.rs index 7db6c7239208c62e83a4dcf92e0cd17ec43bb63f..db0c3154333913f315f573f46f4bc5d08b03161c 100644 --- a/src/ui/panes/pid_drawing_tool/elements.rs +++ b/src/ui/panes/pid_drawing_tool/elements.rs @@ -1,12 +1,14 @@ -use crate::ui::utils::glam_to_egui; +use crate::{msg_broker, ui::utils::glam_to_egui}; use super::grid::GridInfo; use super::symbols::Symbol; +use crate::error::ErrInstrument; use egui::{Rect, Theme, Ui}; use glam::{Mat2, Vec2}; use serde::{Deserialize, Serialize}; -#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug)] +#[serde(from = "SerialElement")] pub struct Element { /// Anchor postion in grid coordinates, top-left corner position: glam::Vec2, @@ -21,9 +23,18 @@ pub struct Element { /// /// 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 { + fn eq(&self, other: &Self) -> bool { + self.position == other.position + && self.symbol == other.symbol + && self.rotation == other.rotation + } +} + impl Element { pub fn new(center: Vec2, symbol: Symbol) -> Self { Self { @@ -113,12 +124,36 @@ impl Element { self.position + Mat2::from_angle(self.rotation) * self.size() * 0.5 } - pub fn draw(&self, grid: &GridInfo, ui: &Ui, theme: Theme) { + 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); + + if let Symbol::MotorValve(motor_valve) = &mut self.symbol { + msg_broker!().refresh_view(motor_valve).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/grid.rs b/src/ui/panes/pid_drawing_tool/grid.rs index 9b0797f3844bf8278b82984fe5d75f708e101371..aa74330e567662659f3c2a6da2678ff805d043ec 100644 --- a/src/ui/panes/pid_drawing_tool/grid.rs +++ b/src/ui/panes/pid_drawing_tool/grid.rs @@ -14,7 +14,6 @@ pub const CONNECTION_POINT_SIZE: f32 = 1.0; // Grid units #[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] pub struct GridInfo { - /// Grid's zero position on screen pub zero_pos: Vec2, size: f32, } diff --git a/src/ui/panes/pid_drawing_tool/symbols.rs b/src/ui/panes/pid_drawing_tool/symbols.rs index 0b848909a6419b2844c89e7baea719e70b39e18c..1245043d5c62f000ad683d0c76c6ed1cce1d4759 100644 --- a/src/ui/panes/pid_drawing_tool/symbols.rs +++ b/src/ui/panes/pid_drawing_tool/symbols.rs @@ -1,5 +1,8 @@ +mod motor_valve; + use egui::{ImageSource, Theme}; use glam::Vec2; +use motor_valve::MotorValve; use serde::{Deserialize, Serialize}; use strum_macros::{Display, EnumIter}; @@ -10,7 +13,7 @@ pub enum Symbol { CheckValve, FlexibleConnection, ManualValve, - MotorValve, + MotorValve(MotorValve), PressureGauge, PressureRegulator, PressureTransducer, @@ -53,12 +56,26 @@ impl Symbol { (Symbol::ReliefValve, Theme::Dark) => { egui::include_image!("../../../../icons/pid_symbols/dark/relief_valve.svg") } - (Symbol::MotorValve, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/motor_valve.svg") - } - (Symbol::MotorValve, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/motor_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") } @@ -112,7 +129,7 @@ impl Symbol { 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::MotorValve(_) => (10.0, 8.0), Symbol::PressureGauge => (7.0, 7.0), Symbol::PressureRegulator => (10.0, 10.0), Symbol::PressureTransducer => (7.0, 7.0), @@ -132,7 +149,7 @@ impl Symbol { 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::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)], @@ -146,3 +163,30 @@ impl Symbol { .collect() } } + +/// Single MavLink value source info +#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] +#[serde(from = "SerialMavlinkValue")] +struct MavlinkValue { + msg_id: u32, + field: String, + + #[serde(skip)] + view_id: egui::Id, +} + +#[derive(Deserialize)] +struct SerialMavlinkValue { + msg_id: u32, + field: String, +} + +impl From<SerialMavlinkValue> for MavlinkValue { + fn from(value: SerialMavlinkValue) -> Self { + Self { + msg_id: value.msg_id, + field: value.field, + view_id: egui::Id::new(""), + } + } +} diff --git a/src/ui/panes/pid_drawing_tool/symbols/motor_valve.rs b/src/ui/panes/pid_drawing_tool/symbols/motor_valve.rs new file mode 100644 index 0000000000000000000000000000000000000000..af4f38f55f56eef934762ba41ab9218be32d8111 --- /dev/null +++ b/src/ui/panes/pid_drawing_tool/symbols/motor_valve.rs @@ -0,0 +1,60 @@ +use crate::mavlink::{extract_from_message, MavlinkResult, MessageView, TimedMessage}; + +use super::MavlinkValue; + +use serde::{Deserialize, Serialize}; +use skyward_mavlink::{mavlink::MessageData, orion}; + +#[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] +pub struct MotorValve { + source: MavlinkValue, + + /// false = closed, true = open + pub last_value: Option<bool>, +} + +impl Default for MotorValve { + fn default() -> Self { + Self { + source: MavlinkValue { + msg_id: orion::GSE_TM_DATA::ID, + field: "n2o_filling_valve_state".to_string(), + view_id: egui::Id::new(""), + }, + last_value: None, + } + } +} + +impl MessageView for MotorValve { + fn widget_id(&self) -> &egui::Id { + &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: &[TimedMessage]) -> MavlinkResult<()> { + self.update_view(msg_slice) + } + + fn update_view(&mut self, msg_slice: &[TimedMessage]) -> MavlinkResult<()> { + if let Some(msg) = msg_slice.last() { + let values: MavlinkResult<Vec<Option<u8>>> = + extract_from_message(&msg.message, [&self.source.field]); + if let Ok(values) = values { + if !values.is_empty() { + if let Some(value) = values[0].map(|v| v != 0) { + self.last_value = Some(value); + } + } + } + } + Ok(()) + } +}