mirror of
https://github.com/hubaldv/bioz-host-rs.git
synced 2025-12-06 05:11:17 +00:00
Added log and automatic reconnect.
This commit is contained in:
22
src/app.rs
22
src/app.rs
@@ -1,16 +1,16 @@
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use tokio::{sync::mpsc::{self, Receiver, Sender}, time::Timeout};
|
||||
use tokio::{sync::mpsc::{Sender}};
|
||||
|
||||
use eframe::egui::{self, Button, Checkbox, Color32, DragValue, Key, Layout, Modifiers, RichText, Rounding, };
|
||||
use eframe::egui::{self, Color32, DragValue, Key, Layout, Modifiers};
|
||||
use egui_plot::{Corner, Legend, Line, Plot, PlotPoints, Points, PlotBounds};
|
||||
|
||||
use crate::plot::TimeSeriesPlot;
|
||||
|
||||
pub struct App {
|
||||
interval_ms: u32,
|
||||
frequency: u32,
|
||||
run_impedancemeter_tx: Sender<u32>,
|
||||
pub magnitude: Arc<Mutex<f32>>,
|
||||
pub phase: Arc<Mutex<f32>>,
|
||||
@@ -21,7 +21,7 @@ pub struct App {
|
||||
impl App {
|
||||
pub fn new(run_impedancemeter_tx: Sender<u32>) -> Self {
|
||||
App {
|
||||
interval_ms: 10, // Default interval
|
||||
frequency: 2, // Default frequency
|
||||
run_impedancemeter_tx,
|
||||
magnitude: Arc::new(Mutex::new(0.0)),
|
||||
phase: Arc::new(Mutex::new(0.0)),
|
||||
@@ -39,18 +39,20 @@ impl eframe::App for App {
|
||||
egui::widgets::global_theme_preference_switch(ui);
|
||||
ui.separator();
|
||||
|
||||
if ui.add(DragValue::new(&mut self.interval_ms).speed(0.1).range(RangeInclusive::new(0, 50)).update_while_editing(false)).changed() {
|
||||
if ui.add(DragValue::new(&mut self.frequency).speed(0.1).range(RangeInclusive::new(0, 50)).update_while_editing(false)).changed() {
|
||||
if let Err(e) = self.run_impedancemeter_tx.try_send(0) {
|
||||
eprintln!("Failed to send stop command: {:?}", e);
|
||||
}
|
||||
// Delay
|
||||
if let Err(e) = self.run_impedancemeter_tx.try_send(self.interval_ms) {
|
||||
eprintln!("Failed to send interval update: {:?}", e);
|
||||
if let Err(e) = self.run_impedancemeter_tx.try_send(self.frequency) {
|
||||
eprintln!("Failed to send frequency update: {:?}", e);
|
||||
}
|
||||
};
|
||||
|
||||
ui.separator();
|
||||
|
||||
if ui.button("Start").clicked() {
|
||||
if let Err(e) = self.run_impedancemeter_tx.try_send(self.interval_ms) {
|
||||
if let Err(e) = self.run_impedancemeter_tx.try_send(self.frequency) {
|
||||
eprintln!("Failed to send start command: {:?}", e);
|
||||
}
|
||||
}
|
||||
@@ -67,7 +69,7 @@ impl eframe::App for App {
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
let available_height = ui.available_height();
|
||||
let mut half_height = available_height / 4.0;
|
||||
let half_height = available_height / 4.0;
|
||||
|
||||
let point_pos = vec![[*self.magnitude.lock().unwrap() as f64, *self.phase.lock().unwrap() as f64]];
|
||||
|
||||
|
||||
@@ -8,7 +8,13 @@ use bioz_host_rs::{client::WorkbookClient, icd, read_line};
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
println!("Connecting to USB device...");
|
||||
let client = WorkbookClient::new();
|
||||
let client = match WorkbookClient::new() {
|
||||
Ok(client) => client,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to connect to USB device: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
for i in 0..10 {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use log::info;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use bioz_host_rs::app::App;
|
||||
@@ -7,7 +9,10 @@ use bioz_host_rs::communication::communicate_with_hardware;
|
||||
use tokio::sync::mpsc::{self};
|
||||
|
||||
fn main() {
|
||||
|
||||
SimpleLogger::new().init().expect("Failed to initialize logger");
|
||||
log::set_max_level(log::LevelFilter::Info);
|
||||
info!("Starting Bioz Impedance Visualizer...");
|
||||
|
||||
let rt = Runtime::new().expect("Unable to create Runtime");
|
||||
|
||||
// Enter the runtime so that `tokio::spawn` is available immediately.
|
||||
|
||||
@@ -26,36 +26,17 @@ impl<E> From<HostErr<WireError>> for WorkbookError<E> {
|
||||
}
|
||||
}
|
||||
|
||||
trait FlattenErr {
|
||||
type Good;
|
||||
type Bad;
|
||||
fn flatten(self) -> Result<Self::Good, WorkbookError<Self::Bad>>;
|
||||
}
|
||||
|
||||
impl<T, E> FlattenErr for Result<T, E> {
|
||||
type Good = T;
|
||||
type Bad = E;
|
||||
fn flatten(self) -> Result<Self::Good, WorkbookError<Self::Bad>> {
|
||||
self.map_err(WorkbookError::Endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
impl WorkbookClient {
|
||||
pub fn new() -> Self {
|
||||
let client = HostClient::new_raw_nusb(
|
||||
pub fn new() -> Result<Self, String> {
|
||||
let client = HostClient::try_new_raw_nusb(
|
||||
|d| d.product_string() == Some("Bioz Amplifier"),
|
||||
ERROR_PATH,
|
||||
8,
|
||||
VarSeqKind::Seq2,
|
||||
);
|
||||
Self { client }
|
||||
}
|
||||
|
||||
pub fn new_serial(port: &str) -> Self {
|
||||
let client = HostClient::new_serial_cobs(port, ERROR_PATH, 8, 9600, VarSeqKind::Seq2);
|
||||
Self { client }
|
||||
)?;
|
||||
Ok(Self { client })
|
||||
}
|
||||
|
||||
pub async fn wait_closed(&self) {
|
||||
@@ -87,7 +68,6 @@ impl WorkbookClient {
|
||||
self.client
|
||||
.send_resp::<StartImpedanceEndpoint>(&StartImpedance { update_frequency: 60, sinus_frequency: frequency })
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -96,13 +76,6 @@ impl WorkbookClient {
|
||||
.client
|
||||
.send_resp::<StopImpedanceEndpoint>(&())
|
||||
.await?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WorkbookClient {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
use log::{error, info};
|
||||
|
||||
use tokio::select;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
|
||||
@@ -15,52 +17,78 @@ pub async fn communicate_with_hardware(
|
||||
magnitude_series: Arc<Mutex<TimeSeriesPlot>>,
|
||||
phase_series: Arc<Mutex<TimeSeriesPlot>>,
|
||||
) {
|
||||
let workbook_client = WorkbookClient::new();
|
||||
|
||||
let mut sub = workbook_client
|
||||
.client
|
||||
.subscribe_multi::<icd::ImpedanceTopic>(8)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Ok(val) = sub.recv().await {
|
||||
let mut mag_plot = magnitude_series.lock().unwrap();
|
||||
let mut phase_plot = phase_series.lock().unwrap();
|
||||
|
||||
*magnitude.lock().unwrap() = val.magnitude;
|
||||
*phase.lock().unwrap() = val.phase;
|
||||
|
||||
mag_plot.add(val.magnitude as f64);
|
||||
phase_plot.add(val.phase as f64);
|
||||
}
|
||||
});
|
||||
|
||||
loop {
|
||||
select! {
|
||||
Some(run) = run_impedancemeter_rx.recv() => {
|
||||
if run > 0 {
|
||||
// Start the impedancemeter
|
||||
if let Err(e) = workbook_client.start_impedancemeter(run as f32).await {
|
||||
eprintln!("Failed to start impedancemeter: {:?}", e);
|
||||
let workbook_client = match WorkbookClient::new() {
|
||||
Ok(client) => {
|
||||
info!("Connected to hardware successfully.");
|
||||
client
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to connect to hardware: {:?}", e);
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let mut sub = workbook_client
|
||||
.client
|
||||
.subscribe_multi::<icd::ImpedanceTopic>(8)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let data = (magnitude_series.clone(), phase_series.clone(), magnitude.clone(), phase.clone());
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Ok(val) = 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();
|
||||
|
||||
*mag_val = val.magnitude;
|
||||
*phase_val = val.phase;
|
||||
|
||||
mag_plot.add(val.magnitude as f64);
|
||||
phase_plot.add(val.phase as f64);
|
||||
}
|
||||
info!("ImpedanceTopic subscription ended.");
|
||||
|
||||
});
|
||||
|
||||
loop {
|
||||
select! {
|
||||
Some(run) = run_impedancemeter_rx.recv() => {
|
||||
if run > 0 {
|
||||
// Start the impedancemeter
|
||||
if let Err(e) = workbook_client.start_impedancemeter(run as f32).await {
|
||||
error!("Failed to start impedancemeter: {:?}", e);
|
||||
} else {
|
||||
info!("Impedancemeter started.");
|
||||
}
|
||||
} else {
|
||||
println!("Impedancemeter started.");
|
||||
}
|
||||
} else {
|
||||
// Stop the impedancemeter
|
||||
if let Err(e) = workbook_client.stop_impedancemeter().await {
|
||||
eprintln!("Failed to stop impedancemeter: {:?}", e);
|
||||
} else {
|
||||
println!("Impedancemeter stopped.");
|
||||
// Stop the impedancemeter
|
||||
if let Err(e) = workbook_client.stop_impedancemeter().await {
|
||||
error!("Failed to stop impedancemeter: {:?}", e);
|
||||
} else {
|
||||
info!("Impedancemeter stopped.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = workbook_client.wait_closed() => {
|
||||
// Handle client closure
|
||||
info!("Client connection closed.");
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
||||
break;
|
||||
}
|
||||
|
||||
else => {
|
||||
// All channels closed
|
||||
break;
|
||||
}
|
||||
else => {
|
||||
// All channels closed
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
info!("Communication with hardware ended.");
|
||||
// Wait for a short period before trying to reconnect
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user