mirror of
https://github.com/hubaldv/bioz-host-rs.git
synced 2026-07-24 00:57:44 +00:00
Pass electrode config from GUI to hardware.
This commit is contained in:
209
src/app.rs
209
src/app.rs
@@ -1,11 +1,13 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use log::{info, error};
|
use log::{info, error};
|
||||||
|
|
||||||
use core::f32;
|
use core::f32;
|
||||||
|
|
||||||
use std::f32::consts::PI;
|
// use std::f32::consts::PI;
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
|
||||||
@@ -22,13 +24,56 @@ use crate::plot::{TimeSeriesPlot, BodePlot};
|
|||||||
|
|
||||||
use crate::signals::{LoggingSignal, StartStopSignal};
|
use crate::signals::{LoggingSignal, StartStopSignal};
|
||||||
|
|
||||||
use crate::icd::{BioImpedanceLeadMode, IcdDftNum, MeasurementPointSet};
|
use crate::icd::{BioImpedanceLeadMode, IcdDftNum, MeasurementPointSet, ElectrodeConfiguration, ElectrodeOptionsWithMultiplexer};
|
||||||
|
|
||||||
const LEAD_MODES: [BioImpedanceLeadMode; 2] = [
|
const LEAD_MODES: [BioImpedanceLeadMode; 2] = [
|
||||||
BioImpedanceLeadMode::TwoLead,
|
BioImpedanceLeadMode::TwoLead,
|
||||||
BioImpedanceLeadMode::FourLead,
|
BioImpedanceLeadMode::FourLead,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
struct ElectrodeSettings {
|
||||||
|
with_multiplexer_2_lead: [ElectrodeOptionsWithMultiplexer; 2],
|
||||||
|
with_multiplexer_4_lead: [ElectrodeOptionsWithMultiplexer; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElectrodeSettings {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
with_multiplexer_2_lead: [
|
||||||
|
ElectrodeOptionsWithMultiplexer::E1, // Electrode+ default
|
||||||
|
ElectrodeOptionsWithMultiplexer::E23, // Electrode- default
|
||||||
|
],
|
||||||
|
with_multiplexer_4_lead: [
|
||||||
|
ElectrodeOptionsWithMultiplexer::E1, // I+ default
|
||||||
|
ElectrodeOptionsWithMultiplexer::E23, // I- default
|
||||||
|
ElectrodeOptionsWithMultiplexer::E10, // V+ default
|
||||||
|
ElectrodeOptionsWithMultiplexer::E12, // V- default
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_electrode_config(
|
||||||
|
&self,
|
||||||
|
hardware_connected: HardwareConnected,
|
||||||
|
lead_mode: BioImpedanceLeadMode,
|
||||||
|
) -> Option<ElectrodeConfiguration> {
|
||||||
|
if hardware_connected != HardwareConnected::WithMultiplexer {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match lead_mode {
|
||||||
|
BioImpedanceLeadMode::TwoLead => {
|
||||||
|
let [a, b] = self.with_multiplexer_2_lead;
|
||||||
|
Some(ElectrodeConfiguration::WithMultiplexer2Lead(a, b))
|
||||||
|
}
|
||||||
|
BioImpedanceLeadMode::FourLead => {
|
||||||
|
let [a, b, c, d] = self.with_multiplexer_4_lead;
|
||||||
|
Some(ElectrodeConfiguration::WithMultiplexer4Lead(a, b, c, d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
IcdDftNum::Num64, IcdDftNum::Num128, IcdDftNum::Num256, IcdDftNum::Num512,
|
IcdDftNum::Num64, IcdDftNum::Num128, IcdDftNum::Num256, IcdDftNum::Num512,
|
||||||
@@ -48,6 +93,13 @@ enum TabActive {
|
|||||||
Shortcuts,
|
Shortcuts,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum HardwareConnected {
|
||||||
|
None,
|
||||||
|
WithoutMultiplexer,
|
||||||
|
WithMultiplexer,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
tree: DockState<String>,
|
tree: DockState<String>,
|
||||||
tab_viewer: TabViewer,
|
tab_viewer: TabViewer,
|
||||||
@@ -58,12 +110,13 @@ pub struct App {
|
|||||||
pub magnitude_series: Arc<Mutex<TimeSeriesPlot>>,
|
pub magnitude_series: Arc<Mutex<TimeSeriesPlot>>,
|
||||||
pub phase_series: Arc<Mutex<TimeSeriesPlot>>,
|
pub phase_series: Arc<Mutex<TimeSeriesPlot>>,
|
||||||
pub bode_plot: Arc<Mutex<BodePlot>>,
|
pub bode_plot: Arc<Mutex<BodePlot>>,
|
||||||
pub connected: Arc<AtomicBool>,
|
pub hardware_connected: Arc<Mutex<HardwareConnected>>,
|
||||||
pub on: Arc<Mutex<bool>>,
|
pub on: Arc<Mutex<bool>>,
|
||||||
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 lead_mode: Arc<Mutex<BioImpedanceLeadMode>>,
|
||||||
|
electrode_settings: Arc<Mutex<ElectrodeSettings>>,
|
||||||
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>>>,
|
||||||
@@ -82,7 +135,9 @@ 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>>,
|
||||||
|
hardware_connected: Arc<Mutex<HardwareConnected>>,
|
||||||
lead_mode: Arc<Mutex<BioImpedanceLeadMode>>,
|
lead_mode: Arc<Mutex<BioImpedanceLeadMode>>,
|
||||||
|
electrode_settings: Arc<Mutex<ElectrodeSettings>>,
|
||||||
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>>>,
|
||||||
@@ -91,6 +146,60 @@ struct TabViewer {
|
|||||||
show_settings_toggle: Option<bool>,
|
show_settings_toggle: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait ElectrodeOption: Copy + Debug + PartialEq + 'static {
|
||||||
|
const ALL: &'static [Self];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElectrodeOption for ElectrodeOptionsWithMultiplexer {
|
||||||
|
const ALL: &'static [Self] = &ElectrodeOptionsWithMultiplexer::ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn electrode_combo<T: ElectrodeOption>(
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
id: &str,
|
||||||
|
value: &mut T,
|
||||||
|
) {
|
||||||
|
egui::ComboBox::from_id_salt(id)
|
||||||
|
.selected_text(format!("{:?}", value))
|
||||||
|
.width(60.0)
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for &option in T::ALL {
|
||||||
|
ui.selectable_value(value, option, format!("{:?}", option));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_two_lead<T: ElectrodeOption>(
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
values: &mut [T; 2],
|
||||||
|
) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Drive/sense: Electrode+ (");
|
||||||
|
electrode_combo(ui, "e1_select", &mut values[0]);
|
||||||
|
ui.label("), Electrode- (");
|
||||||
|
electrode_combo(ui, "e2_select", &mut values[1]);
|
||||||
|
ui.label(")");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_four_lead<T: ElectrodeOption>(
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
values: &mut [T; 4],
|
||||||
|
) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Drive: I+ (");
|
||||||
|
electrode_combo(ui, "i1_select", &mut values[0]);
|
||||||
|
ui.label("), I- (");
|
||||||
|
electrode_combo(ui, "i2_select", &mut values[1]);
|
||||||
|
|
||||||
|
ui.label(") | Sense: V+ (");
|
||||||
|
electrode_combo(ui, "v1_select", &mut values[2]);
|
||||||
|
ui.label("), V- (");
|
||||||
|
electrode_combo(ui, "v2_select", &mut values[3]);
|
||||||
|
ui.label(")");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
impl TabViewer {
|
impl TabViewer {
|
||||||
fn single_tab(&mut self, ui: &mut egui::Ui) {
|
fn single_tab(&mut self, ui: &mut egui::Ui) {
|
||||||
egui::Frame::default().inner_margin(5).show(ui, |ui| {
|
egui::Frame::default().inner_margin(5).show(ui, |ui| {
|
||||||
@@ -125,11 +234,28 @@ impl TabViewer {
|
|||||||
*lead_mode = LEAD_MODES[index];
|
*lead_mode = LEAD_MODES[index];
|
||||||
info!("Lead Mode setting changed!");
|
info!("Lead Mode setting changed!");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
// Show lead configuration
|
// Show lead configuration
|
||||||
match *lead_mode {
|
ui.label("Lead Configuration:");
|
||||||
BioImpedanceLeadMode::TwoLead => ui.label("Drive/sense: Electrode+ (CE0), Electrode- (AIN1)"),
|
let hardware_connected = self.hardware_connected.lock().unwrap();
|
||||||
BioImpedanceLeadMode::FourLead => ui.label("Drive: I+ (CE0), I- (AIN1) | Sense: V+ (AIN2), V- (AIN3)"),
|
let lead_mode = self.lead_mode.lock().unwrap();
|
||||||
|
|
||||||
|
let mut settings = self.electrode_settings.lock().unwrap();
|
||||||
|
match (*hardware_connected, *lead_mode) {
|
||||||
|
(HardwareConnected::WithoutMultiplexer, BioImpedanceLeadMode::TwoLead) => {
|
||||||
|
ui.label(format!("Drive/sense: Electrode+ (CE0), Electrode- (AIN1)"));
|
||||||
|
}
|
||||||
|
(HardwareConnected::WithoutMultiplexer, BioImpedanceLeadMode::FourLead) => {
|
||||||
|
ui.label(format!("Drive: I+ (CE0), I- (AIN1) | Sense: V+ (AIN2), V- (AIN3)"));
|
||||||
|
}
|
||||||
|
(HardwareConnected::WithMultiplexer, BioImpedanceLeadMode::TwoLead) => {
|
||||||
|
render_two_lead(ui, &mut settings.with_multiplexer_2_lead);
|
||||||
|
}
|
||||||
|
(HardwareConnected::WithMultiplexer, BioImpedanceLeadMode::FourLead) => {
|
||||||
|
render_four_lead(ui, &mut settings.with_multiplexer_4_lead);
|
||||||
|
}
|
||||||
|
(HardwareConnected::None, _) => {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -259,11 +385,28 @@ impl TabViewer {
|
|||||||
*lead_mode = LEAD_MODES[index];
|
*lead_mode = LEAD_MODES[index];
|
||||||
info!("Lead Mode setting changed!");
|
info!("Lead Mode setting changed!");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
// Show lead configuration
|
// Show lead configuration
|
||||||
match *lead_mode {
|
ui.label("Lead Configuration:");
|
||||||
BioImpedanceLeadMode::TwoLead => ui.label("Drive/sense: Electrode+ (CE0), Electrode- (AIN1)"),
|
let hardware_connected = self.hardware_connected.lock().unwrap();
|
||||||
BioImpedanceLeadMode::FourLead => ui.label("Drive: I+ (CE0), I- (AIN1) | Sense: V+ (AIN2), V- (AIN3)"),
|
let lead_mode = self.lead_mode.lock().unwrap();
|
||||||
|
|
||||||
|
let mut settings = self.electrode_settings.lock().unwrap();
|
||||||
|
match (*hardware_connected, *lead_mode) {
|
||||||
|
(HardwareConnected::WithoutMultiplexer, BioImpedanceLeadMode::TwoLead) => {
|
||||||
|
ui.label(format!("Drive/sense: Electrode+ (CE0), Electrode- (AIN1)"));
|
||||||
|
}
|
||||||
|
(HardwareConnected::WithoutMultiplexer, BioImpedanceLeadMode::FourLead) => {
|
||||||
|
ui.label("Drive: I+ (CE0), I- (AIN1) | Sense: V+ (AIN2), V- (AIN3)");
|
||||||
|
}
|
||||||
|
(HardwareConnected::WithMultiplexer, BioImpedanceLeadMode::TwoLead) => {
|
||||||
|
render_two_lead(ui, &mut settings.with_multiplexer_2_lead);
|
||||||
|
}
|
||||||
|
(HardwareConnected::WithMultiplexer, BioImpedanceLeadMode::FourLead) => {
|
||||||
|
render_four_lead(ui, &mut settings.with_multiplexer_4_lead);
|
||||||
|
}
|
||||||
|
(HardwareConnected::None, _) => {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -509,7 +652,9 @@ 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 hardware_connected = Arc::new(Mutex::new(HardwareConnected::None));
|
||||||
let lead_mode = Arc::new(Mutex::new(BioImpedanceLeadMode::FourLead));
|
let lead_mode = Arc::new(Mutex::new(BioImpedanceLeadMode::FourLead));
|
||||||
|
let electrode_settings = Arc::new(Mutex::new(ElectrodeSettings::new()));
|
||||||
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));
|
||||||
@@ -525,7 +670,9 @@ 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(),
|
||||||
|
hardware_connected: hardware_connected.clone(),
|
||||||
lead_mode: lead_mode.clone(),
|
lead_mode: lead_mode.clone(),
|
||||||
|
electrode_settings: electrode_settings.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(),
|
||||||
@@ -546,12 +693,13 @@ impl App {
|
|||||||
magnitude_series,
|
magnitude_series,
|
||||||
phase_series,
|
phase_series,
|
||||||
bode_plot,
|
bode_plot,
|
||||||
connected: Arc::new(AtomicBool::new(false)),
|
hardware_connected,
|
||||||
on,
|
on,
|
||||||
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,
|
lead_mode,
|
||||||
|
electrode_settings,
|
||||||
dft_num,
|
dft_num,
|
||||||
measurement_points,
|
measurement_points,
|
||||||
periods_per_dft,
|
periods_per_dft,
|
||||||
@@ -587,12 +735,17 @@ 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.lead_mode.lock().unwrap(), *self.dft_num.lock().unwrap())) {
|
let lead_mode = *self.lead_mode.lock().unwrap();
|
||||||
|
let electrode_config = self.electrode_settings.lock().unwrap().to_electrode_config(*self.hardware_connected.lock().unwrap(), lead_mode);
|
||||||
|
if let Err(e) = self.run_impedancemeter_tx.try_send(
|
||||||
|
StartStopSignal::StartSingle(*self.single_frequency.lock().unwrap(), lead_mode, electrode_config, *self.dft_num.lock().unwrap())) {
|
||||||
error!("Failed to send start command: {:?}", e);
|
error!("Failed to send start command: {:?}", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(TabActive::Sweep, true) => {
|
(TabActive::Sweep, true) => {
|
||||||
if let Err(e) = self.run_impedancemeter_tx.try_send(StartStopSignal::StartSweep(*self.lead_mode.lock().unwrap(), *self.measurement_points.lock().unwrap())) {
|
let lead_mode = *self.lead_mode.lock().unwrap();
|
||||||
|
let electrode_config = self.electrode_settings.lock().unwrap().to_electrode_config(*self.hardware_connected.lock().unwrap(), lead_mode);
|
||||||
|
if let Err(e) = self.run_impedancemeter_tx.try_send(StartStopSignal::StartSweep(lead_mode, electrode_config, *self.measurement_points.lock().unwrap())) {
|
||||||
error!("Failed to send start command: {:?}", e);
|
error!("Failed to send start command: {:?}", e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -616,7 +769,7 @@ impl eframe::App for App {
|
|||||||
// Egui add a top bar
|
// Egui add a top bar
|
||||||
egui::TopBottomPanel::top("top_bar").show(ctx, |ui| {
|
egui::TopBottomPanel::top("top_bar").show(ctx, |ui| {
|
||||||
egui::MenuBar::new().ui(ui, |ui| {
|
egui::MenuBar::new().ui(ui, |ui| {
|
||||||
let connected = self.connected.load(Ordering::Relaxed);
|
let is_connected = *self.hardware_connected.lock().unwrap() != HardwareConnected::None;
|
||||||
|
|
||||||
egui::widgets::global_theme_preference_switch(ui);
|
egui::widgets::global_theme_preference_switch(ui);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
@@ -641,26 +794,26 @@ impl eframe::App for App {
|
|||||||
.min_size(egui::vec2(45.0, 0.0))
|
.min_size(egui::vec2(45.0, 0.0))
|
||||||
.frame(true);
|
.frame(true);
|
||||||
|
|
||||||
if ui.add_enabled(connected, start_stop_button).clicked() {
|
if ui.add_enabled(is_connected, start_stop_button).clicked() {
|
||||||
*self.on.lock().unwrap() = !on;
|
*self.on.lock().unwrap() = !on;
|
||||||
self.update_start_stop();
|
self.update_start_stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
if ui.add_enabled(connected, Button::new("Reset view")).clicked() {
|
if ui.add_enabled(is_connected, Button::new("Reset view")).clicked() {
|
||||||
self.reset_view();
|
self.reset_view();
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
ui.add_enabled(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.gui_logging_state.lock().unwrap();
|
||||||
|
|
||||||
let mut logging_enabled = !matches!(gui_logging_state, LoggingStates::Idle);
|
let mut logging_enabled = !matches!(gui_logging_state, LoggingStates::Idle);
|
||||||
|
|
||||||
if ui.add_enabled(connected, toggle_start_stop(&mut logging_enabled)).changed() {
|
if ui.add_enabled(is_connected, toggle_start_stop(&mut logging_enabled)).changed() {
|
||||||
let signal = match gui_logging_state {
|
let signal = match gui_logging_state {
|
||||||
LoggingStates::Idle => LoggingSignal::StartFileLogging(self.log_filename.clone()),
|
LoggingStates::Idle => LoggingSignal::StartFileLogging(self.log_filename.clone()),
|
||||||
LoggingStates::Starting | LoggingStates::Logging => LoggingSignal::StopFileLogging,
|
LoggingStates::Starting | LoggingStates::Logging => LoggingSignal::StopFileLogging,
|
||||||
@@ -673,7 +826,7 @@ impl eframe::App for App {
|
|||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
ui.add_enabled_ui(on && logging_enabled, |ui| {
|
ui.add_enabled_ui(is_connected && logging_enabled, |ui| {
|
||||||
if Button::new("Add marker").corner_radius(5.0).min_size(egui::vec2(20.0, 0.0)).ui(ui).on_hover_text("Add a marker to the current time series plots").clicked() {
|
if Button::new("Add marker").corner_radius(5.0).min_size(egui::vec2(20.0, 0.0)).ui(ui).on_hover_text("Add a marker to the current time series plots").clicked() {
|
||||||
self.log_marker_modal = true;
|
self.log_marker_modal = true;
|
||||||
}
|
}
|
||||||
@@ -736,9 +889,17 @@ impl eframe::App for App {
|
|||||||
// Spacer to push the LED to the right
|
// Spacer to push the LED to the right
|
||||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||||
ui.scope(|ui| {
|
ui.scope(|ui| {
|
||||||
let is_connected = self.connected.load(Ordering::Relaxed);
|
let (color, tooltip) = match *self.hardware_connected.lock().unwrap() {
|
||||||
let color = if is_connected { Color32::DARK_GREEN } else { Color32::DARK_RED };
|
HardwareConnected::None => {
|
||||||
let tooltip = if is_connected { "Connected" } else { "Disconnected" };
|
(Color32::DARK_RED, "Disconnected")
|
||||||
|
},
|
||||||
|
HardwareConnected::WithoutMultiplexer => {
|
||||||
|
(Color32::DARK_GREEN, "Connected without multiplexer")
|
||||||
|
},
|
||||||
|
HardwareConnected::WithMultiplexer => {
|
||||||
|
(Color32::DARK_GREEN, "Connected with multiplexer")
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// Allocate a fixed-size rectangle for the LED
|
// Allocate a fixed-size rectangle for the LED
|
||||||
let led_size = egui::Vec2::splat(12.0);
|
let led_size = egui::Vec2::splat(12.0);
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ 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::BioImpedanceLeadMode::TwoLead, bioz_icd_rs::IcdDftNum::Num2048).await.unwrap().ok();
|
// Todo, currently passing none for the electrode config, which only works with the non multiplexed hardware. Need to add a way to specify this in the command.
|
||||||
|
client.start_impedancemeter_single(freq, bioz_icd_rs::BioImpedanceLeadMode::TwoLead, None, 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());
|
||||||
|
|
||||||
@@ -96,8 +97,8 @@ async fn main() {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
// Todo, currently passing none for the electrode config, which only works with the non multiplexed hardware. Need to add a way to specify this in the command.
|
||||||
match client.start_impedancemeter_single(freq, bioz_icd_rs::BioImpedanceLeadMode::TwoLead, bioz_icd_rs::IcdDftNum::Num2048).await {
|
match client.start_impedancemeter_single(freq, bioz_icd_rs::BioImpedanceLeadMode::TwoLead, None, 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),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ async fn main() {
|
|||||||
let magnitude_series_clone = app.magnitude_series.clone();
|
let magnitude_series_clone = app.magnitude_series.clone();
|
||||||
let phase_series_clone = app.phase_series.clone();
|
let phase_series_clone = app.phase_series.clone();
|
||||||
let bode_clone = app.bode_plot.clone();
|
let bode_clone = app.bode_plot.clone();
|
||||||
let connected_clone = app.connected.clone();
|
let hardware_connected_clone = app.hardware_connected.clone();
|
||||||
|
|
||||||
let data_frequency_clone = app.data_frequency.clone();
|
let data_frequency_clone = app.data_frequency.clone();
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ async fn main() {
|
|||||||
magnitude_series_clone,
|
magnitude_series_clone,
|
||||||
phase_series_clone,
|
phase_series_clone,
|
||||||
bode_clone,
|
bode_clone,
|
||||||
connected_clone,
|
hardware_connected_clone,
|
||||||
data_frequency_clone,
|
data_frequency_clone,
|
||||||
periods_per_dft,
|
periods_per_dft,
|
||||||
periods_per_dft_sweep,
|
periods_per_dft_sweep,
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ use postcard_rpc::{
|
|||||||
};
|
};
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use bioz_icd_rs::{
|
use bioz_icd_rs::{
|
||||||
BioImpedanceLeadMode, GetUniqueIdEndpoint, ImpedanceInitResult, SweepImpedanceInitResult, SweepImpedanceStartRequest, PingEndpoint, SetGreenLedEndpoint, SingleImpedanceStartRequest, StartSweepImpedanceEndpoint, StartSingleImpedanceEndpoint, StopImpedanceEndpoint
|
BioImpedanceLeadMode, ElectrodeConfiguration, GetMultiplexerCapabilityEndpoint, GetUniqueIdEndpoint, ImpedanceInitResult, PingEndpoint, SetGreenLedEndpoint, SingleImpedanceStartRequest, StartSingleImpedanceEndpoint, StartSweepImpedanceEndpoint, StopImpedanceEndpoint, SweepImpedanceInitResult, SweepImpedanceStartRequest
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::icd::{IcdDftNum, MeasurementPointSet};
|
use crate::icd::{IcdDftNum, MeasurementPointSet, MultiplexerCapability};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WorkbookClient {
|
pub struct WorkbookClient {
|
||||||
@@ -54,6 +54,11 @@ impl WorkbookClient {
|
|||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_device_info(&self) -> Result<MultiplexerCapability, WorkbookError<Infallible>> {
|
||||||
|
let info = self.client.send_resp::<GetMultiplexerCapabilityEndpoint>(&()).await?;
|
||||||
|
Ok(info)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn set_green_led(
|
pub async fn set_green_led(
|
||||||
&self,
|
&self,
|
||||||
frequency: f32,
|
frequency: f32,
|
||||||
@@ -66,10 +71,11 @@ impl WorkbookClient {
|
|||||||
&self,
|
&self,
|
||||||
frequency: u32,
|
frequency: u32,
|
||||||
lead_mode: BioImpedanceLeadMode,
|
lead_mode: BioImpedanceLeadMode,
|
||||||
|
electrode_config: Option<ElectrodeConfiguration>,
|
||||||
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, lead_mode, dft_number})
|
.send_resp::<StartSingleImpedanceEndpoint>(&SingleImpedanceStartRequest { update_frequency: 60, sinus_frequency: frequency, lead_mode, electrode_config, dft_number})
|
||||||
.await?;
|
.await?;
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
@@ -77,10 +83,11 @@ impl WorkbookClient {
|
|||||||
pub async fn start_impedancemeter_sweep(
|
pub async fn start_impedancemeter_sweep(
|
||||||
&self,
|
&self,
|
||||||
lead_mode: BioImpedanceLeadMode,
|
lead_mode: BioImpedanceLeadMode,
|
||||||
|
electrode_config: Option<ElectrodeConfiguration>,
|
||||||
points: MeasurementPointSet,
|
points: MeasurementPointSet,
|
||||||
) -> Result<SweepImpedanceInitResult, WorkbookError<Infallible>> {
|
) -> Result<SweepImpedanceInitResult, WorkbookError<Infallible>> {
|
||||||
let response = self.client
|
let response = self.client
|
||||||
.send_resp::<StartSweepImpedanceEndpoint>(&SweepImpedanceStartRequest { lead_mode, points })
|
.send_resp::<StartSweepImpedanceEndpoint>(&SweepImpedanceStartRequest { lead_mode, electrode_config, points })
|
||||||
.await?;
|
.await?;
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ use log::{error, info};
|
|||||||
use tokio::select;
|
use tokio::select;
|
||||||
use tokio::sync::mpsc::{Receiver, Sender};
|
use tokio::sync::mpsc::{Receiver, Sender};
|
||||||
|
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
use atomic_float::AtomicF32;
|
use atomic_float::AtomicF32;
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use bioz_icd_rs::MeasurementPointSet;
|
use bioz_icd_rs::{MeasurementPointSet, MultiplexerCapability};
|
||||||
|
|
||||||
use crate::icd;
|
use crate::icd;
|
||||||
use crate::client::WorkbookClient;
|
use crate::client::WorkbookClient;
|
||||||
@@ -20,6 +20,8 @@ use crate::plot::{TimeSeriesPlot, BodePlot};
|
|||||||
|
|
||||||
use crate::signals::{LoggingSignal, StartStopSignal};
|
use crate::signals::{LoggingSignal, StartStopSignal};
|
||||||
|
|
||||||
|
use crate::app::HardwareConnected;
|
||||||
|
|
||||||
pub async fn communicate_with_hardware(
|
pub async fn communicate_with_hardware(
|
||||||
mut run_impedancemeter_rx: Receiver<StartStopSignal>,
|
mut run_impedancemeter_rx: Receiver<StartStopSignal>,
|
||||||
run_impedancemeter_tx: Sender<StartStopSignal>,
|
run_impedancemeter_tx: Sender<StartStopSignal>,
|
||||||
@@ -28,7 +30,7 @@ pub async fn communicate_with_hardware(
|
|||||||
magnitude_series: Arc<Mutex<TimeSeriesPlot>>,
|
magnitude_series: Arc<Mutex<TimeSeriesPlot>>,
|
||||||
phase_series: Arc<Mutex<TimeSeriesPlot>>,
|
phase_series: Arc<Mutex<TimeSeriesPlot>>,
|
||||||
bode_series: Arc<Mutex<BodePlot>>,
|
bode_series: Arc<Mutex<BodePlot>>,
|
||||||
connected: Arc<AtomicBool>,
|
connected: Arc<Mutex<HardwareConnected>>,
|
||||||
data_frequency: Arc<AtomicF32>,
|
data_frequency: Arc<AtomicF32>,
|
||||||
periods_per_dft: Arc<Mutex<Option<f32>>>,
|
periods_per_dft: Arc<Mutex<Option<f32>>>,
|
||||||
periods_per_dft_sweep: Arc<Mutex<(Vec<u32>, Option<Vec<f32>>)>>,
|
periods_per_dft_sweep: Arc<Mutex<(Vec<u32>, Option<Vec<f32>>)>>,
|
||||||
@@ -58,7 +60,16 @@ pub async fn communicate_with_hardware(
|
|||||||
if let Some(frequency) = settings.lock().unwrap().frequency {
|
if let Some(frequency) = settings.lock().unwrap().frequency {
|
||||||
run_impedancemeter_tx.send(frequency).await.unwrap();
|
run_impedancemeter_tx.send(frequency).await.unwrap();
|
||||||
}
|
}
|
||||||
connected.store(true, Ordering::Relaxed);
|
match client.get_device_info().await.unwrap() {
|
||||||
|
MultiplexerCapability::Absent => {
|
||||||
|
*connected.lock().unwrap() = HardwareConnected::WithoutMultiplexer;
|
||||||
|
info!("Connected device: Without Multiplexer");
|
||||||
|
},
|
||||||
|
MultiplexerCapability::Present => {
|
||||||
|
*connected.lock().unwrap() = HardwareConnected::WithMultiplexer;
|
||||||
|
info!("Connected device: With Multiplexer");
|
||||||
|
},
|
||||||
|
}
|
||||||
client
|
client
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -104,7 +115,7 @@ pub async fn communicate_with_hardware(
|
|||||||
if *gui_logging_state_clone.lock().unwrap() == LoggingStates::Logging {
|
if *gui_logging_state_clone.lock().unwrap() == LoggingStates::Logging {
|
||||||
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.try_send(LoggingSignal::SingleImpedance(SystemTime::now(), freq, val.magnitude, val.phase)) {
|
if let Err(e) = log_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);
|
||||||
}
|
}
|
||||||
@@ -178,11 +189,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, lead_mode, dft_num) => {
|
StartStopSignal::StartSingle(freq, lead_mode, electrode_config, dft_num) => {
|
||||||
match workbook_client.start_impedancemeter_single(freq, lead_mode, dft_num).await {
|
match workbook_client.start_impedancemeter_single(freq, lead_mode, electrode_config, 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, lead_mode, dft_num));
|
settings.lock().unwrap().frequency = Some(StartStopSignal::StartSingle(freq, lead_mode, electrode_config, dft_num));
|
||||||
*periods_per_dft.lock().unwrap() = Some(periods);
|
*periods_per_dft.lock().unwrap() = Some(periods);
|
||||||
},
|
},
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
@@ -195,10 +206,10 @@ pub async fn communicate_with_hardware(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
StartStopSignal::StartSweep(lead_mode, num_points) => {
|
StartStopSignal::StartSweep(lead_mode, electrode_config, num_points) => {
|
||||||
match workbook_client.start_impedancemeter_sweep(lead_mode, num_points).await {
|
match workbook_client.start_impedancemeter_sweep(lead_mode, electrode_config, num_points).await {
|
||||||
Ok(Ok(periods)) => {
|
Ok(Ok(periods)) => {
|
||||||
settings.lock().unwrap().frequency = Some(StartStopSignal::StartSweep(lead_mode, num_points));
|
settings.lock().unwrap().frequency = Some(StartStopSignal::StartSweep(lead_mode, electrode_config, num_points));
|
||||||
info!("Sweep Impedancemeter started.");
|
info!("Sweep Impedancemeter started.");
|
||||||
match num_points {
|
match num_points {
|
||||||
MeasurementPointSet::Eight => {
|
MeasurementPointSet::Eight => {
|
||||||
@@ -246,7 +257,7 @@ pub async fn communicate_with_hardware(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
info!("Communication with hardware ended.");
|
info!("Communication with hardware ended.");
|
||||||
connected.store(false, Ordering::Relaxed);
|
*connected.lock().unwrap() = HardwareConnected::None;
|
||||||
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use crate::icd::{BioImpedanceLeadMode, IcdDftNum, MeasurementPointSet};
|
use crate::icd::{BioImpedanceLeadMode, IcdDftNum, ElectrodeConfiguration, MeasurementPointSet};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum StartStopSignal {
|
pub enum StartStopSignal {
|
||||||
StartSingle(u32, BioImpedanceLeadMode, IcdDftNum), // frequency in Hz, lead mode, DFT number
|
StartSingle(u32, BioImpedanceLeadMode, Option<ElectrodeConfiguration>, IcdDftNum), // frequency in Hz, lead mode, electrode configuration, DFT number
|
||||||
StartSweep(BioImpedanceLeadMode, MeasurementPointSet), // lead mode, number of points per measurement
|
StartSweep(BioImpedanceLeadMode, Option<ElectrodeConfiguration>, MeasurementPointSet), // lead mode, electrode configuration, number of points per measurement
|
||||||
Stop,
|
Stop,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user