mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-25 01:31:14 -06:00
Merge of pull request Add support for RepetierServer #4384 by @docbobo
with the following refactorings: 1) Removed the "printhost_slug" config from the Printer config and from all the Printer config related spots. 2) "printhost_slug" renamed to "printhost_port". Slug sounds nasty. 3) Improved error reporting of RepetierHost class. 4) Refactored for the new "Physical Printers" Following refactorings were done independently of the Repetier pull request: 1) Removed PrintHost static print config. 2) Clean-up after conversion of print host configuration from Printer config to Physical Printer config. 3) Fixed some issues, where the Printer config was still queried for host configuration. Vojtech believes that this should not happen after the host configuration is converted to physical printers. Vojtech still feels that more refactoring is needed in regard to porting the host configuration from Printer profile to the new Physical Printer profile.
This commit is contained in:
parent
0798fa8185
commit
7c571c1d9d
24 changed files with 556 additions and 200 deletions
|
|
@ -193,6 +193,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
Utils/FlashAir.hpp
|
||||
Utils/AstroBox.cpp
|
||||
Utils/AstroBox.hpp
|
||||
Utils/Repetier.cpp
|
||||
Utils/Repetier.hpp
|
||||
Utils/PrintHost.cpp
|
||||
Utils/PrintHost.hpp
|
||||
Utils/Bonjour.cpp
|
||||
|
|
|
|||
|
|
@ -1048,13 +1048,33 @@ void Choice::set_values(const std::vector<std::string>& values)
|
|||
auto value = ww->GetValue();
|
||||
ww->Clear();
|
||||
ww->Append("");
|
||||
for (auto el : values)
|
||||
for (const auto &el : values)
|
||||
ww->Append(wxString(el));
|
||||
ww->SetValue(value);
|
||||
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
void Choice::set_values(const wxArrayString &values)
|
||||
{
|
||||
if (values.empty())
|
||||
return;
|
||||
|
||||
m_disable_change_event = true;
|
||||
|
||||
// # it looks that Clear() also clears the text field in recent wxWidgets versions,
|
||||
// # but we want to preserve it
|
||||
auto ww = dynamic_cast<wxBitmapComboBox*>(window);
|
||||
auto value = ww->GetValue();
|
||||
ww->Clear();
|
||||
ww->Append("");
|
||||
for (const auto &el : values)
|
||||
ww->Append(el);
|
||||
ww->SetValue(value);
|
||||
|
||||
m_disable_change_event = false;
|
||||
}
|
||||
|
||||
boost::any& Choice::get_value()
|
||||
{
|
||||
wxBitmapComboBox* field = dynamic_cast<wxBitmapComboBox*>(window);
|
||||
|
|
|
|||
|
|
@ -405,6 +405,7 @@ public:
|
|||
void set_value(const std::string& value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false);
|
||||
void set_values(const std::vector<std::string> &values);
|
||||
void set_values(const wxArrayString &values);
|
||||
boost::any& get_value() override;
|
||||
|
||||
void msw_rescale(bool rescale_sidetext = false) override;
|
||||
|
|
|
|||
|
|
@ -750,14 +750,11 @@ bool MainFrame::can_export_gcode() const
|
|||
|
||||
bool MainFrame::can_send_gcode() const
|
||||
{
|
||||
if (m_plater == nullptr)
|
||||
return false;
|
||||
|
||||
if (m_plater->model().objects.empty())
|
||||
return false;
|
||||
|
||||
const auto print_host_opt = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionString>("print_host");
|
||||
return print_host_opt != nullptr && !print_host_opt->value.empty();
|
||||
if (m_plater && ! m_plater->model().objects.empty())
|
||||
if (const DynamicPrintConfig *cfg = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config(); cfg)
|
||||
if (const auto *print_host_opt = cfg->option<ConfigOptionString>("print_host"); print_host_opt)
|
||||
return ! print_host_opt->value.empty();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MainFrame::can_export_gcode_sd() const
|
||||
|
|
|
|||
|
|
@ -32,10 +32,6 @@
|
|||
#include "BitmapCache.hpp"
|
||||
#include "BonjourDialog.hpp"
|
||||
|
||||
using Slic3r::GUI::format_wxstr;
|
||||
|
||||
//static const std::pair<unsigned int, unsigned int> THUMBNAIL_SIZE_3MF = { 256, 256 };
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
|
@ -248,10 +244,30 @@ PhysicalPrinterDialog::~PhysicalPrinterDialog()
|
|||
}
|
||||
}
|
||||
|
||||
void PhysicalPrinterDialog::update_printers()
|
||||
{
|
||||
wxBusyCursor wait;
|
||||
|
||||
std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
|
||||
|
||||
wxArrayString printers;
|
||||
Field *rs = m_optgroup->get_field("printhost_port");
|
||||
try {
|
||||
if (! host->get_printers(printers))
|
||||
printers.clear();
|
||||
} catch (const HostNetworkError &err) {
|
||||
printers.clear();
|
||||
show_error(this, _L("Querying printers connected to a print host failed.") + "\n\n" + from_u8(err.what()));
|
||||
}
|
||||
Choice *choice = dynamic_cast<Choice*>(rs);
|
||||
choice->set_values(printers);
|
||||
printers.empty() ? rs->disable() : rs->enable();
|
||||
}
|
||||
|
||||
void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgroup)
|
||||
{
|
||||
m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
if (opt_key == "printhost_authorization_type")
|
||||
if (opt_key == "host_type" || opt_key == "printhost_authorization_type")
|
||||
this->update();
|
||||
};
|
||||
|
||||
|
|
@ -291,17 +307,30 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
|||
return;
|
||||
}
|
||||
wxString msg;
|
||||
if (host->test(msg)) {
|
||||
bool result;
|
||||
{
|
||||
// Show a wait cursor during the connection test, as it is blocking UI.
|
||||
wxBusyCursor wait;
|
||||
result = host->test(msg);
|
||||
}
|
||||
if (result)
|
||||
show_info(this, host->get_test_ok_msg(), _L("Success!"));
|
||||
}
|
||||
else {
|
||||
else
|
||||
show_error(this, host->get_test_failed_msg(msg));
|
||||
}
|
||||
});
|
||||
|
||||
return sizer;
|
||||
};
|
||||
|
||||
auto print_host_printers = [this, create_sizer_with_btn](wxWindow* parent) {
|
||||
//add_scaled_button(parent, &m_printhost_port_browse_btn, "browse", _(L("Refresh Printers")), wxBU_LEFT | wxBU_EXACTFIT);
|
||||
auto sizer = create_sizer_with_btn(parent, &m_printhost_port_browse_btn, "browse", _(L("Refresh Printers")));
|
||||
ScalableButton* btn = m_printhost_port_browse_btn;
|
||||
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { update_printers(); });
|
||||
return sizer;
|
||||
};
|
||||
|
||||
// Set a wider width for a better alignment
|
||||
Option option = m_optgroup->get_option("print_host");
|
||||
option.opt.width = Field::def_width_wider();
|
||||
|
|
@ -316,6 +345,12 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
|||
option.opt.width = Field::def_width_wider();
|
||||
m_optgroup->append_single_option_line(option);
|
||||
|
||||
option = m_optgroup->get_option("printhost_port");
|
||||
option.opt.width = Field::def_width_wider();
|
||||
Line port_line = m_optgroup->create_single_option_line(option);
|
||||
port_line.append_widget(print_host_printers);
|
||||
m_optgroup->append_line(port_line);
|
||||
|
||||
const auto ca_file_hint = _u8L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.");
|
||||
|
||||
if (Http::ca_file_supported()) {
|
||||
|
|
@ -377,6 +412,14 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
|||
}
|
||||
|
||||
m_optgroup->activate();
|
||||
|
||||
// Always fill in the "printhost_port" combo box from the config and select it.
|
||||
{
|
||||
Choice* choice = dynamic_cast<Choice*>(m_optgroup->get_field("printhost_port"));
|
||||
choice->set_values({ m_config->opt_string("printhost_port") });
|
||||
choice->set_selection();
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
@ -386,11 +429,14 @@ void PhysicalPrinterDialog::update()
|
|||
|
||||
const PrinterTechnology tech = Preset::printer_technology(*m_config);
|
||||
// Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment)
|
||||
bool supports_multiple_printers = false;
|
||||
if (tech == ptFFF) {
|
||||
m_optgroup->show_field("host_type");
|
||||
m_optgroup->hide_field("printhost_authorization_type");
|
||||
for (const std::string& opt_key : std::vector<std::string>{ "printhost_user", "printhost_password" })
|
||||
m_optgroup->hide_field(opt_key);
|
||||
const auto opt = m_config->option<ConfigOptionEnum<PrintHostType>>("host_type");
|
||||
supports_multiple_printers = opt && opt->value == htRepetier;
|
||||
}
|
||||
else {
|
||||
m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false);
|
||||
|
|
@ -401,10 +447,12 @@ void PhysicalPrinterDialog::update()
|
|||
AuthorizationType auth_type = m_config->option<ConfigOptionEnum<AuthorizationType>>("printhost_authorization_type")->value;
|
||||
m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword);
|
||||
|
||||
for (const std::string& opt_key : std::vector<std::string>{ "printhost_user", "printhost_password" })
|
||||
for (const char *opt_key : { "printhost_user", "printhost_password" })
|
||||
m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword);
|
||||
}
|
||||
|
||||
m_optgroup->show_field("printhost_port", supports_multiple_printers);
|
||||
m_printhost_port_browse_btn->Show(supports_multiple_printers);
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ class PhysicalPrinterDialog : public DPIDialog
|
|||
ScalableButton* m_printhost_browse_btn {nullptr};
|
||||
ScalableButton* m_printhost_test_btn {nullptr};
|
||||
ScalableButton* m_printhost_cafile_browse_btn {nullptr};
|
||||
ScalableButton* m_printhost_port_browse_btn {nullptr};
|
||||
|
||||
wxBoxSizer* m_presets_sizer {nullptr};
|
||||
|
||||
|
|
@ -85,6 +86,7 @@ public:
|
|||
~PhysicalPrinterDialog();
|
||||
|
||||
void update();
|
||||
void update_printers();
|
||||
wxString get_printer_name();
|
||||
void update_full_printer_names();
|
||||
PhysicalPrinter* get_printer() {return &m_printer; }
|
||||
|
|
|
|||
|
|
@ -1819,8 +1819,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
, main_frame(main_frame)
|
||||
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
|
||||
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
|
||||
"brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host",
|
||||
"printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material",
|
||||
"brim_width", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
|
||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle",
|
||||
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology",
|
||||
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
|
||||
|
|
@ -4292,11 +4291,8 @@ void Plater::priv::show_action_buttons(const bool ready_to_slice) const
|
|||
wxWindowUpdateLocker noUpdater(sidebar);
|
||||
|
||||
DynamicPrintConfig* selected_printer_config = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config();
|
||||
if (!selected_printer_config)
|
||||
selected_printer_config = config;
|
||||
|
||||
const auto prin_host_opt = selected_printer_config->option<ConfigOptionString>("print_host");
|
||||
const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty();
|
||||
const auto print_host_opt = selected_printer_config ? selected_printer_config->option<ConfigOptionString>("print_host") : nullptr;
|
||||
const bool send_gcode_shown = print_host_opt != nullptr && !print_host_opt->value.empty();
|
||||
|
||||
// when a background processing is ON, export_btn and/or send_btn are showing
|
||||
if (wxGetApp().app_config->get("background_processing") == "1")
|
||||
|
|
@ -5304,12 +5300,14 @@ void Plater::reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &
|
|||
|
||||
void Plater::send_gcode()
|
||||
{
|
||||
if (p->model.objects.empty()) { return; }
|
||||
|
||||
// if physical_printer is selected, send gcode for this printer
|
||||
DynamicPrintConfig* physical_printer_config = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config();
|
||||
PrintHostJob upload_job(physical_printer_config ? physical_printer_config : p->config);
|
||||
if (upload_job.empty()) { return; }
|
||||
if (! physical_printer_config || p->model.objects.empty())
|
||||
return;
|
||||
|
||||
PrintHostJob upload_job(physical_printer_config);
|
||||
if (upload_job.empty())
|
||||
return;
|
||||
|
||||
// Obtain default output path
|
||||
fs::path default_output_file;
|
||||
|
|
@ -5327,11 +5325,18 @@ void Plater::send_gcode()
|
|||
}
|
||||
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
|
||||
|
||||
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->can_start_print());
|
||||
// Repetier specific: Query the server for the list of file groups.
|
||||
wxArrayString groups;
|
||||
{
|
||||
wxBusyCursor wait;
|
||||
upload_job.printhost->get_groups(groups);
|
||||
}
|
||||
|
||||
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->can_start_print(), groups);
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
upload_job.upload_data.upload_path = dlg.filename();
|
||||
upload_job.upload_data.start_print = dlg.start_print();
|
||||
|
||||
upload_job.upload_data.group = dlg.group();
|
||||
p->export_gcode(fs::path(), false, std::move(upload_job));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,18 +28,20 @@ namespace GUI {
|
|||
|
||||
static const char *CONFIG_KEY_PATH = "printhost_path";
|
||||
static const char *CONFIG_KEY_PRINT = "printhost_print";
|
||||
static const char *CONFIG_KEY_GROUP = "printhost_group";
|
||||
|
||||
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)
|
||||
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_print, const wxArrayString &groups)
|
||||
: 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))
|
||||
, box_print(can_start_print ? new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload"))) : nullptr)
|
||||
, box_print(can_start_print ? new wxCheckBox(this, wxID_ANY, _L("Start printing after upload")) : nullptr)
|
||||
, combo_groups(!groups.IsEmpty() ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, groups, wxCB_READONLY) : nullptr)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
txt_filename->OSXDisableAllSmartSubstitutions();
|
||||
#endif
|
||||
const AppConfig *app_config = wxGetApp().app_config;
|
||||
|
||||
auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed.")));
|
||||
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 * wxGetApp().em_unit());
|
||||
|
||||
content_sizer->Add(txt_filename, 0, wxEXPAND);
|
||||
|
|
@ -49,10 +51,19 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
|
|||
content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING);
|
||||
box_print->SetValue(app_config->get("recent", CONFIG_KEY_PRINT) == "1");
|
||||
}
|
||||
|
||||
if (combo_groups != nullptr) {
|
||||
// Repetier specific: Show a selection of file groups.
|
||||
auto *label_group = new wxStaticText(this, wxID_ANY, _L("Group"));
|
||||
content_sizer->Add(label_group);
|
||||
content_sizer->Add(combo_groups, 0, wxBOTTOM, 2*VERT_SPACING);
|
||||
wxString recent_group = from_u8(app_config->get("recent", CONFIG_KEY_GROUP));
|
||||
if (! recent_group.empty())
|
||||
combo_groups->SetValue(recent_group);
|
||||
}
|
||||
|
||||
btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
|
||||
|
||||
|
||||
wxString recent_path = from_u8(app_config->get("recent", CONFIG_KEY_PATH));
|
||||
if (recent_path.Length() > 0 && recent_path[recent_path.Length() - 1] != '/') {
|
||||
recent_path += '/';
|
||||
|
|
@ -64,7 +75,7 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
|
|||
|
||||
txt_filename->SetValue(recent_path);
|
||||
txt_filename->SetFocus();
|
||||
|
||||
|
||||
Fit();
|
||||
|
||||
Bind(wxEVT_SHOW, [=](const wxShowEvent &) {
|
||||
|
|
@ -86,6 +97,16 @@ bool PrintHostSendDialog::start_print() const
|
|||
return box_print != nullptr ? box_print->GetValue() : false;
|
||||
}
|
||||
|
||||
std::string PrintHostSendDialog::group() const
|
||||
{
|
||||
if (combo_groups == nullptr) {
|
||||
return "";
|
||||
} else {
|
||||
wxString group = combo_groups->GetValue();
|
||||
return into_u8(group);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHostSendDialog::EndModal(int ret)
|
||||
{
|
||||
if (ret == wxID_OK) {
|
||||
|
|
@ -96,9 +117,15 @@ void PrintHostSendDialog::EndModal(int ret)
|
|||
path.clear();
|
||||
else
|
||||
path = path.SubString(0, last_slash);
|
||||
|
||||
AppConfig *app_config = wxGetApp().app_config;
|
||||
app_config->set("recent", CONFIG_KEY_PATH, into_u8(path));
|
||||
app_config->set("recent", CONFIG_KEY_PRINT, start_print() ? "1" : "0");
|
||||
|
||||
if (combo_groups != nullptr) {
|
||||
wxString group = combo_groups->GetValue();
|
||||
app_config->set("recent", CONFIG_KEY_GROUP, into_u8(group));
|
||||
}
|
||||
}
|
||||
|
||||
MsgDialog::EndModal(ret);
|
||||
|
|
@ -133,7 +160,7 @@ wxEvent *PrintHostQueueDialog::Event::Clone() const
|
|||
}
|
||||
|
||||
PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||
: DPIDialog(parent, wxID_ANY, _(L("Print host upload queue")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
: DPIDialog(parent, wxID_ANY, _L("Print host upload queue"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
, on_progress_evt(this, EVT_PRINTHOST_PROGRESS, &PrintHostQueueDialog::on_progress, this)
|
||||
, on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this)
|
||||
, on_cancel_evt(this, EVT_PRINTHOST_CANCEL, &PrintHostQueueDialog::on_cancel, this)
|
||||
|
|
@ -144,19 +171,20 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
|||
|
||||
job_list = new wxDataViewListCtrl(this, wxID_ANY);
|
||||
// Note: Keep these in sync with Column
|
||||
job_list->AppendTextColumn(_(L("ID")), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendProgressColumn(_(L("Progress")), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn(_(L("Status")), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn(_(L("Host")), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn(_(L("Filename")), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn(_(L("Error Message")), wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, wxDATAVIEW_COL_HIDDEN);
|
||||
job_list->AppendTextColumn(_L("ID"), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendProgressColumn(_L("Progress"), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn(_L("Status"), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn(_L("Host"), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn(_L("Filename"), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn(_L("Error Message"), wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, wxDATAVIEW_COL_HIDDEN);
|
||||
|
||||
auto *btnsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected")));
|
||||
btn_cancel = new wxButton(this, wxID_DELETE, _L("Cancel selected"));
|
||||
btn_cancel->Disable();
|
||||
btn_error = new wxButton(this, wxID_ANY, _(L("Show error message")));
|
||||
btn_error = new wxButton(this, wxID_ANY, _L("Show error message"));
|
||||
btn_error->Disable();
|
||||
auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close"))); // Note: The label needs to be present, otherwise we get accelerator bugs on Mac
|
||||
// Note: The label needs to be present, otherwise we get accelerator bugs on Mac
|
||||
auto *btn_close = new wxButton(this, wxID_CANCEL, _L("Close"));
|
||||
btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING);
|
||||
btnsizer->Add(btn_error, 0);
|
||||
btnsizer->AddStretchSpacer();
|
||||
|
|
@ -195,7 +223,7 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job)
|
|||
wxVector<wxVariant> fields;
|
||||
fields.push_back(wxVariant(wxString::Format("%d", job_list->GetItemCount() + 1)));
|
||||
fields.push_back(wxVariant(0));
|
||||
fields.push_back(wxVariant(_(L("Enqueued"))));
|
||||
fields.push_back(wxVariant(_L("Enqueued")));
|
||||
fields.push_back(wxVariant(job.printhost->get_host()));
|
||||
fields.push_back(wxVariant(job.upload_data.upload_path.string()));
|
||||
fields.push_back(wxVariant(""));
|
||||
|
|
@ -226,12 +254,12 @@ void PrintHostQueueDialog::set_state(int idx, JobState state)
|
|||
job_list->SetItemData(job_list->RowToItem(idx), static_cast<wxUIntPtr>(state));
|
||||
|
||||
switch (state) {
|
||||
case ST_NEW: job_list->SetValue(_(L("Enqueued")), idx, COL_STATUS); break;
|
||||
case ST_PROGRESS: job_list->SetValue(_(L("Uploading")), idx, COL_STATUS); break;
|
||||
case ST_ERROR: job_list->SetValue(_(L("Error")), idx, COL_STATUS); break;
|
||||
case ST_CANCELLING: job_list->SetValue(_(L("Cancelling")), idx, COL_STATUS); break;
|
||||
case ST_CANCELLED: job_list->SetValue(_(L("Cancelled")), idx, COL_STATUS); break;
|
||||
case ST_COMPLETED: job_list->SetValue(_(L("Completed")), idx, COL_STATUS); break;
|
||||
case ST_NEW: job_list->SetValue(_L("Enqueued"), idx, COL_STATUS); break;
|
||||
case ST_PROGRESS: job_list->SetValue(_L("Uploading"), idx, COL_STATUS); break;
|
||||
case ST_ERROR: job_list->SetValue(_L("Error"), idx, COL_STATUS); break;
|
||||
case ST_CANCELLING: job_list->SetValue(_L("Cancelling"), idx, COL_STATUS); break;
|
||||
case ST_CANCELLED: job_list->SetValue(_L("Cancelled"), idx, COL_STATUS); break;
|
||||
case ST_COMPLETED: job_list->SetValue(_L("Completed"), idx, COL_STATUS); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
class wxButton;
|
||||
class wxTextCtrl;
|
||||
class wxComboBox;
|
||||
class wxCheckBox;
|
||||
class wxDataViewListCtrl;
|
||||
|
||||
|
|
@ -29,14 +30,16 @@ namespace GUI {
|
|||
class PrintHostSendDialog : public GUI::MsgDialog
|
||||
{
|
||||
public:
|
||||
PrintHostSendDialog(const boost::filesystem::path &path, bool can_start_print);
|
||||
PrintHostSendDialog(const boost::filesystem::path &path, bool can_start_print, const wxArrayString& groups);
|
||||
boost::filesystem::path filename() const;
|
||||
bool start_print() const;
|
||||
std::string group() const;
|
||||
|
||||
virtual void EndModal(int ret) override;
|
||||
private:
|
||||
wxTextCtrl *txt_filename;
|
||||
wxCheckBox *box_print;
|
||||
wxComboBox *combo_groups;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -435,9 +435,6 @@ private:
|
|||
std::vector<PageShp> m_pages_sla;
|
||||
|
||||
public:
|
||||
wxButton* m_serial_test_btn = nullptr;
|
||||
ScalableButton* m_print_host_test_btn = nullptr;
|
||||
ScalableButton* m_printhost_browse_btn = nullptr;
|
||||
ScalableButton* m_reset_to_filament_color = nullptr;
|
||||
|
||||
size_t m_extruders_count;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public:
|
|||
bool can_test() const override { return true; }
|
||||
bool can_start_print() const override { return true; }
|
||||
std::string get_host() const override { return host; }
|
||||
|
||||
|
||||
protected:
|
||||
bool validate_version_text(const boost::optional<std::string> &version_text) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public:
|
|||
bool can_test() const override { return true; }
|
||||
bool can_start_print() const override { return true; }
|
||||
std::string get_host() const override { return host; }
|
||||
|
||||
|
||||
private:
|
||||
enum class ConnectionType { rrf, dsf, error };
|
||||
std::string host;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public:
|
|||
bool can_test() const override { return true; }
|
||||
bool can_start_print() const override { return false; }
|
||||
std::string get_host() const override { return host; }
|
||||
|
||||
|
||||
private:
|
||||
std::string host;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <wx/string.h>
|
||||
#include <wx/app.h>
|
||||
#include <wx/arrstr.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/Channel.hpp"
|
||||
|
|
@ -16,6 +17,7 @@
|
|||
#include "Duet.hpp"
|
||||
#include "FlashAir.hpp"
|
||||
#include "AstroBox.hpp"
|
||||
#include "Repetier.hpp"
|
||||
#include "../GUI/PrintHostDialogs.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
|
@ -47,6 +49,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
|||
case htDuet: return new Duet(config);
|
||||
case htFlashAir: return new FlashAir(config);
|
||||
case htAstroBox: return new AstroBox(config);
|
||||
case htRepetier: return new Repetier(config);
|
||||
default: return nullptr;
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "Http.hpp"
|
||||
|
||||
class wxArrayString;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -20,6 +21,9 @@ struct PrintHostUpload
|
|||
{
|
||||
boost::filesystem::path source_path;
|
||||
boost::filesystem::path upload_path;
|
||||
|
||||
std::string group;
|
||||
|
||||
bool start_print = false;
|
||||
};
|
||||
|
||||
|
|
@ -41,8 +45,15 @@ public:
|
|||
virtual bool has_auto_discovery() const = 0;
|
||||
virtual bool can_test() const = 0;
|
||||
virtual bool can_start_print() const = 0;
|
||||
// A print host usually does not support multiple printers, with the exception of Repetier server.
|
||||
virtual bool supports_multiple_printers() const { return false; }
|
||||
virtual std::string get_host() const = 0;
|
||||
|
||||
// Support for Repetier server multiple groups & printers. Not supported by other print hosts.
|
||||
// Returns false if not supported. May throw HostNetworkError.
|
||||
virtual bool get_groups(wxArrayString & /* groups */) const { return false; }
|
||||
virtual bool get_printers(wxArrayString & /* printers */) const { return false; }
|
||||
|
||||
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
||||
|
||||
protected:
|
||||
|
|
|
|||
268
src/slic3r/Utils/Repetier.cpp
Normal file
268
src/slic3r/Utils/Repetier.cpp
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
#include "Repetier.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <wx/progdlg.h>
|
||||
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "Http.hpp"
|
||||
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Repetier::Repetier(DynamicPrintConfig *config) :
|
||||
host(config->opt_string("print_host")),
|
||||
apikey(config->opt_string("printhost_apikey")),
|
||||
cafile(config->opt_string("printhost_cafile")),
|
||||
port(config->opt_string("printhost_port"))
|
||||
{}
|
||||
|
||||
const char* Repetier::get_name() const { return "Repetier"; }
|
||||
|
||||
bool Repetier::test(wxString &msg) const
|
||||
{
|
||||
// Since the request is performed synchronously here,
|
||||
// it is ok to refer to `msg` from within the closure
|
||||
|
||||
const char *name = get_name();
|
||||
|
||||
bool res = true;
|
||||
auto url = make_url("printer/info");
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: List version at: %2%") % name % url;
|
||||
|
||||
auto http = Http::get(std::move(url));
|
||||
set_auth(http);
|
||||
|
||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
|
||||
res = false;
|
||||
msg = format_error(body, error, status);
|
||||
})
|
||||
.on_complete([&, this](std::string body, unsigned) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
|
||||
|
||||
try {
|
||||
std::stringstream ss(body);
|
||||
pt::ptree ptree;
|
||||
pt::read_json(ss, ptree);
|
||||
|
||||
const auto text = ptree.get_optional<std::string>("name");
|
||||
res = validate_version_text(text);
|
||||
if (! res) {
|
||||
msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "Repetier")).str());
|
||||
}
|
||||
}
|
||||
catch (const std::exception &) {
|
||||
res = false;
|
||||
msg = "Could not parse server response";
|
||||
}
|
||||
})
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
wxString Repetier::get_test_ok_msg () const
|
||||
{
|
||||
return _(L("Connection to Repetier works correctly."));
|
||||
}
|
||||
|
||||
wxString Repetier::get_test_failed_msg (wxString &msg) const
|
||||
{
|
||||
return GUI::from_u8((boost::format("%s: %s\n\n%s")
|
||||
% _utf8(L("Could not connect to Repetier"))
|
||||
% std::string(msg.ToUTF8())
|
||||
% _utf8(L("Note: Repetier version at least 0.90.0 is required."))).str());
|
||||
}
|
||||
|
||||
bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
|
||||
{
|
||||
const char *name = get_name();
|
||||
|
||||
const auto upload_filename = upload_data.upload_path.filename();
|
||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||
|
||||
wxString test_msg;
|
||||
if (! test(test_msg)) {
|
||||
error_fn(std::move(test_msg));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
|
||||
auto url = make_url((boost::format("printer/model/%1%") % port).str());
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%, group: %7%")
|
||||
% name
|
||||
% upload_data.source_path
|
||||
% url
|
||||
% upload_filename.string()
|
||||
% upload_parent_path.string()
|
||||
% upload_data.start_print
|
||||
% upload_data.group;
|
||||
|
||||
auto http = Http::post(std::move(url));
|
||||
set_auth(http);
|
||||
|
||||
if (! upload_data.group.empty() && upload_data.group != _utf8(L("Default"))) {
|
||||
http.form_add("group", upload_data.group);
|
||||
}
|
||||
|
||||
http.form_add("a", "upload")
|
||||
.form_add_file("filename", upload_data.source_path.string(), upload_filename.string())
|
||||
.on_complete([&](std::string body, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
|
||||
error_fn(format_error(body, error, status));
|
||||
res = false;
|
||||
})
|
||||
.on_progress([&](Http::Progress progress, bool &cancel) {
|
||||
prorgess_fn(std::move(progress), cancel);
|
||||
if (cancel) {
|
||||
// Upload was canceled
|
||||
BOOST_LOG_TRIVIAL(info) << "Repetier: Upload canceled";
|
||||
res = false;
|
||||
}
|
||||
})
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Repetier::validate_version_text(const boost::optional<std::string> &version_text) const
|
||||
{
|
||||
return version_text ? boost::starts_with(*version_text, "Repetier") : true;
|
||||
}
|
||||
|
||||
void Repetier::set_auth(Http &http) const
|
||||
{
|
||||
http.header("X-Api-Key", apikey);
|
||||
|
||||
if (! cafile.empty()) {
|
||||
http.ca_file(cafile);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Repetier::make_url(const std::string &path) const
|
||||
{
|
||||
if (host.find("http://") == 0 || host.find("https://") == 0) {
|
||||
if (host.back() == '/') {
|
||||
return (boost::format("%1%%2%") % host % path).str();
|
||||
} else {
|
||||
return (boost::format("%1%/%2%") % host % path).str();
|
||||
}
|
||||
} else {
|
||||
return (boost::format("http://%1%/%2%") % host % path).str();
|
||||
}
|
||||
}
|
||||
|
||||
bool Repetier::get_groups(wxArrayString& groups) const
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
const char *name = get_name();
|
||||
auto url = make_url((boost::format("printer/api/%1%") % port).str());
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get groups at: %2%") % name % url;
|
||||
|
||||
auto http = Http::get(std::move(url));
|
||||
set_auth(http);
|
||||
http.form_add("a", "listModelGroups");
|
||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
|
||||
})
|
||||
.on_complete([&, this](std::string body, unsigned) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got groups: %2%") % name % body;
|
||||
|
||||
try {
|
||||
std::stringstream ss(body);
|
||||
pt::ptree ptree;
|
||||
pt::read_json(ss, ptree);
|
||||
|
||||
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, ptree.get_child("groupNames.")) {
|
||||
if (v.second.data() == "#") {
|
||||
groups.push_back(_utf8(L("Default")));
|
||||
} else {
|
||||
// Is it safe to assume that the data are utf-8 encoded?
|
||||
groups.push_back(wxString::FromUTF8(v.second.data()));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception &) {
|
||||
//msg = "Could not parse server response";
|
||||
res = false;
|
||||
}
|
||||
})
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Repetier::get_printers(wxArrayString& printers) const
|
||||
{
|
||||
const char *name = get_name();
|
||||
|
||||
bool res = true;
|
||||
auto url = make_url("printer/list");
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: List printers at: %2%") % name % url;
|
||||
|
||||
auto http = Http::get(std::move(url));
|
||||
set_auth(http);
|
||||
|
||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error listing printers: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
|
||||
res = false;
|
||||
})
|
||||
.on_complete([&, this](std::string body, unsigned http_status) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got printers: %2%, HTTP status: %3%") % name % body % http_status;
|
||||
|
||||
if (http_status != 200)
|
||||
throw HostNetworkError(GUI::format(_L("HTTP status: %1%\nMessage body: \"%2%\""), http_status, body));
|
||||
|
||||
std::stringstream ss(body);
|
||||
pt::ptree ptree;
|
||||
try {
|
||||
pt::read_json(ss, ptree);
|
||||
} catch (const pt::ptree_error &err) {
|
||||
throw HostNetworkError(GUI::format(_L("Parsing of host response failed.\nMessage body: \"%1%\"\nError: \"%2%\""), body, err.what()));
|
||||
}
|
||||
|
||||
const auto error = ptree.get_optional<std::string>("error");
|
||||
if (error)
|
||||
throw HostNetworkError(*error);
|
||||
|
||||
try {
|
||||
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, ptree.get_child("data.")) {
|
||||
const auto port = v.second.get<std::string>("slug");
|
||||
printers.push_back(Slic3r::GUI::from_u8(port));
|
||||
}
|
||||
} catch (const std::exception &err) {
|
||||
throw HostNetworkError(GUI::format(_L("Enumeration of host printers failed.\nMessage body: \"%1%\"\nError: \"%2%\""), body, err.what()));
|
||||
}
|
||||
})
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
52
src/slic3r/Utils/Repetier.hpp
Normal file
52
src/slic3r/Utils/Repetier.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef slic3r_Repetier_hpp_
|
||||
#define slic3r_Repetier_hpp_
|
||||
|
||||
#include <string>
|
||||
#include <wx/string.h>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "PrintHost.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class DynamicPrintConfig;
|
||||
class Http;
|
||||
|
||||
class Repetier : public PrintHost
|
||||
{
|
||||
public:
|
||||
Repetier(DynamicPrintConfig *config);
|
||||
~Repetier() override = default;
|
||||
|
||||
const char* get_name() const;
|
||||
|
||||
bool test(wxString &curl_msg) const override;
|
||||
wxString get_test_ok_msg () const override;
|
||||
wxString get_test_failed_msg (wxString &msg) const override;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
|
||||
bool has_auto_discovery() const override { return false; }
|
||||
bool can_test() const override { return true; }
|
||||
bool can_start_print() const override { return false; }
|
||||
bool supports_multiple_printers() const override { return true; }
|
||||
std::string get_host() const override { return host; }
|
||||
|
||||
bool get_groups(wxArrayString &groups) const override;
|
||||
bool get_printers(wxArrayString &printers) const override;
|
||||
|
||||
protected:
|
||||
virtual bool validate_version_text(const boost::optional<std::string> &version_text) const;
|
||||
|
||||
private:
|
||||
std::string host;
|
||||
std::string apikey;
|
||||
std::string cafile;
|
||||
std::string port;
|
||||
|
||||
void set_auth(Http &http) const;
|
||||
std::string make_url(const std::string &path) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue