Working remote print integration

This commit is contained in:
Connor Slade
2024-07-25 02:35:55 -04:00
parent 0c335bf605
commit c2444b8821
10 changed files with 175 additions and 56 deletions

1
Cargo.lock generated
View File

@@ -1146,6 +1146,7 @@ name = "common"
version = "0.1.0"
dependencies = [
"nalgebra 0.32.6",
"rand 0.8.5",
]
[[package]]

View File

@@ -48,3 +48,7 @@
- [x] Use egui_dock to get a more clean look
- [x] Align to bed button
- [x] Define all crate versions in workspace toml
- [ ] MQTT commands use refs to strings
- [ ] Ask for filename when sending to printer
- [ ] Printer scanning (UDP broadcast)
- [ ] Verify printer capabilities before uploading / printing

View File

@@ -5,3 +5,4 @@ edition = "2021"
[dependencies]
nalgebra.workspace = true
rand.workspace = true

View File

@@ -1,5 +1,7 @@
use std::time::Duration;
use rand::{distributions::Alphanumeric, Rng};
use crate::config::SliceConfig;
pub struct SliceResult<'a, Layer> {
@@ -26,9 +28,27 @@ pub fn human_duration(duration: Duration) -> String {
format!("{:}ms", ms)
} else if ms < 60_000.0 {
format!("{:.2}s", ms / 1000.0)
} else {
} else if ms < 3_600_000.0 {
let minutes = ms / 60_000.0;
let seconds = (minutes - minutes.floor()) * 60.0;
format!("{:.0}m {:.2}s", minutes.floor(), seconds)
} else {
let hours = ms / 3_600_000.0;
let minutes = (hours - hours.floor()) * 60.0;
let seconds = (minutes - minutes.floor()) * 60.0;
format!(
"{:.0}h {:.0}m {:.2}s",
hours.floor(),
minutes.floor(),
seconds
)
}
}
pub fn random_string(len: usize) -> String {
rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(len)
.map(char::from)
.collect()
}

View File

@@ -169,7 +169,7 @@ impl Default for App {
let surface = dock_state.main_surface_mut();
let [_old_node, new_node] = surface.split_left(NodeIndex::root(), 0.20, vec![Tab::Models]);
let [_old_node, new_node] = surface.split_below(new_node, 0.5, vec![Tab::SliceConfig]);
surface.split_below(new_node, 0.5, vec![Tab::Workspace]);
surface.split_below(new_node, 0.5, vec![Tab::Workspace, Tab::RemotePrint]);
Self {
dock_state,

View File

@@ -5,12 +5,17 @@ use std::{
};
use anyhow::Result;
use common::misc::random_string;
use parking_lot::{Mutex, MutexGuard};
use tracing::info;
use remote_send::{
commands::DisconnectCommand, http_server::HttpServer, mqtt::MqttServer, mqtt_server::Mqtt,
status::FullStatusData, Response,
commands::{DisconnectCommand, StartPrinting, UploadFile},
http_server::HttpServer,
mqtt::MqttServer,
mqtt_server::Mqtt,
status::FullStatusData,
Response,
};
pub struct RemotePrint {
@@ -125,4 +130,38 @@ impl RemotePrint {
Ok(())
}
pub fn upload(&self, mainboard_id: &str, data: Arc<Vec<u8>>) -> Result<()> {
let services = self.services.as_ref().unwrap();
let filename = format!("{}.goo", random_string(8));
services.http.add_file(&filename, data.clone());
services
.mqtt
.send_command(
&mainboard_id,
UploadFile::new(filename, services.http_port, &data),
)
.unwrap();
Ok(())
}
pub fn print(&self, mainboard_id: &str, filename: &str) -> Result<()> {
let services = self.services.as_ref().unwrap();
services
.mqtt
.send_command(
&mainboard_id,
StartPrinting {
filename: filename.to_owned(),
start_layer: 0,
},
)
.unwrap();
Ok(())
}
}

View File

@@ -1,6 +1,7 @@
use std::sync::atomic::Ordering;
use std::{sync::atomic::Ordering, time::Duration};
use chrono::DateTime;
use common::misc::human_duration;
use egui::{vec2, Align, Context, Grid, Layout, ProgressBar, Separator, TextEdit, Ui};
use remote_send::status::{FileTransferStatus, PrintInfoStatus};
@@ -58,6 +59,59 @@ pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) {
},
);
let status = client.status.lock();
let print_info = &status.print_info;
let printing = !matches!(
print_info.status,
PrintInfoStatus::None | PrintInfoStatus::Complete
);
if printing {
ui.horizontal(|ui| {
ui.label("Printing ");
ui.monospace(&print_info.filename);
ui.label(format!("({:?})", print_info.status));
});
let eta = human_duration(Duration::from_millis(
(print_info.total_ticks - print_info.current_ticks) as u64,
));
ui.label(format!("ETA: {eta}"));
ui.add(
ProgressBar::new(print_info.current_layer as f32 / print_info.total_layer as f32)
.text(format!(
"{}/{}",
print_info.current_layer, print_info.total_layer
))
.desired_width(ui.available_width()),
);
}
let file_transfer = &status.file_transfer_info;
if file_transfer.status == FileTransferStatus::None && file_transfer.file_total_size != 0 {
ui.horizontal(|ui| {
ui.label("Transferring ");
ui.monospace(&file_transfer.filename);
ui.label(".");
});
ui.add(
ProgressBar::new(
file_transfer.download_offset as f32 / file_transfer.file_total_size as f32,
)
.desired_width(ui.available_width()),
);
}
if file_transfer.status == FileTransferStatus::Done && !printing {
ui.label("File transfer complete.");
if ui.button("Print").clicked() {
app.remote_print
.print(&attributes.mainboard_id, &file_transfer.filename)
.unwrap();
}
}
Grid::new(format!("printer_{}", attributes.mainboard_id))
.num_columns(2)
.striped(true)
@@ -94,39 +148,6 @@ pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) {
ui.monospace(&last_update.format("%Y-%m-%d %H:%M:%S").to_string());
});
let status = client.status.lock();
let print_info = &status.print_info;
if !matches!(
print_info.status,
PrintInfoStatus::None | PrintInfoStatus::Complete
) {
ui.horizontal(|ui| {
ui.label("Printing ");
ui.monospace(&print_info.filename);
ui.label(format!(". ({:?})", print_info.status));
});
ui.add(
ProgressBar::new(print_info.current_layer as f32 / print_info.total_layer as f32)
.desired_width(ui.available_width()),
);
}
let file_transfer = &status.file_transfer_info;
if file_transfer.status == FileTransferStatus::None && file_transfer.file_total_size != 0 {
ui.horizontal(|ui| {
ui.label("Transferring ");
ui.monospace(&file_transfer.filename);
ui.label(".");
});
ui.add(
ProgressBar::new(
file_transfer.download_offset as f32 / file_transfer.file_total_size as f32,
)
.desired_width(ui.available_width()),
);
}
if i + 1 != printers.len() {
ui.separator();
}

View File

@@ -1,8 +1,8 @@
use std::{fs::File, io::Write};
use std::{fs::File, io::Write, sync::Arc};
use egui::{
style::HandleShape, Align, Context, DragValue, Layout, ProgressBar, RichText, Sense, Slider,
Vec2, Window,
style::HandleShape, text::LayoutJob, Align, Context, DragValue, FontId, FontSelection, Layout,
ProgressBar, RichText, Sense, Slider, Style, TextFormat, Vec2, WidgetInfo, WidgetText, Window,
};
use egui_wgpu::Callback;
use goo_format::LayerDecoder;
@@ -47,15 +47,46 @@ pub fn ui(app: &mut App, ctx: &Context) {
ui.with_layout(Layout::default().with_cross_align(Align::Max), |ui| {
ui.horizontal(|ui| {
if ui.button("Send to Printer").clicked() {
let result = app.slice_operation.as_ref().unwrap().result();
let result = result.as_ref().unwrap();
ui.add_enabled_ui(app.remote_print.is_initialized(), |ui| {
ui.menu_button("Send to Printer", |ui| {
let mqtt = app.remote_print.mqtt();
for printer in app.remote_print.printers().iter() {
let client = mqtt.get_client(&printer.mainboard_id);
let mut serializer = DynamicSerializer::new();
result.goo.serialize(&mut serializer);
let data = serializer.into_inner();
save_complete = true;
}
let mut layout_job = LayoutJob::default();
RichText::new(&format!("{} ", client.attributes.name))
.append_to(
&mut layout_job,
&Style::default(),
FontSelection::Default,
Align::LEFT,
);
RichText::new(&client.attributes.mainboard_id)
.monospace()
.append_to(
&mut layout_job,
&Style::default(),
FontSelection::Default,
Align::LEFT,
);
if ui.button(layout_job).clicked() {
let result =
app.slice_operation.as_ref().unwrap().result();
let result = result.as_ref().unwrap();
let mut serializer = DynamicSerializer::new();
result.goo.serialize(&mut serializer);
let data = Arc::new(serializer.into_inner());
save_complete = true;
app.remote_print
.upload(&printer.mainboard_id, data)
.unwrap();
}
}
});
});
if ui.button("Save").clicked() {
let result = app.slice_operation.as_ref().unwrap().result();

View File

@@ -9,6 +9,7 @@ use std::{
use anyhow::Result;
use parking_lot::{MappedRwLockReadGuard, Mutex, RwLock, RwLockReadGuard};
use serde_json::Value;
use soon::Soon;
use tracing::{info, trace, warn};
@@ -111,16 +112,15 @@ impl MqttHandler for Mqtt {
if let Some(board_id) = packet.topic.strip_prefix("/sdcp/status/") {
let status = serde_json::from_slice::<Response<StatusData>>(&packet.data)?;
trace!("Got status from `{board_id}`: {status:?}");
let clients = self.clients.write();
let client = clients.get(board_id).unwrap();
*client.status.lock() = status.data.status;
client.last_update.store(epoch(), Ordering::Relaxed);
} else if let Some(board_id) = packet.topic.strip_prefix("/sdcp/response/") {
trace!(
"Got command response from `{board_id}`: {}",
String::from_utf8_lossy(&packet.data)
);
let json = serde_json::from_slice::<Value>(&packet.data)?;
trace!("Got command response from `{board_id}`: {json}");
}
Ok(())

View File

@@ -84,15 +84,17 @@ pub struct FileTransferInfo {
pub enum CurrentStatus {
Ready = 0,
Busy = 1,
TransferringFile = 2,
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize_repr)]
pub enum PrintInfoStatus {
None = 0,
Exposure = 2,
Retracting = 3,
Lowering = 4,
Unknown = 1,
Lowering = 2,
Exposure = 3,
Retracting = 4,
Complete = 16,
}