Add 2/4-lead option to GUI.

This commit is contained in:
2025-10-18 17:52:29 +02:00
parent 7229d4cd33
commit 5d80fcdcd8
6 changed files with 438 additions and 127 deletions

499
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,12 @@ use crate::plot::{TimeSeriesPlot, BodePlot};
use crate::signals::{LoggingSignal, StartStopSignal}; use crate::signals::{LoggingSignal, StartStopSignal};
use crate::icd::{IcdDftNum, MeasurementPointSet}; use crate::icd::{BioImpedanceLeadMode, IcdDftNum, MeasurementPointSet};
const LEAD_MODES: [BioImpedanceLeadMode; 2] = [
BioImpedanceLeadMode::TwoLead,
BioImpedanceLeadMode::FourLead,
];
const DFTNUM_VARIANTS: [IcdDftNum; 13] = [ const DFTNUM_VARIANTS: [IcdDftNum; 13] = [
IcdDftNum::Num4, IcdDftNum::Num8, IcdDftNum::Num16, IcdDftNum::Num32, IcdDftNum::Num4, IcdDftNum::Num8, IcdDftNum::Num16, IcdDftNum::Num32,
@@ -57,6 +62,7 @@ pub struct App {
tab_active: TabActive, tab_active: TabActive,
pub data_frequency: Arc<AtomicF32>, pub data_frequency: Arc<AtomicF32>,
pub single_frequency: Arc<Mutex<u32>>, pub single_frequency: Arc<Mutex<u32>>,
pub lead_mode: Arc<Mutex<BioImpedanceLeadMode>>,
pub dft_num: Arc<Mutex<IcdDftNum>>, pub dft_num: Arc<Mutex<IcdDftNum>>,
pub measurement_points: Arc<Mutex<MeasurementPointSet>>, pub measurement_points: Arc<Mutex<MeasurementPointSet>>,
pub periods_per_dft: Arc<Mutex<Option<f32>>>, pub periods_per_dft: Arc<Mutex<Option<f32>>>,
@@ -73,6 +79,7 @@ struct TabViewer {
bode_plot: Arc<Mutex<BodePlot>>, bode_plot: Arc<Mutex<BodePlot>>,
on: Arc<Mutex<bool>>, on: Arc<Mutex<bool>>,
single_frequency: Arc<Mutex<u32>>, single_frequency: Arc<Mutex<u32>>,
lead_mode: Arc<Mutex<BioImpedanceLeadMode>>,
dft_num: Arc<Mutex<IcdDftNum>>, dft_num: Arc<Mutex<IcdDftNum>>,
measurement_points: Arc<Mutex<MeasurementPointSet>>, measurement_points: Arc<Mutex<MeasurementPointSet>>,
periods_per_dft: Arc<Mutex<Option<f32>>>, periods_per_dft: Arc<Mutex<Option<f32>>>,
@@ -88,6 +95,35 @@ impl TabViewer {
.open(self.show_settings_toggle) .open(self.show_settings_toggle)
.show(ui, |ui| { .show(ui, |ui| {
if let Ok(on) = self.on.lock() { if let Ok(on) = self.on.lock() {
ui.add_enabled_ui(!*on, |ui| {
ui.horizontal(|ui| {
ui.label("Lead Mode:");
let mut lead_mode = self.lead_mode.lock().unwrap();
// Map current lead mode to index
let mut index = LEAD_MODES
.iter()
.position(|&m| m == *lead_mode)
.unwrap_or(0);
ComboBox::from_id_salt("LeadMode")
.width(60.0)
.show_index(ui, &mut index, LEAD_MODES.len(), |i| {
match LEAD_MODES[i] {
BioImpedanceLeadMode::TwoLead => "2-Lead",
BioImpedanceLeadMode::FourLead => "4-Lead",
}
.to_string()
});
// Update lead mode if changed
if *lead_mode != LEAD_MODES[index] {
*lead_mode = LEAD_MODES[index];
info!("Lead Mode setting changed!");
}
});
});
ui.add_enabled_ui(!*on, |ui| { ui.add_enabled_ui(!*on, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("Single Frequency:"); ui.label("Single Frequency:");
@@ -402,7 +438,7 @@ fn log10x_formatter(name: &str, value: &PlotPoint) -> String {
impl egui_dock::TabViewer for TabViewer { impl egui_dock::TabViewer for TabViewer {
type Tab = String; type Tab = String;
fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText { fn title(&mut self, tab: &mut Self::Tab) -> eframe::egui::WidgetText {
(&*tab).into() (&*tab).into()
} }
@@ -427,6 +463,7 @@ impl App {
let phase_series = Arc::new(Mutex::new(TimeSeriesPlot::new())); let phase_series = Arc::new(Mutex::new(TimeSeriesPlot::new()));
let bode_plot = Arc::new(Mutex::new(BodePlot::new())); let bode_plot = Arc::new(Mutex::new(BodePlot::new()));
let single_frequency = Arc::new(Mutex::new(50000)); let single_frequency = Arc::new(Mutex::new(50000));
let lead_mode = Arc::new(Mutex::new(BioImpedanceLeadMode::TwoLead));
let dft_num = Arc::new(Mutex::new(IcdDftNum::Num2048)); let dft_num = Arc::new(Mutex::new(IcdDftNum::Num2048));
let measurement_points = Arc::new(Mutex::new(MeasurementPointSet::Eighteen)); let measurement_points = Arc::new(Mutex::new(MeasurementPointSet::Eighteen));
let periods_per_dft = Arc::new(Mutex::new(None)); let periods_per_dft = Arc::new(Mutex::new(None));
@@ -442,6 +479,7 @@ impl App {
phase_series: phase_series.clone(), phase_series: phase_series.clone(),
bode_plot: bode_plot.clone(), bode_plot: bode_plot.clone(),
single_frequency: single_frequency.clone(), single_frequency: single_frequency.clone(),
lead_mode: lead_mode.clone(),
dft_num: dft_num.clone(), dft_num: dft_num.clone(),
measurement_points: measurement_points.clone(), measurement_points: measurement_points.clone(),
periods_per_dft: periods_per_dft.clone(), periods_per_dft: periods_per_dft.clone(),
@@ -467,6 +505,7 @@ impl App {
tab_active, tab_active,
data_frequency: Arc::new(AtomicF32::new(0.0)), data_frequency: Arc::new(AtomicF32::new(0.0)),
single_frequency, single_frequency,
lead_mode,
dft_num, dft_num,
measurement_points, measurement_points,
periods_per_dft, periods_per_dft,
@@ -500,7 +539,7 @@ impl App {
pub fn update_start_stop(&self) { pub fn update_start_stop(&self) {
match (self.tab_active, *self.on.lock().unwrap()) { match (self.tab_active, *self.on.lock().unwrap()) {
(TabActive::Single, true) => { (TabActive::Single, true) => {
if let Err(e) = self.run_impedancemeter_tx.try_send(StartStopSignal::StartSingle(*self.single_frequency.lock().unwrap(), *self.dft_num.lock().unwrap())) { if let Err(e) = self.run_impedancemeter_tx.try_send(StartStopSignal::StartSingle(*self.single_frequency.lock().unwrap(), *self.lead_mode.lock().unwrap(), *self.dft_num.lock().unwrap())) {
error!("Failed to send start command: {:?}", e); error!("Failed to send start command: {:?}", e);
} }
}, },

View File

@@ -78,7 +78,7 @@ async fn main() {
.subscribe_multi::<icd::SingleImpedanceOutputTopic>(8) .subscribe_multi::<icd::SingleImpedanceOutputTopic>(8)
.await .await
.unwrap(); .unwrap();
client.start_impedancemeter_single(freq, bioz_icd_rs::IcdDftNum::Num2048).await.unwrap().ok(); client.start_impedancemeter_single(freq, bioz_icd_rs::BioImpedanceLeadMode::TwoLead, bioz_icd_rs::IcdDftNum::Num2048).await.unwrap().ok();
println!("Started with dft_num 2048!"); println!("Started with dft_num 2048!");
let dur = Duration::from_millis(dur.into()); let dur = Duration::from_millis(dur.into());
@@ -97,7 +97,7 @@ async fn main() {
}; };
match client.start_impedancemeter_single(freq, bioz_icd_rs::IcdDftNum::Num2048).await { match client.start_impedancemeter_single(freq, bioz_icd_rs::BioImpedanceLeadMode::TwoLead, bioz_icd_rs::IcdDftNum::Num2048).await {
Ok(_) => println!("Started with dft_num 2048!"), Ok(_) => println!("Started with dft_num 2048!"),
Err(e) => println!("Error starting impedancemeter: {:?}", e), Err(e) => println!("Error starting impedancemeter: {:?}", e),
}; };

View File

@@ -5,7 +5,7 @@ use postcard_rpc::{
}; };
use std::convert::Infallible; use std::convert::Infallible;
use bioz_icd_rs::{ use bioz_icd_rs::{
GetUniqueIdEndpoint, ImpedanceInitResult, MultiImpedanceInitResult, MultiImpedanceStartRequest, PingEndpoint, SetGreenLedEndpoint, SingleImpedanceStartRequest, StartMultiImpedanceEndpoint, StartSingleImpedanceEndpoint, StopSingleImpedanceEndpoint BioImpedanceLeadMode, GetUniqueIdEndpoint, ImpedanceInitResult, MultiImpedanceInitResult, MultiImpedanceStartRequest, PingEndpoint, SetGreenLedEndpoint, SingleImpedanceStartRequest, StartMultiImpedanceEndpoint, StartSingleImpedanceEndpoint, StopSingleImpedanceEndpoint
}; };
use crate::icd::{IcdDftNum, MeasurementPointSet}; use crate::icd::{IcdDftNum, MeasurementPointSet};
@@ -65,10 +65,11 @@ impl WorkbookClient {
pub async fn start_impedancemeter_single( pub async fn start_impedancemeter_single(
&self, &self,
frequency: u32, frequency: u32,
lead_mode: BioImpedanceLeadMode,
dft_number: IcdDftNum, dft_number: IcdDftNum,
) -> Result<ImpedanceInitResult, WorkbookError<Infallible>> { ) -> Result<ImpedanceInitResult, WorkbookError<Infallible>> {
let response = self.client let response = self.client
.send_resp::<StartSingleImpedanceEndpoint>(&SingleImpedanceStartRequest { update_frequency: 60, sinus_frequency: frequency, dft_number}) .send_resp::<StartSingleImpedanceEndpoint>(&SingleImpedanceStartRequest { update_frequency: 60, sinus_frequency: frequency, lead_mode, dft_number})
.await?; .await?;
Ok(response) Ok(response)
} }

View File

@@ -103,7 +103,7 @@ pub async fn communicate_with_hardware(
if gui_logging_enabled_clone.load(Ordering::Relaxed) { if gui_logging_enabled_clone.load(Ordering::Relaxed) {
let settings = *settings_clone.lock().unwrap(); let settings = *settings_clone.lock().unwrap();
match settings.frequency { match settings.frequency {
Some(StartStopSignal::StartSingle(freq, _)) => { Some(StartStopSignal::StartSingle(freq, _, _)) => {
if let Err(e) = log_tx_clone.send(LoggingSignal::SingleImpedance(SystemTime::now(), freq, val.magnitude, val.phase)).await { if let Err(e) = log_tx_clone.send(LoggingSignal::SingleImpedance(SystemTime::now(), freq, val.magnitude, val.phase)).await {
error!("Failed to send logging signal: {:?}", e); error!("Failed to send logging signal: {:?}", e);
} }
@@ -177,11 +177,11 @@ pub async fn communicate_with_hardware(
select! { select! {
Some(frequency) = run_impedancemeter_rx.recv() => { Some(frequency) = run_impedancemeter_rx.recv() => {
match frequency { match frequency {
StartStopSignal::StartSingle(freq, dft_num) => { StartStopSignal::StartSingle(freq, lead_mode, dft_num) => {
match workbook_client.start_impedancemeter_single(freq, dft_num).await { match workbook_client.start_impedancemeter_single(freq, lead_mode, dft_num).await {
Ok(Ok(periods)) => { Ok(Ok(periods)) => {
info!("Impedance meter started at frequency: {} with periods per DFT: {}", freq, periods); info!("Impedance meter started at frequency: {} with periods per DFT: {}", freq, periods);
settings.lock().unwrap().frequency = Some(StartStopSignal::StartSingle(freq, dft_num)); settings.lock().unwrap().frequency = Some(StartStopSignal::StartSingle(freq, lead_mode, dft_num));
*periods_per_dft.lock().unwrap() = Some(periods); *periods_per_dft.lock().unwrap() = Some(periods);
}, },
Ok(Err(e)) => { Ok(Err(e)) => {

View File

@@ -1,10 +1,10 @@
use std::time::SystemTime; use std::time::SystemTime;
use crate::icd::{IcdDftNum, MeasurementPointSet}; use crate::icd::{BioImpedanceLeadMode, IcdDftNum, MeasurementPointSet};
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum StartStopSignal { pub enum StartStopSignal {
StartSingle(u32, IcdDftNum), // frequency in Hz, DFT number StartSingle(u32, BioImpedanceLeadMode, IcdDftNum), // frequency in Hz, lead mode, DFT number
StartMulti(MeasurementPointSet), // DFT number, number of points per measurement StartMulti(MeasurementPointSet), // DFT number, number of points per measurement
Stop, Stop,
} }