mirror of
https://github.com/hubaldv/bioz-host-rs.git
synced 2026-09-07 13:37:21 +00:00
Add feedback to controller after hardware commands.
This commit is contained in:
@@ -9,7 +9,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use bioz_icd_rs::{SweepPoints, MultiplexerCapability};
|
||||
use bioz_icd_rs::{BioImpedanceLeadMode, ElectrodeConfiguration, IcdDftNum, MultiplexerCapability, SweepPoints};
|
||||
|
||||
use crate::state::{HardwareState, MeasurementDataState};
|
||||
|
||||
@@ -42,9 +42,23 @@ pub async fn communicate_with_hardware(
|
||||
}
|
||||
});
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct Settings {
|
||||
mode: Option<StartStopSignal>,
|
||||
mode: Option<MeasurementMode>,
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
enum MeasurementMode {
|
||||
Single {
|
||||
freq: u32,
|
||||
lead_mode: BioImpedanceLeadMode,
|
||||
electrode_config: Option<ElectrodeConfiguration>,
|
||||
dft_num: IcdDftNum,
|
||||
},
|
||||
Sweep {
|
||||
lead_mode: BioImpedanceLeadMode,
|
||||
electrode_config: Option<ElectrodeConfiguration>,
|
||||
num_points: SweepPoints,
|
||||
},
|
||||
}
|
||||
let settings = Arc::new(Mutex::new(Settings::default()));
|
||||
|
||||
@@ -54,9 +68,45 @@ pub async fn communicate_with_hardware(
|
||||
let workbook_client = match WorkbookClient::new() {
|
||||
Ok(client) => {
|
||||
info!("Connected to hardware successfully.");
|
||||
if let Some(mode) = settings.lock().unwrap().mode {
|
||||
hardware_control_tx.send(mode).await.unwrap();
|
||||
let mode = {
|
||||
let settings = settings.lock().unwrap();
|
||||
settings.mode.clone()
|
||||
};
|
||||
|
||||
// If there was a previous mode, attempt to reconnect with the same settings
|
||||
if let Some(mode) = mode {
|
||||
|
||||
let command = match mode {
|
||||
MeasurementMode::Single {
|
||||
freq,
|
||||
lead_mode,
|
||||
electrode_config,
|
||||
dft_num,
|
||||
} => StartStopSignal::StartSingle(
|
||||
freq,
|
||||
lead_mode,
|
||||
electrode_config,
|
||||
dft_num,
|
||||
None,
|
||||
),
|
||||
|
||||
MeasurementMode::Sweep {
|
||||
lead_mode,
|
||||
electrode_config,
|
||||
num_points,
|
||||
} => StartStopSignal::StartSweep(
|
||||
lead_mode,
|
||||
electrode_config,
|
||||
num_points,
|
||||
None,
|
||||
),
|
||||
};
|
||||
|
||||
hardware_control_tx.send(command).await.unwrap();
|
||||
// Do not wait for the start confirmation, as the select! loop is not running yet (see below)
|
||||
}
|
||||
|
||||
// Check the multiplexer capability of the connected device
|
||||
match client.get_device_info().await.unwrap() {
|
||||
MultiplexerCapability::Absent => {
|
||||
hardware_state.hardware_connected = HardwareConnected::WithoutMultiplexer;
|
||||
@@ -112,9 +162,9 @@ pub async fn communicate_with_hardware(
|
||||
}
|
||||
// Send logging signal
|
||||
if *logging_state_rx_clone.borrow() == LoggingState::Logging {
|
||||
let settings = *settings_clone.lock().unwrap();
|
||||
let settings = settings_clone.lock().unwrap();
|
||||
match settings.mode {
|
||||
Some(StartStopSignal::StartSingle(freq, _, _, _)) => {
|
||||
Some(MeasurementMode::Single { freq, .. }) => {
|
||||
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);
|
||||
}
|
||||
@@ -201,17 +251,28 @@ pub async fn communicate_with_hardware(
|
||||
let logging_control_tx_clone = logging_control_tx.clone();
|
||||
loop {
|
||||
select! {
|
||||
Some(frequency) = hardware_control_rx.recv() => {
|
||||
match frequency {
|
||||
StartStopSignal::StartSingle(freq, lead_mode, electrode_config, dft_num) => {
|
||||
Some(start_stop_signal) = hardware_control_rx.recv() => {
|
||||
match start_stop_signal {
|
||||
StartStopSignal::StartSingle(freq, lead_mode, electrode_config, dft_num, start_tx) => {
|
||||
match workbook_client.start_impedancemeter_single(freq, lead_mode, electrode_config, dft_num).await {
|
||||
Ok(Ok(periods)) => {
|
||||
info!("Impedance meter started at frequency: {} with periods per DFT: {}", freq, periods);
|
||||
settings.lock().unwrap().mode = Some(StartStopSignal::StartSingle(freq, lead_mode, electrode_config, dft_num));
|
||||
|
||||
// Store reconnect information only
|
||||
settings.lock().unwrap().mode = Some(MeasurementMode::Single {
|
||||
freq,
|
||||
lead_mode,
|
||||
electrode_config,
|
||||
dft_num,
|
||||
});
|
||||
|
||||
hardware_state.running = true;
|
||||
hardware_state.periods_per_dft = Some(periods);
|
||||
hardware_state_tx.send(hardware_state.clone()).unwrap();
|
||||
|
||||
// Send confirmation back to the caller
|
||||
if let Some(start_tx) = start_tx {
|
||||
let _ = start_tx.send(());
|
||||
}
|
||||
// When logging add electrode configuration to logging file
|
||||
if *logging_state_rx.borrow() == LoggingState::Logging {
|
||||
if let Err(e) = logging_control_tx_clone.try_send(LoggingSignal::ElectrodeCongiguration(electrode_config)) {
|
||||
@@ -233,11 +294,18 @@ pub async fn communicate_with_hardware(
|
||||
}
|
||||
}
|
||||
},
|
||||
StartStopSignal::StartSweep(lead_mode, electrode_config, num_points) => {
|
||||
StartStopSignal::StartSweep(lead_mode, electrode_config, num_points, start_tx) => {
|
||||
match workbook_client.start_impedancemeter_sweep(lead_mode, electrode_config, num_points).await {
|
||||
Ok(Ok(periods)) => {
|
||||
info!("Sweep Impedancemeter started.");
|
||||
settings.lock().unwrap().mode = Some(StartStopSignal::StartSweep(lead_mode, electrode_config, num_points));
|
||||
|
||||
// Store reconnect information only
|
||||
settings.lock().unwrap().mode = Some(MeasurementMode::Sweep {
|
||||
lead_mode,
|
||||
electrode_config,
|
||||
num_points,
|
||||
});
|
||||
|
||||
hardware_state.running = true;
|
||||
match num_points {
|
||||
SweepPoints::Partial => {
|
||||
@@ -250,7 +318,10 @@ pub async fn communicate_with_hardware(
|
||||
hardware_state_tx.send(hardware_state.clone()).unwrap();
|
||||
},
|
||||
}
|
||||
|
||||
// Send confirmation back to the caller
|
||||
if let Some(start_tx) = start_tx {
|
||||
let _ = start_tx.send(());
|
||||
}
|
||||
// When logging add electrode configuration to logging file
|
||||
if *logging_state_rx.borrow() == LoggingState::Logging {
|
||||
if let Err(e) = logging_control_tx_clone.try_send(LoggingSignal::ElectrodeCongiguration(electrode_config)) {
|
||||
@@ -272,17 +343,20 @@ pub async fn communicate_with_hardware(
|
||||
}
|
||||
}
|
||||
},
|
||||
StartStopSignal::Stop => {
|
||||
StartStopSignal::Stop(stop_tx) => {
|
||||
if let Err(e) = workbook_client.stop_impedancemeter().await {
|
||||
error!("Failed to stop impedancemeter: {:?}", e);
|
||||
} else {
|
||||
settings.lock().unwrap().mode = Some(StartStopSignal::Stop);
|
||||
settings.lock().unwrap().mode = None;
|
||||
hardware_state.running = false;
|
||||
hardware_state.periods_per_dft = None;
|
||||
let (freq, _) = hardware_state.periods_per_dft_sweep.clone();
|
||||
hardware_state.periods_per_dft_sweep = (freq, None);
|
||||
hardware_state_tx.send(hardware_state.clone()).unwrap();
|
||||
info!("Impedancemeter stopped.");
|
||||
stop_tx.send(()).unwrap_or_else(|e| {
|
||||
error!("Failed to send stop confirmation: {:?}", e);
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use log::info;
|
||||
use tokio::sync::{mpsc, watch};
|
||||
use tokio::sync::{mpsc, watch, oneshot};
|
||||
use crate::{app::TabActive, signals::StartStopSignal, state::{AppState, ControlCommand, HardwareConnected, HardwareState}};
|
||||
|
||||
use crate::state::Mode;
|
||||
@@ -37,43 +37,82 @@ pub async fn app_control_loop(mut app_control_rx: mpsc::Receiver<ControlCommand>
|
||||
info!("Active tab changed to {:?}!", tab_active);
|
||||
}
|
||||
ControlCommand::Start(mode) => {
|
||||
if hardware_state_rx.borrow().hardware_connected == HardwareConnected::None {
|
||||
// Snapshot the current hardware state to avoid holding a `watch::Ref`
|
||||
// (which is !Send) across `.await` points.
|
||||
let hw = hardware_state_rx.borrow().clone();
|
||||
if hw.hardware_connected == HardwareConnected::None {
|
||||
info!("Cannot start measurement: No hardware connected!");
|
||||
continue;
|
||||
}
|
||||
if hardware_state_rx.borrow().running {
|
||||
if hw.running {
|
||||
info!("Measurement already running, stopping first...");
|
||||
hardware_control_tx.try_send(StartStopSignal::Stop).unwrap();
|
||||
|
||||
let (stop_tx, stop_rx) = oneshot::channel();
|
||||
if let Err(e) = hardware_control_tx.send(StartStopSignal::Stop(stop_tx)).await {
|
||||
info!("Failed to send stop command: {:?}", e);
|
||||
}
|
||||
// Wait for the stop confirmation
|
||||
if let Err(e) = stop_rx.await {
|
||||
info!("Failed to receive stop confirmation: {:?}", e);
|
||||
}
|
||||
}
|
||||
info!("Starting impedance hardware with mode {:?}...", mode);
|
||||
state.mode = mode;
|
||||
|
||||
match (mode, hardware_state_rx.borrow().hardware_connected) {
|
||||
match (mode, hw.hardware_connected) {
|
||||
(Mode::Single, HardwareConnected::WithoutMultiplexer) => {
|
||||
info!("Starting single measurement...");
|
||||
state.tab_active = TabActive::Single;
|
||||
hardware_control_tx.try_send(StartStopSignal::StartSingle(
|
||||
state.single_frequency, state.lead_mode, None, state.dft_num)).unwrap();
|
||||
let (start_tx, start_rx) = oneshot::channel();
|
||||
if let Err(e) = hardware_control_tx.send(StartStopSignal::StartSingle(
|
||||
state.single_frequency, state.lead_mode, None, state.dft_num, Some(start_tx))).await {
|
||||
info!("Failed to send start command: {:?}", e);
|
||||
}
|
||||
// Wait for the start confirmation
|
||||
if let Err(e) = start_rx.await {
|
||||
info!("Failed to receive start confirmation: {:?}", e);
|
||||
}
|
||||
}
|
||||
(Mode::Single, HardwareConnected::WithMultiplexer) => {
|
||||
info!("Starting single measurement...");
|
||||
state.tab_active = TabActive::Single;
|
||||
let electrode_config = state.electrode_settings.to_electrode_config(hardware_state_rx.borrow().hardware_connected, state.lead_mode);
|
||||
hardware_control_tx.try_send(StartStopSignal::StartSingle(
|
||||
state.single_frequency, state.lead_mode, electrode_config, state.dft_num)).unwrap();
|
||||
let electrode_config = state.electrode_settings.to_electrode_config(hw.hardware_connected, state.lead_mode);
|
||||
let (start_tx, start_rx) = oneshot::channel();
|
||||
if let Err(e) = hardware_control_tx.send(StartStopSignal::StartSingle(
|
||||
state.single_frequency, state.lead_mode, electrode_config, state.dft_num, Some(start_tx))).await {
|
||||
info!("Failed to send start command: {:?}", e);
|
||||
}
|
||||
// Wait for the start confirmation
|
||||
if let Err(e) = start_rx.await {
|
||||
info!("Failed to receive start confirmation: {:?}", e);
|
||||
}
|
||||
}
|
||||
(Mode::Sweep, HardwareConnected::WithoutMultiplexer) => {
|
||||
info!("Starting sweep measurement...");
|
||||
state.tab_active = TabActive::Sweep;
|
||||
hardware_control_tx.try_send(StartStopSignal::StartSweep(
|
||||
state.lead_mode, None, state.sweep_points)).unwrap();
|
||||
let (start_tx, start_rx) = oneshot::channel();
|
||||
if let Err(e) = hardware_control_tx.send(StartStopSignal::StartSweep(
|
||||
state.lead_mode, None, state.sweep_points, Some(start_tx))).await {
|
||||
info!("Failed to send start command: {:?}", e);
|
||||
}
|
||||
// Wait for the start confirmation
|
||||
if let Err(e) = start_rx.await {
|
||||
info!("Failed to receive start confirmation: {:?}", e);
|
||||
}
|
||||
}
|
||||
(Mode::Sweep, HardwareConnected::WithMultiplexer) => {
|
||||
info!("Starting sweep measurement...");
|
||||
state.tab_active = TabActive::Sweep;
|
||||
let electrode_config = state.electrode_settings.to_electrode_config(hardware_state_rx.borrow().hardware_connected, state.lead_mode);
|
||||
hardware_control_tx.try_send(StartStopSignal::StartSweep(
|
||||
state.lead_mode, electrode_config, state.sweep_points)).unwrap();
|
||||
let electrode_config = state.electrode_settings.to_electrode_config(hw.hardware_connected, state.lead_mode);
|
||||
let (start_tx, start_rx) = oneshot::channel();
|
||||
if let Err(e) = hardware_control_tx.send(StartStopSignal::StartSweep(
|
||||
state.lead_mode, electrode_config, state.sweep_points, Some(start_tx))).await {
|
||||
info!("Failed to send start command: {:?}", e);
|
||||
}
|
||||
// Wait for the start confirmation
|
||||
if let Err(e) = start_rx.await {
|
||||
info!("Failed to receive start confirmation: {:?}", e);
|
||||
}
|
||||
}
|
||||
(_, HardwareConnected::None) => {
|
||||
info!("Cannot start measurement: No hardware connected!");
|
||||
@@ -81,14 +120,21 @@ pub async fn app_control_loop(mut app_control_rx: mpsc::Receiver<ControlCommand>
|
||||
}
|
||||
}
|
||||
ControlCommand::Stop => {
|
||||
if hardware_state_rx.borrow().hardware_connected == HardwareConnected::None {
|
||||
let hw = hardware_state_rx.borrow().clone();
|
||||
if hw.hardware_connected == HardwareConnected::None {
|
||||
info!("Cannot stop measurement: No hardware connected!");
|
||||
continue;
|
||||
}
|
||||
info!("Stopping impedance hardware...");
|
||||
if let Err(e) = hardware_control_tx.try_send(StartStopSignal::Stop) {
|
||||
|
||||
let (stop_tx, stop_rx) = oneshot::channel();
|
||||
if let Err(e) = hardware_control_tx.send(StartStopSignal::Stop(stop_tx)).await {
|
||||
info!("Failed to send stop command: {:?}", e);
|
||||
}
|
||||
// Wait for the stop confirmation
|
||||
if let Err(e) = stop_rx.await {
|
||||
info!("Failed to receive stop confirmation: {:?}", e);
|
||||
}
|
||||
}
|
||||
ControlCommand::TcpConnected(connected) => {
|
||||
state.tcp_connected = connected;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use std::time::SystemTime;
|
||||
|
||||
use tokio::sync::{oneshot};
|
||||
|
||||
use crate::icd::{BioImpedanceLeadMode, IcdDftNum, ElectrodeConfiguration, SweepPoints};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub enum StartStopSignal {
|
||||
StartSingle(u32, BioImpedanceLeadMode, Option<ElectrodeConfiguration>, IcdDftNum), // frequency in Hz, lead mode, electrode configuration, DFT number
|
||||
StartSweep(BioImpedanceLeadMode, Option<ElectrodeConfiguration>, SweepPoints), // lead mode, electrode configuration, number of points per measurement
|
||||
Stop,
|
||||
StartSingle(u32, BioImpedanceLeadMode, Option<ElectrodeConfiguration>, IcdDftNum, Option<oneshot::Sender<()>>), // frequency in Hz, lead mode, electrode configuration, DFT number, signal channel to confirm start
|
||||
StartSweep(BioImpedanceLeadMode, Option<ElectrodeConfiguration>, SweepPoints, Option<oneshot::Sender<()>>), // lead mode, electrode configuration, number of points per measurement, signal channel to confirm start
|
||||
Stop(oneshot::Sender<()>), // signal channel to confirm stop
|
||||
}
|
||||
|
||||
pub enum LoggingSignal {
|
||||
|
||||
Reference in New Issue
Block a user