mirror of
https://github.com/hubaldv/bioz-host-rs.git
synced 2026-07-24 00:57:44 +00:00
Updated logger as separate module.
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
49
src/app.rs
49
src/app.rs
@@ -20,7 +20,7 @@ use egui_dock::{DockArea, DockState, Style, TabViewer};
|
|||||||
use egui_extras::{TableBuilder, Column};
|
use egui_extras::{TableBuilder, Column};
|
||||||
|
|
||||||
use crate::state::{AppState, ControlCommand, HardwareConnected, HardwareState, MeasurementDataState, Mode};
|
use crate::state::{AppState, ControlCommand, HardwareConnected, HardwareState, MeasurementDataState, Mode};
|
||||||
use crate::logging::LoggingStates;
|
use crate::logging::LoggingState;
|
||||||
use crate::signals::LoggingSignal;
|
use crate::signals::LoggingSignal;
|
||||||
use crate::icd::{BioImpedanceLeadMode, IcdDftNum, SweepPoints, ElectrodeOptionsWithMultiplexer};
|
use crate::icd::{BioImpedanceLeadMode, IcdDftNum, SweepPoints, ElectrodeOptionsWithMultiplexer};
|
||||||
|
|
||||||
@@ -52,15 +52,15 @@ pub struct App {
|
|||||||
tree: DockState<String>,
|
tree: DockState<String>,
|
||||||
tab_viewer: CustomTabs,
|
tab_viewer: CustomTabs,
|
||||||
// tab_active: TabActive,
|
// tab_active: TabActive,
|
||||||
log_tx: mpsc::Sender<LoggingSignal>,
|
|
||||||
pub gui_logging_state: Arc<Mutex<LoggingStates>>,
|
|
||||||
log_filename: Arc<Mutex<String>>,
|
log_filename: Arc<Mutex<String>>,
|
||||||
log_marker_modal: bool,
|
log_marker_modal: bool,
|
||||||
log_marker: String,
|
log_marker: String,
|
||||||
measurement_data: Arc<MeasurementDataState>,
|
measurement_data: Arc<MeasurementDataState>,
|
||||||
control_tx: mpsc::Sender<ControlCommand>,
|
control_tx: mpsc::Sender<ControlCommand>,
|
||||||
|
logging_control_tx: mpsc::Sender<LoggingSignal>,
|
||||||
app_state_rx: sync::watch::Receiver<AppState>,
|
app_state_rx: sync::watch::Receiver<AppState>,
|
||||||
hardware_state_rx: sync::watch::Receiver<HardwareState>
|
hardware_state_rx: sync::watch::Receiver<HardwareState>,
|
||||||
|
logging_state_rx: sync::watch::Receiver<LoggingState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CustomTabs {
|
struct CustomTabs {
|
||||||
@@ -616,11 +616,13 @@ impl TabViewer for CustomTabs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new(log_tx: mpsc::Sender<LoggingSignal>,
|
pub fn new(measurement_data: Arc<MeasurementDataState>,
|
||||||
measurement_data: Arc<MeasurementDataState>,
|
|
||||||
control_tx: mpsc::Sender<ControlCommand>,
|
control_tx: mpsc::Sender<ControlCommand>,
|
||||||
|
logging_control_tx: mpsc::Sender<LoggingSignal>,
|
||||||
app_state_rx: watch::Receiver<AppState>,
|
app_state_rx: watch::Receiver<AppState>,
|
||||||
hardware_state_rx: watch::Receiver<HardwareState>) -> Self {
|
hardware_state_rx: watch::Receiver<HardwareState>,
|
||||||
|
logging_state_rx: watch::Receiver<LoggingState>
|
||||||
|
) -> Self {
|
||||||
// Step 1: Initialize shared fields first
|
// Step 1: Initialize shared fields first
|
||||||
// let tab_active = app_state_rx.borrow().tab_active;
|
// let tab_active = app_state_rx.borrow().tab_active;
|
||||||
|
|
||||||
@@ -647,15 +649,15 @@ impl App {
|
|||||||
tree: DockState::new(vec!["Single".to_string(), "Sweep".to_string(), "Shortcuts".to_string()]),
|
tree: DockState::new(vec!["Single".to_string(), "Sweep".to_string(), "Shortcuts".to_string()]),
|
||||||
tab_viewer,
|
tab_viewer,
|
||||||
// tab_active,
|
// tab_active,
|
||||||
log_tx,
|
|
||||||
gui_logging_state: Arc::new(Mutex::new(LoggingStates::Idle)),
|
|
||||||
log_filename,
|
log_filename,
|
||||||
log_marker_modal: false,
|
log_marker_modal: false,
|
||||||
log_marker: String::new(),
|
log_marker: String::new(),
|
||||||
measurement_data,
|
measurement_data,
|
||||||
control_tx,
|
control_tx,
|
||||||
|
logging_control_tx,
|
||||||
app_state_rx,
|
app_state_rx,
|
||||||
hardware_state_rx,
|
hardware_state_rx,
|
||||||
|
logging_state_rx,
|
||||||
};
|
};
|
||||||
|
|
||||||
// For testing purposes, populate the Bode plot with a sample low-pass filter response
|
// For testing purposes, populate the Bode plot with a sample low-pass filter response
|
||||||
@@ -745,17 +747,17 @@ impl eframe::App for App {
|
|||||||
|
|
||||||
ui.add_enabled(is_connected, Label::new("Logging:"));
|
ui.add_enabled(is_connected, Label::new("Logging:"));
|
||||||
|
|
||||||
let gui_logging_state = *self.gui_logging_state.lock().unwrap();
|
let gui_logging_state = *self.logging_state_rx.borrow();
|
||||||
|
|
||||||
let mut logging_enabled = !matches!(gui_logging_state, LoggingStates::Idle);
|
let mut logging_enabled = !matches!(gui_logging_state, LoggingState::Idle);
|
||||||
|
|
||||||
if ui.add_enabled(is_connected, custom_toggle_widget(&mut logging_enabled)).changed() {
|
if ui.add_enabled(is_connected, custom_toggle_widget(&mut logging_enabled)).changed() {
|
||||||
let signal = match gui_logging_state {
|
let signal = match gui_logging_state {
|
||||||
LoggingStates::Idle => LoggingSignal::StartFileLogging(self.log_filename.lock().unwrap().clone()),
|
LoggingState::Idle => LoggingSignal::StartFileLogging(self.log_filename.lock().unwrap().clone()),
|
||||||
LoggingStates::Starting | LoggingStates::Logging => LoggingSignal::StopFileLogging,
|
LoggingState::Starting | LoggingState::Logging => LoggingSignal::StopFileLogging,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.log_tx.try_send(signal).unwrap_or_else(|e| {
|
self.logging_control_tx.try_send(signal).unwrap_or_else(|e| {
|
||||||
error!("Failed to send logging toggle signal: {:?}", e);
|
error!("Failed to send logging toggle signal: {:?}", e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -796,8 +798,7 @@ impl eframe::App for App {
|
|||||||
let enter_pressed = text_edit_response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter));
|
let enter_pressed = text_edit_response.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter));
|
||||||
|
|
||||||
if add_clicked || enter_pressed {
|
if add_clicked || enter_pressed {
|
||||||
info!("Adding marker: {}", self.log_marker);
|
self.logging_control_tx.try_send(LoggingSignal::AddMarker(self.log_marker.clone())).unwrap_or_else(|e| {
|
||||||
self.log_tx.try_send(LoggingSignal::AddMarker(self.log_marker.clone())).unwrap_or_else(|e| {
|
|
||||||
error!("Failed to send logging marker signal: {:?}", e);
|
error!("Failed to send logging marker signal: {:?}", e);
|
||||||
});
|
});
|
||||||
self.log_marker = String::new();
|
self.log_marker = String::new();
|
||||||
@@ -817,7 +818,7 @@ impl eframe::App for App {
|
|||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
ui.add_enabled_ui(gui_logging_state == LoggingStates::Idle, |ui| {
|
ui.add_enabled_ui(gui_logging_state == LoggingState::Idle, |ui| {
|
||||||
ui.label("Log filename:");
|
ui.label("Log filename:");
|
||||||
TextEdit::singleline(&mut *self.log_filename.lock().unwrap()).desired_width(150.0).id(Id::new("file_name_field")).ui(ui);
|
TextEdit::singleline(&mut *self.log_filename.lock().unwrap()).desired_width(150.0).id(Id::new("file_name_field")).ui(ui);
|
||||||
});
|
});
|
||||||
@@ -894,25 +895,25 @@ impl eframe::App for App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Toggle marker modal
|
// Toggle marker modal
|
||||||
if ctx.input(|i| i.key_pressed(egui::Key::A)) && *self.gui_logging_state.lock().unwrap() == LoggingStates::Logging {
|
if ctx.input(|i| i.key_pressed(egui::Key::A)) && *self.logging_state_rx.borrow() == LoggingState::Logging {
|
||||||
self.log_marker_modal = !self.log_marker_modal;
|
self.log_marker_modal = !self.log_marker_modal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable/disable GUI logging
|
// Enable/disable GUI logging
|
||||||
if ctx.input(|i| i.key_pressed(egui::Key::L)) {
|
if ctx.input(|i| i.key_pressed(egui::Key::L)) {
|
||||||
let gui_logging_enabled = *self.gui_logging_state.lock().unwrap();
|
let gui_logging_enabled = *self.logging_state_rx.borrow();
|
||||||
match gui_logging_enabled {
|
match gui_logging_enabled {
|
||||||
LoggingStates::Starting => {
|
LoggingState::Starting => {
|
||||||
// If currently starting, do nothing
|
// If currently starting, do nothing
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
LoggingStates::Logging => {
|
LoggingState::Logging => {
|
||||||
if let Err(e) = self.log_tx.try_send(LoggingSignal::StopFileLogging) {
|
if let Err(e) = self.logging_control_tx.try_send(LoggingSignal::StopFileLogging) {
|
||||||
error!("Failed to send logging signal: {:?}", e);
|
error!("Failed to send logging signal: {:?}", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
LoggingStates::Idle => {
|
LoggingState::Idle => {
|
||||||
if let Err(e) = self.log_tx.try_send(LoggingSignal::StartFileLogging(self.log_filename.lock().unwrap().clone())) {
|
if let Err(e) = self.logging_control_tx.try_send(LoggingSignal::StartFileLogging(self.log_filename.lock().unwrap().clone())) {
|
||||||
error!("Failed to send logging signal: {:?}", e);
|
error!("Failed to send logging signal: {:?}", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use bioz_host_rs::communication::communicate_with_hardware;
|
|||||||
use tokio::sync::{watch, mpsc};
|
use tokio::sync::{watch, mpsc};
|
||||||
|
|
||||||
use bioz_host_rs::signals::StartStopSignal;
|
use bioz_host_rs::signals::StartStopSignal;
|
||||||
use bioz_host_rs::logging::log_data;
|
use bioz_host_rs::logging::{LoggingState, log_data};
|
||||||
|
|
||||||
use bioz_host_rs::state::{AppState, ControlCommand, HardwareState, MeasurementDataState};
|
use bioz_host_rs::state::{AppState, ControlCommand, HardwareState, MeasurementDataState};
|
||||||
use bioz_host_rs::control::app_control_loop;
|
use bioz_host_rs::control::app_control_loop;
|
||||||
@@ -25,45 +25,46 @@ async fn main() {
|
|||||||
log::set_max_level(log::LevelFilter::Info);
|
log::set_max_level(log::LevelFilter::Info);
|
||||||
info!("Starting Bioz Impedance Visualizer...");
|
info!("Starting Bioz Impedance Visualizer...");
|
||||||
|
|
||||||
// Enter the runtime so that `tokio::spawn` is available immediately.
|
// Init watches for hardware state, app state and logging state
|
||||||
// let _enter = rt.enter();
|
|
||||||
|
|
||||||
// Init watches for hardware state and app state
|
|
||||||
let (hardware_state_tx, hardware_state_rx) = watch::channel(HardwareState::default());
|
|
||||||
let (app_state_tx, app_state_rx) = watch::channel(AppState::default());
|
let (app_state_tx, app_state_rx) = watch::channel(AppState::default());
|
||||||
|
let (hardware_state_tx, hardware_state_rx) = watch::channel(HardwareState::default());
|
||||||
|
let (logging_state_tx, logging_state_rx) = watch::channel(LoggingState::default());
|
||||||
|
|
||||||
// Init control channels to update hardware and app
|
// Init control channels to update hardware, app and logging
|
||||||
let (hardware_control_tx, hardware_control_rx) = mpsc::channel::<StartStopSignal>(2);
|
|
||||||
let (app_control_tx, app_control_rx) = mpsc::channel::<ControlCommand>(32);
|
let (app_control_tx, app_control_rx) = mpsc::channel::<ControlCommand>(32);
|
||||||
|
let (hardware_control_tx, hardware_control_rx) = mpsc::channel::<StartStopSignal>(2);
|
||||||
// let run_impedancemeter_tx_clone = run_impedancemeter_tx.clone();
|
let (logging_control_tx, logging_control_rx) = mpsc::channel::<LoggingSignal>(10);
|
||||||
let hardware_state_rx_clone = hardware_state_rx.clone();
|
|
||||||
let hardware_control_tx_clone = hardware_control_tx.clone();
|
|
||||||
let app_control_tx_clone = app_control_tx.clone();
|
|
||||||
|
|
||||||
tokio::spawn(async move {
|
|
||||||
app_control_loop(app_control_rx, app_state_tx, hardware_state_rx, hardware_control_tx_clone).await;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Init measurement data state
|
// Init measurement data state
|
||||||
let measurement_data = Arc::new(MeasurementDataState::default());
|
let measurement_data = Arc::new(MeasurementDataState::default());
|
||||||
|
|
||||||
// Logging
|
|
||||||
let (log_tx, log_rx) = mpsc::channel::<LoggingSignal>(10);
|
|
||||||
let log_tx_clone = log_tx.clone();
|
|
||||||
|
|
||||||
let measurement_data_clone = measurement_data.clone();
|
let measurement_data_clone = measurement_data.clone();
|
||||||
|
|
||||||
|
// Start control loop
|
||||||
|
let hardware_control_tx_clone = hardware_control_tx.clone();
|
||||||
|
let hardware_state_rx_clone = hardware_state_rx.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
app_control_loop(app_control_rx, app_state_tx, hardware_control_tx_clone, hardware_state_rx_clone).await;
|
||||||
|
});
|
||||||
|
|
||||||
let app = App::new(log_tx, measurement_data_clone, app_control_tx_clone, app_state_rx, hardware_state_rx_clone);
|
// Start hardware communication loop in separate thread
|
||||||
|
let rt = Runtime::new().expect("Unable to create Runtime");
|
||||||
|
let logging_control_tx_clone = logging_control_tx.clone();
|
||||||
|
let logging_state_rx_clone = logging_state_rx.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
rt.block_on(communicate_with_hardware(
|
||||||
|
hardware_control_rx,
|
||||||
|
hardware_control_tx,
|
||||||
|
measurement_data,
|
||||||
|
hardware_state_tx,
|
||||||
|
logging_control_tx_clone,
|
||||||
|
logging_state_rx_clone,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
let gui_logging_state_1 = app.gui_logging_state.clone();
|
// Start logging loop
|
||||||
let gui_logging_state_2 = app.gui_logging_state.clone();
|
tokio::spawn(async move { log_data(logging_control_rx, logging_state_tx).await });
|
||||||
|
|
||||||
// Log thread
|
// Start TCP command server in separate thread
|
||||||
tokio::spawn(async move { log_data(log_rx, gui_logging_state_1).await });
|
|
||||||
|
|
||||||
// TCP command server in separate thread
|
|
||||||
let rt = Runtime::new().expect("Unable to create Runtime");
|
let rt = Runtime::new().expect("Unable to create Runtime");
|
||||||
|
|
||||||
let app_control_tx_clone = app_control_tx.clone();
|
let app_control_tx_clone = app_control_tx.clone();
|
||||||
@@ -71,18 +72,14 @@ async fn main() {
|
|||||||
rt.block_on(bioz_host_rs::tcp::tcp_command_server(app_control_tx_clone));
|
rt.block_on(bioz_host_rs::tcp::tcp_command_server(app_control_tx_clone));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Execute the runtime in its own thread.
|
// Create the app and run it in the main thread
|
||||||
let rt = Runtime::new().expect("Unable to create Runtime");
|
let app = App::new(
|
||||||
std::thread::spawn(move || {
|
measurement_data_clone,
|
||||||
rt.block_on(communicate_with_hardware(
|
app_control_tx,
|
||||||
hardware_control_rx,
|
logging_control_tx,
|
||||||
hardware_control_tx,
|
app_state_rx,
|
||||||
measurement_data,
|
hardware_state_rx,
|
||||||
hardware_state_tx,
|
logging_state_rx);
|
||||||
gui_logging_state_2,
|
|
||||||
log_tx_clone,
|
|
||||||
));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Run the GUI in the main thread.
|
// Run the GUI in the main thread.
|
||||||
let mut native_options = NativeOptions::default();
|
let mut native_options = NativeOptions::default();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use crate::state::{HardwareState, MeasurementDataState};
|
|||||||
use crate::icd;
|
use crate::icd;
|
||||||
use crate::client::WorkbookClient;
|
use crate::client::WorkbookClient;
|
||||||
|
|
||||||
use crate::logging::LoggingStates;
|
use crate::logging::LoggingState;
|
||||||
|
|
||||||
use crate::signals::{LoggingSignal, StartStopSignal};
|
use crate::signals::{LoggingSignal, StartStopSignal};
|
||||||
|
|
||||||
@@ -27,8 +27,8 @@ pub async fn communicate_with_hardware(
|
|||||||
hardware_control_tx: Sender<StartStopSignal>,
|
hardware_control_tx: Sender<StartStopSignal>,
|
||||||
measurement_data: Arc<MeasurementDataState>,
|
measurement_data: Arc<MeasurementDataState>,
|
||||||
hardware_state_tx: watch::Sender<HardwareState>,
|
hardware_state_tx: watch::Sender<HardwareState>,
|
||||||
gui_logging_state: Arc<Mutex<LoggingStates>>,
|
logging_control_tx: Sender<LoggingSignal>,
|
||||||
log_tx: Sender<LoggingSignal>,
|
logging_state_rx: watch::Receiver<LoggingState>,
|
||||||
) {
|
) {
|
||||||
let data_counter = Arc::new(AtomicU32::new(0));
|
let data_counter = Arc::new(AtomicU32::new(0));
|
||||||
let data_counter_clone = data_counter.clone();
|
let data_counter_clone = data_counter.clone();
|
||||||
@@ -89,8 +89,8 @@ pub async fn communicate_with_hardware(
|
|||||||
let data_counter_clone_single = data_counter_clone.clone();
|
let data_counter_clone_single = data_counter_clone.clone();
|
||||||
|
|
||||||
// Clone log_tx for the task
|
// Clone log_tx for the task
|
||||||
let gui_logging_state_clone = gui_logging_state.clone();
|
let logging_control_tx_clone = logging_control_tx.clone();
|
||||||
let log_tx_clone = log_tx.clone();
|
let logging_state_rx_clone = logging_state_rx.clone();
|
||||||
|
|
||||||
let settings_clone = settings.clone();
|
let settings_clone = settings.clone();
|
||||||
|
|
||||||
@@ -111,11 +111,11 @@ pub async fn communicate_with_hardware(
|
|||||||
data_counter_clone_single.fetch_add(1, Ordering::Relaxed);
|
data_counter_clone_single.fetch_add(1, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
// Send logging signal
|
// Send logging signal
|
||||||
if *gui_logging_state_clone.lock().unwrap() == LoggingStates::Logging {
|
if *logging_state_rx_clone.borrow() == LoggingState::Logging {
|
||||||
let settings = *settings_clone.lock().unwrap();
|
let settings = *settings_clone.lock().unwrap();
|
||||||
match settings.mode {
|
match settings.mode {
|
||||||
Some(StartStopSignal::StartSingle(freq, _, _, _)) => {
|
Some(StartStopSignal::StartSingle(freq, _, _, _)) => {
|
||||||
if let Err(e) = log_tx_clone.try_send(LoggingSignal::SingleImpedance(SystemTime::now(), freq, val.magnitude, val.phase)) {
|
if let Err(e) = logging_control_tx_clone.try_send(LoggingSignal::SingleImpedance(SystemTime::now(), freq, val.magnitude, val.phase)) {
|
||||||
error!("Failed to send logging signal: {:?}", e);
|
error!("Failed to send logging signal: {:?}", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -142,8 +142,8 @@ pub async fn communicate_with_hardware(
|
|||||||
let data_counter_clone_sweep = data_counter_clone.clone();
|
let data_counter_clone_sweep = data_counter_clone.clone();
|
||||||
|
|
||||||
// Clone log_tx for the task
|
// Clone log_tx for the task
|
||||||
let gui_logging_state_clone = gui_logging_state.clone();
|
let logging_control_tx_clone = logging_control_tx.clone();
|
||||||
let log_tx_clone = log_tx.clone();
|
let logging_state_rx_clone = logging_state_rx.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Ok(val) = sweep_impedance_sub.recv().await {
|
while let Ok(val) = sweep_impedance_sub.recv().await {
|
||||||
@@ -158,8 +158,8 @@ pub async fn communicate_with_hardware(
|
|||||||
bode_plot.update_magnitudes(SweepPoints::Eight, magnitudes.clone());
|
bode_plot.update_magnitudes(SweepPoints::Eight, magnitudes.clone());
|
||||||
bode_plot.update_phases(SweepPoints::Eight, phases.clone());
|
bode_plot.update_phases(SweepPoints::Eight, phases.clone());
|
||||||
}
|
}
|
||||||
if *gui_logging_state_clone.lock().unwrap() == LoggingStates::Logging {
|
if *logging_state_rx_clone.borrow() == LoggingState::Logging {
|
||||||
if let Err(e) = log_tx_clone.send(LoggingSignal::SweepImpedance(SystemTime::now(), SweepPoints::Eight.values().to_vec(), magnitudes.clone(), phases.clone())).await {
|
if let Err(e) = logging_control_tx_clone.try_send(LoggingSignal::SweepImpedance(SystemTime::now(), SweepPoints::Eight.values().to_vec(), magnitudes.clone(), phases.clone())) {
|
||||||
error!("Failed to send logging signal: {:?}", e);
|
error!("Failed to send logging signal: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,8 +172,8 @@ pub async fn communicate_with_hardware(
|
|||||||
bode_plot.update_magnitudes(SweepPoints::Eighteen, magnitudes.clone());
|
bode_plot.update_magnitudes(SweepPoints::Eighteen, magnitudes.clone());
|
||||||
bode_plot.update_phases(SweepPoints::Eighteen, phases.clone());
|
bode_plot.update_phases(SweepPoints::Eighteen, phases.clone());
|
||||||
}
|
}
|
||||||
if *gui_logging_state_clone.lock().unwrap() == LoggingStates::Logging {
|
if *logging_state_rx_clone.borrow() == LoggingState::Logging {
|
||||||
if let Err(e) = log_tx_clone.send(LoggingSignal::SweepImpedance(SystemTime::now(), SweepPoints::Eighteen.values().to_vec(), magnitudes.clone(), phases.clone())).await {
|
if let Err(e) = logging_control_tx_clone.try_send(LoggingSignal::SweepImpedance(SystemTime::now(), SweepPoints::Eighteen.values().to_vec(), magnitudes.clone(), phases.clone())) {
|
||||||
error!("Failed to send logging signal: {:?}", e);
|
error!("Failed to send logging signal: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,7 +184,7 @@ pub async fn communicate_with_hardware(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let log_tx_clone = log_tx.clone();
|
let logging_control_tx_clone = logging_control_tx.clone();
|
||||||
loop {
|
loop {
|
||||||
select! {
|
select! {
|
||||||
Some(frequency) = hardware_control_rx.recv() => {
|
Some(frequency) = hardware_control_rx.recv() => {
|
||||||
@@ -199,8 +199,8 @@ pub async fn communicate_with_hardware(
|
|||||||
hardware_state_tx.send(hardware_state.clone()).unwrap();
|
hardware_state_tx.send(hardware_state.clone()).unwrap();
|
||||||
|
|
||||||
// When logging add electrode configuration to logging file
|
// When logging add electrode configuration to logging file
|
||||||
if *gui_logging_state.lock().unwrap() == LoggingStates::Logging {
|
if *logging_state_rx.borrow() == LoggingState::Logging {
|
||||||
if let Err(e) = log_tx_clone.send(LoggingSignal::ElectrodeCongiguration(electrode_config)).await {
|
if let Err(e) = logging_control_tx_clone.try_send(LoggingSignal::ElectrodeCongiguration(electrode_config)) {
|
||||||
error!("Failed to send logging signal: {:?}", e);
|
error!("Failed to send logging signal: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,8 +238,8 @@ pub async fn communicate_with_hardware(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// When logging add electrode configuration to logging file
|
// When logging add electrode configuration to logging file
|
||||||
if *gui_logging_state.lock().unwrap() == LoggingStates::Logging {
|
if *logging_state_rx.borrow() == LoggingState::Logging {
|
||||||
if let Err(e) = log_tx_clone.send(LoggingSignal::ElectrodeCongiguration(electrode_config)).await {
|
if let Err(e) = logging_control_tx_clone.try_send(LoggingSignal::ElectrodeCongiguration(electrode_config)) {
|
||||||
error!("Failed to send logging signal: {:?}", e);
|
error!("Failed to send logging signal: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use crate::state::Mode;
|
|||||||
|
|
||||||
pub async fn app_control_loop(mut app_control_rx: mpsc::Receiver<ControlCommand>,
|
pub async fn app_control_loop(mut app_control_rx: mpsc::Receiver<ControlCommand>,
|
||||||
app_state_tx: watch::Sender<AppState>,
|
app_state_tx: watch::Sender<AppState>,
|
||||||
hardware_state_rx: watch::Receiver<HardwareState>,
|
hardware_control_tx: mpsc::Sender<StartStopSignal>,
|
||||||
hardware_control_tx: mpsc::Sender<StartStopSignal>) {
|
hardware_state_rx: watch::Receiver<HardwareState>) {
|
||||||
let mut state = AppState::default();
|
let mut state = AppState::default();
|
||||||
|
|
||||||
while let Some(cmd) = app_control_rx.recv().await {
|
while let Some(cmd) = app_control_rx.recv().await {
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use bioz_icd_rs::ElectrodeConfiguration;
|
use bioz_icd_rs::ElectrodeConfiguration;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::time::UNIX_EPOCH;
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
use tokio::sync::mpsc::Receiver;
|
use tokio::sync::{mpsc::Receiver, watch::Sender};
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
|
||||||
@@ -13,22 +12,28 @@ use rfd::AsyncFileDialog;
|
|||||||
use crate::signals::LoggingSignal;
|
use crate::signals::LoggingSignal;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum LoggingStates {
|
pub enum LoggingState {
|
||||||
Idle,
|
Idle,
|
||||||
Starting,
|
Starting,
|
||||||
Logging,
|
Logging,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for LoggingState {
|
||||||
|
fn default() -> Self {
|
||||||
|
LoggingState::Idle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn log_data(
|
pub async fn log_data(
|
||||||
mut data: Receiver<LoggingSignal>,
|
mut logging_control_rx: Receiver<LoggingSignal>,
|
||||||
gui_logging_state: Arc<Mutex<LoggingStates>>,
|
logging_state_tx: Sender<LoggingState>,
|
||||||
) {
|
) {
|
||||||
let mut file: Option<File> = None;
|
let mut file: Option<File> = None;
|
||||||
let mut logging_marker = String::new();
|
let mut logging_marker = String::new();
|
||||||
let mut electrode_configuration = String::new();
|
let mut electrode_configuration = String::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match data.recv().await {
|
match logging_control_rx.recv().await {
|
||||||
Some(signal) => {
|
Some(signal) => {
|
||||||
match signal {
|
match signal {
|
||||||
LoggingSignal::SingleImpedance(timestamp, frequency, magnitude, phase) => {
|
LoggingSignal::SingleImpedance(timestamp, frequency, magnitude, phase) => {
|
||||||
@@ -75,7 +80,7 @@ pub async fn log_data(
|
|||||||
}
|
}
|
||||||
LoggingSignal::StartFileLogging(filename) => {
|
LoggingSignal::StartFileLogging(filename) => {
|
||||||
// Update global logging state
|
// Update global logging state
|
||||||
*gui_logging_state.lock().unwrap() = LoggingStates::Starting;
|
logging_state_tx.send(LoggingState::Starting).unwrap();
|
||||||
|
|
||||||
// Open a folder picker for the user
|
// Open a folder picker for the user
|
||||||
let folder = match AsyncFileDialog::new()
|
let folder = match AsyncFileDialog::new()
|
||||||
@@ -85,7 +90,7 @@ pub async fn log_data(
|
|||||||
Some(f) => f,
|
Some(f) => f,
|
||||||
None => {
|
None => {
|
||||||
info!("File logging cancelled by user");
|
info!("File logging cancelled by user");
|
||||||
*gui_logging_state.lock().unwrap() = LoggingStates::Idle;
|
logging_state_tx.send(LoggingState::Idle).unwrap();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -123,7 +128,7 @@ pub async fn log_data(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update global logging state
|
// Update global logging state
|
||||||
*gui_logging_state.lock().unwrap() = LoggingStates::Logging;
|
logging_state_tx.send(LoggingState::Logging).unwrap();
|
||||||
}
|
}
|
||||||
LoggingSignal::StopFileLogging => {
|
LoggingSignal::StopFileLogging => {
|
||||||
if file.is_some() {
|
if file.is_some() {
|
||||||
@@ -131,9 +136,10 @@ pub async fn log_data(
|
|||||||
file = None;
|
file = None;
|
||||||
}
|
}
|
||||||
// Update global logging state
|
// Update global logging state
|
||||||
*gui_logging_state.lock().unwrap() = LoggingStates::Idle;
|
logging_state_tx.send(LoggingState::Idle).unwrap();
|
||||||
}
|
}
|
||||||
LoggingSignal::AddMarker(marker) => {
|
LoggingSignal::AddMarker(marker) => {
|
||||||
|
info!("Adding marker: {}", marker);
|
||||||
logging_marker = marker;
|
logging_marker = marker;
|
||||||
}
|
}
|
||||||
LoggingSignal::ElectrodeCongiguration(config) => {
|
LoggingSignal::ElectrodeCongiguration(config) => {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use crate::app::TabActive;
|
use crate::app::TabActive;
|
||||||
use crate::icd::{BioImpedanceLeadMode, IcdDftNum, SweepPoints, ElectrodeConfiguration, ElectrodeOptionsWithMultiplexer};
|
use crate::icd::{BioImpedanceLeadMode, IcdDftNum, SweepPoints, ElectrodeConfiguration, ElectrodeOptionsWithMultiplexer};
|
||||||
use crate::logging::LoggingStates;
|
|
||||||
use crate::plot::{TimeSeriesPlot, BodePlot};
|
use crate::plot::{TimeSeriesPlot, BodePlot};
|
||||||
|
|
||||||
use atomic_float::AtomicF32;
|
use atomic_float::AtomicF32;
|
||||||
@@ -108,7 +107,6 @@ pub struct AppState {
|
|||||||
pub electrode_settings: ElectrodeSettings,
|
pub electrode_settings: ElectrodeSettings,
|
||||||
pub sweep_points: SweepPoints,
|
pub sweep_points: SweepPoints,
|
||||||
pub tab_active: TabActive,
|
pub tab_active: TabActive,
|
||||||
pub logging: LoggingStates,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AppState {
|
impl Default for AppState {
|
||||||
@@ -121,7 +119,6 @@ impl Default for AppState {
|
|||||||
electrode_settings: ElectrodeSettings::new(),
|
electrode_settings: ElectrodeSettings::new(),
|
||||||
sweep_points: SweepPoints::Eighteen,
|
sweep_points: SweepPoints::Eighteen,
|
||||||
tab_active: TabActive::Single,
|
tab_active: TabActive::Single,
|
||||||
logging: LoggingStates::Idle,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user