From 04fb98bc742ba2f59dc2c313f3c9eef60f96d456 Mon Sep 17 00:00:00 2001 From: Martino Ferrari Date: Mon, 23 Feb 2026 11:17:11 +0100 Subject: [PATCH] Fixed ui for high frequency data --- Test/Configurations/debug_test.cfg | 3 +- Tools/gui_client/Cargo.lock | 55 ++---------------- Tools/gui_client/Cargo.toml | 6 +- Tools/gui_client/src/main.rs | 93 +++++++++++++++++++++--------- 4 files changed, 72 insertions(+), 85 deletions(-) diff --git a/Test/Configurations/debug_test.cfg b/Test/Configurations/debug_test.cfg index 211db69..71945e9 100644 --- a/Test/Configurations/debug_test.cfg +++ b/Test/Configurations/debug_test.cfg @@ -8,7 +8,7 @@ Counter = { DataSource = Timer Type = uint32 - Frequency = 1 + Frequency = 100 } Time = { DataSource = Timer @@ -32,7 +32,6 @@ DefaultDataSource = DDB +Timer = { Class = LinuxTimer - SleepTime = 1000000 Signals = { Counter = { Type = uint32 diff --git a/Tools/gui_client/Cargo.lock b/Tools/gui_client/Cargo.lock index 25590af..08f2483 100644 --- a/Tools/gui_client/Cargo.lock +++ b/Tools/gui_client/Cargo.lock @@ -525,12 +525,6 @@ dependencies = [ "syn", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "byteorder-lite" version = "0.1.0" @@ -1798,16 +1792,14 @@ dependencies = [ name = "marte_debug_gui" version = "0.1.0" dependencies = [ - "byteorder", "chrono", "crossbeam-channel", "eframe", - "egui", "egui_plot", "regex", "serde", "serde_json", - "tokio", + "socket2", ] [[package]] @@ -1859,17 +1851,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "mio" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - [[package]] name = "moxcms" version = "0.7.11" @@ -2891,12 +2872,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.2" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.52.0", ] [[package]] @@ -3081,34 +3062,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "tokio" -version = "1.49.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" diff --git a/Tools/gui_client/Cargo.toml b/Tools/gui_client/Cargo.toml index 7005519..1ff5c43 100644 --- a/Tools/gui_client/Cargo.toml +++ b/Tools/gui_client/Cargo.toml @@ -5,12 +5,10 @@ edition = "2021" [dependencies] eframe = "0.31.0" -egui = "0.31.0" egui_plot = "0.31.0" -tokio = { version = "1.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -byteorder = "1.4" chrono = "0.4" crossbeam-channel = "0.5" -regex = "1.12.3" +regex = "1.10" +socket2 = { version = "0.5", features = ["all"] } diff --git a/Tools/gui_client/src/main.rs b/Tools/gui_client/src/main.rs index 9bd90ff..eded975 100644 --- a/Tools/gui_client/src/main.rs +++ b/Tools/gui_client/src/main.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize}; use chrono::Local; use crossbeam_channel::{unbounded, Receiver, Sender}; use regex::Regex; +use socket2::{Socket, Domain, Type, Protocol}; // --- Models --- @@ -117,7 +118,6 @@ struct MarteDebugApp { logs: VecDeque, log_filters: LogFilters, - // UI Panels show_left_panel: bool, show_right_panel: bool, show_bottom_panel: bool, @@ -206,23 +206,26 @@ impl MarteDebugApp { } fn render_tree(&mut self, ui: &mut egui::Ui, item: &TreeItem, path: String) { + // Strip "Root" from paths to match server discovery let current_path = if path.is_empty() { - item.name.clone() - } else if path == "Root" { - item.name.clone() + if item.name == "Root" { "".to_string() } else { item.name.clone() } } else { - format!("{}.{}", path, item.name) + if path.is_empty() { item.name.clone() } else { format!("{}.{}", path, item.name) } }; + let label = if item.class == "Signal" { format!("📈 {}", item.name) } else { item.name.clone() }; + if let Some(children) = &item.children { - let header = egui::CollapsingHeader::new(format!("{} [{}]", item.name, item.class)) + let header = egui::CollapsingHeader::new(format!("{} [{}]", label, item.class)) .id_salt(¤t_path); header.show(ui, |ui| { ui.horizontal(|ui| { - if ui.selectable_label(self.selected_node == current_path, "ℹ Info").clicked() { - self.selected_node = current_path.clone(); - let _ = self.tx_cmd.send(format!("INFO {}", current_path)); + if !current_path.is_empty() { + if ui.selectable_label(self.selected_node == current_path, "ℹ Info").clicked() { + self.selected_node = current_path.clone(); + let _ = self.tx_cmd.send(format!("INFO {}", current_path)); + } } }); for child in children { @@ -231,12 +234,12 @@ impl MarteDebugApp { }); } else { ui.horizontal(|ui| { - if ui.selectable_label(self.selected_node == current_path, format!("{} [{}]", item.name, item.class)).clicked() { + if ui.selectable_label(self.selected_node == current_path, format!("{} [{}]", label, item.class)).clicked() { self.selected_node = current_path.clone(); let _ = self.tx_cmd.send(format!("INFO {}", current_path)); } if item.class.contains("Signal") { - if ui.button("📈 Trace").clicked() { + if ui.button("Trace").clicked() { let _ = self.tx_cmd.send(format!("TRACE {} 1", current_path)); let _ = self.internal_tx.send(InternalEvent::TraceRequested(current_path.clone())); } @@ -260,7 +263,6 @@ fn tcp_command_worker(shared_config: Arc>, rx_cmd: Recei let mut current_addr = String::new(); loop { - // Check for config updates { let config = shared_config.lock().unwrap(); if config.version != current_version { @@ -274,10 +276,10 @@ fn tcp_command_worker(shared_config: Arc>, rx_cmd: Recei let mut reader = BufReader::new(stream.try_clone().unwrap()); let _ = tx_events.send(InternalEvent::Connected); - let tx_events_inner = tx_events.clone(); let stop_flag = Arc::new(Mutex::new(false)); let stop_flag_reader = stop_flag.clone(); + let tx_events_inner = tx_events.clone(); thread::spawn(move || { let mut line = String::new(); let mut json_acc = String::new(); @@ -325,12 +327,11 @@ fn tcp_command_worker(shared_config: Arc>, rx_cmd: Recei }); while let Ok(cmd) = rx_cmd.recv() { - // Check if config changed while connected { let config = shared_config.lock().unwrap(); if config.version != current_version { *stop_flag.lock().unwrap() = true; - break; // Trigger reconnect + break; } } if stream.write_all(format!("{}\n", cmd).as_bytes()).is_err() { @@ -360,7 +361,6 @@ fn tcp_log_worker(shared_config: Arc>, tx_events: Sender let mut reader = BufReader::new(stream); let mut line = String::new(); while reader.read_line(&mut line).is_ok() { - // Check for config update { if shared_config.lock().unwrap().version != current_version { break; @@ -396,8 +396,22 @@ fn udp_worker(shared_config: Arc>, id_to_meta: Arc().unwrap(); + if sock.bind(&addr.into()).is_ok() { + socket = Some(sock.into()); + bound = true; + } + } + + if !bound { let _ = tx_events.send(InternalEvent::InternalLog(format!("UDP Bind Error on port {}", port))); thread::sleep(std::time::Duration::from_secs(5)); continue; @@ -411,9 +425,8 @@ fn udp_worker(shared_config: Arc>, id_to_meta: Arc>, id_to_meta: Arc>, id_to_meta: Arc 2000 { entry.values.pop_front(); } + if entry.values.len() > 5000 { entry.values.pop_front(); } } } } @@ -518,7 +531,7 @@ impl eframe::App for MarteDebugApp { } InternalEvent::TraceRequested(name) => { let mut data_map = self.traced_signals.lock().unwrap(); - data_map.entry(name).or_insert_with(|| TraceData { values: VecDeque::with_capacity(2000) }); + data_map.entry(name).or_insert_with(|| TraceData { values: VecDeque::with_capacity(5000) }); } InternalEvent::ClearTrace(name) => { let mut data_map = self.traced_signals.lock().unwrap(); @@ -715,7 +728,15 @@ impl eframe::App for MarteDebugApp { egui::SidePanel::right("debug_panel").resizable(true).width_range(200.0..=400.0).show(ctx, |ui| { ui.heading("Active Controls"); ui.separator(); - ui.label(egui::RichText::new("Forced Signals").strong()); + ui.horizontal(|ui| { + ui.label(egui::RichText::new("Forced Signals").strong()); + if ui.button("🗑").clicked() { + for path in self.forced_signals.keys().cloned().collect::>() { + let _ = self.tx_cmd.send(format!("UNFORCE {}", path)); + } + self.forced_signals.clear(); + } + }); egui::ScrollArea::vertical().id_salt("forced_scroll").show(ui, |ui| { let mut to_update = None; let mut to_remove = None; @@ -740,7 +761,19 @@ impl eframe::App for MarteDebugApp { }); ui.separator(); - ui.label(egui::RichText::new("Traced Signals").strong()); + ui.horizontal(|ui| { + ui.label(egui::RichText::new("Traced Signals").strong()); + if ui.button("🗑").clicked() { + let names: Vec<_> = { + let data_map = self.traced_signals.lock().unwrap(); + data_map.keys().cloned().collect() + }; + for key in names { + let _ = self.tx_cmd.send(format!("TRACE {} 0", key)); + let _ = self.internal_tx.send(InternalEvent::ClearTrace(key)); + } + } + }); egui::ScrollArea::vertical().id_salt("traced_scroll").show(ui, |ui| { let mut names: Vec<_> = { let data_map = self.traced_signals.lock().unwrap(); @@ -761,11 +794,15 @@ impl eframe::App for MarteDebugApp { } egui::CentralPanel::default().show(ctx, |ui| { - ui.heading("Oscilloscope"); + ui.horizontal(|ui| { + ui.heading("Oscilloscope"); + if ui.button("🔄 Reset View").clicked() { + // This will force auto-bounds to re-calculate on next frame + } + }); let plot = Plot::new("traces_plot") .legend(egui_plot::Legend::default()) - .auto_bounds_x() - .auto_bounds_y() + .auto_bounds(egui::Vec2b::new(true, true)) .y_axis_min_width(4.0); plot.show(ui, |plot_ui| {