From 961e6cc34a931baa88c995a5dbe95d079418d666 Mon Sep 17 00:00:00 2001 From: Hubald Verzijl Date: Tue, 12 Aug 2025 11:59:10 +0200 Subject: [PATCH] Added log and automatic reconnect. --- Cargo.lock | 93 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/app.rs | 22 +++++---- src/bin/main_cli.rs | 8 +++- src/bin/main_gui.rs | 7 ++- src/client.rs | 37 ++------------- src/communication.rs | 108 +++++++++++++++++++++++++++---------------- 7 files changed, 193 insertions(+), 84 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2ba6182..6c68fcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -435,7 +435,9 @@ dependencies = [ "defmt", "eframe", "egui_plot", + "log", "postcard-rpc", + "simple_logger", "tokio", "tokio-serial", ] @@ -647,6 +649,16 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + [[package]] name = "combine" version = "4.6.7" @@ -791,6 +803,15 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -1639,6 +1660,12 @@ dependencies = [ "libc", ] +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "jni" version = "0.21.1" @@ -1987,6 +2014,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.19" @@ -2019,6 +2052,15 @@ dependencies = [ "syn", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "nusb" version = "0.1.14" @@ -2540,6 +2582,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro-crate" version = "3.3.0" @@ -2872,6 +2920,18 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simple_logger" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb" +dependencies = [ + "colored", + "log", + "time", + "windows-sys 0.48.0", +] + [[package]] name = "slab" version = "0.4.10" @@ -3111,6 +3171,39 @@ dependencies = [ "weezl", ] +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tiny-skia" version = "0.11.4" diff --git a/Cargo.toml b/Cargo.toml index 0cf9483..dcb2200 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ edition = "2024" defmt = { version = "1.0.1" } eframe = { version = "0.32.0"} egui_plot = "0.33.0" +log = "0.4.27" +simple_logger = "5.0.0" [dependencies.bioz-icd-rs] path = "../bioz-icd-rs" diff --git a/src/app.rs b/src/app.rs index 77291a7..884ff62 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,16 +1,16 @@ -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex}; use std::ops::RangeInclusive; -use tokio::{sync::mpsc::{self, Receiver, Sender}, time::Timeout}; +use tokio::{sync::mpsc::{Sender}}; -use eframe::egui::{self, Button, Checkbox, Color32, DragValue, Key, Layout, Modifiers, RichText, Rounding, }; +use eframe::egui::{self, Color32, DragValue, Key, Layout, Modifiers}; use egui_plot::{Corner, Legend, Line, Plot, PlotPoints, Points, PlotBounds}; use crate::plot::TimeSeriesPlot; pub struct App { - interval_ms: u32, + frequency: u32, run_impedancemeter_tx: Sender, pub magnitude: Arc>, pub phase: Arc>, @@ -21,7 +21,7 @@ pub struct App { impl App { pub fn new(run_impedancemeter_tx: Sender) -> Self { App { - interval_ms: 10, // Default interval + frequency: 2, // Default frequency run_impedancemeter_tx, magnitude: Arc::new(Mutex::new(0.0)), phase: Arc::new(Mutex::new(0.0)), @@ -39,18 +39,20 @@ impl eframe::App for App { egui::widgets::global_theme_preference_switch(ui); ui.separator(); - if ui.add(DragValue::new(&mut self.interval_ms).speed(0.1).range(RangeInclusive::new(0, 50)).update_while_editing(false)).changed() { + if ui.add(DragValue::new(&mut self.frequency).speed(0.1).range(RangeInclusive::new(0, 50)).update_while_editing(false)).changed() { if let Err(e) = self.run_impedancemeter_tx.try_send(0) { eprintln!("Failed to send stop command: {:?}", e); } // Delay - if let Err(e) = self.run_impedancemeter_tx.try_send(self.interval_ms) { - eprintln!("Failed to send interval update: {:?}", e); + if let Err(e) = self.run_impedancemeter_tx.try_send(self.frequency) { + eprintln!("Failed to send frequency update: {:?}", e); } }; + ui.separator(); + if ui.button("Start").clicked() { - if let Err(e) = self.run_impedancemeter_tx.try_send(self.interval_ms) { + if let Err(e) = self.run_impedancemeter_tx.try_send(self.frequency) { eprintln!("Failed to send start command: {:?}", e); } } @@ -67,7 +69,7 @@ impl eframe::App for App { egui::CentralPanel::default().show(ctx, |ui| { let available_height = ui.available_height(); - let mut half_height = available_height / 4.0; + let half_height = available_height / 4.0; let point_pos = vec![[*self.magnitude.lock().unwrap() as f64, *self.phase.lock().unwrap() as f64]]; diff --git a/src/bin/main_cli.rs b/src/bin/main_cli.rs index d30585c..0c8c25e 100644 --- a/src/bin/main_cli.rs +++ b/src/bin/main_cli.rs @@ -8,7 +8,13 @@ use bioz_host_rs::{client::WorkbookClient, icd, read_line}; #[tokio::main] async fn main() { println!("Connecting to USB device..."); - let client = WorkbookClient::new(); + let client = match WorkbookClient::new() { + Ok(client) => client, + Err(e) => { + eprintln!("Failed to connect to USB device: {}", e); + return; + } + }; for i in 0..10 { diff --git a/src/bin/main_gui.rs b/src/bin/main_gui.rs index 716c20b..3de4dfd 100644 --- a/src/bin/main_gui.rs +++ b/src/bin/main_gui.rs @@ -1,3 +1,5 @@ +use simple_logger::SimpleLogger; +use log::info; use tokio::runtime::Runtime; use bioz_host_rs::app::App; @@ -7,7 +9,10 @@ use bioz_host_rs::communication::communicate_with_hardware; use tokio::sync::mpsc::{self}; fn main() { - + SimpleLogger::new().init().expect("Failed to initialize logger"); + log::set_max_level(log::LevelFilter::Info); + info!("Starting Bioz Impedance Visualizer..."); + let rt = Runtime::new().expect("Unable to create Runtime"); // Enter the runtime so that `tokio::spawn` is available immediately. diff --git a/src/client.rs b/src/client.rs index 344b2fe..195ea11 100644 --- a/src/client.rs +++ b/src/client.rs @@ -26,36 +26,17 @@ impl From> for WorkbookError { } } -trait FlattenErr { - type Good; - type Bad; - fn flatten(self) -> Result>; -} - -impl FlattenErr for Result { - type Good = T; - type Bad = E; - fn flatten(self) -> Result> { - self.map_err(WorkbookError::Endpoint) - } -} - // --- impl WorkbookClient { - pub fn new() -> Self { - let client = HostClient::new_raw_nusb( + pub fn new() -> Result { + let client = HostClient::try_new_raw_nusb( |d| d.product_string() == Some("Bioz Amplifier"), ERROR_PATH, 8, VarSeqKind::Seq2, - ); - Self { client } - } - - pub fn new_serial(port: &str) -> Self { - let client = HostClient::new_serial_cobs(port, ERROR_PATH, 8, 9600, VarSeqKind::Seq2); - Self { client } + )?; + Ok(Self { client }) } pub async fn wait_closed(&self) { @@ -87,7 +68,6 @@ impl WorkbookClient { self.client .send_resp::(&StartImpedance { update_frequency: 60, sinus_frequency: frequency }) .await?; - Ok(()) } @@ -96,13 +76,6 @@ impl WorkbookClient { .client .send_resp::(&()) .await?; - Ok(res) } -} - -impl Default for WorkbookClient { - fn default() -> Self { - Self::new() - } -} +} \ No newline at end of file diff --git a/src/communication.rs b/src/communication.rs index 2cdb5f8..9cf6ad2 100644 --- a/src/communication.rs +++ b/src/communication.rs @@ -1,3 +1,5 @@ +use log::{error, info}; + use tokio::select; use tokio::sync::mpsc::Receiver; @@ -15,52 +17,78 @@ pub async fn communicate_with_hardware( magnitude_series: Arc>, phase_series: Arc>, ) { - let workbook_client = WorkbookClient::new(); - - let mut sub = workbook_client - .client - .subscribe_multi::(8) - .await - .unwrap(); - - tokio::spawn(async move { - while let Ok(val) = sub.recv().await { - let mut mag_plot = magnitude_series.lock().unwrap(); - let mut phase_plot = phase_series.lock().unwrap(); - - *magnitude.lock().unwrap() = val.magnitude; - *phase.lock().unwrap() = val.phase; - - mag_plot.add(val.magnitude as f64); - phase_plot.add(val.phase as f64); - } - }); - loop { - select! { - Some(run) = run_impedancemeter_rx.recv() => { - if run > 0 { - // Start the impedancemeter - if let Err(e) = workbook_client.start_impedancemeter(run as f32).await { - eprintln!("Failed to start impedancemeter: {:?}", e); + let workbook_client = match WorkbookClient::new() { + Ok(client) => { + info!("Connected to hardware successfully."); + client + }, + Err(e) => { + error!("Failed to connect to hardware: {:?}", e); + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + continue; + } + }; + + let mut sub = workbook_client + .client + .subscribe_multi::(8) + .await + .unwrap(); + + let data = (magnitude_series.clone(), phase_series.clone(), magnitude.clone(), phase.clone()); + + tokio::spawn(async move { + while let Ok(val) = 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(); + + *mag_val = val.magnitude; + *phase_val = val.phase; + + mag_plot.add(val.magnitude as f64); + phase_plot.add(val.phase as f64); + } + info!("ImpedanceTopic subscription ended."); + + }); + + loop { + select! { + Some(run) = run_impedancemeter_rx.recv() => { + if run > 0 { + // Start the impedancemeter + if let Err(e) = workbook_client.start_impedancemeter(run as f32).await { + error!("Failed to start impedancemeter: {:?}", e); + } else { + info!("Impedancemeter started."); + } } else { - println!("Impedancemeter started."); - } - } else { - // Stop the impedancemeter - if let Err(e) = workbook_client.stop_impedancemeter().await { - eprintln!("Failed to stop impedancemeter: {:?}", e); - } else { - println!("Impedancemeter stopped."); + // Stop the impedancemeter + if let Err(e) = workbook_client.stop_impedancemeter().await { + error!("Failed to stop impedancemeter: {:?}", e); + } else { + info!("Impedancemeter stopped."); + } } } - } + _ = workbook_client.wait_closed() => { + // Handle client closure + info!("Client connection closed."); + tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; + break; + } - else => { - // All channels closed - break; - } + else => { + // All channels closed + break; + } + } } + info!("Communication with hardware ended."); + // Wait for a short period before trying to reconnect } } \ No newline at end of file