#![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::::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>, 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::::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>, 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 = 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::(&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)); } }