diff --git a/src/ui/panes/pid_drawing_tool.rs b/src/ui/panes/pid_drawing_tool.rs
index 26ca3ea3056035eb29826bb54806cb111e51d700..627990fdfb58436bb65ef698b0329dacb3579bf6 100644
--- a/src/ui/panes/pid_drawing_tool.rs
+++ b/src/ui/panes/pid_drawing_tool.rs
@@ -12,9 +12,13 @@ use glam::Vec2;
 use grid::GridInfo;
 use serde::{Deserialize, Serialize};
 use strum::IntoEnumIterator;
-use symbols::{icons::Icon, Symbol};
+use symbols::{Symbol, icons::Icon};
 
-use crate::ui::{composable_view::PaneResponse, utils::egui_to_glam};
+use crate::{
+    error::ErrInstrument,
+    mavlink::{GSE_TM_DATA, MessageData, TimedMessage},
+    ui::{app::PaneResponse, utils::egui_to_glam},
+};
 
 use super::PaneBehavior;
 
@@ -98,6 +102,18 @@ impl PaneBehavior for PidPane {
     fn contains_pointer(&self) -> bool {
         false
     }
+
+    fn update(&mut self, messages: &[TimedMessage]) {
+        if let Some(msg) = messages.last() {
+            for element in &mut self.elements {
+                element.update(&msg.message);
+            }
+        }
+    }
+
+    fn get_message_subscription(&self) -> Option<u32> {
+        Some(GSE_TM_DATA::ID)
+    }
 }
 
 impl PidPane {
@@ -295,24 +311,24 @@ impl PidPane {
             .iter()
             .map(|p| p.x)
             .min_by(|a, b| a.total_cmp(b))
-            .unwrap();
+            .log_unwrap();
         let min_y = points
             .iter()
             .map(|p| p.y)
             .min_by(|a, b| a.total_cmp(b))
-            .unwrap();
+            .log_unwrap();
         let min = Vec2::new(min_x, min_y);
 
         let max_x = points
             .iter()
             .map(|p| p.x)
             .max_by(|a, b| a.total_cmp(b))
-            .unwrap();
+            .log_unwrap();
         let max_y = points
             .iter()
             .map(|p| p.y)
             .max_by(|a, b| a.total_cmp(b))
-            .unwrap();
+            .log_unwrap();
         let max = Vec2::new(max_x, max_y);
 
         self.grid.zero_pos = ui_center - min.midpoint(max) * self.grid.size();
@@ -326,7 +342,7 @@ impl PidPane {
             // Invalidate the cache to redraw the images
             for icon in Icon::iter() {
                 let img: egui::ImageSource = icon.get_image(theme);
-                ui.ctx().forget_image(img.uri().unwrap());
+                ui.ctx().forget_image(img.uri().log_unwrap());
             }
         }
     }
diff --git a/src/ui/panes/pid_drawing_tool/connections.rs b/src/ui/panes/pid_drawing_tool/connections.rs
index 2a5020f28c79df7b6f4dc09a67118e84fb3553e5..3cde5f8e6a6ac5ecca442e120f25cd9044bdfff7 100644
--- a/src/ui/panes/pid_drawing_tool/connections.rs
+++ b/src/ui/panes/pid_drawing_tool/connections.rs
@@ -1,12 +1,12 @@
-use egui::{epaint::PathStroke, Color32, Painter, Rect, Rounding, Stroke, Theme};
+use egui::{Color32, CornerRadius, Painter, Rect, Stroke, StrokeKind, Theme};
 use glam::{Mat2, Vec2};
 use serde::{Deserialize, Serialize};
 
-use crate::ui::utils::glam_to_egui;
+use crate::{error::ErrInstrument, ui::utils::glam_to_egui};
 
 use super::{
-    grid::{GridInfo, CONNECTION_LINE_THICKNESS, CONNECTION_LINE_THRESHOLD, CONNECTION_POINT_SIZE},
     PidPane,
+    grid::{CONNECTION_LINE_THICKNESS, CONNECTION_LINE_THRESHOLD, CONNECTION_POINT_SIZE, GridInfo},
 };
 
 #[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
@@ -111,11 +111,17 @@ impl Connection {
                 .iter()
                 .map(|p| pid.grid.grid_to_screen(*p))
                 .collect();
-            Connection::draw_segment(&pid.grid, painter, color, start, *points.first().unwrap());
+            Connection::draw_segment(
+                &pid.grid,
+                painter,
+                color,
+                start,
+                *points.first().log_unwrap(),
+            );
             for i in 0..(points.len() - 1) {
                 Connection::draw_segment(&pid.grid, painter, color, points[i], points[i + 1]);
             }
-            Connection::draw_segment(&pid.grid, painter, color, *points.last().unwrap(), end);
+            Connection::draw_segment(&pid.grid, painter, color, *points.last().log_unwrap(), end);
 
             if pid.editable {
                 for point in points {
@@ -124,9 +130,10 @@ impl Connection {
                             glam_to_egui(point).to_pos2(),
                             egui::Vec2::splat(CONNECTION_POINT_SIZE * pid.grid.size()),
                         ),
-                        Rounding::ZERO,
+                        CornerRadius::ZERO,
                         Color32::DARK_GRAY,
                         Stroke::NONE,
+                        StrokeKind::Middle,
                     );
                 }
             }
@@ -136,7 +143,7 @@ impl Connection {
     fn draw_segment(grid: &GridInfo, painter: &Painter, color: Color32, a: Vec2, b: Vec2) {
         painter.line_segment(
             [glam_to_egui(a).to_pos2(), glam_to_egui(b).to_pos2()],
-            PathStroke::new(CONNECTION_LINE_THICKNESS * grid.size(), color),
+            (CONNECTION_LINE_THICKNESS * grid.size(), color),
         );
     }
 }
diff --git a/src/ui/panes/pid_drawing_tool/elements.rs b/src/ui/panes/pid_drawing_tool/elements.rs
index f64649842abc007f0dd81773b9737bc9757e2ca6..dc8f55fae1447c662a0749436956f6c2a7d7770b 100644
--- a/src/ui/panes/pid_drawing_tool/elements.rs
+++ b/src/ui/panes/pid_drawing_tool/elements.rs
@@ -1,14 +1,15 @@
-use std::f32::consts::PI;
+use std::f32::consts::FRAC_PI_2;
 
-use crate::{error::ErrInstrument, msg_broker, MSG_MANAGER};
+use egui::{Theme, Ui};
+use glam::{Mat2, Vec2};
+use serde::{Deserialize, Serialize};
+
+use crate::mavlink::MavMessage;
 
 use super::{
     grid::GridInfo,
-    symbols::{icons::Icon, Symbol, SymbolBehavior},
+    symbols::{Symbol, SymbolBehavior},
 };
-use egui::{Theme, Ui};
-use glam::{Mat2, Vec2};
-use serde::{Deserialize, Serialize};
 
 #[derive(Clone, Serialize, Deserialize, Debug)]
 pub struct Element {
@@ -77,11 +78,11 @@ impl Element {
         match &mut self.symbol {
             Symbol::Icon(_) => {
                 if ui.button("Rotate 90° ⟲").clicked() {
-                    self.rotate(-PI / 2.0);
+                    self.rotate(-FRAC_PI_2);
                     ui.close_menu();
                 }
                 if ui.button("Rotate 90° ⟳").clicked() {
-                    self.rotate(PI / 2.0);
+                    self.rotate(FRAC_PI_2);
                     ui.close_menu();
                 }
             }
@@ -134,11 +135,9 @@ impl Element {
         let pos = grid.grid_to_screen(self.position);
         let size = grid.size();
         self.symbol.paint(ui, theme, pos, size, self.rotation);
+    }
 
-        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");
-        }
+    pub fn update(&mut self, message: &MavMessage) {
+        self.symbol.update(message);
     }
 }
diff --git a/src/ui/panes/pid_drawing_tool/symbols.rs b/src/ui/panes/pid_drawing_tool/symbols.rs
index 4f8f4a8b26befc953a20d79578743b82893eb20e..30bdb17b6f49c9cf8e46764e9a37e9eb68df7306 100644
--- a/src/ui/panes/pid_drawing_tool/symbols.rs
+++ b/src/ui/panes/pid_drawing_tool/symbols.rs
@@ -1,7 +1,6 @@
 pub mod icons;
 mod labels;
 
-use crate::mavlink::ViewId;
 use egui::{Theme, Ui};
 use enum_dispatch::enum_dispatch;
 use glam::Vec2;
@@ -10,6 +9,8 @@ use labels::Label;
 use serde::{Deserialize, Serialize};
 use strum_macros::{Display, EnumIter};
 
+use crate::mavlink::MavMessage;
+
 #[derive(Clone, Serialize, Deserialize, PartialEq, EnumIter, Display, Debug)]
 #[enum_dispatch]
 pub enum Symbol {
@@ -36,6 +37,8 @@ pub trait SymbolBehavior {
     /// Symbol size in grid coordinates
     fn size(&self) -> Vec2;
 
+    fn update(&mut self, message: &MavMessage);
+
     // /// Anchor point position relative to top right corner in grid units
     // pub fn anchor_points(&self) -> Vec<Vec2> {
     //     match self {
@@ -60,30 +63,3 @@ pub trait SymbolBehavior {
 
     fn context_menu(&mut self, ui: &mut Ui) {}
 }
-
-/// 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: ViewId,
-}
-
-#[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: ViewId::new(),
-        }
-    }
-}
diff --git a/src/ui/panes/pid_drawing_tool/symbols/icons.rs b/src/ui/panes/pid_drawing_tool/symbols/icons.rs
index 5653b2635be6f62d10537c3be77b26c232051296..fcadab83a20f758aaa917cb29072f88396857d46 100644
--- a/src/ui/panes/pid_drawing_tool/symbols/icons.rs
+++ b/src/ui/panes/pid_drawing_tool/symbols/icons.rs
@@ -1,16 +1,12 @@
 mod motor_valve;
 
-use egui::{ImageSource, Theme, Ui};
+use egui::{ImageSource, Theme};
 use glam::Vec2;
 use motor_valve::MotorValve;
 use serde::{Deserialize, Serialize};
 use strum_macros::{Display, EnumIter};
 
-use crate::{
-    mavlink::{MessageBroker, ViewId},
-    msg_broker,
-    ui::utils::glam_to_egui,
-};
+use crate::{mavlink::MavMessage, ui::utils::glam_to_egui};
 
 use super::SymbolBehavior;
 
@@ -167,6 +163,12 @@ impl SymbolBehavior for Icon {
             .paint_at(ui, image_rect);
     }
 
+    fn update(&mut self, message: &MavMessage) {
+        if let Icon::MotorValve(state) = self {
+            state.update(message)
+        }
+    }
+
     fn anchor_points(&self) -> Option<Vec<glam::Vec2>> {
         Some(
             match self {
@@ -209,30 +211,3 @@ impl SymbolBehavior for Icon {
         .into()
     }
 }
-
-/// 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: ViewId,
-}
-
-#[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: 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 09819c6f1e26a087185386955b340e0e51338457..584e21e4157338f4ca930b7233e00cc670f08ab6 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,60 +1,38 @@
-use crate::mavlink::{extract_from_message, MavlinkResult, MessageView, TimedMessage, ViewId};
-
-use super::MavlinkValue;
-
 use serde::{Deserialize, Serialize};
-use skyward_mavlink::{mavlink::MessageData, orion};
+
+use crate::{
+    MAVLINK_PROFILE,
+    error::ErrInstrument,
+    mavlink::{
+        GSE_TM_DATA, MavMessage, Message, MessageData,
+        reflection::{FieldLike, IndexedField},
+    },
+};
 
 #[derive(Clone, Serialize, Deserialize, PartialEq, Debug)]
 pub struct MotorValve {
-    source: MavlinkValue,
+    mavlink_field: IndexedField,
 
     /// 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: ViewId::new(),
-            },
-            last_value: None,
+impl MotorValve {
+    pub(super) fn update(&mut self, msg: &MavMessage) {
+        if msg.message_id() == GSE_TM_DATA::ID {
+            let value = self.mavlink_field.extract_as_f64(msg).log_unwrap();
+            self.last_value = Some(value != 0.0);
         }
     }
 }
 
-impl MessageView for MotorValve {
-    fn view_id(&self) -> crate::mavlink::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: &[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);
-                    }
-                }
-            }
+impl Default for MotorValve {
+    fn default() -> Self {
+        Self {
+            mavlink_field: 19
+                .to_mav_field(GSE_TM_DATA::ID, &MAVLINK_PROFILE)
+                .log_unwrap(), // n2_filling_valve_state for GSE_TM_DATA
+            last_value: None,
         }
-        Ok(())
     }
 }
diff --git a/src/ui/panes/pid_drawing_tool/symbols/labels.rs b/src/ui/panes/pid_drawing_tool/symbols/labels.rs
index 7534969d4018fe799721a4b7e8486803eb4f26b9..1ca1979e23939fdd9770d525846ae750abb6a562 100644
--- a/src/ui/panes/pid_drawing_tool/symbols/labels.rs
+++ b/src/ui/panes/pid_drawing_tool/symbols/labels.rs
@@ -1,14 +1,19 @@
 use serde::{Deserialize, Serialize};
-use skyward_mavlink::{mavlink::MessageData, orion};
+
+use egui::{Align2, Color32, CornerRadius, FontId, Stroke, StrokeKind, Theme, Ui};
+use glam::Vec2;
 
 use crate::{
-    mavlink::{extract_from_message, MavlinkResult, MessageView, ViewId},
+    MAVLINK_PROFILE,
+    error::ErrInstrument,
+    mavlink::{
+        GSE_TM_DATA, MavMessage, Message, MessageData,
+        reflection::{FieldLike, IndexedField},
+    },
     ui::utils::{egui_to_glam, glam_to_egui},
 };
 
-use super::{MavlinkValue, SymbolBehavior};
-use egui::{Align2, Color32, FontId, Rounding, Stroke, Theme, Ui};
-use glam::Vec2;
+use super::SymbolBehavior;
 
 const FONT_SIZE: f32 = 2.0;
 
@@ -20,7 +25,7 @@ pub struct Label {
     show_window: bool,
 
     last_value: Option<f32>,
-    source: MavlinkValue,
+    mavlink_field: IndexedField,
     size: Vec2,
 }
 
@@ -30,11 +35,9 @@ impl Default for Label {
             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(),
-            },
+            mavlink_field: 6
+                .to_mav_field(GSE_TM_DATA::ID, &MAVLINK_PROFILE)
+                .log_unwrap(), // n2_vessel_1_pressure for GSE_TM_DATA
             last_value: Some(0.0),
             size: Vec2::new(FONT_SIZE * 0.6 * 4.0, FONT_SIZE),
         }
@@ -62,9 +65,10 @@ impl SymbolBehavior for Label {
                 glam_to_egui(pos).to_pos2(),
                 glam_to_egui(self.size()) * size,
             ),
-            Rounding::ZERO,
+            CornerRadius::ZERO,
             Color32::TRANSPARENT,
-            Stroke::new(1.0, color),
+            Stroke::NONE,
+            StrokeKind::Middle,
         );
 
         println!("Drawing label edit window {}", self.show_window);
@@ -81,6 +85,14 @@ impl SymbolBehavior for Label {
         self.show_window = show_window;
     }
 
+    fn update(&mut self, message: &MavMessage) {
+        if message.message_id() == GSE_TM_DATA::ID {
+            let value = self.mavlink_field.extract_as_f64(message).log_unwrap();
+            self.last_value = Some(value as f32);
+            self.text = format!("{:.2}{}", value, self.units);
+        }
+    }
+
     fn anchor_points(&self) -> Option<Vec<Vec2>> {
         None
     }
@@ -97,43 +109,3 @@ impl SymbolBehavior for Label {
         }
     }
 }
-
-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(())
-    }
-}