Start on remote print UI

This commit is contained in:
Connor Slade
2024-07-24 20:39:49 -04:00
parent d4da7f37dc
commit 009c29da7a
10 changed files with 209 additions and 12 deletions

6
Cargo.lock generated
View File

@@ -2730,7 +2730,9 @@ dependencies = [
"parking_lot",
"plexus",
"rand 0.8.5",
"remote_send",
"rfd",
"serde_json",
"slicer",
"tracing",
"tracing-subscriber",
@@ -4054,9 +4056,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.117"
version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
dependencies = [
"itoa",
"ryu",

View File

@@ -47,3 +47,4 @@
- [x] Disable slice button while slicing
- [x] Use egui_dock to get a more clean look
- [x] Align to bed button
- [ ] Define all crate versions in workspace toml

View File

@@ -21,10 +21,12 @@ parking_lot = "0.12.3"
plexus = "0.0.11"
rand = "0.8.5"
rfd = "0.14.1"
serde_json = "1.0.120"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
wgpu = "0.19.4"
common = { path = "../common" }
goo_format = { path = "../goo_format" }
remote_send = { path = "../remote_send" }
slicer = { path = "../slicer" }

View File

@@ -11,6 +11,7 @@ use slicer::{slicer::Slicer, Pos};
use tracing::info;
use crate::{
remote_print::RemotePrint,
render::{camera::Camera, pipelines::model::RenderStyle, rendered_mesh::RenderedMesh},
slice_operation::{SliceOperation, SliceResult},
windows::{self, Tab},
@@ -20,12 +21,14 @@ use goo_format::{File as GooFile, LayerEncoder, PreviewImage};
pub struct App {
pub dock_state: DockState<Tab>,
pub state: UiState,
modal: Option<Modal>,
pub camera: Camera,
pub slice_config: SliceConfig,
pub meshes: Arc<RwLock<Vec<RenderedMesh>>>,
pub slice_operation: Option<SliceOperation>,
pub remote_print: RemotePrint,
pub render_style: RenderStyle,
pub grid_size: f32,
@@ -33,6 +36,11 @@ pub struct App {
pub theme: Theme,
}
#[derive(Default)]
pub struct UiState {
pub working_address: String,
}
pub struct FpsTracker {
last_frame: Instant,
last_frame_time: f32,
@@ -166,6 +174,7 @@ impl Default for App {
Self {
dock_state,
modal: None,
state: UiState::default(),
camera: Camera::default(),
slice_config: SliceConfig {
@@ -190,6 +199,7 @@ impl Default for App {
theme: Theme::Dark,
grid_size: 12.16,
slice_operation: None,
remote_print: RemotePrint::uninitialized(),
}
}
}

View File

@@ -12,6 +12,7 @@ const TEXTURE_FORMAT: TextureFormat = TextureFormat::Bgra8Unorm;
mod app;
mod components;
mod remote_print;
mod render;
mod slice_operation;
mod windows;

112
mslicer/src/remote_print.rs Normal file
View File

@@ -0,0 +1,112 @@
use std::{
net::{IpAddr, SocketAddr, TcpListener, UdpSocket},
str::FromStr,
sync::Arc,
};
use anyhow::Result;
use parking_lot::{Mutex, MutexGuard};
use tracing::info;
use remote_send::{
http_server::HttpServer, mqtt::MqttServer, mqtt_server::Mqtt, status::FullStatusData, Response,
};
pub struct RemotePrint {
services: Option<Services>,
printers: Arc<Mutex<Vec<Printer>>>,
}
struct Services {
mqtt: Mqtt,
http: HttpServer,
udp: UdpSocket,
mqtt_port: u16,
http_port: u16,
udp_port: u16,
}
pub struct Printer {
pub data: FullStatusData,
}
impl RemotePrint {
pub fn uninitialized() -> Self {
Self {
services: None,
printers: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn is_initialized(&self) -> bool {
self.services.is_some()
}
pub fn printers(&self) -> MutexGuard<Vec<Printer>> {
self.printers.lock()
}
pub fn init(&mut self) -> Result<()> {
info!("Starting remote print services");
let mqtt_listener = TcpListener::bind("0.0.0.0:0")?;
let mqtt_port = mqtt_listener.local_addr()?.port();
let mqtt = Mqtt::new();
MqttServer::new(mqtt.clone()).start_async(mqtt_listener)?;
let http_listener = TcpListener::bind("0.0.0.0:0")?;
let http_port = http_listener.local_addr()?.port();
let http = HttpServer::new(http_listener);
http.start_async();
let udp = UdpSocket::bind("0.0.0.0:0")?;
let udp_port = udp.local_addr()?.port();
info!("Binds: {{ UDP: {udp_port}, MQTT: {mqtt_port}, HTTP: {http_port} }}");
self.services = Some(Services {
mqtt,
http,
udp,
mqtt_port,
http_port,
udp_port,
});
Ok(())
}
// todo: async
pub fn add_printer(&mut self, address: &str) -> Result<()> {
let services = self.services.as_ref().unwrap();
let address = IpAddr::from_str(address)?;
let address = SocketAddr::new(address, 3000);
services.udp.send_to(b"M99999", address)?;
let mut buffer = [0; 1024];
let (len, _addr) = services.udp.recv_from(&mut buffer)?;
let received = String::from_utf8_lossy(&buffer[..len]);
let response = serde_json::from_str::<Response<FullStatusData>>(&received)?;
info!(
"Got status from `{}`",
response.data.attributes.machine_name
);
self.printers.lock().push(Printer {
data: response.data.clone(),
});
services.mqtt.add_future_client(response);
services
.udp
.send_to(format!("M66666 {}", services.mqtt_port).as_bytes(), address)?;
Ok(())
}
}

View File

@@ -7,6 +7,7 @@ use crate::{app::App, render::workspace::WorkspaceRenderCallback};
mod about;
mod models;
mod remote_print;
mod slice_config;
mod slice_operation;
mod stats;
@@ -22,10 +23,11 @@ struct Tabs<'a> {
pub enum Tab {
About,
Models,
RemotePrint,
SliceConfig,
Stats,
Workspace,
Viewport,
Workspace,
}
impl Tab {
@@ -33,10 +35,11 @@ impl Tab {
match self {
Tab::About => "About",
Tab::Models => "Models",
Tab::RemotePrint => "Remote Print",
Tab::SliceConfig => "Slice Config",
Tab::Stats => "Stats",
Tab::Workspace => "Workspace",
Tab::Viewport => "Viewport",
Tab::Workspace => "Workspace",
}
}
}
@@ -52,6 +55,7 @@ impl<'a> TabViewer for Tabs<'a> {
match tab {
Tab::About => about::ui(self.app, ui, self.ctx),
Tab::Models => models::ui(self.app, ui, self.ctx),
Tab::RemotePrint => remote_print::ui(self.app, ui, self.ctx),
Tab::SliceConfig => slice_config::ui(self.app, ui, self.ctx),
Tab::Stats => stats::ui(self.app, ui, self.ctx),
Tab::Viewport => viewport(self.app, ui, self.ctx),
@@ -66,6 +70,7 @@ impl<'a> TabViewer for Tabs<'a> {
for tab in [
Tab::About,
Tab::Models,
Tab::RemotePrint,
Tab::SliceConfig,
Tab::Stats,
Tab::Workspace,

View File

@@ -0,0 +1,64 @@
use egui::{vec2, Align, Context, Layout, Separator, TextEdit, Ui};
use crate::app::App;
pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) {
if !app.remote_print.is_initialized() {
ui.label("Remote print services have not been initialized.");
ui.add_space(8.0);
ui.vertical_centered(|ui| {
if ui.button("Initialize").clicked() {
app.remote_print.init().unwrap();
}
});
return;
}
ui.heading("Printers");
let printers = app.remote_print.printers();
if printers.is_empty() {
ui.label("No printers have been added yet.");
}
for (i, printer) in printers.iter().enumerate() {
ui.horizontal(|ui| {
ui.strong(&printer.data.attributes.name);
ui.monospace(&printer.data.attributes.mainboard_id);
});
if i + 1 != printers.len() {
ui.separator();
}
}
drop(printers);
ui.add_space(16.0);
ui.heading("Add Printer");
ui.with_layout(Layout::right_to_left(Align::Min), |ui| {
let scan = ui.button("Scan");
let height = scan.rect.height();
if scan.clicked() {
app.dialog_builder()
.with_title("Unimplemented")
.with_body("Printer scanning is not implemented yet.")
.open();
}
ui.add_sized(vec2(2.0, height), Separator::default());
if ui.button("Connect").clicked() {
app.remote_print
.add_printer(&app.state.working_address)
.unwrap();
app.state.working_address.clear();
}
ui.add_sized(
vec2(ui.available_width(), height),
TextEdit::singleline(&mut app.state.working_address)
.hint_text("192.168.1.233")
.desired_width(ui.available_width()),
);
});
}

View File

@@ -14,7 +14,7 @@ pub struct Response<Data> {
pub data: Data,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Resolution {
pub x: u16,
pub y: u16,

View File

@@ -2,14 +2,14 @@ use serde::Deserialize;
use crate::{parse_resolution, Resolution};
#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct FullStatusData {
pub attributes: Attributes,
pub status: Status,
}
#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct StatusData {
pub status: Status,
@@ -18,7 +18,7 @@ pub struct StatusData {
pub time_stamp: u64,
}
#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Attributes {
pub name: String,
@@ -40,14 +40,14 @@ pub struct Attributes {
pub capabilities: Vec<Capability>,
}
#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Capability {
FileTransfer,
PrintControl,
}
#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Status {
pub current_status: u8,
@@ -56,7 +56,7 @@ pub struct Status {
pub file_transfer_info: FileTransferInfo,
}
#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct PrintInfo {
pub status: u8,
@@ -68,7 +68,7 @@ pub struct PrintInfo {
pub filename: String,
}
#[derive(Debug, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct FileTransferInfo {
pub status: u8,