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"] }
|
tokio = { version = "1.43.0", features = ["full"] }
|
||||||
common = {path = "../common"}
|
common = {path = "../common"}
|
||||||
heapless = "0.7.0"
|
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)]
|
#![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 anyhow::{Context, Ok, Result};
|
||||||
use common::{ControlPacket, TelemetryPacket};
|
use common::{ControlPacket, TelemetryPacket};
|
||||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
|
use egui_toast::{Toast, ToastKind};
|
||||||
|
use gui::{gui, GUIData};
|
||||||
use pitch_detection::{detector::{mcleod::McLeodDetector, PitchDetector}, utils};
|
use pitch_detection::{detector::{mcleod::McLeodDetector, PitchDetector}, utils};
|
||||||
use rust_music_theory::note::{Note, NoteLetter, Pitch, Tuning};
|
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<()> {
|
fn main() -> Result<()> {
|
||||||
// assumes pulseaudio system with f32 samples and 2204 sample packets
|
// 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 latest_telem = Arc::new(RwLock::new(None));
|
||||||
|
|
||||||
let control = executor.block_on(async {
|
let (toast_sender, toasts) = mpsc::channel(5);
|
||||||
let cruisecontrol = TcpStream::connect("192.168.1.2:1234").await?;
|
let (data_sender, gui_data) =watch::channel(GUIData::default());
|
||||||
println!("connected");
|
|
||||||
cruisecontrol.set_nodelay(true)?;
|
|
||||||
let (telem, control) = cruisecontrol.into_split();
|
|
||||||
|
|
||||||
tokio::spawn(telemetry_handler(telem));
|
|
||||||
|
|
||||||
Ok(control)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
drop(stream);
|
||||||
|
|
||||||
Ok(())
|
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];
|
let mut buf = vec![0; 2048];
|
||||||
loop {
|
loop {
|
||||||
let len = telem.read_u32().await.context("bad length")? as usize;
|
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:?}");
|
println!("telem: {telem:?}");
|
||||||
|
|
||||||
|
gui.send_modify(|gui| {
|
||||||
|
gui.telemetry = Some(telem);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue