Start on remote print UI
This commit is contained in:
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -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",
|
||||
|
1
TODO.md
1
TODO.md
@@ -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
|
||||
|
@@ -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" }
|
||||
|
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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
112
mslicer/src/remote_print.rs
Normal 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(())
|
||||
}
|
||||
}
|
@@ -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,
|
||||
|
64
mslicer/src/windows/remote_print.rs
Normal file
64
mslicer/src/windows/remote_print.rs
Normal 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()),
|
||||
);
|
||||
});
|
||||
}
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user