Merge remote-tracking branch 'origin/master'

This commit is contained in:
tamasmeszaros 2018-12-12 14:25:27 +01:00
commit dfc92fb5cf
20 changed files with 439 additions and 176 deletions

104
src/libslic3r/Channel.hpp Normal file
View 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_

View file

@ -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);

View file

@ -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

View file

@ -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());

View file

@ -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;

View file

@ -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
{

View file

@ -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
{}

View file

@ -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)

View file

@ -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();

View file

@ -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();

View file

@ -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;

View 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(); }
}

View file

@ -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

View file

@ -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;

View file

@ -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:

View file

@ -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;

View file

@ -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:

View file

@ -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();
// }
}

View file

@ -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;
};
}

View file

@ -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(); }
}