Switched channels instead of shared state

This commit is contained in:
Sebastian 2024-01-21 15:45:29 +01:00
parent 6dd49234df
commit 565d17b383
2 changed files with 138 additions and 97 deletions

View File

@ -7,6 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
cheapsdo-protocol = { path = "../protocol" } cheapsdo-protocol = { path = "../protocol" }
crossbeam-channel = "0.5.11"
eframe = "0.24.1" eframe = "0.24.1"
egui_plot = "0.24.1" egui_plot = "0.24.1"
postcard = {version = "1.0.8", features = ["use-std"]} postcard = {version = "1.0.8", features = ["use-std"]}

View File

@ -1,12 +1,14 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use std::io; use std::io;
use std::sync::{Arc, Mutex};
use std::io::Write;
use std::time::Duration; use std::time::Duration;
use std::{io::Write, thread};
use crossbeam_channel::{select, unbounded, Receiver, Sender};
use eframe::egui; use eframe::egui;
use egui_plot::{Line, Plot, PlotBounds, PlotPoints}; use egui_plot::{Line, Plot, PlotPoints};
use cheapsdo_protocol::*; use cheapsdo_protocol::*;
use postcard::{from_bytes_cobs, to_stdvec_cobs}; use postcard::{from_bytes_cobs, to_stdvec_cobs};
@ -21,40 +23,43 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native( eframe::run_native(
"Cheapsdo Control", "Cheapsdo Control",
options, options,
Box::new(|cc| Box::<CheapsdoControl>::default()), Box::new(|_cc| Box::<CheapsdoControl>::default()),
) )
} }
struct SharedState { #[derive(PartialEq, Debug)]
device_state: StatusMessage, enum SerialPortCmd {
average_points: Vec<[f64; 2]>, Disconnect,
measured_points: Vec<[f64; 2]>,
pwm_points: Vec<[f64; 2]>,
} }
impl Default for SharedState { #[derive(PartialEq, Debug)]
fn default() -> Self { enum SerialPortData {
Self { DeviceState(StatusMessage),
device_state: StatusMessage::default(),
average_points: Vec::<[f64; 2]>::new(),
measured_points: Vec::<[f64; 2]>::new(),
pwm_points: Vec::<[f64; 2]>::new(),
}
}
} }
struct CheapsdoControl { struct CheapsdoControl {
shared_state: Arc<Mutex<SharedState>>,
serial_device: String, serial_device: String,
serial_connected: bool, serial_connected: bool,
data_rx: Option<Receiver<SerialPortData>>,
cmd_tx: Option<Sender<SerialPortCmd>>,
device_state: StatusMessage,
average_points: Vec<[f64; 2]>,
pwm_points: Vec<[f64; 2]>,
} }
impl Default for CheapsdoControl { impl Default for CheapsdoControl {
fn default() -> Self { fn default() -> Self {
Self { Self {
shared_state: Arc::new(Mutex::new(SharedState::default())),
serial_device: "".to_owned(), serial_device: "".to_owned(),
serial_connected: false, serial_connected: false,
data_rx: None,
cmd_tx: None,
device_state: StatusMessage::default(),
average_points: Vec::new(),
pwm_points: Vec::new(),
} }
} }
} }
@ -64,6 +69,24 @@ impl eframe::App for CheapsdoControl {
let ports = let ports =
serialport::available_ports().unwrap_or(Vec::<serialport::SerialPortInfo>::new()); serialport::available_ports().unwrap_or(Vec::<serialport::SerialPortInfo>::new());
if let Some(data_rx) = &self.data_rx {
loop {
select! {
recv(data_rx) -> data => {
match data {
Ok(SerialPortData::DeviceState(status_msg)) => {
self.device_state = status_msg.clone();
self.average_points.push([self.average_points.len() as f64, status_msg.average_frequency]);
self.pwm_points.push([self.pwm_points.len() as f64, status_msg.pwm as f64]);
},
Err(_) => break,
};
},
default => break,
}
}
}
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
egui::ComboBox::from_label("Select Serialport") egui::ComboBox::from_label("Select Serialport")
@ -82,101 +105,114 @@ impl eframe::App for CheapsdoControl {
.add_enabled(!self.serial_connected, egui::Button::new("Open")) .add_enabled(!self.serial_connected, egui::Button::new("Open"))
.clicked() .clicked()
{ {
self.serial_connected = true;
let serial_device = self.serial_device.clone(); let serial_device = self.serial_device.clone();
let devicestate = self.shared_state.clone();
let ctx = ctx.clone(); let ctx = ctx.clone();
self.serial_connected = true;
self.average_points.clear();
self.pwm_points.clear();
let (cmd_tx, cmd_rx) = unbounded();
let (data_tx, data_rx) = unbounded();
self.cmd_tx = Some(cmd_tx);
self.data_rx = Some(data_rx);
std::thread::spawn(move || { std::thread::spawn(move || {
poll_device(serial_device, devicestate, ctx); poll_device(serial_device, cmd_rx, data_tx, ctx);
}); });
} }
if ui if ui
.add_enabled(self.serial_connected, egui::Button::new("Close")) .add_enabled(self.serial_connected, egui::Button::new("Close"))
.clicked() .clicked()
{} {
if let Some(cmd_tx) = &self.cmd_tx {
cmd_tx.send(SerialPortCmd::Disconnect).unwrap();
}
self.serial_connected = false;
}
}); });
ui.separator(); ui.separator();
{ ui.horizontal(|ui| {
let mut state = self.shared_state.lock().unwrap(); ui.label(
ui.horizontal(|ui| { egui::RichText::new(format!(
ui.label( "Measured: {}",
egui::RichText::new(format!( formatters::frequency(self.device_state.measured_frequency)
"Measured: {}", ))
formatters::frequency(state.device_state.measured_frequency) .family(egui::FontFamily::Monospace)
)) .size(20.0),
.family(egui::FontFamily::Monospace) );
.size(20.0),
);
ui.add_space(100.0); ui.add_space(100.0);
ui.label( ui.label(
egui::RichText::new(format!( egui::RichText::new(format!(
"Average: {}", "Average: {}",
formatters::frequency(state.device_state.average_frequency) formatters::frequency(self.device_state.average_frequency)
)) ))
.family(egui::FontFamily::Monospace) .family(egui::FontFamily::Monospace)
.size(20.0), .size(20.0),
); );
});
ui.add_space(20.0);
let average_line = Line::new(PlotPoints::new(self.average_points.clone()));
//let measured_line = Line::new(PlotPoints::new(self.measured_points.clone()));
Plot::new("frequency_plot")
.view_aspect(4.0)
.allow_zoom(false)
.allow_scroll(false)
.allow_drag(false)
.allow_boxed_zoom(false)
.y_axis_width(12)
.y_axis_formatter(|val, _, _| formatters::frequency(val))
.label_formatter(|name, value| {
format!("{}: {}", name, formatters::frequency(value.y))
})
.show(ui, |plot_ui| {
plot_ui.set_auto_bounds([true, true].into());
plot_ui.line(average_line);
//plot_ui.line(measured_line);
}); });
ui.add_space(20.0); ui.add_space(20.0);
let average_line = Line::new(PlotPoints::new(state.average_points.clone())); let pwm_line = Line::new(PlotPoints::new(self.pwm_points.clone()));
let measured_line = Line::new(PlotPoints::new(state.measured_points.clone())); Plot::new("pwm_plot")
Plot::new("frequency_plot") .view_aspect(4.0)
.view_aspect(4.0) .allow_zoom(false)
.allow_zoom(false) .allow_scroll(false)
.allow_scroll(false) .allow_drag(false)
.allow_drag(false) .allow_boxed_zoom(false)
.allow_boxed_zoom(false) .y_axis_width(12)
.y_axis_width(12) .show(ui, |plot_ui| {
.y_axis_formatter(|val, _, _| formatters::frequency(val)) plot_ui.set_auto_bounds([true, true].into());
.label_formatter(|name, value| { plot_ui.line(pwm_line);
format!("{}: {}", name, formatters::frequency(value.y)) });
})
.show(ui, |plot_ui| {
plot_ui.set_auto_bounds([true, true].into());
plot_ui.line(average_line);
//plot_ui.line(measured_line);
});
ui.add_space(20.0);
let pwm_line = Line::new(PlotPoints::new(state.pwm_points.clone()));
Plot::new("pwm_plot")
.view_aspect(4.0)
.allow_zoom(false)
.allow_scroll(false)
.allow_drag(false)
.allow_boxed_zoom(false)
.y_axis_width(12)
.show(ui, |plot_ui| {
plot_ui.set_auto_bounds([true, true].into());
plot_ui.line(pwm_line);
});
}
}); });
} }
} }
fn poll_device(port: String, state: Arc<Mutex<SharedState>>, ctx: egui::Context) { fn poll_device(
port: String,
cmd_rx: Receiver<SerialPortCmd>,
data_tx: Sender<SerialPortData>,
ctx: egui::Context,
) {
let mut port = serialport::new(port, 115_200) let mut port = serialport::new(port, 115_200)
.timeout(Duration::from_millis(10)) .timeout(Duration::from_millis(10))
.open() .open()
.expect("Failed to open port"); .expect("Failed to open port");
let host_msg = HostMessage::RequestStatus;
let msg_bytes = to_stdvec_cobs(&host_msg).unwrap();
port.write_all(&msg_bytes).unwrap();
loop { loop {
let host_msg = HostMessage::RequestStatus;
let msg_bytes = to_stdvec_cobs(&host_msg).unwrap();
port.write_all(&msg_bytes).unwrap();
let mut serial_buf: Vec<u8> = vec![0; 128]; let mut serial_buf: Vec<u8> = vec![0; 128];
match port.read(serial_buf.as_mut_slice()) { match port.read(serial_buf.as_mut_slice()) {
Ok(t) => { Ok(t) => {
@ -187,16 +223,10 @@ fn poll_device(port: String, state: Arc<Mutex<SharedState>>, ctx: egui::Context)
match dev_msg { match dev_msg {
DeviceMessage::Status(status_msg) => { DeviceMessage::Status(status_msg) => {
let mut state = state.lock().unwrap(); data_tx
let x = state.average_points.len() as f64; .send(SerialPortData::DeviceState(status_msg))
state.average_points.push([x, status_msg.average_frequency]); .unwrap();
state ctx.request_repaint();
.measured_points
.push([x, status_msg.measured_frequency]);
state.pwm_points.push([x, status_msg.pwm as f64]);
state.device_state = status_msg;
ctx.request_repaint()
} }
} }
} }
@ -204,6 +234,16 @@ fn poll_device(port: String, state: Arc<Mutex<SharedState>>, ctx: egui::Context)
Err(e) => eprintln!("{:?}", e), Err(e) => eprintln!("{:?}", e),
} }
thread::sleep(Duration::from_millis(1000)); select! {
recv(cmd_rx) -> cmd => match cmd {
Ok(SerialPortCmd::Disconnect) => return,
Err(_) => {},
},
default(Duration::from_secs(1)) => {
let host_msg = HostMessage::RequestStatus;
let msg_bytes = to_stdvec_cobs(&host_msg).unwrap();
port.write_all(&msg_bytes).unwrap();
},
}
} }
} }