cheapsdo2.0/hostsoftware/src/main.rs

210 lines
7.0 KiB
Rust

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use std::io;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::{io::Write, thread};
use eframe::egui;
use egui_plot::{Line, Plot, PlotBounds, PlotPoints};
use cheapsdo_protocol::*;
use postcard::{from_bytes_cobs, to_stdvec_cobs};
mod formatters;
fn main() -> Result<(), eframe::Error> {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default(),
..Default::default()
};
eframe::run_native(
"Cheapsdo Control",
options,
Box::new(|cc| Box::<CheapsdoControl>::default()),
)
}
struct SharedState {
device_state: StatusMessage,
average_points: Vec<[f64; 2]>,
measured_points: Vec<[f64; 2]>,
pwm_points: Vec<[f64; 2]>,
}
impl Default for SharedState {
fn default() -> Self {
Self {
device_state: StatusMessage::default(),
average_points: Vec::<[f64; 2]>::new(),
measured_points: Vec::<[f64; 2]>::new(),
pwm_points: Vec::<[f64; 2]>::new(),
}
}
}
struct CheapsdoControl {
shared_state: Arc<Mutex<SharedState>>,
serial_device: String,
serial_connected: bool,
}
impl Default for CheapsdoControl {
fn default() -> Self {
Self {
shared_state: Arc::new(Mutex::new(SharedState::default())),
serial_device: "".to_owned(),
serial_connected: false,
}
}
}
impl eframe::App for CheapsdoControl {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
let ports =
serialport::available_ports().unwrap_or(Vec::<serialport::SerialPortInfo>::new());
egui::CentralPanel::default().show(ctx, |ui| {
ui.horizontal(|ui| {
egui::ComboBox::from_label("Select Serialport")
.selected_text(self.serial_device.to_owned())
.show_ui(ui, |ui| {
for port in ports {
ui.selectable_value(
&mut self.serial_device,
port.port_name.to_owned(),
port.port_name,
);
}
});
if ui
.add_enabled(!self.serial_connected, egui::Button::new("Open"))
.clicked()
{
self.serial_connected = true;
let serial_device = self.serial_device.clone();
let devicestate = self.shared_state.clone();
let ctx = ctx.clone();
std::thread::spawn(move || {
poll_device(serial_device, devicestate, ctx);
});
}
if ui
.add_enabled(self.serial_connected, egui::Button::new("Close"))
.clicked()
{}
});
ui.separator();
{
let mut state = self.shared_state.lock().unwrap();
ui.horizontal(|ui| {
ui.label(
egui::RichText::new(format!(
"Measured: {}",
formatters::frequency(state.device_state.measured_frequency)
))
.family(egui::FontFamily::Monospace)
.size(20.0),
);
ui.add_space(100.0);
ui.label(
egui::RichText::new(format!(
"Average: {}",
formatters::frequency(state.device_state.average_frequency)
))
.family(egui::FontFamily::Monospace)
.size(20.0),
);
});
ui.add_space(20.0);
let average_line = Line::new(PlotPoints::new(state.average_points.clone()));
let measured_line = Line::new(PlotPoints::new(state.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);
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) {
let mut port = serialport::new(port, 115_200)
.timeout(Duration::from_millis(10))
.open()
.expect("Failed to open port");
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];
match port.read(serial_buf.as_mut_slice()) {
Ok(t) => {
serial_buf.truncate(t);
println!("Data: {:?}", serial_buf);
let dev_msg = from_bytes_cobs::<DeviceMessage>(&mut serial_buf).unwrap();
match dev_msg {
DeviceMessage::Status(status_msg) => {
let mut state = state.lock().unwrap();
let x = state.average_points.len() as f64;
state.average_points.push([x, status_msg.average_frequency]);
state
.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()
}
}
}
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => (),
Err(e) => eprintln!("{:?}", e),
}
thread::sleep(Duration::from_millis(1000));
}
}