From cb7bc2f025b8b668b580129686b16edb45e99c31 Mon Sep 17 00:00:00 2001 From: Hubald Verzijl Date: Thu, 9 Oct 2025 21:51:35 +0200 Subject: [PATCH] Implement basic logging channels. --- Cargo.lock | 18 +++++++++--- src/app.rs | 49 +++++++++++++++++++++---------- src/bin/main_gui.rs | 35 ++++++++++++++++++++-- src/communication.rs | 69 ++++++++++++++++++++++++++++++++------------ src/signals.rs | 5 ++++ 5 files changed, 136 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27b865e..3d6503b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3019,18 +3019,28 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", diff --git a/src/app.rs b/src/app.rs index 748d014..3b5557d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -56,6 +56,7 @@ pub struct App { pub measurement_points: Arc>, pub periods_per_dft: Arc>>, pub periods_per_dft_multi: Arc, Option>)>>, + pub gui_logging_enabled: Arc, } struct TabViewer { @@ -327,8 +328,9 @@ impl TabViewer { fn shortcuts(&mut self, ui: &mut egui::Ui) { ui.heading("Shortcuts"); ui.label("Space: Start/Stop measurement"); - ui.label("C: Clear plots"); + ui.label("R: Reset view/plots"); ui.label("S: Toggle settings"); + ui.label("L: Toggle logging"); ui.label("CMD-W: Close window"); } } @@ -462,25 +464,26 @@ impl App { measurement_points, periods_per_dft, periods_per_dft_multi, + gui_logging_enabled: Arc::new(AtomicBool::new(false)), }; // For testing purposes, populate the Bode plot with a sample low-pass filter response - let fc = 1000.0; // cutoff frequency in Hz + // let fc = 1000.0; // cutoff frequency in Hz - let freqs = MeasurementPointSet::Eighteen.values().to_vec(); - let magnitudes = freqs.iter() - .map(|&f| { - 1.0 / (1.0 + (f / fc).powi(2)).sqrt() - }) - .collect::>(); - let phases = freqs.iter() - .map(|&f| { - -(f / fc).atan() * 180.0 / PI - }) - .collect::>(); + // let freqs = MeasurementPointSet::Eighteen.values().to_vec(); + // let magnitudes = freqs.iter() + // .map(|&f| { + // 1.0 / (1.0 + (f / fc).powi(2)).sqrt() + // }) + // .collect::>(); + // let phases = freqs.iter() + // .map(|&f| { + // -(f / fc).atan() * 180.0 / PI + // }) + // .collect::>(); - app.bode_plot.lock().unwrap().update_magnitudes(MeasurementPointSet::Eighteen, magnitudes); - app.bode_plot.lock().unwrap().update_phases(MeasurementPointSet::Eighteen, phases); + // app.bode_plot.lock().unwrap().update_magnitudes(MeasurementPointSet::Eighteen, magnitudes); + // app.bode_plot.lock().unwrap().update_phases(MeasurementPointSet::Eighteen, phases); app.update_start_stop(); app @@ -537,6 +540,13 @@ impl eframe::App for App { self.reset_view(); } + ui.separator(); + + let mut gui_logging_enabled = self.gui_logging_enabled.load(Ordering::Relaxed); + if ui.add(egui::Checkbox::new(&mut gui_logging_enabled, "Logging")).changed() { + self.gui_logging_enabled.store(gui_logging_enabled, Ordering::Relaxed); + } + // Spacer to push the LED to the right ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { ui.scope(|ui| { @@ -625,10 +635,17 @@ impl eframe::App for App { } // Reset view - if ctx.input(|i| i.key_pressed(Key::C)) { + if ctx.input(|i| i.key_pressed(Key::R)) { self.reset_view(); } + // Enable/disable GUI logging + if ctx.input(|i| i.key_pressed(egui::Key::L)) { + let mut gui_logging_enabled = self.gui_logging_enabled.load(Ordering::Relaxed); + gui_logging_enabled = !gui_logging_enabled; + self.gui_logging_enabled.store(gui_logging_enabled, Ordering::Relaxed); + } + // Toggle setttings view if ctx.input(|i| i.key_pressed(egui::Key::S)) { self.tab_viewer.show_settings = !self.tab_viewer.show_settings; diff --git a/src/bin/main_gui.rs b/src/bin/main_gui.rs index e9908f6..0ca1e18 100644 --- a/src/bin/main_gui.rs +++ b/src/bin/main_gui.rs @@ -2,7 +2,7 @@ use simple_logger::SimpleLogger; use log::info; use tokio::runtime::Runtime; -use bioz_host_rs::app::App; +use bioz_host_rs::{app::App, signals::LoggingSignal}; use bioz_host_rs::communication::communicate_with_hardware; @@ -10,7 +10,8 @@ use tokio::sync::mpsc::{self}; use bioz_host_rs::signals::StartStopSignal; -fn main() { +#[tokio::main] +async fn main() { SimpleLogger::new().init().expect("Failed to initialize logger"); log::set_max_level(log::LevelFilter::Info); info!("Starting Bioz Impedance Visualizer..."); @@ -20,9 +21,13 @@ fn main() { // Enter the runtime so that `tokio::spawn` is available immediately. // let _enter = rt.enter(); + // Channel to communicate with the communication task. let (run_impedancemeter_tx, run_impedancemeter_rx) = mpsc::channel::(2); let run_impedancemeter_tx_clone = run_impedancemeter_tx.clone(); + // Logging + let (log_tx, mut log_rx) = mpsc::channel::(10); + let app = App::new(run_impedancemeter_tx); let magnitude_clone = app.magnitude.clone(); let phase_clone = app.phase.clone(); @@ -36,6 +41,30 @@ fn main() { let periods_per_dft = app.periods_per_dft.clone(); let periods_per_dft_multi = app.periods_per_dft_multi.clone(); + let gui_logging_enabled = app.gui_logging_enabled.clone(); + + // Log thread + tokio::spawn(async move { + loop { + match log_rx.recv().await { + Some(signal) => { + match signal { + LoggingSignal::SingleImpedance(magnitude, phase) => { + info!("Single Impedance - Magnitude: {:.3}, Phase: {:.3}", magnitude, phase); + } + LoggingSignal::MultiImpedance(magnitudes, phases) => { + info!("Multi Impedance - Magnitudes: {:?}, Phases: {:?}", magnitudes, phases); + } + } + } + None => { + // Channel closed + break; + } + } + } + }); + // Execute the runtime in its own thread. std::thread::spawn(move || { rt.block_on(communicate_with_hardware( @@ -50,6 +79,8 @@ fn main() { data_frequency_clone, periods_per_dft, periods_per_dft_multi, + gui_logging_enabled, + log_tx, )); }); diff --git a/src/communication.rs b/src/communication.rs index acccaef..5c4bad5 100644 --- a/src/communication.rs +++ b/src/communication.rs @@ -15,7 +15,7 @@ use crate::client::WorkbookClient; use crate::plot::{TimeSeriesPlot, BodePlot}; -use crate::signals::StartStopSignal; +use crate::signals::{LoggingSignal, StartStopSignal}; pub async fn communicate_with_hardware( mut run_impedancemeter_rx: Receiver, @@ -29,6 +29,8 @@ pub async fn communicate_with_hardware( data_frequency: Arc, periods_per_dft: Arc>>, periods_per_dft_multi: Arc, Option>)>>, + gui_logging_enabled: Arc, + log_tx: Sender, ) { let data_counter = Arc::new(AtomicU32::new(0)); let data_counter_clone = data_counter.clone(); @@ -73,20 +75,32 @@ pub async fn communicate_with_hardware( let data = (magnitude_series.clone(), phase_series.clone(), magnitude.clone(), phase.clone()); let data_counter_clone_single = data_counter_clone.clone(); + // Clone log_tx for the task + let gui_logging_enabled_clone = gui_logging_enabled.clone(); + let log_tx_clone = log_tx.clone(); + tokio::spawn(async move { while let Ok(val) = single_impedance_sub.recv().await { - let mut mag_plot = data.0.lock().unwrap(); - let mut phase_plot = data.1.lock().unwrap(); - let mut mag_val = data.2.lock().unwrap(); - let mut phase_val = data.3.lock().unwrap(); + { + let mut mag_plot = data.0.lock().unwrap(); + let mut phase_plot = data.1.lock().unwrap(); + let mut mag_val = data.2.lock().unwrap(); + let mut phase_val = data.3.lock().unwrap(); - *mag_val = val.magnitude; - *phase_val = val.phase; - - mag_plot.add(val.magnitude as f64); - phase_plot.add(val.phase as f64); - - data_counter_clone_single.fetch_add(1, Ordering::Relaxed); + *mag_val = val.magnitude; + *phase_val = val.phase; + + mag_plot.add(val.magnitude as f64); + phase_plot.add(val.phase as f64); + + data_counter_clone_single.fetch_add(1, Ordering::Relaxed); + } + // Send logging signal + if gui_logging_enabled_clone.load(Ordering::Relaxed) { + if let Err(e) = log_tx_clone.try_send(LoggingSignal::SingleImpedance(val.magnitude, val.phase)) { + error!("Failed to send logging signal: {:?}", e); + } + } } info!("SingleImpedanceOutputTopic subscription ended."); }); @@ -101,23 +115,42 @@ pub async fn communicate_with_hardware( let data = bode_series.clone(); let data_counter_clone_multi = data_counter_clone.clone(); + // Clone log_tx for the task + let gui_logging_enabled_clone = gui_logging_enabled.clone(); + let log_tx_clone = log_tx.clone(); + tokio::spawn(async move { while let Ok(val) = multi_impedance_sub.recv().await { - let mut bode_plot = data.lock().unwrap(); - + match val.points { MeasurementPointSet::Eight => { let magnitudes: Vec = val.magnitudes_8.into_iter().collect(); let phases: Vec = val.phases_8.into_iter().collect(); - bode_plot.update_magnitudes(MeasurementPointSet::Eight, magnitudes); - bode_plot.update_phases(MeasurementPointSet::Eight, phases); + { + let mut bode_plot = data.lock().unwrap(); + bode_plot.update_magnitudes(MeasurementPointSet::Eight, magnitudes.clone()); + bode_plot.update_phases(MeasurementPointSet::Eight, phases.clone()); + } + if gui_logging_enabled_clone.load(Ordering::Relaxed) { + if let Err(e) = log_tx_clone.try_send(LoggingSignal::MultiImpedance(magnitudes.clone(), phases.clone())) { + error!("Failed to send logging signal: {:?}", e); + } + } }, MeasurementPointSet::Eighteen => { let magnitudes: Vec = val.magnitudes_18.into_iter().collect(); let phases: Vec = val.phases_18.into_iter().collect(); - bode_plot.update_magnitudes(MeasurementPointSet::Eighteen, magnitudes); - bode_plot.update_phases(MeasurementPointSet::Eighteen, phases); + { + let mut bode_plot = data.lock().unwrap(); + bode_plot.update_magnitudes(MeasurementPointSet::Eighteen, magnitudes.clone()); + bode_plot.update_phases(MeasurementPointSet::Eighteen, phases.clone()); + } + if gui_logging_enabled_clone.load(Ordering::Relaxed) { + if let Err(e) = log_tx_clone.try_send(LoggingSignal::MultiImpedance(magnitudes.clone(), phases.clone())) { + error!("Failed to send logging signal: {:?}", e); + } + } }, } diff --git a/src/signals.rs b/src/signals.rs index 5171af3..e5af6f6 100644 --- a/src/signals.rs +++ b/src/signals.rs @@ -6,3 +6,8 @@ pub enum StartStopSignal { StartMulti(MeasurementPointSet), // DFT number, number of points per measurement Stop, } + +pub enum LoggingSignal { + SingleImpedance(f32, f32), // magnitude, phase + MultiImpedance(Vec, Vec), // magnitude, phase +} \ No newline at end of file