added basic control gui
tried fltk, didn't like it, using egui
This commit is contained in:
parent
971c57fa13
commit
79b22b3cb7
4 changed files with 3178 additions and 23 deletions
3092
interface/Cargo.lock
generated
3092
interface/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -13,3 +13,7 @@ serde = "1.0.217"
|
|||
tokio = { version = "1.43.0", features = ["full"] }
|
||||
common = {path = "../common"}
|
||||
heapless = "0.7.0"
|
||||
|
||||
eframe = "0.30"
|
||||
egui_extras = { version = "0.30", features = ["default", "image"] }
|
||||
egui-toast = "0.16.0"
|
||||
|
|
59
interface/src/gui.rs
Normal file
59
interface/src/gui.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
|
||||
use common::{ControlPacket, TelemetryPacket};
|
||||
use eframe::egui::{self, Align2};
|
||||
use tokio::sync::{mpsc, watch::Receiver};
|
||||
use egui_toast::{Toast, Toasts};
|
||||
|
||||
pub fn gui(data: Receiver<GUIData>, toasts: mpsc::Receiver<Toast>) -> eframe::Result {
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),
|
||||
..Default::default()
|
||||
};
|
||||
eframe::run_native(
|
||||
"Cruise Control Dashboard",
|
||||
options,
|
||||
Box::new(|cc| {
|
||||
// This gives us image support:
|
||||
egui_extras::install_image_loaders(&cc.egui_ctx);
|
||||
|
||||
Ok(Box::new(GUI::with_receivers(data, toasts)))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct GUIData {
|
||||
pub telemetry: Option<TelemetryPacket>,
|
||||
pub last_command: Option<ControlPacket>,
|
||||
}
|
||||
|
||||
struct GUI {
|
||||
data: Receiver<GUIData>,
|
||||
toasts: mpsc::Receiver<Toast>,
|
||||
}
|
||||
|
||||
impl GUI {
|
||||
fn with_receivers(data: Receiver<GUIData>, toasts: mpsc::Receiver<Toast>) -> Self {
|
||||
Self {
|
||||
data,
|
||||
toasts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for GUI {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("Cruise Control");
|
||||
});
|
||||
let mut toasts = Toasts::new()
|
||||
.anchor(Align2::RIGHT_BOTTOM, (-10.0, -10.0)) // 10 units from the bottom right corner
|
||||
.direction(egui::Direction::BottomUp);
|
||||
|
||||
while let Ok(toast) = self.toasts.try_recv() {
|
||||
toasts.add(toast);
|
||||
}
|
||||
|
||||
toasts.show(ctx);
|
||||
}
|
||||
}
|
|
@ -1,12 +1,17 @@
|
|||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
#![feature(iter_collect_into)]
|
||||
use std::{ops::ControlFlow, result, sync::Arc, thread::sleep, time::Duration};
|
||||
use std::{ops::ControlFlow, result, sync::Arc, thread::{self, sleep}, time::Duration};
|
||||
|
||||
use anyhow::{Context, Ok, Result};
|
||||
use common::{ControlPacket, TelemetryPacket};
|
||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
use egui_toast::{Toast, ToastKind};
|
||||
use gui::{gui, GUIData};
|
||||
use pitch_detection::{detector::{mcleod::McLeodDetector, PitchDetector}, utils};
|
||||
use rust_music_theory::note::{Note, NoteLetter, Pitch, Tuning};
|
||||
use tokio::{io::{AsyncReadExt, AsyncWriteExt, BufWriter, WriteHalf}, net::{tcp::{OwnedReadHalf, OwnedWriteHalf}, TcpStream}, spawn, sync::{self, mpsc, RwLock}, time::timeout};
|
||||
use tokio::{io::{AsyncReadExt, AsyncWriteExt, BufWriter, WriteHalf}, net::{tcp::{OwnedReadHalf, OwnedWriteHalf}, TcpStream}, spawn, sync::{self, mpsc, watch, RwLock}, time::timeout};
|
||||
|
||||
mod gui;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// assumes pulseaudio system with f32 samples and 2204 sample packets
|
||||
|
@ -45,26 +50,36 @@ fn main() -> Result<()> {
|
|||
|
||||
//let latest_telem = Arc::new(RwLock::new(None));
|
||||
|
||||
let control = executor.block_on(async {
|
||||
let cruisecontrol = TcpStream::connect("192.168.1.2:1234").await?;
|
||||
println!("connected");
|
||||
cruisecontrol.set_nodelay(true)?;
|
||||
let (telem, control) = cruisecontrol.into_split();
|
||||
|
||||
tokio::spawn(telemetry_handler(telem));
|
||||
|
||||
Ok(control)
|
||||
})?;
|
||||
let (toast_sender, toasts) = mpsc::channel(5);
|
||||
let (data_sender, gui_data) =watch::channel(GUIData::default());
|
||||
|
||||
|
||||
executor.block_on(controller(notes, control)).unwrap();
|
||||
thread::spawn(move || {
|
||||
let control = executor.block_on(async {
|
||||
toast_sender.send(Toast::new().text("connecting to bot").kind(ToastKind::Info)).await.unwrap();
|
||||
let cruisecontrol = TcpStream::connect("192.168.1.2:1234").await?;
|
||||
println!("connected");
|
||||
toast_sender.send(Toast::new().text("connected").kind(ToastKind::Success)).await.unwrap();
|
||||
cruisecontrol.set_nodelay(true)?;
|
||||
let (telem, control) = cruisecontrol.into_split();
|
||||
|
||||
tokio::spawn(telemetry_handler(telem, data_sender.clone()));
|
||||
|
||||
Ok(control)
|
||||
}).unwrap();
|
||||
|
||||
executor.block_on(controller(notes, control)).unwrap()
|
||||
});
|
||||
|
||||
println!("launching gui");
|
||||
gui(gui_data, toasts).unwrap();
|
||||
|
||||
drop(stream);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn telemetry_handler(mut telem: OwnedReadHalf) -> Result<()> {
|
||||
async fn telemetry_handler(mut telem: OwnedReadHalf, gui: watch::Sender<GUIData>) -> Result<()> {
|
||||
let mut buf = vec![0; 2048];
|
||||
loop {
|
||||
let len = telem.read_u32().await.context("bad length")? as usize;
|
||||
|
@ -74,6 +89,9 @@ async fn telemetry_handler(mut telem: OwnedReadHalf) -> Result<()> {
|
|||
|
||||
println!("telem: {telem:?}");
|
||||
|
||||
gui.send_modify(|gui| {
|
||||
gui.telemetry = Some(telem);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue