Various improvements to SimplyPrint integration (#4831)
* Allow using BBL's device tab when 3rd party print host is used * Add option to open SimplyPrint panel in device tab after uploading * Fix default print host for prusa connect * Do not set api key in device view when SimplyPrint is used * Sending 3mf file to SimplyPrint when using BBL printers * Fix file extension when uploading 3mf * Prepare for large file uploading * Implement chunk upload * Fix file uploading exceeding content size * Fix wrong field type * Add `temp=true` to all chunk upload calls * Add macro to enable test api * Merge branch 'main' into dev/simplyprint-improve * Fix another missing `temp=true` * Add delete token * Try fixing build error on *nix systems * Merge branch 'main' into dev/simplyprint-improve * Merge branch 'main' into dev/simplyprint-improve * Merge remote-tracking branch 'remote/main' into dev/simplyprint-improve # Conflicts: # src/slic3r/GUI/BackgroundSlicingProcess.cpp * Move the `bbl_use_print_host_webui` option to print host dialog. Also make it a derived option of `print_host_webui` instead. * Merge branch 'main' into dev/simplyprint-improve # Conflicts: # src/slic3r/GUI/MainFrame.cpp # src/slic3r/GUI/Plater.cpp * Merge branch 'main' into dev/simplyprint-improve # Conflicts: # src/slic3r/GUI/Plater.cpp * Use a more generic option instead of SimplyPrint specific * Merge branch 'main' into dev/simplyprint-improve * Merge branch 'main' into dev/simplyprint-improve
This commit is contained in:
parent
b7a0b30578
commit
cd6cd0786f
17 changed files with 505 additions and 110 deletions
|
@ -351,6 +351,20 @@ bool PresetBundle::use_bbl_network()
|
||||||
return use_bbl_network;
|
return use_bbl_network;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PresetBundle::use_bbl_device_tab() {
|
||||||
|
if (!is_bbl_vendor()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use_bbl_network()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto cfg = printers.get_edited_preset().config;
|
||||||
|
// Use bbl device tab if printhost webui url is not set
|
||||||
|
return cfg.opt_string("print_host_webui").empty();
|
||||||
|
}
|
||||||
|
|
||||||
//BBS: load project embedded presets
|
//BBS: load project embedded presets
|
||||||
PresetsConfigSubstitutions PresetBundle::load_project_embedded_presets(std::vector<Preset*> project_presets, ForwardCompatibilitySubstitutionRule substitution_rule)
|
PresetsConfigSubstitutions PresetBundle::load_project_embedded_presets(std::vector<Preset*> project_presets, ForwardCompatibilitySubstitutionRule substitution_rule)
|
||||||
{
|
{
|
||||||
|
|
|
@ -92,7 +92,10 @@ public:
|
||||||
VendorType get_current_vendor_type();
|
VendorType get_current_vendor_type();
|
||||||
// Vendor related handy functions
|
// Vendor related handy functions
|
||||||
bool is_bbl_vendor() { return get_current_vendor_type() == VendorType::Marlin_BBL; }
|
bool is_bbl_vendor() { return get_current_vendor_type() == VendorType::Marlin_BBL; }
|
||||||
|
// Whether using bbl network for print upload
|
||||||
bool use_bbl_network();
|
bool use_bbl_network();
|
||||||
|
// Whether using bbl's device tab
|
||||||
|
bool use_bbl_device_tab();
|
||||||
|
|
||||||
//BBS: project embedded preset logic
|
//BBS: project embedded preset logic
|
||||||
PresetsConfigSubstitutions load_project_embedded_presets(std::vector<Preset*> project_presets, ForwardCompatibilitySubstitutionRule substitution_rule);
|
PresetsConfigSubstitutions load_project_embedded_presets(std::vector<Preset*> project_presets, ForwardCompatibilitySubstitutionRule substitution_rule);
|
||||||
|
|
|
@ -549,7 +549,6 @@ void PrintConfigDef::init_common_params()
|
||||||
def->cli = ConfigOptionDef::nocli;
|
def->cli = ConfigOptionDef::nocli;
|
||||||
def->set_default_value(new ConfigOptionString(""));
|
def->set_default_value(new ConfigOptionString(""));
|
||||||
|
|
||||||
|
|
||||||
def = this->add("printhost_apikey", coString);
|
def = this->add("printhost_apikey", coString);
|
||||||
def->label = L("API Key / Password");
|
def->label = L("API Key / Password");
|
||||||
def->tooltip = L("Orca Slicer can upload G-code files to a printer host. This field should contain "
|
def->tooltip = L("Orca Slicer can upload G-code files to a printer host. This field should contain "
|
||||||
|
|
|
@ -901,23 +901,27 @@ void BackgroundSlicingProcess::prepare_upload()
|
||||||
/ boost::filesystem::unique_path("." SLIC3R_APP_KEY ".upload.%%%%-%%%%-%%%%-%%%%");
|
/ boost::filesystem::unique_path("." SLIC3R_APP_KEY ".upload.%%%%-%%%%-%%%%-%%%%");
|
||||||
|
|
||||||
if (m_print == m_fff_print) {
|
if (m_print == m_fff_print) {
|
||||||
m_print->set_status(95, _utf8(L("Running post-processing scripts")));
|
if (m_upload_job.upload_data.use_3mf) {
|
||||||
std::string error_message;
|
source_path = m_upload_job.upload_data.source_path;
|
||||||
if (copy_file(m_temp_output_path, source_path.string(), error_message) != SUCCESS)
|
} else {
|
||||||
throw Slic3r::RuntimeError(_utf8(L("Copying of the temporary G-code to the output G-code failed")));
|
m_print->set_status(95, _utf8(L("Running post-processing scripts")));
|
||||||
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
std::string error_message;
|
||||||
// Orca: skip post-processing scripts for BBL printers as we have run them already in finalize_gcode()
|
if (copy_file(m_temp_output_path, source_path.string(), error_message) != SUCCESS)
|
||||||
// todo: do we need to copy the file?
|
throw Slic3r::RuntimeError(_utf8(L("Copying of the temporary G-code to the output G-code failed")));
|
||||||
|
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
||||||
|
// Orca: skip post-processing scripts for BBL printers as we have run them already in finalize_gcode()
|
||||||
|
// todo: do we need to copy the file?
|
||||||
|
|
||||||
// Make a copy of the source path, as run_post_process_scripts() is allowed to change it when making a copy of the source file
|
// Make a copy of the source path, as run_post_process_scripts() is allowed to change it when making a copy of the source file
|
||||||
// (not here, but when the final target is a file).
|
// (not here, but when the final target is a file).
|
||||||
if (!m_fff_print->is_BBL_printer()) {
|
if (!m_fff_print->is_BBL_printer()) {
|
||||||
std::string source_path_str = source_path.string();
|
std::string source_path_str = source_path.string();
|
||||||
std::string output_name_str = m_upload_job.upload_data.upload_path.string();
|
std::string output_name_str = m_upload_job.upload_data.upload_path.string();
|
||||||
if (run_post_process_scripts(source_path_str, false, m_upload_job.printhost->get_name(), output_name_str,
|
if (run_post_process_scripts(source_path_str, false, m_upload_job.printhost->get_name(), output_name_str,
|
||||||
m_fff_print->full_print_config()))
|
m_fff_print->full_print_config()))
|
||||||
m_upload_job.upload_data.upload_path = output_name_str;
|
m_upload_job.upload_data.upload_path = output_name_str;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
m_upload_job.upload_data.upload_path = m_sla_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
m_upload_job.upload_data.upload_path = m_sla_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
||||||
|
|
||||||
|
|
|
@ -1615,7 +1615,7 @@ wxBoxSizer* MainFrame::create_side_tools()
|
||||||
SidePopup* p = new SidePopup(this);
|
SidePopup* p = new SidePopup(this);
|
||||||
|
|
||||||
if (wxGetApp().preset_bundle
|
if (wxGetApp().preset_bundle
|
||||||
&& !wxGetApp().preset_bundle->use_bbl_network()) {
|
&& !wxGetApp().preset_bundle->is_bbl_vendor()) {
|
||||||
// ThirdParty Buttons
|
// ThirdParty Buttons
|
||||||
SideButton* export_gcode_btn = new SideButton(p, _L("Export G-code file"), "");
|
SideButton* export_gcode_btn = new SideButton(p, _L("Export G-code file"), "");
|
||||||
export_gcode_btn->SetCornerRadius(0);
|
export_gcode_btn->SetCornerRadius(0);
|
||||||
|
@ -1715,10 +1715,32 @@ wxBoxSizer* MainFrame::create_side_tools()
|
||||||
p->Dismiss();
|
p->Dismiss();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bool support_send = true;
|
||||||
|
bool support_print_all = true;
|
||||||
|
|
||||||
|
const auto preset_bundle = wxGetApp().preset_bundle;
|
||||||
|
if (preset_bundle) {
|
||||||
|
if (preset_bundle->use_bbl_network()) {
|
||||||
|
// BBL network support everything
|
||||||
|
} else {
|
||||||
|
support_send = false; // All 3rd print hosts do not have the send options
|
||||||
|
|
||||||
|
auto cfg = preset_bundle->printers.get_edited_preset().config;
|
||||||
|
const auto host_type = cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
|
||||||
|
|
||||||
|
// Only simply print support uploading all plates
|
||||||
|
support_print_all = host_type == PrintHostType::htSimplyPrint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
p->append_button(print_plate_btn);
|
p->append_button(print_plate_btn);
|
||||||
p->append_button(print_all_btn);
|
if (support_print_all) {
|
||||||
p->append_button(send_to_printer_btn);
|
p->append_button(print_all_btn);
|
||||||
p->append_button(send_to_printer_all_btn);
|
}
|
||||||
|
if (support_send) {
|
||||||
|
p->append_button(send_to_printer_btn);
|
||||||
|
p->append_button(send_to_printer_all_btn);
|
||||||
|
}
|
||||||
if (enable_multi_machine) {
|
if (enable_multi_machine) {
|
||||||
SideButton* print_multi_machine_btn = new SideButton(p, _L("Send to Multi-device"), "");
|
SideButton* print_multi_machine_btn = new SideButton(p, _L("Send to Multi-device"), "");
|
||||||
print_multi_machine_btn->SetCornerRadius(0);
|
print_multi_machine_btn->SetCornerRadius(0);
|
||||||
|
@ -3636,14 +3658,14 @@ void MainFrame::load_printer_url(wxString url, wxString apikey)
|
||||||
void MainFrame::load_printer_url()
|
void MainFrame::load_printer_url()
|
||||||
{
|
{
|
||||||
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
|
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
|
||||||
if (preset_bundle.use_bbl_network())
|
if (preset_bundle.use_bbl_device_tab())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto cfg = preset_bundle.printers.get_edited_preset().config;
|
auto cfg = preset_bundle.printers.get_edited_preset().config;
|
||||||
wxString url = cfg.opt_string("print_host_webui").empty() ? cfg.opt_string("print_host") : cfg.opt_string("print_host_webui");
|
wxString url = cfg.opt_string("print_host_webui").empty() ? cfg.opt_string("print_host") : cfg.opt_string("print_host_webui");
|
||||||
wxString apikey;
|
wxString apikey;
|
||||||
if (cfg.has("printhost_apikey") && (cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value == htPrusaLink ||
|
const auto host_type = cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
|
||||||
cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value == htPrusaConnect))
|
if (cfg.has("printhost_apikey") && (host_type == htPrusaLink || host_type == htPrusaConnect))
|
||||||
apikey = cfg.opt_string("printhost_apikey");
|
apikey = cfg.opt_string("printhost_apikey");
|
||||||
if (!url.empty()) {
|
if (!url.empty()) {
|
||||||
if (!url.Lower().starts_with("http"))
|
if (!url.Lower().starts_with("http"))
|
||||||
|
|
|
@ -130,6 +130,8 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
||||||
this->update_printhost_buttons();
|
this->update_printhost_buttons();
|
||||||
if (opt_key == "printhost_port")
|
if (opt_key == "printhost_port")
|
||||||
this->update_ports();
|
this->update_ports();
|
||||||
|
if (opt_key == "bbl_use_print_host_webui")
|
||||||
|
this->update_webui();
|
||||||
};
|
};
|
||||||
|
|
||||||
m_optgroup->append_single_option_line("host_type");
|
m_optgroup->append_single_option_line("host_type");
|
||||||
|
@ -253,6 +255,19 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
||||||
option.opt.width = Field::def_width_wider();
|
option.opt.width = Field::def_width_wider();
|
||||||
m_optgroup->append_single_option_line(option);
|
m_optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
|
{
|
||||||
|
// For bbl printers, we build a fake option to control whether the original device tab should be used
|
||||||
|
ConfigOptionDef def;
|
||||||
|
def.type = coBool;
|
||||||
|
def.width = Field::def_width();
|
||||||
|
def.label = L("View print host webui in Device tab");
|
||||||
|
def.tooltip = L("Replace the BambuLab's device tab with print host webui");
|
||||||
|
def.set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
|
auto option = Option(def, "bbl_use_print_host_webui");
|
||||||
|
m_optgroup->append_single_option_line(option);
|
||||||
|
}
|
||||||
|
|
||||||
m_optgroup->append_single_option_line("printhost_authorization_type");
|
m_optgroup->append_single_option_line("printhost_authorization_type");
|
||||||
|
|
||||||
option = m_optgroup->get_option("printhost_apikey");
|
option = m_optgroup->get_option("printhost_apikey");
|
||||||
|
@ -402,6 +417,30 @@ void PhysicalPrinterDialog::update_ports() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysicalPrinterDialog::update_webui()
|
||||||
|
{
|
||||||
|
const PrinterTechnology tech = Preset::printer_technology(*m_config);
|
||||||
|
if (tech == ptFFF) {
|
||||||
|
const auto opt = m_config->option<ConfigOptionEnum<PrintHostType>>("host_type");
|
||||||
|
if (opt->value == htSimplyPrint) {
|
||||||
|
bool bbl_use_print_host_webui = false;
|
||||||
|
if (Field* printhost_webui_field = m_optgroup->get_field("bbl_use_print_host_webui"); printhost_webui_field) {
|
||||||
|
if (CheckBox* temp = dynamic_cast<CheckBox*>(printhost_webui_field); temp) {
|
||||||
|
bbl_use_print_host_webui = boost::any_cast<bool>(temp->get_value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string v = bbl_use_print_host_webui ? "https://simplyprint.io/panel" : "";
|
||||||
|
if (Field* printhost_webui_field = m_optgroup->get_field("print_host_webui"); printhost_webui_field) {
|
||||||
|
if (wxTextCtrl* temp = dynamic_cast<TextCtrl*>(printhost_webui_field)->text_ctrl(); temp) {
|
||||||
|
temp->SetValue(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_config->opt_string("print_host_webui") = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PhysicalPrinterDialog::update_printhost_buttons()
|
void PhysicalPrinterDialog::update_printhost_buttons()
|
||||||
{
|
{
|
||||||
std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
|
std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
|
||||||
|
@ -504,7 +543,8 @@ void PhysicalPrinterDialog::update(bool printer_change)
|
||||||
m_optgroup->show_field("host_type");
|
m_optgroup->show_field("host_type");
|
||||||
|
|
||||||
m_optgroup->enable_field("print_host");
|
m_optgroup->enable_field("print_host");
|
||||||
m_optgroup->enable_field("print_host_webui");
|
m_optgroup->show_field("print_host_webui");
|
||||||
|
m_optgroup->hide_field("bbl_use_print_host_webui");
|
||||||
m_optgroup->enable_field("printhost_cafile");
|
m_optgroup->enable_field("printhost_cafile");
|
||||||
m_optgroup->enable_field("printhost_ssl_ignore_revoke");
|
m_optgroup->enable_field("printhost_ssl_ignore_revoke");
|
||||||
if (m_printhost_cafile_browse_btn)
|
if (m_printhost_cafile_browse_btn)
|
||||||
|
@ -516,21 +556,12 @@ void PhysicalPrinterDialog::update(bool printer_change)
|
||||||
const auto current_host = temp->GetValue();
|
const auto current_host = temp->GetValue();
|
||||||
if (current_host == L"https://connect.prusa3d.com" ||
|
if (current_host == L"https://connect.prusa3d.com" ||
|
||||||
current_host == L"https://app.obico.io" ||
|
current_host == L"https://app.obico.io" ||
|
||||||
current_host == "https://simplyprint.io") {
|
current_host == "https://simplyprint.io" || current_host == "https://simplyprint.io/panel") {
|
||||||
temp->SetValue(wxString());
|
temp->SetValue(wxString());
|
||||||
m_config->opt_string("print_host") = "";
|
m_config->opt_string("print_host") = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Field* printhost_webui_field = m_optgroup->get_field("print_host_webui"); printhost_webui_field) {
|
|
||||||
if (wxTextCtrl* temp = dynamic_cast<TextCtrl*>(printhost_webui_field)->text_ctrl(); temp) {
|
|
||||||
const auto current_host = temp->GetValue();
|
|
||||||
if (current_host == "https://simplyprint.io/panel") {
|
|
||||||
temp->SetValue(wxString());
|
|
||||||
m_config->opt_string("print_host_webui") = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opt->value == htPrusaLink) { // PrusaConnect does NOT allow http digest
|
if (opt->value == htPrusaLink) { // PrusaConnect does NOT allow http digest
|
||||||
m_optgroup->show_field("printhost_authorization_type");
|
m_optgroup->show_field("printhost_authorization_type");
|
||||||
AuthorizationType auth_type = m_config->option<ConfigOptionEnum<AuthorizationType>>("printhost_authorization_type")->value;
|
AuthorizationType auth_type = m_config->option<ConfigOptionEnum<AuthorizationType>>("printhost_authorization_type")->value;
|
||||||
|
@ -548,6 +579,7 @@ void PhysicalPrinterDialog::update(bool printer_change)
|
||||||
if (Field* printhost_field = m_optgroup->get_field("print_host"); printhost_field) {
|
if (Field* printhost_field = m_optgroup->get_field("print_host"); printhost_field) {
|
||||||
if (wxTextCtrl* temp = dynamic_cast<TextCtrl*>(printhost_field)->text_ctrl(); temp && temp->GetValue().IsEmpty()) {
|
if (wxTextCtrl* temp = dynamic_cast<TextCtrl*>(printhost_field)->text_ctrl(); temp && temp->GetValue().IsEmpty()) {
|
||||||
temp->SetValue(L"https://connect.prusa3d.com");
|
temp->SetValue(L"https://connect.prusa3d.com");
|
||||||
|
m_config->opt_string("print_host") = "https://connect.prusa3d.com";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (opt->value == htObico) { // automatically show default obico address
|
} else if (opt->value == htObico) { // automatically show default obico address
|
||||||
|
@ -562,17 +594,33 @@ void PhysicalPrinterDialog::update(bool printer_change)
|
||||||
if (Field* printhost_field = m_optgroup->get_field("print_host"); printhost_field) {
|
if (Field* printhost_field = m_optgroup->get_field("print_host"); printhost_field) {
|
||||||
printhost_field->disable();
|
printhost_field->disable();
|
||||||
if (wxTextCtrl* temp = dynamic_cast<TextCtrl*>(printhost_field)->text_ctrl(); temp && temp->GetValue().IsEmpty()) {
|
if (wxTextCtrl* temp = dynamic_cast<TextCtrl*>(printhost_field)->text_ctrl(); temp && temp->GetValue().IsEmpty()) {
|
||||||
temp->SetValue("https://simplyprint.io");
|
|
||||||
}
|
|
||||||
m_config->opt_string("print_host") = "https://simplyprint.io";
|
|
||||||
}
|
|
||||||
if (Field* printhost_webui_field = m_optgroup->get_field("print_host_webui"); printhost_webui_field) {
|
|
||||||
printhost_webui_field->disable();
|
|
||||||
if (wxTextCtrl* temp = dynamic_cast<TextCtrl*>(printhost_webui_field)->text_ctrl(); temp && temp->GetValue().IsEmpty()) {
|
|
||||||
temp->SetValue("https://simplyprint.io/panel");
|
temp->SetValue("https://simplyprint.io/panel");
|
||||||
}
|
}
|
||||||
|
m_config->opt_string("print_host") = "https://simplyprint.io/panel";
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto current_webui = m_config->opt_string("print_host_webui");
|
||||||
|
if (!current_webui.empty()) {
|
||||||
|
if (Field* printhost_webui_field = m_optgroup->get_field("print_host_webui"); printhost_webui_field) {
|
||||||
|
if (wxTextCtrl* temp = dynamic_cast<TextCtrl*>(printhost_webui_field)->text_ctrl(); temp) {
|
||||||
|
temp->SetValue("https://simplyprint.io/panel");
|
||||||
|
}
|
||||||
|
}
|
||||||
m_config->opt_string("print_host_webui") = "https://simplyprint.io/panel";
|
m_config->opt_string("print_host_webui") = "https://simplyprint.io/panel";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For bbl printers, show option to control the device tab
|
||||||
|
if (wxGetApp().preset_bundle->is_bbl_vendor()) {
|
||||||
|
m_optgroup->show_field("bbl_use_print_host_webui");
|
||||||
|
const bool use_print_host_webui = !current_webui.empty();
|
||||||
|
if (Field* printhost_webui_field = m_optgroup->get_field("bbl_use_print_host_webui"); printhost_webui_field) {
|
||||||
|
if (CheckBox* temp = dynamic_cast<CheckBox*>(printhost_webui_field); temp) {
|
||||||
|
temp->set_value(use_print_host_webui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_optgroup->hide_field("print_host_webui");
|
||||||
m_optgroup->hide_field("printhost_apikey");
|
m_optgroup->hide_field("printhost_apikey");
|
||||||
m_optgroup->disable_field("printhost_cafile");
|
m_optgroup->disable_field("printhost_cafile");
|
||||||
m_optgroup->disable_field("printhost_ssl_ignore_revoke");
|
m_optgroup->disable_field("printhost_ssl_ignore_revoke");
|
||||||
|
|
|
@ -63,6 +63,7 @@ public:
|
||||||
void update_printhost_buttons();
|
void update_printhost_buttons();
|
||||||
void update_printers();
|
void update_printers();
|
||||||
void update_ports();
|
void update_ports();
|
||||||
|
void update_webui();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||||
|
|
|
@ -1181,12 +1181,11 @@ void Sidebar::update_all_preset_comboboxes()
|
||||||
const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology();
|
const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology();
|
||||||
|
|
||||||
bool is_bbl_vendor = preset_bundle.is_bbl_vendor();
|
bool is_bbl_vendor = preset_bundle.is_bbl_vendor();
|
||||||
const bool use_bbl_network = preset_bundle.use_bbl_network();
|
|
||||||
|
|
||||||
auto p_mainframe = wxGetApp().mainframe;
|
auto p_mainframe = wxGetApp().mainframe;
|
||||||
auto cfg = preset_bundle.printers.get_edited_preset().config;
|
auto cfg = preset_bundle.printers.get_edited_preset().config;
|
||||||
|
|
||||||
if (use_bbl_network) {
|
if (preset_bundle.use_bbl_network()) {
|
||||||
//only show connection button for not-BBL printer
|
//only show connection button for not-BBL printer
|
||||||
connection_btn->Hide();
|
connection_btn->Hide();
|
||||||
//only show sync-ams button for BBL printer
|
//only show sync-ams button for BBL printer
|
||||||
|
@ -1204,9 +1203,10 @@ void Sidebar::update_all_preset_comboboxes()
|
||||||
else {
|
else {
|
||||||
if (!url.Lower().starts_with("http"))
|
if (!url.Lower().starts_with("http"))
|
||||||
url = wxString::Format("http://%s", url);
|
url = wxString::Format("http://%s", url);
|
||||||
if (cfg.has("printhost_apikey"))
|
const auto host_type = cfg.option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
|
||||||
|
if (cfg.has("printhost_apikey") && (host_type != htSimplyPrint))
|
||||||
apikey = cfg.opt_string("printhost_apikey");
|
apikey = cfg.opt_string("printhost_apikey");
|
||||||
print_btn_type = MainFrame::PrintSelectType::eSendGcode;
|
print_btn_type = preset_bundle.is_bbl_vendor() ? MainFrame::PrintSelectType::ePrintPlate : MainFrame::PrintSelectType::eSendGcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
p_mainframe->load_printer_url(url, apikey);
|
p_mainframe->load_printer_url(url, apikey);
|
||||||
|
@ -1250,7 +1250,7 @@ void Sidebar::update_all_preset_comboboxes()
|
||||||
p->combo_printer->update();
|
p->combo_printer->update();
|
||||||
|
|
||||||
// Orca:: show device tab based on vendor type
|
// Orca:: show device tab based on vendor type
|
||||||
p_mainframe->show_device(use_bbl_network);
|
p_mainframe->show_device(preset_bundle.use_bbl_device_tab());
|
||||||
p_mainframe->m_tabpanel->SetSelection(p_mainframe->m_tabpanel->GetSelection());
|
p_mainframe->m_tabpanel->SetSelection(p_mainframe->m_tabpanel->GetSelection());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7069,12 +7069,18 @@ void Plater::priv::on_action_print_plate(SimpleEvent&)
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":received print plate event\n" ;
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":received print plate event\n" ;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BBS
|
PresetBundle& preset_bundle = *wxGetApp().preset_bundle;
|
||||||
if (!m_select_machine_dlg) m_select_machine_dlg = new SelectMachineDialog(q);
|
if (preset_bundle.use_bbl_network()) {
|
||||||
m_select_machine_dlg->set_print_type(PrintFromType::FROM_NORMAL);
|
// BBS
|
||||||
m_select_machine_dlg->prepare(partplate_list.get_curr_plate_index());
|
if (!m_select_machine_dlg)
|
||||||
m_select_machine_dlg->ShowModal();
|
m_select_machine_dlg = new SelectMachineDialog(q);
|
||||||
record_start_print_preset("print_plate");
|
m_select_machine_dlg->set_print_type(PrintFromType::FROM_NORMAL);
|
||||||
|
m_select_machine_dlg->prepare(partplate_list.get_curr_plate_index());
|
||||||
|
m_select_machine_dlg->ShowModal();
|
||||||
|
record_start_print_preset("print_plate");
|
||||||
|
} else {
|
||||||
|
q->send_gcode_legacy(PLATE_CURRENT_IDX, nullptr, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::on_action_send_to_multi_machine(SimpleEvent&)
|
void Plater::priv::on_action_send_to_multi_machine(SimpleEvent&)
|
||||||
|
@ -7104,7 +7110,7 @@ void Plater::priv::on_tab_selection_changing(wxBookCtrlEvent& e)
|
||||||
sidebar_layout.show = new_sel == MainFrame::tp3DEditor || new_sel == MainFrame::tpPreview;
|
sidebar_layout.show = new_sel == MainFrame::tp3DEditor || new_sel == MainFrame::tpPreview;
|
||||||
update_sidebar();
|
update_sidebar();
|
||||||
int old_sel = e.GetOldSelection();
|
int old_sel = e.GetOldSelection();
|
||||||
if (wxGetApp().preset_bundle && wxGetApp().preset_bundle->use_bbl_network() && new_sel == MainFrame::tpMonitor) {
|
if (wxGetApp().preset_bundle && wxGetApp().preset_bundle->use_bbl_device_tab() && new_sel == MainFrame::tpMonitor) {
|
||||||
if (!wxGetApp().getAgent()) {
|
if (!wxGetApp().getAgent()) {
|
||||||
e.Veto();
|
e.Veto();
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("skipped tab switch from %1% to %2%, lack of network plugins") % old_sel % new_sel;
|
BOOST_LOG_TRIVIAL(info) << boost::format("skipped tab switch from %1% to %2%, lack of network plugins") % old_sel % new_sel;
|
||||||
|
@ -7159,12 +7165,18 @@ void Plater::priv::on_action_print_all(SimpleEvent&)
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":received print all event\n" ;
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":received print all event\n" ;
|
||||||
}
|
}
|
||||||
|
|
||||||
//BBS
|
PresetBundle& preset_bundle = *wxGetApp().preset_bundle;
|
||||||
if (!m_select_machine_dlg) m_select_machine_dlg = new SelectMachineDialog(q);
|
if (preset_bundle.use_bbl_network()) {
|
||||||
m_select_machine_dlg->set_print_type(PrintFromType::FROM_NORMAL);
|
// BBS
|
||||||
m_select_machine_dlg->prepare(PLATE_ALL_IDX);
|
if (!m_select_machine_dlg)
|
||||||
m_select_machine_dlg->ShowModal();
|
m_select_machine_dlg = new SelectMachineDialog(q);
|
||||||
record_start_print_preset("print_all");
|
m_select_machine_dlg->set_print_type(PrintFromType::FROM_NORMAL);
|
||||||
|
m_select_machine_dlg->prepare(PLATE_ALL_IDX);
|
||||||
|
m_select_machine_dlg->ShowModal();
|
||||||
|
record_start_print_preset("print_all");
|
||||||
|
} else {
|
||||||
|
q->send_gcode_legacy(PLATE_ALL_IDX, nullptr, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::on_action_export_gcode(SimpleEvent&)
|
void Plater::priv::on_action_export_gcode(SimpleEvent&)
|
||||||
|
@ -12332,7 +12344,7 @@ void Plater::reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &
|
||||||
// and let the background processing start.
|
// and let the background processing start.
|
||||||
this->p->restart_background_process(state | priv::UPDATE_BACKGROUND_PROCESS_FORCE_RESTART);
|
this->p->restart_background_process(state | priv::UPDATE_BACKGROUND_PROCESS_FORCE_RESTART);
|
||||||
}
|
}
|
||||||
void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn)
|
void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn, bool use_3mf)
|
||||||
{
|
{
|
||||||
// if physical_printer is selected, send gcode for this printer
|
// if physical_printer is selected, send gcode for this printer
|
||||||
// DynamicPrintConfig* physical_printer_config = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config();
|
// DynamicPrintConfig* physical_printer_config = wxGetApp().preset_bundle->physical_printers.get_selected_printer_config();
|
||||||
|
@ -12344,6 +12356,8 @@ void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn)
|
||||||
if (upload_job.empty())
|
if (upload_job.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
upload_job.upload_data.use_3mf = use_3mf;
|
||||||
|
|
||||||
// Obtain default output path
|
// Obtain default output path
|
||||||
fs::path default_output_file;
|
fs::path default_output_file;
|
||||||
try {
|
try {
|
||||||
|
@ -12362,6 +12376,9 @@ void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn)
|
||||||
return;
|
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()));
|
||||||
|
if (use_3mf) {
|
||||||
|
default_output_file.replace_extension("3mf");
|
||||||
|
}
|
||||||
|
|
||||||
// Repetier specific: Query the server for the list of file groups.
|
// Repetier specific: Query the server for the list of file groups.
|
||||||
wxArrayString groups;
|
wxArrayString groups;
|
||||||
|
@ -12383,8 +12400,11 @@ void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups, storage_paths, storage_names);
|
auto config = get_app_config();
|
||||||
|
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->get_post_upload_actions(), groups, storage_paths, storage_names, config->get_bool("open_device_tab_post_upload"));
|
||||||
if (dlg.ShowModal() == wxID_OK) {
|
if (dlg.ShowModal() == wxID_OK) {
|
||||||
|
config->set_bool("open_device_tab_post_upload", dlg.switch_to_device_tab());
|
||||||
|
upload_job.switch_to_device_tab = dlg.switch_to_device_tab();
|
||||||
upload_job.upload_data.upload_path = dlg.filename();
|
upload_job.upload_data.upload_path = dlg.filename();
|
||||||
upload_job.upload_data.post_action = dlg.post_action();
|
upload_job.upload_data.post_action = dlg.post_action();
|
||||||
upload_job.upload_data.group = dlg.group();
|
upload_job.upload_data.group = dlg.group();
|
||||||
|
@ -12397,6 +12417,19 @@ void Plater::send_gcode_legacy(int plate_idx, Export3mfProgressFn proFn)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (use_3mf) {
|
||||||
|
// Process gcode
|
||||||
|
const int result = send_gcode(plate_idx, nullptr);
|
||||||
|
|
||||||
|
if (result < 0) {
|
||||||
|
wxString msg = _L("Abnormal print file data. Please slice again");
|
||||||
|
show_error(this, msg, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
upload_job.upload_data.source_path = p->m_print_job_data._3mf_path;
|
||||||
|
}
|
||||||
|
|
||||||
p->export_gcode(fs::path(), false, std::move(upload_job));
|
p->export_gcode(fs::path(), false, std::move(upload_job));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -431,7 +431,7 @@ public:
|
||||||
/* -1: send current gcode if not specified
|
/* -1: send current gcode if not specified
|
||||||
* -2: send all gcode to target machine */
|
* -2: send all gcode to target machine */
|
||||||
int send_gcode(int plate_idx = -1, Export3mfProgressFn proFn = nullptr);
|
int send_gcode(int plate_idx = -1, Export3mfProgressFn proFn = nullptr);
|
||||||
void send_gcode_legacy(int plate_idx = -1, Export3mfProgressFn proFn = nullptr);
|
void send_gcode_legacy(int plate_idx = -1, Export3mfProgressFn proFn = nullptr, bool use_3mf = false);
|
||||||
int export_config_3mf(int plate_idx = -1, Export3mfProgressFn proFn = nullptr);
|
int export_config_3mf(int plate_idx = -1, Export3mfProgressFn proFn = nullptr);
|
||||||
//BBS jump to nonitor after print job finished
|
//BBS jump to nonitor after print job finished
|
||||||
void send_calibration_job_finished(wxCommandEvent &evt);
|
void send_calibration_job_finished(wxCommandEvent &evt);
|
||||||
|
|
|
@ -38,7 +38,7 @@ static const char *CONFIG_KEY_PATH = "printhost_path";
|
||||||
static const char *CONFIG_KEY_GROUP = "printhost_group";
|
static const char *CONFIG_KEY_GROUP = "printhost_group";
|
||||||
static const char* CONFIG_KEY_STORAGE = "printhost_storage";
|
static const char* CONFIG_KEY_STORAGE = "printhost_storage";
|
||||||
|
|
||||||
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUploadActions post_actions, const wxArrayString &groups, const wxArrayString& storage_paths, const wxArrayString& storage_names)
|
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUploadActions post_actions, const wxArrayString &groups, const wxArrayString& storage_paths, const wxArrayString& storage_names, bool switch_to_device_tab)
|
||||||
: MsgDialog(static_cast<wxWindow*>(wxGetApp().mainframe), _L("Send G-Code to printer host"), _L("Upload to Printer Host with the following filename:"), 0) // Set style = 0 to avoid default creation of the "OK" button.
|
: MsgDialog(static_cast<wxWindow*>(wxGetApp().mainframe), _L("Send G-Code to printer host"), _L("Upload to Printer Host with the following filename:"), 0) // Set style = 0 to avoid default creation of the "OK" button.
|
||||||
// All buttons will be added later in this constructor
|
// All buttons will be added later in this constructor
|
||||||
, txt_filename(new wxTextCtrl(this, wxID_ANY))
|
, txt_filename(new wxTextCtrl(this, wxID_ANY))
|
||||||
|
@ -46,6 +46,7 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo
|
||||||
, combo_storage(storage_names.GetCount() > 1 ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, storage_names, wxCB_READONLY) : nullptr)
|
, combo_storage(storage_names.GetCount() > 1 ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, storage_names, wxCB_READONLY) : nullptr)
|
||||||
, post_upload_action(PrintHostPostUploadAction::None)
|
, post_upload_action(PrintHostPostUploadAction::None)
|
||||||
, m_paths(storage_paths)
|
, m_paths(storage_paths)
|
||||||
|
, m_switch_to_device_tab(switch_to_device_tab)
|
||||||
{
|
{
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
txt_filename->OSXDisableAllSmartSubstitutions();
|
txt_filename->OSXDisableAllSmartSubstitutions();
|
||||||
|
@ -97,6 +98,22 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, PrintHostPostUplo
|
||||||
|
|
||||||
txt_filename->SetValue(recent_path);
|
txt_filename->SetValue(recent_path);
|
||||||
|
|
||||||
|
auto checkbox_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
auto checkbox = new ::CheckBox(this, wxID_APPLY);
|
||||||
|
checkbox->SetValue(m_switch_to_device_tab);
|
||||||
|
checkbox->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& e) {
|
||||||
|
m_switch_to_device_tab = e.IsChecked();
|
||||||
|
e.Skip();
|
||||||
|
});
|
||||||
|
checkbox_sizer->Add(checkbox, 0, wxALL | wxALIGN_CENTER, FromDIP(2));
|
||||||
|
|
||||||
|
auto checkbox_text = new wxStaticText(this, wxID_ANY, _L("Switch to Device tab after upload."), wxDefaultPosition, wxDefaultSize, 0);
|
||||||
|
checkbox_sizer->Add(checkbox_text, 0, wxALL | wxALIGN_CENTER, FromDIP(2));
|
||||||
|
checkbox_text->SetFont(::Label::Body_13);
|
||||||
|
checkbox_text->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#323A3D")));
|
||||||
|
content_sizer->Add(checkbox_sizer);
|
||||||
|
content_sizer->AddSpacer(VERT_SPACING);
|
||||||
|
|
||||||
if (size_t extension_start = recent_path.find_last_of('.'); extension_start != std::string::npos)
|
if (size_t extension_start = recent_path.find_last_of('.'); extension_start != std::string::npos)
|
||||||
m_valid_suffix = recent_path.substr(extension_start);
|
m_valid_suffix = recent_path.substr(extension_start);
|
||||||
// .gcode suffix control
|
// .gcode suffix control
|
||||||
|
|
|
@ -26,11 +26,12 @@ namespace GUI {
|
||||||
class PrintHostSendDialog : public GUI::MsgDialog
|
class PrintHostSendDialog : public GUI::MsgDialog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PrintHostSendDialog(const boost::filesystem::path &path, PrintHostPostUploadActions post_actions, const wxArrayString& groups, const wxArrayString& storage_paths, const wxArrayString& storage_names);
|
PrintHostSendDialog(const boost::filesystem::path &path, PrintHostPostUploadActions post_actions, const wxArrayString& groups, const wxArrayString& storage_paths, const wxArrayString& storage_names, bool switch_to_device_tab);
|
||||||
boost::filesystem::path filename() const;
|
boost::filesystem::path filename() const;
|
||||||
PrintHostPostUploadAction post_action() const;
|
PrintHostPostUploadAction post_action() const;
|
||||||
std::string group() const;
|
std::string group() const;
|
||||||
std::string storage() const;
|
std::string storage() const;
|
||||||
|
bool switch_to_device_tab() const {return m_switch_to_device_tab;}
|
||||||
|
|
||||||
virtual void EndModal(int ret) override;
|
virtual void EndModal(int ret) override;
|
||||||
private:
|
private:
|
||||||
|
@ -41,6 +42,7 @@ private:
|
||||||
wxString m_valid_suffix;
|
wxString m_valid_suffix;
|
||||||
wxString m_preselected_storage;
|
wxString m_preselected_storage;
|
||||||
wxArrayString m_paths;
|
wxArrayString m_paths;
|
||||||
|
bool m_switch_to_device_tab;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,13 @@ std::unique_ptr<CurlGlobalInit> CurlGlobalInit::instance;
|
||||||
std::map<std::string, std::string> extra_headers;
|
std::map<std::string, std::string> extra_headers;
|
||||||
std::mutex g_mutex;
|
std::mutex g_mutex;
|
||||||
|
|
||||||
|
struct form_file
|
||||||
|
{
|
||||||
|
fs::ifstream ifs;
|
||||||
|
boost::filesystem::ifstream::off_type init_offset;
|
||||||
|
size_t content_length;
|
||||||
|
};
|
||||||
|
|
||||||
struct Http::priv
|
struct Http::priv
|
||||||
{
|
{
|
||||||
enum {
|
enum {
|
||||||
|
@ -103,7 +110,7 @@ struct Http::priv
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
// Used for storing file streams added as multipart form parts
|
// Used for storing file streams added as multipart form parts
|
||||||
// Using a deque here because unlike vector it doesn't ivalidate pointers on insertion
|
// Using a deque here because unlike vector it doesn't ivalidate pointers on insertion
|
||||||
std::deque<fs::ifstream> form_files;
|
std::deque<form_file> form_files;
|
||||||
std::string postfields;
|
std::string postfields;
|
||||||
std::string error_buffer; // Used for CURLOPT_ERRORBUFFER
|
std::string error_buffer; // Used for CURLOPT_ERRORBUFFER
|
||||||
std::string headers;
|
std::string headers;
|
||||||
|
@ -130,7 +137,7 @@ struct Http::priv
|
||||||
|
|
||||||
void set_timeout_connect(long timeout);
|
void set_timeout_connect(long timeout);
|
||||||
void set_timeout_max(long timeout);
|
void set_timeout_max(long timeout);
|
||||||
void form_add_file(const char *name, const fs::path &path, const char* filename);
|
void form_add_file(const char *name, const fs::path &path, const char* filename, boost::filesystem::ifstream::off_type offset, size_t length);
|
||||||
/* mime */
|
/* mime */
|
||||||
void mime_form_add_text(const char* name, const char* value);
|
void mime_form_add_text(const char* name, const char* value);
|
||||||
void mime_form_add_file(const char* name, const char* path);
|
void mime_form_add_file(const char* name, const char* path);
|
||||||
|
@ -254,15 +261,27 @@ int Http::priv::xfercb_legacy(void *userp, double dltotal, double dlnow, double
|
||||||
|
|
||||||
size_t Http::priv::form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp)
|
size_t Http::priv::form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp)
|
||||||
{
|
{
|
||||||
auto stream = reinterpret_cast<fs::ifstream*>(userp);
|
auto f = reinterpret_cast<form_file*>(userp);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stream->read(buffer, size * nitems);
|
size_t max_read_size = size * nitems;
|
||||||
|
if (f->content_length == 0) {
|
||||||
|
// Unlimited
|
||||||
|
f->ifs.read(buffer, max_read_size);
|
||||||
|
} else {
|
||||||
|
unsigned long long read_size = f->ifs.tellg() - f->init_offset;
|
||||||
|
if (read_size >= f->content_length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
max_read_size = std::min(max_read_size, size_t(f->content_length - read_size));
|
||||||
|
f->ifs.read(buffer, max_read_size);
|
||||||
|
}
|
||||||
} catch (const std::exception &) {
|
} catch (const std::exception &) {
|
||||||
return CURL_READFUNC_ABORT;
|
return CURL_READFUNC_ABORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return stream->gcount();
|
return f->ifs.gcount();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Http::priv::headers_cb(char *buffer, size_t size, size_t nitems, void *userp)
|
size_t Http::priv::headers_cb(char *buffer, size_t size, size_t nitems, void *userp)
|
||||||
|
@ -286,7 +305,7 @@ void Http::priv::set_timeout_max(long timeout)
|
||||||
::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
|
::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Http::priv::form_add_file(const char *name, const fs::path &path, const char* filename)
|
void Http::priv::form_add_file(const char *name, const fs::path &path, const char* filename, boost::filesystem::ifstream::off_type offset, size_t length)
|
||||||
{
|
{
|
||||||
// We can't use CURLFORM_FILECONTENT, because curl doesn't support Unicode filenames on Windows
|
// We can't use CURLFORM_FILECONTENT, because curl doesn't support Unicode filenames on Windows
|
||||||
// and so we use CURLFORM_STREAM with boost ifstream to read the file.
|
// and so we use CURLFORM_STREAM with boost ifstream to read the file.
|
||||||
|
@ -295,18 +314,21 @@ void Http::priv::form_add_file(const char *name, const fs::path &path, const cha
|
||||||
filename = path.string().c_str();
|
filename = path.string().c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
form_files.emplace_back(path, std::ios::in | std::ios::binary);
|
form_files.emplace_back(form_file{{path, std::ios::in | std::ios::binary}, offset, length});
|
||||||
auto &stream = form_files.back();
|
auto &f = form_files.back();
|
||||||
stream.seekg(0, std::ios::end);
|
size_t size = length;
|
||||||
size_t size = stream.tellg();
|
if (length == 0) {
|
||||||
stream.seekg(0);
|
f.ifs.seekg(0, std::ios::end);
|
||||||
|
size = f.ifs.tellg() - offset;
|
||||||
|
}
|
||||||
|
f.ifs.seekg(offset);
|
||||||
|
|
||||||
if (filename != nullptr) {
|
if (filename != nullptr) {
|
||||||
::curl_formadd(&form, &form_end,
|
::curl_formadd(&form, &form_end,
|
||||||
CURLFORM_COPYNAME, name,
|
CURLFORM_COPYNAME, name,
|
||||||
CURLFORM_FILENAME, filename,
|
CURLFORM_FILENAME, filename,
|
||||||
CURLFORM_CONTENTTYPE, "application/octet-stream",
|
CURLFORM_CONTENTTYPE, "application/octet-stream",
|
||||||
CURLFORM_STREAM, static_cast<void*>(&stream),
|
CURLFORM_STREAM, static_cast<void*>(&f),
|
||||||
CURLFORM_CONTENTSLENGTH, static_cast<long>(size),
|
CURLFORM_CONTENTSLENGTH, static_cast<long>(size),
|
||||||
CURLFORM_END
|
CURLFORM_END
|
||||||
);
|
);
|
||||||
|
@ -587,9 +609,9 @@ Http& Http::form_add(const std::string &name, const std::string &contents)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Http& Http::form_add_file(const std::string &name, const fs::path &path)
|
Http& Http::form_add_file(const std::string &name, const fs::path &path, boost::filesystem::ifstream::off_type offset, size_t length)
|
||||||
{
|
{
|
||||||
if (p) { p->form_add_file(name.c_str(), path.c_str(), nullptr); }
|
if (p) { p->form_add_file(name.c_str(), path.c_str(), nullptr, offset, length); }
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,15 +629,15 @@ Http& Http::mime_form_add_file(std::string &name, const char* path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Http& Http::form_add_file(const std::wstring& name, const fs::path& path)
|
Http& Http::form_add_file(const std::wstring& name, const fs::path& path, boost::filesystem::ifstream::off_type offset, size_t length)
|
||||||
{
|
{
|
||||||
if (p) { p->form_add_file((char*)name.c_str(), path.c_str(), nullptr); }
|
if (p) { p->form_add_file((char*)name.c_str(), path.c_str(), nullptr, offset, length); }
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Http& Http::form_add_file(const std::string &name, const fs::path &path, const std::string &filename)
|
Http& Http::form_add_file(const std::string &name, const fs::path &path, const std::string &filename, boost::filesystem::ifstream::off_type offset, size_t length)
|
||||||
{
|
{
|
||||||
if (p) { p->form_add_file(name.c_str(), path.c_str(), filename.c_str()); }
|
if (p) { p->form_add_file(name.c_str(), path.c_str(), filename.c_str(), offset, length); }
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,15 +121,15 @@ public:
|
||||||
// Add a HTTP multipart form field
|
// Add a HTTP multipart form field
|
||||||
Http& form_add(const std::string &name, const std::string &contents);
|
Http& form_add(const std::string &name, const std::string &contents);
|
||||||
// Add a HTTP multipart form file data contents, `name` is the name of the part
|
// Add a HTTP multipart form file data contents, `name` is the name of the part
|
||||||
Http& form_add_file(const std::string &name, const boost::filesystem::path &path);
|
Http& form_add_file(const std::string &name, const boost::filesystem::path &path, boost::filesystem::ifstream::off_type offset = 0, size_t length = 0);
|
||||||
// Add a HTTP mime form field
|
// Add a HTTP mime form field
|
||||||
Http& mime_form_add_text(std::string& name, std::string& value);
|
Http& mime_form_add_text(std::string& name, std::string& value);
|
||||||
// Add a HTTP mime form file
|
// Add a HTTP mime form file
|
||||||
Http& mime_form_add_file(std::string& name, const char* path);
|
Http& mime_form_add_file(std::string& name, const char* path);
|
||||||
// Same as above except also override the file's filename with a wstring type
|
// Same as above except also override the file's filename with a wstring type
|
||||||
Http& form_add_file(const std::wstring& name, const boost::filesystem::path& path);
|
Http& form_add_file(const std::wstring& name, const boost::filesystem::path& path, boost::filesystem::ifstream::off_type offset = 0, size_t length = 0);
|
||||||
// Same as above except also override the file's filename with a custom one
|
// Same as above except also override the file's filename with a custom one
|
||||||
Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename);
|
Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename, boost::filesystem::ifstream::off_type offset = 0, size_t length = 0);
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
// Tells libcurl to ignore certificate revocation checks in case of missing or offline distribution points for those SSL backends where such behavior is present.
|
// Tells libcurl to ignore certificate revocation checks in case of missing or offline distribution points for those SSL backends where such behavior is present.
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "MKS.hpp"
|
#include "MKS.hpp"
|
||||||
#include "ESP3D.hpp"
|
#include "ESP3D.hpp"
|
||||||
#include "../GUI/PrintHostDialogs.hpp"
|
#include "../GUI/PrintHostDialogs.hpp"
|
||||||
|
#include "../GUI/MainFrame.hpp"
|
||||||
#include "Obico.hpp"
|
#include "Obico.hpp"
|
||||||
#include "Flashforge.hpp"
|
#include "Flashforge.hpp"
|
||||||
#include "SimplyPrint.hpp"
|
#include "SimplyPrint.hpp"
|
||||||
|
@ -314,6 +315,10 @@ void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
emit_progress(100);
|
emit_progress(100);
|
||||||
|
if (the_job.switch_to_device_tab) {
|
||||||
|
const auto mainframe = GUI::wxGetApp().mainframe;
|
||||||
|
mainframe->request_select_tab(MainFrame::TabPosition::tpMonitor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ ENABLE_ENUM_BITMASK_OPERATORS(PrintHostPostUploadAction);
|
||||||
|
|
||||||
struct PrintHostUpload
|
struct PrintHostUpload
|
||||||
{
|
{
|
||||||
|
bool use_3mf;
|
||||||
boost::filesystem::path source_path;
|
boost::filesystem::path source_path;
|
||||||
boost::filesystem::path upload_path;
|
boost::filesystem::path upload_path;
|
||||||
|
|
||||||
|
@ -85,6 +86,7 @@ struct PrintHostJob
|
||||||
{
|
{
|
||||||
PrintHostUpload upload_data;
|
PrintHostUpload upload_data;
|
||||||
std::unique_ptr<PrintHost> printhost;
|
std::unique_ptr<PrintHost> printhost;
|
||||||
|
bool switch_to_device_tab{false};
|
||||||
bool cancelled = false;
|
bool cancelled = false;
|
||||||
|
|
||||||
PrintHostJob() {}
|
PrintHostJob() {}
|
||||||
|
@ -92,6 +94,7 @@ struct PrintHostJob
|
||||||
PrintHostJob(PrintHostJob &&other)
|
PrintHostJob(PrintHostJob &&other)
|
||||||
: upload_data(std::move(other.upload_data))
|
: upload_data(std::move(other.upload_data))
|
||||||
, printhost(std::move(other.printhost))
|
, printhost(std::move(other.printhost))
|
||||||
|
, switch_to_device_tab(other.switch_to_device_tab)
|
||||||
, cancelled(other.cancelled)
|
, cancelled(other.cancelled)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -103,7 +106,8 @@ struct PrintHostJob
|
||||||
PrintHostJob& operator=(PrintHostJob &&other)
|
PrintHostJob& operator=(PrintHostJob &&other)
|
||||||
{
|
{
|
||||||
upload_data = std::move(other.upload_data);
|
upload_data = std::move(other.upload_data);
|
||||||
printhost = std::move(other.printhost);
|
printhost = std::move(other.printhost);
|
||||||
|
switch_to_device_tab = other.switch_to_device_tab;
|
||||||
cancelled = other.cancelled;
|
cancelled = other.cancelled;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,17 +9,36 @@
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "slic3r/GUI/format.hpp"
|
#include "slic3r/GUI/format.hpp"
|
||||||
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
|
#include "slic3r/GUI/MainFrame.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
// to make testing easier
|
||||||
|
//#define SIMPLYPRINT_TEST
|
||||||
|
|
||||||
|
#ifdef SIMPLYPRINT_TEST
|
||||||
|
#define URL_BASE_HOME "https://test.simplyprint.io"
|
||||||
|
#define URL_BASE_API "https://testapi.simplyprint.io"
|
||||||
|
#else
|
||||||
|
#define URL_BASE_HOME "https://simplyprint.io"
|
||||||
|
#define URL_BASE_API "https://api.simplyprint.io"
|
||||||
|
#endif
|
||||||
|
|
||||||
static constexpr boost::asio::ip::port_type CALLBACK_PORT = 21328;
|
static constexpr boost::asio::ip::port_type CALLBACK_PORT = 21328;
|
||||||
static const std::string CALLBACK_URL = "http://localhost:21328/callback";
|
static const std::string CALLBACK_URL = "http://localhost:21328/callback";
|
||||||
static const std::string RESPONSE_TYPE = "code";
|
static const std::string RESPONSE_TYPE = "code";
|
||||||
static const std::string CLIENT_ID = "simplyprintorcaslicer";
|
static const std::string CLIENT_ID = "simplyprintorcaslicer";
|
||||||
static const std::string CLIENT_SCOPES = "user.read files.temp_upload";
|
static const std::string CLIENT_SCOPES = "user.read files.temp_upload";
|
||||||
static const std::string OAUTH_CREDENTIAL_PATH = "simplyprint_oauth.json";
|
static const std::string OAUTH_CREDENTIAL_PATH = "simplyprint_oauth.json";
|
||||||
static const std::string TOKEN_URL = "https://simplyprint.io/api/oauth2/Token";
|
static const std::string TOKEN_URL = URL_BASE_API"/oauth2/Token";
|
||||||
|
#ifdef SIMPLYPRINT_TEST
|
||||||
|
static constexpr uint64_t MAX_SINGLE_UPLOAD_FILE_SIZE = 100000ull; // Max file size that can be uploaded in a single http request
|
||||||
|
#else
|
||||||
|
static constexpr uint64_t MAX_SINGLE_UPLOAD_FILE_SIZE = 100000000ull; // Max file size that can be uploaded in a single http request
|
||||||
|
#endif
|
||||||
|
static const std::string CHUNCK_RECEIVE_URL = URL_BASE_API"/0/files/ChunkReceive";
|
||||||
|
|
||||||
static std::string generate_verification_code(int code_length = 32)
|
static std::string generate_verification_code(int code_length = 32)
|
||||||
{
|
{
|
||||||
|
@ -57,6 +76,9 @@ static std::string url_encode(const std::vector<std::pair<std::string, std::stri
|
||||||
q.reserve(query.size());
|
q.reserve(query.size());
|
||||||
|
|
||||||
std::transform(query.begin(), query.end(), std::back_inserter(q), [](const auto& kv) {
|
std::transform(query.begin(), query.end(), std::back_inserter(q), [](const auto& kv) {
|
||||||
|
if (kv.second.empty()) {
|
||||||
|
return Http::url_encode(kv.first);
|
||||||
|
}
|
||||||
return Http::url_encode(kv.first) + "=" + Http::url_encode(kv.second);
|
return Http::url_encode(kv.first) + "=" + Http::url_encode(kv.second);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -65,6 +87,19 @@ static std::string url_encode(const std::vector<std::pair<std::string, std::stri
|
||||||
|
|
||||||
static void set_auth(Http& http, const std::string& access_token) { http.header("Authorization", "Bearer " + access_token); }
|
static void set_auth(Http& http, const std::string& access_token) { http.header("Authorization", "Bearer " + access_token); }
|
||||||
|
|
||||||
|
static bool should_open_in_external_browser()
|
||||||
|
{
|
||||||
|
const auto& app = wxGetApp();
|
||||||
|
|
||||||
|
if (app.preset_bundle->use_bbl_device_tab()) {
|
||||||
|
// When using bbl device tab, we always need to open external browser
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, if user choose to switch to device tab, then don't bother opening external browser
|
||||||
|
return !app.app_config->get_bool("open_device_tab_post_upload");
|
||||||
|
}
|
||||||
|
|
||||||
SimplyPrint::SimplyPrint(DynamicPrintConfig* config)
|
SimplyPrint::SimplyPrint(DynamicPrintConfig* config)
|
||||||
{
|
{
|
||||||
cred_file = (boost::filesystem::path(data_dir()) / OAUTH_CREDENTIAL_PATH).make_preferred().string();
|
cred_file = (boost::filesystem::path(data_dir()) / OAUTH_CREDENTIAL_PATH).make_preferred().string();
|
||||||
|
@ -87,7 +122,7 @@ GUI::OAuthParams SimplyPrint::get_oauth_params() const
|
||||||
{"code_challenge", code_challenge},
|
{"code_challenge", code_challenge},
|
||||||
{"code_challenge_method", "S256"},
|
{"code_challenge_method", "S256"},
|
||||||
};
|
};
|
||||||
const auto login_url = (boost::format("https://simplyprint.io/panel/oauth2/authorize?%s") % url_encode(query_parameters)).str();
|
const auto login_url = (boost::format(URL_BASE_HOME"/panel/oauth2/authorize?%s") % url_encode(query_parameters)).str();
|
||||||
|
|
||||||
return GUI::OAuthParams{
|
return GUI::OAuthParams{
|
||||||
login_url,
|
login_url,
|
||||||
|
@ -96,8 +131,8 @@ GUI::OAuthParams SimplyPrint::get_oauth_params() const
|
||||||
CALLBACK_URL,
|
CALLBACK_URL,
|
||||||
CLIENT_SCOPES,
|
CLIENT_SCOPES,
|
||||||
RESPONSE_TYPE,
|
RESPONSE_TYPE,
|
||||||
"https://simplyprint.io/login-success",
|
URL_BASE_HOME"/login-success",
|
||||||
"https://simplyprint.io/login-success",
|
URL_BASE_HOME"/login-success",
|
||||||
TOKEN_URL,
|
TOKEN_URL,
|
||||||
verification_code,
|
verification_code,
|
||||||
state,
|
state,
|
||||||
|
@ -226,7 +261,7 @@ bool SimplyPrint::test(wxString& curl_msg) const
|
||||||
|
|
||||||
return do_api_call(
|
return do_api_call(
|
||||||
[](bool is_retry) {
|
[](bool is_retry) {
|
||||||
auto http = Http::get("https://api.simplyprint.io/oauth2/TokenInfo");
|
auto http = Http::get(URL_BASE_API"/oauth2/TokenInfo");
|
||||||
http.header("Accept", "application/json");
|
http.header("Accept", "application/json");
|
||||||
return http;
|
return http;
|
||||||
},
|
},
|
||||||
|
@ -241,30 +276,31 @@ bool SimplyPrint::test(wxString& curl_msg) const
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimplyPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
bool SimplyPrint::do_temp_upload(const boost::filesystem::path& file_path,
|
||||||
|
const std::string& chunk_id,
|
||||||
|
const std::string& filename,
|
||||||
|
ProgressFn prorgess_fn,
|
||||||
|
ErrorFn error_fn) const
|
||||||
{
|
{
|
||||||
if (cred.find("access_token") == cred.end()) {
|
if (file_path.empty() == chunk_id.empty()) {
|
||||||
error_fn(_L("SimplyPrint account not linked. Go to Connect options to set it up."));
|
BOOST_LOG_TRIVIAL(error) << "SimplyPrint: Invalid arguments: both file_path and chunk_id are set or not provided";
|
||||||
|
error_fn(_L("Internel error"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If file is over 100 MB, fail
|
|
||||||
if (boost::filesystem::file_size(upload_data.source_path) > 104857600ull) {
|
|
||||||
error_fn(_L("File size exceeds the 100MB upload limit. Please upload your file through the panel."));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto filename = upload_data.upload_path.filename().string();
|
|
||||||
|
|
||||||
return do_api_call(
|
return do_api_call(
|
||||||
[&upload_data, &prorgess_fn, &filename](bool is_retry) {
|
[&file_path, &chunk_id, &prorgess_fn, &filename](bool is_retry) {
|
||||||
auto http = Http::post("https://simplyprint.io/api/files/TempUpload");
|
auto http = Http::post(URL_BASE_HOME"/api/files/TempUpload");
|
||||||
http.form_add_file("file", upload_data.source_path.string(), filename)
|
if (!file_path.empty()) {
|
||||||
.on_progress([&prorgess_fn](Http::Progress progress, bool& cancel) { prorgess_fn(std::move(progress), cancel); });
|
http.form_add_file("file", file_path, filename);
|
||||||
|
} else {
|
||||||
|
http.form_add("chunkId", chunk_id);
|
||||||
|
}
|
||||||
|
http.on_progress([&prorgess_fn](Http::Progress progress, bool& cancel) { prorgess_fn(std::move(progress), cancel); });
|
||||||
|
|
||||||
return http;
|
return http;
|
||||||
},
|
},
|
||||||
[&error_fn, &filename](std::string body, unsigned status) {
|
[&error_fn, &filename, this](std::string body, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("SimplyPrint: File uploaded: HTTP %1%: %2%") % status % body;
|
BOOST_LOG_TRIVIAL(info) << boost::format("SimplyPrint: File uploaded: HTTP %1%: %2%") % status % body;
|
||||||
|
|
||||||
// Get file UUID
|
// Get file UUID
|
||||||
|
@ -283,8 +319,15 @@ bool SimplyPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Er
|
||||||
const std::string uuid = j["uuid"];
|
const std::string uuid = j["uuid"];
|
||||||
|
|
||||||
// Launch external browser for file importing after uploading
|
// Launch external browser for file importing after uploading
|
||||||
const auto url = "https://simplyprint.io/panel?" + url_encode({{"import", "tmp:" + uuid}, {"filename", filename}});
|
const auto url = URL_BASE_HOME"/panel?" + url_encode({{"import", "tmp:" + uuid}, {"filename", filename}});
|
||||||
wxLaunchDefaultBrowser(url);
|
|
||||||
|
if (should_open_in_external_browser()) {
|
||||||
|
wxLaunchDefaultBrowser(url);
|
||||||
|
} else {
|
||||||
|
const auto mainframe = GUI::wxGetApp().mainframe;
|
||||||
|
mainframe->request_select_tab(MainFrame::TabPosition::tpMonitor);
|
||||||
|
mainframe->load_printer_url(url);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -295,4 +338,155 @@ bool SimplyPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Er
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SimplyPrint::do_chunk_upload(const boost::filesystem::path& file_path, const std::string& filename, ProgressFn prorgess_fn, ErrorFn error_fn) const
|
||||||
|
{
|
||||||
|
const auto file_size = boost::filesystem::file_size(file_path);
|
||||||
|
#ifdef SIMPLYPRINT_TEST
|
||||||
|
constexpr auto buffer_size = MAX_SINGLE_UPLOAD_FILE_SIZE;
|
||||||
|
#else
|
||||||
|
constexpr auto buffer_size = MAX_SINGLE_UPLOAD_FILE_SIZE - 1000000;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const auto chunk_amount = (size_t)ceil((double) file_size / buffer_size);
|
||||||
|
|
||||||
|
std::string chunk_id;
|
||||||
|
std::string delete_token;
|
||||||
|
|
||||||
|
// Tell SimplyPrint that the upload has failed and the chunks should be deleted
|
||||||
|
// Note: any error happens here won't be notified to the user
|
||||||
|
const auto clean_up = [this, &chunk_id, &delete_token]() {
|
||||||
|
if (chunk_id.empty()) {
|
||||||
|
// The initial upload failed, do nothing
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "SimplyPrint: Initial chunk upload failed, skip delete";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!delete_token.empty());
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << boost::format("SimplyPrint: Deleting file chunk %s...") % chunk_id;
|
||||||
|
const std::vector<std::pair<std::string, std::string>> query_parameters{
|
||||||
|
{"id", chunk_id},
|
||||||
|
{"temp", "true"},
|
||||||
|
{"delete", delete_token},
|
||||||
|
};
|
||||||
|
const auto url = (boost::format("%s?%s") % CHUNCK_RECEIVE_URL % url_encode(query_parameters)).str();
|
||||||
|
do_api_call(
|
||||||
|
[&url](bool is_retry) {
|
||||||
|
auto http = Http::get(url);
|
||||||
|
return http;
|
||||||
|
},
|
||||||
|
[&chunk_id](std::string body, unsigned status) {
|
||||||
|
BOOST_LOG_TRIVIAL(info) << boost::format("SimplyPrint: File chunk %1% deleted: HTTP %2%: %3%") % chunk_id % status % body;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[&chunk_id](std::string body, std::string error, unsigned status) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << boost::format("SimplyPrint: Error deleting file chunk %1%: %2%, HTTP %3%, body: `%4%`") %
|
||||||
|
chunk_id % error % status % body;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Do chunk upload
|
||||||
|
for (size_t i = 0; i < chunk_amount; i++) {
|
||||||
|
std::string url;
|
||||||
|
{
|
||||||
|
std::vector<std::pair<std::string, std::string>> query_parameters{
|
||||||
|
{"i", std::to_string(i)},
|
||||||
|
{"temp", "true"},
|
||||||
|
};
|
||||||
|
if (i == 0) {
|
||||||
|
query_parameters.emplace_back("filename", filename);
|
||||||
|
query_parameters.emplace_back("chunks", std::to_string(chunk_amount));
|
||||||
|
query_parameters.emplace_back("totalsize", std::to_string(file_size));
|
||||||
|
} else {
|
||||||
|
query_parameters.emplace_back("id", chunk_id);
|
||||||
|
}
|
||||||
|
url = (boost::format("%s?%s") % CHUNCK_RECEIVE_URL % url_encode(query_parameters)).str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the offset and length of current chunk
|
||||||
|
const boost::filesystem::ifstream::off_type offset = i * buffer_size;
|
||||||
|
const size_t length = i == (chunk_amount - 1) ? file_size - offset : buffer_size;
|
||||||
|
|
||||||
|
const bool succ = do_api_call(
|
||||||
|
[&url, &file_path, &filename, i, chunk_amount, file_size, offset, length, prorgess_fn](bool is_retry) {
|
||||||
|
BOOST_LOG_TRIVIAL(info) << boost::format("SimplyPrint: Start uploading file chunk [%1%/%2%]...") % (i + 1) % chunk_amount;
|
||||||
|
auto http = Http::post(url);
|
||||||
|
http.form_add_file("file", file_path, filename, offset, length);
|
||||||
|
|
||||||
|
http.on_progress([&prorgess_fn, file_size, offset](Http::Progress progress, bool& cancel) {
|
||||||
|
progress.ultotal = file_size;
|
||||||
|
progress.ulnow += offset;
|
||||||
|
|
||||||
|
prorgess_fn(std::move(progress), cancel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return http;
|
||||||
|
},
|
||||||
|
[&error_fn, i, chunk_amount, this, &chunk_id, &delete_token](std::string body, unsigned status) {
|
||||||
|
BOOST_LOG_TRIVIAL(info) << boost::format("SimplyPrint: File chunk [%1%/%2%] uploaded: HTTP %3%: %4%") % (i + 1) % chunk_amount % status % body;
|
||||||
|
if (i == 0) {
|
||||||
|
// First chunk, parse chunk id
|
||||||
|
const auto j = nlohmann::json::parse(body, nullptr, false, true);
|
||||||
|
if (j.is_discarded()) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "SimplyPrint: Invalid or no JSON data on ChunkReceive: " << body;
|
||||||
|
error_fn(_L("Unknown error"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j.find("id") == j.end() || j.find("delete_token") == j.end()) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "SimplyPrint: Invalid or no JSON data on ChunkReceive: " << body;
|
||||||
|
error_fn(_L("Unknown error"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned long id = j["id"];
|
||||||
|
|
||||||
|
chunk_id = std::to_string(id);
|
||||||
|
delete_token = j["delete_token"];
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[this, &error_fn, i, chunk_amount](std::string body, std::string error, unsigned status) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << boost::format("SimplyPrint: Error uploading file chunk [%1%/%2%]: %3%, HTTP %4%, body: `%5%`") %
|
||||||
|
(i + 1) % chunk_amount % error % status % body;
|
||||||
|
error_fn(format_error(body, error, status));
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!succ) {
|
||||||
|
clean_up();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!chunk_id.empty());
|
||||||
|
|
||||||
|
// Finally, complete the upload using the chunk id
|
||||||
|
const bool succ = do_temp_upload({}, chunk_id, filename, prorgess_fn, error_fn);
|
||||||
|
if (!succ) {
|
||||||
|
clean_up();
|
||||||
|
}
|
||||||
|
|
||||||
|
return succ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SimplyPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||||
|
{
|
||||||
|
if (cred.find("access_token") == cred.end()) {
|
||||||
|
error_fn(_L("SimplyPrint account not linked. Go to Connect options to set it up."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto filename = upload_data.upload_path.filename().string();
|
||||||
|
|
||||||
|
if (boost::filesystem::file_size(upload_data.source_path) > MAX_SINGLE_UPLOAD_FILE_SIZE) {
|
||||||
|
// If file is over 100 MB, do chunk upload
|
||||||
|
return do_chunk_upload(upload_data.source_path, filename, prorgess_fn, error_fn);
|
||||||
|
} else {
|
||||||
|
// Normal upload
|
||||||
|
return do_temp_upload(upload_data.source_path, {}, filename, prorgess_fn, error_fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
|
@ -15,10 +15,37 @@ class SimplyPrint : public PrintHost
|
||||||
|
|
||||||
void load_oauth_credential();
|
void load_oauth_credential();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Call the given SimplyPrint API, and if the token expired do a token refresh then retry
|
||||||
|
* \param build_request the http request builder
|
||||||
|
* \param on_complete
|
||||||
|
* \param on_error
|
||||||
|
* \return whether the API call succeeded
|
||||||
|
*/
|
||||||
bool do_api_call(std::function<Http(bool /*is_retry*/)> build_request,
|
bool do_api_call(std::function<Http(bool /*is_retry*/)> build_request,
|
||||||
std::function<bool(std::string /* body */, unsigned /* http_status */)> on_complete,
|
std::function<bool(std::string /* body */, unsigned /* http_status */)> on_complete,
|
||||||
std::function<bool(std::string /* body */, std::string /* error */, unsigned /* http_status */)> on_error) const;
|
std::function<bool(std::string /* body */, std::string /* error */, unsigned /* http_status */)> on_error) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Upload a temp file and open SimplyPrint panel for file importing
|
||||||
|
* \param file_path for file smaller than 100MB, this is the file path, otherwise must left empty
|
||||||
|
* \param chunk_id for file greater than 100MB, this is the chunk id returned by the ChunkReceive API, otherwise must left empty
|
||||||
|
* \param filename the target file name
|
||||||
|
* \param prorgess_fn
|
||||||
|
* \param error_fn
|
||||||
|
* \return whether upload succeeded
|
||||||
|
*/
|
||||||
|
bool do_temp_upload(const boost::filesystem::path& file_path,
|
||||||
|
const std::string& chunk_id,
|
||||||
|
const std::string& filename,
|
||||||
|
ProgressFn prorgess_fn,
|
||||||
|
ErrorFn error_fn) const;
|
||||||
|
|
||||||
|
bool do_chunk_upload(const boost::filesystem::path& file_path,
|
||||||
|
const std::string& filename,
|
||||||
|
ProgressFn prorgess_fn,
|
||||||
|
ErrorFn error_fn) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SimplyPrint(DynamicPrintConfig* config);
|
SimplyPrint(DynamicPrintConfig* config);
|
||||||
~SimplyPrint() override = default;
|
~SimplyPrint() override = default;
|
||||||
|
|
Loading…
Reference in a new issue