Implement basic logging channels.

This commit is contained in:
2025-10-09 21:51:35 +02:00
parent 73f1c9633c
commit cb7bc2f025
5 changed files with 136 additions and 40 deletions

View File

@@ -56,6 +56,7 @@ pub struct App {
pub measurement_points: Arc<Mutex<MeasurementPointSet>>,
pub periods_per_dft: Arc<Mutex<Option<f32>>>,
pub periods_per_dft_multi: Arc<Mutex<(Vec<f32>, Option<Vec<f32>>)>>,
pub gui_logging_enabled: Arc<AtomicBool>,
}
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::<Vec<f32>>();
let phases = freqs.iter()
.map(|&f| {
-(f / fc).atan() * 180.0 / PI
})
.collect::<Vec<f32>>();
// let freqs = MeasurementPointSet::Eighteen.values().to_vec();
// let magnitudes = freqs.iter()
// .map(|&f| {
// 1.0 / (1.0 + (f / fc).powi(2)).sqrt()
// })
// .collect::<Vec<f32>>();
// let phases = freqs.iter()
// .map(|&f| {
// -(f / fc).atan() * 180.0 / PI
// })
// .collect::<Vec<f32>>();
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;

View File

@@ -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::<StartStopSignal>(2);
let run_impedancemeter_tx_clone = run_impedancemeter_tx.clone();
// Logging
let (log_tx, mut log_rx) = mpsc::channel::<LoggingSignal>(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,
));
});

View File

@@ -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<StartStopSignal>,
@@ -29,6 +29,8 @@ pub async fn communicate_with_hardware(
data_frequency: Arc<AtomicF32>,
periods_per_dft: Arc<Mutex<Option<f32>>>,
periods_per_dft_multi: Arc<Mutex<(Vec<f32>, Option<Vec<f32>>)>>,
gui_logging_enabled: Arc<AtomicBool>,
log_tx: Sender<LoggingSignal>,
) {
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<f32> = val.magnitudes_8.into_iter().collect();
let phases: Vec<f32> = 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<f32> = val.magnitudes_18.into_iter().collect();
let phases: Vec<f32> = 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);
}
}
},
}

View File

@@ -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<f32>, Vec<f32>), // magnitude, phase
}