Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
dfc92fb5cf
20 changed files with 439 additions and 176 deletions
104
src/libslic3r/Channel.hpp
Normal file
104
src/libslic3r/Channel.hpp
Normal file
|
@ -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_
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 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.
|
||||
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
||||
this->invalidate_step(bspsGCodeFinalize);
|
||||
m_export_path = path.string();
|
||||
m_upload_job = std::move(upload_job);
|
||||
}
|
||||
|
||||
void BackgroundSlicingProcess::reset_export()
|
||||
{
|
||||
assert(! this->running());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,11 @@ 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 +559,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 +570,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 +820,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 +827,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 +985,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 +2037,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 +2929,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 +2959,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 +3070,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 +3141,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 +3180,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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
49
src/slic3r/GUI/PrintHostDialogs.cpp
Normal file
49
src/slic3r/GUI/PrintHostDialogs.cpp
Normal file
|
@ -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(); }
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,31 +3,87 @@
|
|||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
|
||||
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(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; }
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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(); }
|
||||
}
|
Loading…
Reference in a new issue