From 0bba11645533e4db2e7b839092a3c36e9b6f85d7 Mon Sep 17 00:00:00 2001 From: Enrico Turri <enricoturri@seznam.cz> Date: Wed, 12 Dec 2018 13:04:06 +0100 Subject: [PATCH 1/6] Fixed rendering of gizmo move for wipe tower --- src/slic3r/GUI/GLGizmo.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 119838dad..7f62d0901 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1292,9 +1292,11 @@ void GLGizmoMove3D::on_render(const GLCanvas3D::Selection& selection) const // draw grabbers render_grabbers(box); - render_grabber_extension(X, box, false); - render_grabber_extension(Y, box, false); - render_grabber_extension(Z, box, false); + for (unsigned int i = 0; i < 3; ++i) + { + if (m_grabbers[i].enabled) + render_grabber_extension((Axis)i, box, false); + } } else { From 2350fb62b9db77d67b5722751ad8013e38573464 Mon Sep 17 00:00:00 2001 From: Vojtech Kral <vojtech@kral.hk> Date: Tue, 11 Dec 2018 10:33:11 +0100 Subject: [PATCH 2/6] WIP OctoPrint integration --- src/libslic3r/PrintConfig.cpp | 3 +- src/slic3r/CMakeLists.txt | 4 +- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 28 +++- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 7 + src/slic3r/GUI/GUI_App.cpp | 2 + src/slic3r/GUI/GUI_App.hpp | 5 + src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 154 ++++++++++-------- src/slic3r/GUI/Plater.hpp | 14 +- src/slic3r/GUI/PrintHostDialogs.cpp | 49 ++++++ .../PrintHostDialogs.hpp} | 27 ++- src/slic3r/Utils/Duet.cpp | 11 +- src/slic3r/Utils/Duet.hpp | 1 + src/slic3r/Utils/OctoPrint.cpp | 15 +- src/slic3r/Utils/OctoPrint.hpp | 1 + src/slic3r/Utils/PrintHost.cpp | 51 +++++- src/slic3r/Utils/PrintHost.hpp | 60 +++++-- src/slic3r/Utils/PrintHostSendDialog.cpp | 52 ------ 18 files changed, 314 insertions(+), 172 deletions(-) create mode 100644 src/slic3r/GUI/PrintHostDialogs.cpp rename src/slic3r/{Utils/PrintHostSendDialog.hpp => GUI/PrintHostDialogs.hpp} (60%) delete mode 100644 src/slic3r/Utils/PrintHostSendDialog.cpp diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 4b3b1e80a..086d456de 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1297,10 +1297,11 @@ void PrintConfigDef::init_fff_params() def->default_value = new ConfigOptionString(""); def = this->add("printhost_cafile", coString); - def->label = "HTTPS CA file"; + def->label = "HTTPS CA File"; def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. " "If left blank, the default OS CA certificate repository is used."; def->cli = "printhost-cafile=s"; + def->mode = comAdvanced; def->default_value = new ConfigOptionString(""); def = this->add("print_host", coString); diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index e8031309c..3b6bad16d 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -103,12 +103,12 @@ add_library(libslic3r_gui STATIC GUI/ProgressIndicator.hpp GUI/ProgressStatusBar.hpp GUI/ProgressStatusBar.cpp + GUI/PrintHostDialogs.cpp + GUI/PrintHostDialogs.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp Utils/FixModelByWin10.hpp - Utils/PrintHostSendDialog.cpp - Utils/PrintHostSendDialog.hpp Utils/OctoPrint.cpp Utils/OctoPrint.hpp Utils/Duet.cpp diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index f2249de3d..1829de959 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -19,9 +19,12 @@ //#undef NDEBUG #include <cassert> #include <stdexcept> +#include <cctype> +#include <algorithm> #include <boost/format.hpp> #include <boost/filesystem/path.hpp> +#include <boost/filesystem.hpp> #include <boost/log/trivial.hpp> #include <boost/nowide/cstdio.hpp> @@ -62,6 +65,11 @@ PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const return m_print->technology(); } +static bool isspace(int ch) +{ + return std::isspace(ch) != 0; +} + // This function may one day be merged into the Print, but historically the print was separated // from the G-code generator. void BackgroundSlicingProcess::process_fff() @@ -80,8 +88,8 @@ void BackgroundSlicingProcess::process_fff() PlaceholderParser pp; std::string normal_print_time = stats.estimated_normal_print_time; std::string silent_print_time = stats.estimated_silent_print_time; - normal_print_time.erase(std::remove_if(normal_print_time.begin(), normal_print_time.end(), std::isspace), normal_print_time.end()); - silent_print_time.erase(std::remove_if(silent_print_time.begin(), silent_print_time.end(), std::isspace), silent_print_time.end()); + normal_print_time.erase(std::remove_if(normal_print_time.begin(), normal_print_time.end(), isspace), normal_print_time.end()); + silent_print_time.erase(std::remove_if(silent_print_time.begin(), silent_print_time.end(), isspace), silent_print_time.end()); pp.set("print_time", new ConfigOptionString(normal_print_time)); pp.set("normal_print_time", new ConfigOptionString(normal_print_time)); pp.set("silent_print_time", new ConfigOptionString(silent_print_time)); @@ -373,6 +381,22 @@ void BackgroundSlicingProcess::schedule_export(const std::string &path) m_export_path = path; } +void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job) +{ + assert(m_export_path.empty()); + if (! m_export_path.empty()) + return; + + const auto path = boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path(".upload.%%%%-%%%%-%%%%-%%%%.gcode"); + + // Guard against entering the export step before changing the export path. + tbb::mutex::scoped_lock lock(m_print->state_mutex()); + this->invalidate_step(bspsGCodeFinalize); + m_export_path = path.native(); + m_upload_job = std::move(upload_job); +} + void BackgroundSlicingProcess::reset_export() { assert(! this->running()); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index bb072fb98..222ed147e 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -9,6 +9,7 @@ #include <wx/event.h> #include "libslic3r/Print.hpp" +#include "slic3r/Utils/PrintHost.hpp" namespace Slic3r { @@ -86,6 +87,9 @@ public: // Set the export path of the G-code. // Once the path is set, the G-code void schedule_export(const std::string &path); + // Set print host upload job data to be enqueued to the PrintHostJobQueue + // after current print slicing is complete + void schedule_upload(Slic3r::PrintHostJob upload_job); // Clear m_export_path. void reset_export(); // Once the G-code export is scheduled, the apply() methods will do nothing. @@ -143,6 +147,9 @@ private: // Output path provided by the user. The output path may be set even if the slicing is running, // but once set, it cannot be re-set. std::string m_export_path; + // Print host upload job to schedule after slicing is complete, used by schedule_upload(), + // empty by default (ie. no upload to schedule) + PrintHostJob m_upload_job; // Thread, on which the background processing is executed. The thread will always be present // and ready to execute the slicing process. std::thread m_thread; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 73d6a03b0..f4047ae3e 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -27,6 +27,7 @@ #include "3DScene.hpp" #include "../Utils/PresetUpdater.hpp" +#include "../Utils/PrintHost.hpp" #include "ConfigWizard_private.hpp" #include "slic3r/Config/Snapshot.hpp" #include "ConfigSnapshotDialog.hpp" @@ -72,6 +73,7 @@ GUI_App::GUI_App() : wxApp() #if ENABLE_IMGUI , m_imgui(new ImGuiWrapper()) + , m_printhost_queue(new PrintHostJobQueue()) #endif // ENABLE_IMGUI {} diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 9df90259a..875a92456 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -27,6 +27,7 @@ class AppConfig; class PresetBundle; class PresetUpdater; class ModelObject; +class PrintHostJobQueue; namespace GUI { @@ -91,6 +92,8 @@ class GUI_App : public wxApp std::unique_ptr<ImGuiWrapper> m_imgui; #endif // ENABLE_IMGUI + std::unique_ptr<PrintHostJobQueue> m_printhost_queue; + public: bool OnInit() override; @@ -161,6 +164,8 @@ public: ImGuiWrapper* imgui() { return m_imgui.get(); } #endif // ENABLE_IMGUI + PrintHostJobQueue& printhost_queue() { return *m_printhost_queue.get(); } + }; DECLARE_APP(GUI_App) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index b3dc8ba75..a74d5f72a 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -794,7 +794,7 @@ void MainFrame::update_ui_from_settings() { bool bp_on = wxGetApp().app_config->get("background_processing") == "1"; m_menu_item_reslice_now->Enable(bp_on); - m_plater->sidebar().show_button(baReslice, !bp_on); + m_plater->sidebar().show_reslice(!bp_on); m_plater->sidebar().Layout(); if (m_plater) m_plater->update_ui_from_settings(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index af8769814..5ce49259a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -54,7 +54,9 @@ #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" #include "ProgressStatusBar.hpp" +#include "PrintHostDialogs.hpp" #include "../Utils/ASCIIFolding.hpp" +#include "../Utils/PrintHost.hpp" #include "../Utils/FixModelByWin10.hpp" #include <wx/glcanvas.h> // Needs to be last because reasons :-/ @@ -64,6 +66,7 @@ using boost::optional; namespace fs = boost::filesystem; using Slic3r::_3DScene; using Slic3r::Preset; +using Slic3r::PrintHostJob; namespace Slic3r { @@ -447,7 +450,6 @@ struct Sidebar::priv wxButton *btn_export_gcode; wxButton *btn_reslice; - // wxButton *btn_print; // XXX: remove wxButton *btn_send_gcode; priv(Plater *plater) : plater(plater) {} @@ -543,13 +545,12 @@ Sidebar::Sidebar(Plater *parent) p->object_settings->Hide(); p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxLEFT | wxTOP, 20); - // Buttons in the scrolled area wxBitmap arrow_up(GUI::from_u8(Slic3r::var("brick_go.png")), wxBITMAP_TYPE_PNG); p->btn_send_gcode = new wxButton(p->scrolled, wxID_ANY, _(L("Send to printer"))); + p->btn_send_gcode = new wxButton(this, wxID_ANY, _(L("Send to printer"))); p->btn_send_gcode->SetBitmap(arrow_up); + p->btn_send_gcode->SetFont(wxGetApp().bold_font()); p->btn_send_gcode->Hide(); - auto *btns_sizer_scrolled = new wxBoxSizer(wxHORIZONTAL); - btns_sizer_scrolled->Add(p->btn_send_gcode); // Info boxes p->object_info = new ObjectInfo(p->scrolled); @@ -559,7 +560,6 @@ Sidebar::Sidebar(Plater *parent) scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | wxLEFT, 2); scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND); scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | wxLEFT, 20); - scrolled_sizer->Add(btns_sizer_scrolled, 0, wxEXPAND, 0); scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | wxLEFT, 20); // Buttons underneath the scrolled area @@ -571,6 +571,7 @@ Sidebar::Sidebar(Plater *parent) auto *btns_sizer = new wxBoxSizer(wxVERTICAL); btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, 5); + btns_sizer->Add(p->btn_send_gcode, 0, wxEXPAND | wxTOP, 5); btns_sizer->Add(p->btn_export_gcode, 0, wxEXPAND | wxTOP, 5); auto *sizer = new wxBoxSizer(wxVERTICAL); @@ -820,14 +821,6 @@ void Sidebar::show_sliced_info_sizer(const bool show) p->scrolled->Refresh(); } -void Sidebar::show_buttons(const bool show) -{ - p->btn_reslice->Show(show); - TabPrinter *tab = dynamic_cast<TabPrinter*>(wxGetApp().get_tab(Preset::TYPE_PRINTER)); - if (tab && p->plater->printer_technology() == ptFFF) - p->btn_send_gcode->Show(show && !tab->m_config->opt_string("print_host").empty()); -} - void Sidebar::enable_buttons(bool enable) { p->btn_reslice->Enable(enable); @@ -835,23 +828,8 @@ void Sidebar::enable_buttons(bool enable) p->btn_send_gcode->Enable(enable); } -void Sidebar::show_button(ButtonAction but_action, bool show) -{ - switch (but_action) - { - case baReslice: - p->btn_reslice->Show(show); - break; - case baExportGcode: - p->btn_export_gcode->Show(show); - break; - case baSendGcode: - p->btn_send_gcode->Show(show); - break; - default: - break; - } -} +void Sidebar::show_reslice(bool show) { p->btn_reslice->Show(show); } +void Sidebar::show_send(bool show) { p->btn_send_gcode->Show(show); } bool Sidebar::is_multifilament() { @@ -1008,6 +986,7 @@ struct Plater::priv }; // returns bit mask of UpdateBackgroundProcessReturnState unsigned int update_background_process(); + void export_gcode(fs::path output_path, PrintHostJob upload_job); void async_apply_config(); void reload_from_disk(); void fix_through_netfabb(const int obj_idx); @@ -2059,6 +2038,45 @@ unsigned int Plater::priv::update_background_process() return return_state; } +void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job) +{ + wxCHECK_RET(!(output_path.empty() && upload_job.empty()), "export_gcode: output_path and upload_job empty"); + + if (model.objects.empty()) + return; + + if (background_process.is_export_scheduled()) { + GUI::show_error(q, _(L("Another export job is currently running."))); + return; + } + + // bitmask of UpdateBackgroundProcessReturnState + unsigned int state = update_background_process(); + if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) +#if ENABLE_REMOVE_TABS_FROM_PLATER + view3D->reload_scene(false); +#else + canvas3D->reload_scene(false); +#endif // ENABLE_REMOVE_TABS_FROM_PLATER + if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0) + return; + + if (! output_path.empty()) { + background_process.schedule_export(output_path.string()); + } else { + background_process.schedule_upload(std::move(upload_job)); + } + + if (! background_process.running()) { + // The print is valid and it should be started. + if (background_process.start()) + statusbar()->set_cancel_callback([this]() { + statusbar()->set_status_text(L("Cancelling")); + background_process.stop(); + }); + } +} + void Plater::priv::async_apply_config() { // bitmask of UpdateBackgroundProcessReturnState @@ -2912,42 +2930,26 @@ void Plater::export_gcode(fs::path output_path) if (p->model.objects.empty()) return; - if (this->p->background_process.is_export_scheduled()) { - GUI::show_error(this, _(L("Another export job is currently running."))); - return; - } - - // bitmask of UpdateBackgroundProcessReturnState - unsigned int state = this->p->update_background_process(); - if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) -#if ENABLE_REMOVE_TABS_FROM_PLATER - this->p->view3D->reload_scene(false); -#else - this->p->canvas3D->reload_scene(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER - if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0) - return; - // select output file if (output_path.empty()) { // XXX: take output path from CLI opts? Ancient Slic3r versions used to do that... // If possible, remove accents from accented latin characters. // This function is useful for generating file names to be processed by legacy firmwares. - fs::path default_output_file; + fs::path default_output_file; try { - default_output_file = this->p->background_process.current_print()->output_filepath(output_path.string()); + default_output_file = this->p->background_process.current_print()->output_filepath(output_path.string()); } catch (const std::exception &ex) { show_error(this, ex.what()); return; } - default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string())); + default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string())); auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string()); - wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save Zip file as:")), + wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save Zip file as:")), start_dir, default_output_file.filename().string(), - GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP, default_output_file.extension().string()), + GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP, default_output_file.extension().string()), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); @@ -2958,23 +2960,15 @@ void Plater::export_gcode(fs::path output_path) } } else { try { - output_path = this->p->background_process.current_print()->output_filepath(output_path.string()); + output_path = this->p->background_process.current_print()->output_filepath(output_path.string()); } catch (const std::exception &ex) { show_error(this, ex.what()); return; } } - if (! output_path.empty()) - this->p->background_process.schedule_export(output_path.string()); - - if ((! output_path.empty() || this->p->background_processing_enabled()) && ! this->p->background_process.running()) { - // The print is valid and it should be started. - if (this->p->background_process.start()) - this->p->statusbar()->set_cancel_callback([this]() { - this->p->statusbar()->set_status_text(L("Cancelling")); - this->p->background_process.stop(); - }); + if (! output_path.empty()) { + p->export_gcode(std::move(output_path), PrintHostJob()); } } @@ -3077,7 +3071,28 @@ void Plater::reslice() void Plater::send_gcode() { -// p->send_gcode_file = export_gcode(); + if (p->model.objects.empty()) { return; } + + PrintHostJob upload_job(p->config); + if (upload_job.empty()) { return; } + + // Obtain default output path + fs::path default_output_file; + try { + default_output_file = this->p->background_process.current_print()->output_filepath(""); + } catch (const std::exception &ex) { + show_error(this, ex.what()); + return; + } + default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string())); + + Slic3r::PrintHostSendDialog dlg(default_output_file); + if (dlg.ShowModal() == wxID_OK) { + upload_job.upload_data.upload_path = dlg.filename(); + upload_job.upload_data.start_print = dlg.start_print(); + + p->export_gcode(fs::path(), std::move(upload_job)); + } } void Plater::on_extruders_change(int num_extruders) @@ -3127,14 +3142,6 @@ void Plater::on_config_change(const DynamicPrintConfig &config) opt_key == "single_extruder_multi_material") { update_scheduled = true; } -// else if(opt_key == "serial_port") { -// sidebar()->p->btn_print->Show(config.get("serial_port")); // ???: btn_print is removed -// Layout(); -// } - else if (opt_key == "print_host") { - sidebar().show_button(baReslice, !p->config->option<ConfigOptionString>(opt_key)->value.empty()); - Layout(); - } else if(opt_key == "variable_layer_height") { if (p->config->opt_bool("variable_layer_height") != true) { #if ENABLE_REMOVE_TABS_FROM_PLATER @@ -3174,6 +3181,11 @@ void Plater::on_config_change(const DynamicPrintConfig &config) } } + { + const auto prin_host_opt = p->config->option<ConfigOptionString>("print_host"); + p->sidebar->show_send(prin_host_opt != nullptr && !prin_host_opt->value.empty()); + } + if (update_scheduled) update(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 96755ab10..334ab740a 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -55,14 +55,6 @@ private: int extruder_idx = -1; }; -enum ButtonAction -{ - baUndef, - baReslice, - baExportGcode, - baSendGcode -}; - class Sidebar : public wxPanel { /*ConfigMenuIDs*/int m_mode; @@ -88,9 +80,9 @@ public: void update_objects_list_extruder_column(int extruders_count); void show_info_sizer(); void show_sliced_info_sizer(const bool show); - void show_buttons(const bool show); - void show_button(ButtonAction but_action, bool show); void enable_buttons(bool enable); + void show_reslice(bool show); + void show_send(bool show); bool is_multifilament(); void set_mode_value(const /*ConfigMenuIDs*/int mode) { m_mode = mode; } @@ -103,6 +95,8 @@ private: class Plater: public wxPanel { public: + using fs_path = boost::filesystem::path; + Plater(wxWindow *parent, MainFrame *main_frame); Plater(Plater &&) = delete; Plater(const Plater &) = delete; diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp new file mode 100644 index 000000000..a5de7c3c6 --- /dev/null +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -0,0 +1,49 @@ +#include "PrintHostDialogs.hpp" + +#include <wx/frame.h> +#include <wx/event.h> +#include <wx/progdlg.h> +#include <wx/sizer.h> +#include <wx/stattext.h> +#include <wx/textctrl.h> +#include <wx/checkbox.h> + +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/I18N.hpp" + +namespace fs = boost::filesystem; + +namespace Slic3r { + +PrintHostSendDialog::PrintHostSendDialog(const fs::path &path) + : MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE) + , txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring())) + , box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload")))) +{ + auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed."))); + label_dir_hint->Wrap(CONTENT_WIDTH); + + content_sizer->Add(txt_filename, 0, wxEXPAND); + content_sizer->Add(label_dir_hint); + content_sizer->AddSpacer(VERT_SPACING); + content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING); + + btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL)); + + txt_filename->SetFocus(); + wxString stem(path.stem().wstring()); + txt_filename->SetSelection(0, stem.Length()); + + Fit(); +} + +fs::path PrintHostSendDialog::filename() const +{ + return fs::path(txt_filename->GetValue().wx_str()); +} + +bool PrintHostSendDialog::start_print() const +{ + return box_print->GetValue(); } +} diff --git a/src/slic3r/Utils/PrintHostSendDialog.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp similarity index 60% rename from src/slic3r/Utils/PrintHostSendDialog.hpp rename to src/slic3r/GUI/PrintHostDialogs.hpp index dc4a8d6f7..d27fbe576 100644 --- a/src/slic3r/Utils/PrintHostSendDialog.hpp +++ b/src/slic3r/GUI/PrintHostDialogs.hpp @@ -20,19 +20,30 @@ namespace Slic3r { + class PrintHostSendDialog : public GUI::MsgDialog { -private: - wxTextCtrl *txt_filename; - wxCheckBox *box_print; - bool can_start_print; - public: - PrintHostSendDialog(const boost::filesystem::path &path, bool can_start_print); - boost::filesystem::path filename() const; - bool print() const; + PrintHostSendDialog(const boost::filesystem::path &path); + boost::filesystem::path filename() const; + bool start_print() const; + +private: + wxTextCtrl *txt_filename; + wxCheckBox *box_print; + bool can_start_print; }; + +class PrintHostQueueDialog : public wxDialog +{ +public: + PrintHostQueueDialog(); + +private: +}; + + } #endif diff --git a/src/slic3r/Utils/Duet.cpp b/src/slic3r/Utils/Duet.cpp index c242e918e..4eda7bd46 100644 --- a/src/slic3r/Utils/Duet.cpp +++ b/src/slic3r/Utils/Duet.cpp @@ -1,5 +1,4 @@ #include "Duet.hpp" -#include "PrintHostSendDialog.hpp" #include <algorithm> #include <ctime> @@ -21,6 +20,7 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX #include "Http.hpp" namespace fs = boost::filesystem; @@ -62,10 +62,10 @@ bool Duet::send_gcode(const std::string &filename) const const auto errortitle = _(L("Error while uploading to the Duet")); fs::path filepath(filename); - PrintHostSendDialog send_dialog(filepath.filename(), true); + PrintHostSendDialog send_dialog(filepath.filename()); if (send_dialog.ShowModal() != wxID_OK) { return false; } - const bool print = send_dialog.print(); + const bool print = send_dialog.start_print(); const auto upload_filepath = send_dialog.filename(); const auto upload_filename = upload_filepath.filename(); const auto upload_parent_path = upload_filepath.parent_path(); @@ -136,6 +136,11 @@ bool Duet::send_gcode(const std::string &filename) const return res; } +bool Duet::upload(PrintHostUpload upload_data) const +{ + throw "unimplemented"; +} + bool Duet::has_auto_discovery() const { return false; diff --git a/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp index bc210d7a4..db21fd0a1 100644 --- a/src/slic3r/Utils/Duet.hpp +++ b/src/slic3r/Utils/Duet.hpp @@ -24,6 +24,7 @@ public: wxString get_test_failed_msg (wxString &msg) const; // Send gcode file to duet, filename is expected to be in UTF-8 bool send_gcode(const std::string &filename) const; + bool upload(PrintHostUpload upload_data) const; bool has_auto_discovery() const; bool can_test() const; private: diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index d975578fd..b2e2d4d45 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -1,14 +1,14 @@ #include "OctoPrint.hpp" -#include "PrintHostSendDialog.hpp" #include <algorithm> #include <boost/format.hpp> #include <boost/log/trivial.hpp> #include "libslic3r/PrintConfig.hpp" +#include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX #include "Http.hpp" -#include "slic3r/GUI/I18N.hpp" namespace fs = boost::filesystem; @@ -66,10 +66,10 @@ bool OctoPrint::send_gcode(const std::string &filename) const const auto errortitle = _(L("Error while uploading to the OctoPrint server")); fs::path filepath(filename); - PrintHostSendDialog send_dialog(filepath.filename(), true); + PrintHostSendDialog send_dialog(filepath.filename()); if (send_dialog.ShowModal() != wxID_OK) { return false; } - const bool print = send_dialog.print(); + const bool print = send_dialog.start_print(); const auto upload_filepath = send_dialog.filename(); const auto upload_filename = upload_filepath.filename(); const auto upload_parent_path = upload_filepath.parent_path(); @@ -101,7 +101,7 @@ bool OctoPrint::send_gcode(const std::string &filename) const auto http = Http::post(std::move(url)); set_auth(http); http.form_add("print", print ? "true" : "false") - .form_add("path", upload_parent_path.string()) + .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? .form_add_file("file", filename, upload_filename.string()) .on_complete([&](std::string body, unsigned status) { BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body; @@ -129,6 +129,11 @@ bool OctoPrint::send_gcode(const std::string &filename) const return res; } +bool OctoPrint::upload(PrintHostUpload upload_data) const +{ + throw "unimplemented"; +} + bool OctoPrint::has_auto_discovery() const { return true; diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index f6c4d58c8..314e4cfae 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -24,6 +24,7 @@ public: wxString get_test_failed_msg (wxString &msg) const; // Send gcode file to octoprint, filename is expected to be in UTF-8 bool send_gcode(const std::string &filename) const; + bool upload(PrintHostUpload upload_data) const; bool has_auto_discovery() const; bool can_test() const; private: diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index dd72bae40..570d72f68 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -1,7 +1,15 @@ #include "OctoPrint.hpp" #include "Duet.hpp" +#include <vector> +#include <thread> +#include <boost/optional.hpp> + #include "libslic3r/PrintConfig.hpp" +#include "libslic3r/Channel.hpp" + +using boost::optional; + namespace Slic3r { @@ -10,13 +18,42 @@ PrintHost::~PrintHost() {} PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) { - PrintHostType kind = config->option<ConfigOptionEnum<PrintHostType>>("host_type")->value; - if (kind == htOctoPrint) { - return new OctoPrint(config); - } else if (kind == htDuet) { - return new Duet(config); - } - return nullptr; + PrintHostType kind = config->option<ConfigOptionEnum<PrintHostType>>("host_type")->value; + if (kind == htOctoPrint) { + return new OctoPrint(config); + } else if (kind == htDuet) { + return new Duet(config); + } + return nullptr; +} + + +struct PrintHostJobQueue::priv +{ + std::vector<PrintHostJob> jobs; + Channel<unsigned> channel; + + std::thread bg_thread; + optional<PrintHostJob> bg_job; +}; + +PrintHostJobQueue::PrintHostJobQueue() + : p(new priv()) +{ + std::shared_ptr<priv> p2 = p; + p->bg_thread = std::thread([p2]() { + // Wait for commands on the channel: + auto cmd = p2->channel.pop(); + // TODO + }); +} + +PrintHostJobQueue::~PrintHostJobQueue() +{ + // TODO: stop the thread + // if (p && p->bg_thread.joinable()) { + // p->bg_thread.detach(); + // } } diff --git a/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp index bc828ea46..2ccd1382a 100644 --- a/src/slic3r/Utils/PrintHost.hpp +++ b/src/slic3r/Utils/PrintHost.hpp @@ -8,26 +8,66 @@ namespace Slic3r { - class DynamicPrintConfig; + +struct PrintHostUpload +{ + boost::filesystem::path source_path; + boost::filesystem::path upload_path; + bool start_print = false; +}; + + class PrintHost { public: - virtual ~PrintHost(); + virtual ~PrintHost(); - virtual bool test(wxString &curl_msg) const = 0; - virtual wxString get_test_ok_msg () const = 0; - virtual wxString get_test_failed_msg (wxString &msg) const = 0; - // Send gcode file to print host, filename is expected to be in UTF-8 - virtual bool send_gcode(const std::string &filename) const = 0; - virtual bool has_auto_discovery() const = 0; - virtual bool can_test() const = 0; + virtual bool test(wxString &curl_msg) const = 0; + virtual wxString get_test_ok_msg () const = 0; + virtual wxString get_test_failed_msg (wxString &msg) const = 0; + // Send gcode file to print host, filename is expected to be in UTF-8 + virtual bool send_gcode(const std::string &filename) const = 0; // XXX: remove in favor of upload() + virtual bool upload(PrintHostUpload upload_data) const = 0; + virtual bool has_auto_discovery() const = 0; + virtual bool can_test() const = 0; - static PrintHost* get_print_host(DynamicPrintConfig *config); + static PrintHost* get_print_host(DynamicPrintConfig *config); }; +struct PrintHostJob +{ + PrintHostUpload upload_data; + std::unique_ptr<PrintHost> printhost; + + PrintHostJob() {} + PrintHostJob(DynamicPrintConfig *config) + : printhost(PrintHost::get_print_host(config)) + {} + + bool empty() const { return !printhost; } + operator bool() const { return !!printhost; } +}; + + +class PrintHostJobQueue +{ +public: + PrintHostJobQueue(); + PrintHostJobQueue(const PrintHostJobQueue &) = delete; + PrintHostJobQueue(PrintHostJobQueue &&other) = delete; + ~PrintHostJobQueue(); + + PrintHostJobQueue& operator=(const PrintHostJobQueue &) = delete; + PrintHostJobQueue& operator=(PrintHostJobQueue &&other) = delete; + +private: + struct priv; + std::shared_ptr<priv> p; +}; + } diff --git a/src/slic3r/Utils/PrintHostSendDialog.cpp b/src/slic3r/Utils/PrintHostSendDialog.cpp deleted file mode 100644 index 84428d3b5..000000000 --- a/src/slic3r/Utils/PrintHostSendDialog.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "PrintHostSendDialog.hpp" - -#include <wx/frame.h> -#include <wx/event.h> -#include <wx/progdlg.h> -#include <wx/sizer.h> -#include <wx/stattext.h> -#include <wx/textctrl.h> -#include <wx/checkbox.h> - -#include "slic3r/GUI/GUI.hpp" -#include "slic3r/GUI/MsgDialog.hpp" -#include "slic3r/GUI/I18N.hpp" - -namespace fs = boost::filesystem; - -namespace Slic3r { - -PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_print) : - MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE), - txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring())), - box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload")))), - can_start_print(can_start_print) -{ - auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed."))); - label_dir_hint->Wrap(CONTENT_WIDTH); - - content_sizer->Add(txt_filename, 0, wxEXPAND); - content_sizer->Add(label_dir_hint); - content_sizer->AddSpacer(VERT_SPACING); - content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING); - - btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL)); - - txt_filename->SetFocus(); - wxString stem(path.stem().wstring()); - txt_filename->SetSelection(0, stem.Length()); - - box_print->Enable(can_start_print); - - Fit(); -} - -fs::path PrintHostSendDialog::filename() const -{ - return fs::path(txt_filename->GetValue().wx_str()); -} - -bool PrintHostSendDialog::print() const -{ - return box_print->GetValue(); } -} From 2eaca46b75e7d49375509f5ad5ad47e2c9cdf392 Mon Sep 17 00:00:00 2001 From: Vojtech Kral <vojtech@kral.hk> Date: Wed, 12 Dec 2018 13:35:00 +0100 Subject: [PATCH 3/6] OctoPrint WIP: Fix build --- src/libslic3r/Channel.hpp | 104 ++++++++++++++++++++ src/slic3r/GUI/BackgroundSlicingProcess.cpp | 7 +- src/slic3r/Utils/PrintHost.hpp | 14 +++ 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/libslic3r/Channel.hpp diff --git a/src/libslic3r/Channel.hpp b/src/libslic3r/Channel.hpp new file mode 100644 index 000000000..8d1a07d35 --- /dev/null +++ b/src/libslic3r/Channel.hpp @@ -0,0 +1,104 @@ +#ifndef slic3r_Channel_hpp_ +#define slic3r_Channel_hpp_ + +#include <deque> +#include <condition_variable> +#include <mutex> +#include <utility> +#include <boost/optional.hpp> + + +namespace Slic3r { + + +template<class T> class Channel +{ +private: + using UniqueLock = std::unique_lock<std::mutex>; + using Queue = std::deque<T>; +public: + class Guard + { + public: + Guard(UniqueLock lock, const Queue &queue) : m_lock(std::move(lock)), m_queue(queue) {} + Guard(const Guard &other) = delete; + Guard(Guard &&other) = delete; + ~Guard() {} + + // Access trampolines + size_t size() const noexcept { return m_queue.size(); } + bool empty() const noexcept { return m_queue.empty(); } + typename Queue::const_iterator begin() const noexcept { return m_queue.begin(); } + typename Queue::const_iterator end() const noexcept { return m_queue.end(); } + typename Queue::const_reference operator[](size_t i) const { return m_queue[i]; } + + Guard& operator=(const Guard &other) = delete; + Guard& operator=(Guard &&other) = delete; + private: + UniqueLock m_lock; + const Queue &m_queue; + }; + + + Channel() {} + ~Channel() {} + + void push(const T& item, bool silent = false) + { + { + UniqueLock lock(m_mutex); + m_queue.push_back(item); + } + if (! silent) { m_condition.notify_one(); } + } + + void push(T &&item, bool silent = false) + { + { + UniqueLock lock(m_mutex); + m_queue.push_back(std::forward(item)); + } + if (! silent) { m_condition.notify_one(); } + } + + T pop() + { + UniqueLock lock(m_mutex); + m_condition.wait(lock, [this]() { return !m_queue.empty(); }); + auto item = std::move(m_queue.front()); + m_queue.pop_front(); + return item; + } + + boost::optional<T> try_pop() + { + UniqueLock lock(m_mutex); + if (m_queue.empty()) { + return boost::none; + } else { + auto item = std::move(m_queue.front()); + m_queue.pop(); + return item; + } + } + + // Unlocked observers + // Thread unsafe! Keep in mind you need to re-verify the result after acquiring lock! + size_t size() const noexcept { return m_queue.size(); } + bool empty() const noexcept { return m_queue.empty(); } + + Guard read() const + { + return Guard(UniqueLock(m_mutex), m_queue); + } + +private: + Queue m_queue; + std::mutex m_mutex; + std::condition_variable m_condition; +}; + + +} // namespace Slic3r + +#endif // slic3r_Channel_hpp_ diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 1829de959..3a34b3381 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -17,6 +17,7 @@ #include "libslic3r/GCode/PostProcessor.hpp" //#undef NDEBUG +#include <iostream> // XXX #include <cassert> #include <stdexcept> #include <cctype> @@ -79,6 +80,10 @@ void BackgroundSlicingProcess::process_fff() wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id)); m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); if (this->set_step_started(bspsGCodeFinalize)) { + + std::cerr << "BackgroundSlicingProcess: m_upload_job: " << !!m_upload_job << std::endl; + std::cerr << "BackgroundSlicingProcess: m_export_path: " << m_export_path << std::endl; + if (! m_export_path.empty()) { //FIXME localize the messages // Perform the final post-processing of the export path by applying the print statistics over the file name. @@ -387,7 +392,7 @@ void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job) if (! m_export_path.empty()) return; - const auto path = boost::filesystem::temp_directory_path() + const boost::filesystem::path path = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(".upload.%%%%-%%%%-%%%%-%%%%.gcode"); // Guard against entering the export step before changing the export path. diff --git a/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp index 2ccd1382a..115807e9a 100644 --- a/src/slic3r/Utils/PrintHost.hpp +++ b/src/slic3r/Utils/PrintHost.hpp @@ -43,10 +43,24 @@ struct PrintHostJob std::unique_ptr<PrintHost> printhost; PrintHostJob() {} + PrintHostJob(const PrintHostJob&) = delete; + PrintHostJob(PrintHostJob &&other) + : upload_data(std::move(other.upload_data)) + , printhost(std::move(other.printhost)) + {} + PrintHostJob(DynamicPrintConfig *config) : printhost(PrintHost::get_print_host(config)) {} + PrintHostJob& operator=(const PrintHostJob&) = delete; + PrintHostJob& operator=(PrintHostJob &&other) + { + upload_data = std::move(other.upload_data); + printhost = std::move(other.printhost); + return *this; + } + bool empty() const { return !printhost; } operator bool() const { return !!printhost; } }; From 4e7749a50dd8e35c68fe65d8d5ae9a65c89158ff Mon Sep 17 00:00:00 2001 From: Vojtech Kral <vojtech@kral.hk> Date: Wed, 12 Dec 2018 13:56:38 +0100 Subject: [PATCH 4/6] OctoPrint WIP: Fix build --- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 3a34b3381..b31fa6b5f 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -17,7 +17,6 @@ #include "libslic3r/GCode/PostProcessor.hpp" //#undef NDEBUG -#include <iostream> // XXX #include <cassert> #include <stdexcept> #include <cctype> @@ -80,10 +79,6 @@ void BackgroundSlicingProcess::process_fff() wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id)); m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); if (this->set_step_started(bspsGCodeFinalize)) { - - std::cerr << "BackgroundSlicingProcess: m_upload_job: " << !!m_upload_job << std::endl; - std::cerr << "BackgroundSlicingProcess: m_export_path: " << m_export_path << std::endl; - if (! m_export_path.empty()) { //FIXME localize the messages // Perform the final post-processing of the export path by applying the print statistics over the file name. @@ -398,7 +393,7 @@ void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job) // Guard against entering the export step before changing the export path. tbb::mutex::scoped_lock lock(m_print->state_mutex()); this->invalidate_step(bspsGCodeFinalize); - m_export_path = path.native(); + m_export_path = path.string(); m_upload_job = std::move(upload_job); } From 5dd0d505ee4874a2be4bb3acf05c69eb1702e668 Mon Sep 17 00:00:00 2001 From: Vojtech Kral <vojtech@kral.hk> Date: Wed, 12 Dec 2018 14:15:08 +0100 Subject: [PATCH 5/6] Add missing include --- src/slic3r/Utils/PrintHost.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp index 115807e9a..53f7c43d3 100644 --- a/src/slic3r/Utils/PrintHost.hpp +++ b/src/slic3r/Utils/PrintHost.hpp @@ -3,6 +3,8 @@ #include <memory> #include <string> +#include <boost/filesystem/path.hpp> + #include <wx/string.h> From 913cece5a604a2e53967af3cbd4f864013d601dc Mon Sep 17 00:00:00 2001 From: Vojtech Kral <vojtech@kral.hk> Date: Wed, 12 Dec 2018 14:18:03 +0100 Subject: [PATCH 6/6] Plater: Remove extraneous button --- src/slic3r/GUI/Plater.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5ce49259a..67ea8c717 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -546,7 +546,6 @@ Sidebar::Sidebar(Plater *parent) p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxLEFT | wxTOP, 20); wxBitmap arrow_up(GUI::from_u8(Slic3r::var("brick_go.png")), wxBITMAP_TYPE_PNG); - p->btn_send_gcode = new wxButton(p->scrolled, wxID_ANY, _(L("Send to printer"))); p->btn_send_gcode = new wxButton(this, wxID_ANY, _(L("Send to printer"))); p->btn_send_gcode->SetBitmap(arrow_up); p->btn_send_gcode->SetFont(wxGetApp().bold_font());