261 lines
9.2 KiB
Rust
261 lines
9.2 KiB
Rust
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
|
|
|
use eframe::egui::{self};
|
|
|
|
use crossbeam_channel::{select, unbounded, Receiver, Sender};
|
|
|
|
use cheapsdo_protocol::*;
|
|
|
|
mod formatters;
|
|
mod plots;
|
|
mod serial;
|
|
mod widgets;
|
|
|
|
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 CheapsdoControl {
|
|
serial_device: String,
|
|
serial_connected: bool,
|
|
|
|
data_rx: Option<Receiver<serial::Data>>,
|
|
cmd_tx: Option<Sender<serial::Cmd>>,
|
|
|
|
device_state: StatusMessage,
|
|
average_points: Vec<[f64; 2]>,
|
|
pwm_points: Vec<[f64; 2]>,
|
|
|
|
ms1_frequency_id: egui::Id,
|
|
ms2_frequency_id: egui::Id,
|
|
|
|
output_frequency_id: [egui::Id; 3],
|
|
|
|
pll_settings: PLLSettings,
|
|
}
|
|
|
|
impl Default for CheapsdoControl {
|
|
fn default() -> Self {
|
|
Self {
|
|
serial_device: "".to_owned(),
|
|
serial_connected: false,
|
|
|
|
data_rx: None,
|
|
cmd_tx: None,
|
|
|
|
device_state: StatusMessage::default(),
|
|
average_points: Vec::new(),
|
|
pwm_points: Vec::new(),
|
|
|
|
ms1_frequency_id: egui::Id::new("ms1_frequency_id"),
|
|
ms2_frequency_id: egui::Id::new("ms2_frequency_id"),
|
|
output_frequency_id: [
|
|
egui::Id::new("output_frequency1_id"),
|
|
egui::Id::new("output_frequency2_id"),
|
|
egui::Id::new("output_frequency3_id"),
|
|
],
|
|
|
|
pll_settings: PLLSettings::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
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());
|
|
|
|
if let Some(data_rx) = &self.data_rx {
|
|
loop {
|
|
select! {
|
|
recv(data_rx) -> data => {
|
|
match data {
|
|
Ok(serial::Data::DeviceState(status_msg)) => {
|
|
self.device_state = status_msg.clone();
|
|
self.average_points.push([
|
|
self.average_points.len() as f64,
|
|
status_msg.average_frequency as f64 / 1000.0
|
|
]);
|
|
self.pwm_points.push([
|
|
self.pwm_points.len() as f64,
|
|
status_msg.pwm as f64
|
|
]);
|
|
},
|
|
Ok(serial::Data::PLLSettings(settings)) => {
|
|
self.pll_settings = settings;
|
|
}
|
|
Err(_) => break,
|
|
};
|
|
},
|
|
default => break,
|
|
}
|
|
}
|
|
}
|
|
|
|
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 && !self.serial_device.is_empty(),
|
|
egui::Button::new("Open"),
|
|
)
|
|
.clicked()
|
|
{
|
|
let serial_device = self.serial_device.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 || {
|
|
serial::poll_device(serial_device, cmd_rx, data_tx, ctx);
|
|
});
|
|
}
|
|
|
|
if ui
|
|
.add_enabled(self.serial_connected, egui::Button::new("Close"))
|
|
.clicked()
|
|
{
|
|
if let Some(cmd_tx) = &self.cmd_tx {
|
|
cmd_tx.send(serial::Cmd::Disconnect).unwrap();
|
|
}
|
|
|
|
self.serial_connected = false;
|
|
self.cmd_tx = None;
|
|
self.data_rx = None;
|
|
}
|
|
});
|
|
|
|
ui.separator();
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.vertical(|ui| {
|
|
ui.vertical(|ui| ui.label(egui::RichText::new("TCXO Status").size(20.0)));
|
|
ui.add_space(20.0);
|
|
plots::show_freuqencies(ui, &self.device_state);
|
|
ui.add_space(20.0);
|
|
plots::show_plots(ui, &self.average_points, &self.pwm_points);
|
|
});
|
|
|
|
ui.separator();
|
|
|
|
ui.vertical(|ui| {
|
|
ui.label(egui::RichText::new("Output Settings").size(20.0));
|
|
ui.add_space(20.0);
|
|
egui::Grid::new("output_pll_settings")
|
|
.spacing([20.0, 5.0])
|
|
.show(ui, |ui| {
|
|
ui.label("MS1 Frequency [Hz]");
|
|
widgets::frequency_input(
|
|
self.ms1_frequency_id,
|
|
&mut self.pll_settings.ms1_frequency,
|
|
&ctx,
|
|
ui,
|
|
);
|
|
ui.end_row();
|
|
|
|
ui.label("MS2 Frequency [Hz]");
|
|
widgets::frequency_input(
|
|
self.ms2_frequency_id,
|
|
&mut self.pll_settings.ms2_frequency,
|
|
&ctx,
|
|
ui,
|
|
);
|
|
ui.end_row();
|
|
});
|
|
|
|
for i in 0..3 {
|
|
ui.separator();
|
|
|
|
egui::Grid::new(format!("output_clk{}_settings", i + 1))
|
|
.spacing([20.0, 5.0])
|
|
.show(ui, |ui| {
|
|
ui.label(format!("CLK{} Multisynth", i + 1));
|
|
widgets::multisynth_selector(
|
|
&mut self.pll_settings.outputs[i].source,
|
|
ui,
|
|
);
|
|
ui.end_row();
|
|
|
|
ui.label(format!("CLK{} Frequency [Hz]", i + 1));
|
|
widgets::frequency_input(
|
|
self.output_frequency_id[i],
|
|
&mut self.pll_settings.outputs[i].frequency,
|
|
&ctx,
|
|
ui,
|
|
);
|
|
ui.end_row();
|
|
|
|
ui.label(format!("CLK{} Enable", i + 1));
|
|
|
|
widgets::on_off_toggle(
|
|
&mut self.pll_settings.outputs[i].enable,
|
|
ui,
|
|
);
|
|
|
|
ui.end_row();
|
|
});
|
|
}
|
|
|
|
ui.separator();
|
|
ui.horizontal(|ui| {
|
|
if ui
|
|
.add_enabled(
|
|
self.cmd_tx.is_some(),
|
|
egui::Button::new("Apply").fill(egui::Color32::DARK_GREEN),
|
|
)
|
|
.clicked()
|
|
{
|
|
if let Some(cmd_tx) = &self.cmd_tx {
|
|
cmd_tx
|
|
.send(serial::Cmd::SetPLLSetting(self.pll_settings.clone()))
|
|
.unwrap();
|
|
}
|
|
}
|
|
|
|
ui.add_space(40.0);
|
|
if ui
|
|
.add_enabled(
|
|
self.cmd_tx.is_some(),
|
|
egui::Button::new("Reset").fill(egui::Color32::DARK_RED),
|
|
)
|
|
.clicked()
|
|
{
|
|
if let Some(cmd_tx) = &self.cmd_tx {
|
|
cmd_tx.send(serial::Cmd::GetPLLSettings).unwrap();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|