OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::PrusaConnect Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/slic3r/Utils/OctoPrint.hpp>

Inheritance diagram for Slic3r::PrusaConnect:
Collaboration diagram for Slic3r::PrusaConnect:

Public Types

typedef Http::ProgressFn ProgressFn
typedef std::function< void(wxString)> ErrorFn
typedef std::function< void(wxString, wxString)> InfoFn

Public Member Functions

 PrusaConnect (DynamicPrintConfig *config)
 ~PrusaConnect () override=default
wxString get_test_ok_msg () const override
wxString get_test_failed_msg (wxString &msg) const override
PrintHostPostUploadActions get_post_upload_actions () const override
const char * get_name () const override
bool get_storage (wxArrayString &storage_path, wxArrayString &storage_name) const override
bool upload (PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override
bool has_auto_discovery () const override
bool can_test () const override
std::string get_host () const override
const std::string & get_apikey () const
const std::string & get_cafile () const
virtual bool supports_multiple_printers () const
virtual std::string get_sn () const
virtual bool get_groups (wxArrayString &) const
virtual bool get_printers (wxArrayString &) const
virtual bool is_cloud () const
virtual bool is_logged_in () const
virtual void log_out () const
virtual bool get_login_url (wxString &auth_url) const

Static Public Member Functions

static PrintHost * get_print_host (DynamicPrintConfig *config)
static std::string get_print_host_webui (DynamicPrintConfig *config)
static int get_err_code_from_body (const std::string &body)

Protected Member Functions

void set_http_post_header_args (Http &http, PrintHostPostUploadAction post_action) const override
bool test (wxString &curl_msg) const override
bool validate_version_text (const boost::optional< std::string > &version_text) const override
bool upload_inner_with_host (PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override
virtual bool upload_inner_with_host (PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
void set_auth (Http &http) const override
std::string make_url (const std::string &path) const
virtual wxString format_error (const std::string &body, const std::string &error, unsigned status) const

Static Protected Member Functions

static wxString timed_out_message ()

Protected Attributes

AuthorizationType m_authorization_type
std::string m_username
std::string m_password
std::string m_host
std::string m_apikey
std::string m_cafile
bool m_ssl_revoke_best_effort

Private Member Functions

bool test_with_method_check (wxString &curl_msg, bool &use_put) const
bool put_inner (PrintHostUpload upload_data, std::string url, const std::string &name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
bool post_inner (PrintHostUpload upload_data, std::string url, const std::string &name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const

Private Attributes

bool m_show_after_message

Detailed Description

Member Typedef Documentation

◆ ErrorFn

typedef std::function<void(wxString )> Slic3r::PrintHost::ErrorFn
inherited

◆ InfoFn

typedef std::function<void(wxString , wxString )> Slic3r::PrintHost::InfoFn
inherited

◆ ProgressFn

Constructor & Destructor Documentation

◆ PrusaConnect()

Slic3r::PrusaConnect::PrusaConnect ( DynamicPrintConfig * config)
1138 : PrusaLink(config, true)
1139{
1140}

References Slic3r::PrusaLink::PrusaLink().

Here is the call graph for this function:

◆ ~PrusaConnect()

Slic3r::PrusaConnect::~PrusaConnect ( )
overridedefault

Member Function Documentation

◆ can_test()

bool Slic3r::OctoPrint::can_test ( ) const
inlineoverridevirtualinherited

Implements Slic3r::PrintHost.

33{ return true; }

◆ format_error()

wxString Slic3r::PrintHost::format_error ( const std::string & body,
const std::string & error,
unsigned status ) const
protectedvirtualinherited
203{
204 if (status != 0) {
205 const std::string reason = moonraker_error_reason(body);
206 auto wxbody = wxString::FromUTF8(reason.empty() ? body : reason);
207 return wxString::Format("HTTP %u: %s", status, wxbody);
208 } else {
209 if (error.find("curl:Timeout was reached") != std::string::npos) {
210 return timed_out_message();
211 }else if(error.find("curl:Couldn't resolve host name")!= std::string::npos){
212 return _L("The Hostname/IP/URL could not be parsed, please check it and try again.");
213 } else if (error.find("Connection was reset") != std::string::npos){
214 return _L("File/data transfer interrupted. Please check the printer and network, then try it again.");
215 }
216 else {
217 return wxString::FromUTF8(error.data());
218 }
219 }
220}
#define _L(s)
Definition OBJ.cpp:34
static wxString timed_out_message()
Definition PrintHost.cpp:222
std::string moonraker_error_reason(const std::string &body)
Definition PrintHost.cpp:137

References _L, and timed_out_message().

Referenced by Slic3r::Duet::connect(), Slic3r::UltiMaker::connect(), Slic3r::SimplyPrint::do_chunk_upload(), Slic3r::SimplyPrint::do_temp_upload(), Slic3r::ElegooLink::elegoo_cc2_test(), Slic3r::ElegooLink::elegoo_test(), Slic3r::PrusaLink::post_inner(), Slic3r::PrusaLink::put_inner(), Slic3r::Flashforge::request_local_api_json(), Slic3r::Duet::start_print(), Slic3r::Moonraker::start_print(), Slic3r::UltiMaker::start_print(), Slic3r::AstroBox::test(), Slic3r::CrealityPrint::test(), Slic3r::ESP3D::test(), Slic3r::FlashAir::test(), Slic3r::Moonraker::test(), Slic3r::Obico::test(), Slic3r::OctoPrint::test(), Slic3r::PrusaLink::test(), Slic3r::Repetier::test(), Slic3r::UltiMaker::test(), Slic3r::PrusaLink::test_with_method_check(), Slic3r::AstroBox::upload(), Slic3r::C3DPrinterOS::upload(), Slic3r::CrealityPrint::upload(), Slic3r::Duet::upload(), Slic3r::ESP3D::upload(), Slic3r::FlashAir::upload(), Slic3r::MKS::upload(), Slic3r::Moonraker::upload(), Slic3r::Obico::upload(), Slic3r::Repetier::upload(), Slic3r::UltiMaker::upload(), Slic3r::OctoPrint::upload_inner_with_host(), Slic3r::Flashforge::upload_local_api(), Slic3r::ElegooLink::uploadPart(), and Slic3r::ElegooLink::uploadPartCC2().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_apikey()

const std::string & Slic3r::OctoPrint::get_apikey ( ) const
inlineinherited
36{ return m_apikey; }
std::string m_apikey
Definition OctoPrint.hpp:47

References m_apikey.

Referenced by Slic3r::PrusaLink::set_auth().

Here is the caller graph for this function:

◆ get_cafile()

const std::string & Slic3r::OctoPrint::get_cafile ( ) const
inlineinherited
37{ return m_cafile; }
std::string m_cafile
Definition OctoPrint.hpp:48

References m_cafile.

Referenced by Slic3r::PrusaLink::set_auth().

Here is the caller graph for this function:

◆ get_err_code_from_body()

int Slic3r::PrintHost::get_err_code_from_body ( const std::string & body)
staticinherited
189{
190 boost::property_tree::ptree root;
191 std::istringstream iss(body);
192 try {
193 boost::property_tree::read_json(iss, root);
194 } catch (const std::exception &ex) {
195 BOOST_LOG_TRIVIAL(error) << "PrintHost: response is not valid JSON: " << ex.what();
196 return -1;
197 }
198
199 return root.get<int>("err", 0);
200}

Referenced by Slic3r::Duet::connect(), Slic3r::UltiMaker::connect(), Slic3r::Duet::upload(), Slic3r::MKS::upload(), and Slic3r::UltiMaker::upload().

Here is the caller graph for this function:

◆ get_groups()

virtual bool Slic3r::PrintHost::get_groups ( wxArrayString & ) const
inlinevirtualinherited

Reimplemented in Slic3r::Repetier.

84{ return false; }

◆ get_host()

std::string Slic3r::OctoPrint::get_host ( ) const
inlineoverridevirtualinherited

Implements Slic3r::PrintHost.

35{ return m_host; }
std::string m_host
Definition OctoPrint.hpp:46

References m_host.

◆ get_login_url()

virtual bool Slic3r::PrintHost::get_login_url ( wxString & auth_url) const
inlinevirtualinherited

Reimplemented in Slic3r::Obico.

99{ return false; }

Referenced by Slic3r::GUI::PrinterCloudAuthDialog::PrinterCloudAuthDialog().

Here is the caller graph for this function:

◆ get_name()

const char * Slic3r::PrusaConnect::get_name ( ) const
inlineoverridevirtual

Reimplemented from Slic3r::OctoPrint.

115{ return "PrusaConnect"; }

◆ get_post_upload_actions()

PrintHostPostUploadActions Slic3r::PrusaConnect::get_post_upload_actions ( ) const
inlineoverridevirtual

◆ get_print_host()

PrintHost * Slic3r::PrintHost::get_print_host ( DynamicPrintConfig * config)
staticinherited
59{
61
62 {
63 const auto opt = config->option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
64 if (opt != nullptr) {
65 tech = opt->value;
66 }
67 }
68
69 if (tech == ptFFF) {
70 const auto opt = config->option<ConfigOptionEnum<PrintHostType>>("host_type");
71 const auto host_type = opt != nullptr ? opt->value : htOctoPrint;
72
73 switch (host_type) {
74 case htOctoPrint: return new OctoPrint(config);
75 case htDuet: return new Duet(config);
76 case htUltiMaker: return new UltiMaker(config);
77 case htFlashAir: return new FlashAir(config);
78 case htAstroBox: return new AstroBox(config);
79 case htRepetier: return new Repetier(config);
80 case htPrusaLink: return new PrusaLink(config);
81 case htPrusaConnect: return new PrusaConnect(config);
82 case htMKS: return new MKS(config);
83 case htESP3D: return new ESP3D(config);
84 case htCrealityPrint: return new CrealityPrint(config);
85 case htObico: return new Obico(config);
86 case htFlashforge: return new Flashforge(config);
87 case htSimplyPrint: return new SimplyPrint(config);
88 case htElegooLink: return new ElegooLink(config);
89 case ht3DPrinterOS: return new C3DPrinterOS(config);
90 case htMoonraker: return new Moonraker(config);
91 default: return nullptr;
92 }
93 } else {
94 return new SL1Host(config);
95 }
96}
PrinterTechnology
Definition Config.hpp:229
@ ptFFF
Definition Config.hpp:231
@ htSimplyPrint
Definition PrintConfig.hpp:119
@ htMoonraker
Definition PrintConfig.hpp:119
@ htElegooLink
Definition PrintConfig.hpp:119
@ htCrealityPrint
Definition PrintConfig.hpp:119
@ ht3DPrinterOS
Definition PrintConfig.hpp:119
@ htPrusaLink
Definition PrintConfig.hpp:119
@ htRepetier
Definition PrintConfig.hpp:119
@ htMKS
Definition PrintConfig.hpp:119
@ htPrusaConnect
Definition PrintConfig.hpp:119
@ htDuet
Definition PrintConfig.hpp:119
@ htUltiMaker
Definition PrintConfig.hpp:119
@ htObico
Definition PrintConfig.hpp:119
@ htOctoPrint
Definition PrintConfig.hpp:119
@ htFlashforge
Definition PrintConfig.hpp:119
@ htFlashAir
Definition PrintConfig.hpp:119
@ htAstroBox
Definition PrintConfig.hpp:119
@ htESP3D
Definition PrintConfig.hpp:119
ConfigOptionEnum< PrinterTechnology >
Definition PrintConfig.hpp:2358

References Slic3r::ht3DPrinterOS, Slic3r::htAstroBox, Slic3r::htCrealityPrint, Slic3r::htDuet, Slic3r::htElegooLink, Slic3r::htESP3D, Slic3r::htFlashAir, Slic3r::htFlashforge, Slic3r::htMKS, Slic3r::htMoonraker, Slic3r::htObico, Slic3r::htOctoPrint, Slic3r::htPrusaConnect, Slic3r::htPrusaLink, Slic3r::htRepetier, Slic3r::htSimplyPrint, Slic3r::htUltiMaker, Slic3r::ConfigBase::option(), and Slic3r::ptFFF.

Referenced by Slic3r::GUI::PhysicalPrinterDialog::build_printhost_settings(), Slic3r::GUI::anonymous_namespace{PrinterWebViewHandler.cpp}::ElegooPrinterWebViewHandler::handle_get_sn_request(), Slic3r::GUI::anonymous_namespace{PrinterWebViewHandler.cpp}::ElegooPrinterWebViewHandler::handle_upload_request(), Slic3r::GUI::PhysicalPrinterDialog::update_printers(), and Slic3r::GUI::PhysicalPrinterDialog::update_printhost_buttons().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_print_host_webui()

std::string Slic3r::PrintHost::get_print_host_webui ( DynamicPrintConfig * config)
staticinherited
99{
100 if (config == nullptr)
101 return {};
102
103 std::string webui_url;
104 const auto* host_type_opt = config->option<ConfigOptionEnum<PrintHostType>>("host_type");
105 const auto host_type = host_type_opt != nullptr ? host_type_opt->value : htOctoPrint;
106
107 switch (host_type) {
108 case htElegooLink: {
109 webui_url = ElegooLink::get_print_host_webui(config);
110 break;
111 }
112 default: break;
113 }
114
115 if (webui_url.empty()) {
116 webui_url = config->opt_string("print_host_webui");
117 if (webui_url.empty())
118 webui_url = config->opt_string("print_host");
119 if (webui_url.empty())
120 return webui_url;
121 }
122
123 const bool has_http_scheme = boost::algorithm::istarts_with(webui_url, "http");
124 const bool has_file_scheme = boost::algorithm::istarts_with(webui_url, "file:");
125
126 if (!has_http_scheme && !has_file_scheme)
127 webui_url = "http://" + webui_url;
128
129 return webui_url;
130}

References Slic3r::ElegooLink::get_print_host_webui(), Slic3r::htElegooLink, Slic3r::htOctoPrint, Slic3r::DynamicConfig::opt_string(), Slic3r::ConfigBase::option(), and Slic3r::ConfigOptionSingle< T >::value.

Referenced by Slic3r::GUI::MainFrame::load_printer_url(), Slic3r::GUI::Plater::priv::on_tab_selection_changing(), and Slic3r::GUI::Sidebar::update_all_preset_comboboxes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_printers()

virtual bool Slic3r::PrintHost::get_printers ( wxArrayString & ) const
inlinevirtualinherited

Reimplemented in Slic3r::Obico, and Slic3r::Repetier.

85{ return false; }

◆ get_sn()

virtual std::string Slic3r::PrintHost::get_sn ( ) const
inlinevirtualinherited

Get the serial number for connecting to the printer. For Elegoo CC2, the device details connection to the printer requires the serial number. Other print hosts do not need to implement this interface, and it returns an empty string by default.

Reimplemented in Slic3r::ElegooLink.

80{ return ""; }

◆ get_storage()

bool Slic3r::PrusaConnect::get_storage ( wxArrayString & storage_path,
wxArrayString & storage_name ) const
inlineoverridevirtual

Reimplemented from Slic3r::PrintHost.

116{ return false; }

◆ get_test_failed_msg()

wxString Slic3r::PrusaConnect::get_test_failed_msg ( wxString & msg) const
overridevirtual

Reimplemented from Slic3r::OctoPrint.

1163{
1164 return GUI::format_wxstr("%s: %s", _L("Could not connect to Prusa Connect"), msg);
1165}
wxString format_wxstr(const char *fmt, TArgs &&... args)
Definition format.hpp:22

References _L, and Slic3r::GUI::format_wxstr().

Here is the call graph for this function:

◆ get_test_ok_msg()

wxString Slic3r::PrusaConnect::get_test_ok_msg ( ) const
overridevirtual

Reimplemented from Slic3r::OctoPrint.

1158{
1159 return _(L("Connection to Prusa Connect is working correctly."));
1160}
#define _(s)
Definition 3mf.cpp:306
#define L(s)
Definition ExtrusionEntity.cpp:25

References _, and L.

◆ has_auto_discovery()

bool Slic3r::OctoPrint::has_auto_discovery ( ) const
inlineoverridevirtualinherited

Implements Slic3r::PrintHost.

32{ return true; }

◆ is_cloud()

virtual bool Slic3r::PrintHost::is_cloud ( ) const
inlinevirtualinherited

Reimplemented in Slic3r::C3DPrinterOS, Slic3r::Obico, and Slic3r::SimplyPrint.

96{ return false; }

◆ is_logged_in()

virtual bool Slic3r::PrintHost::is_logged_in ( ) const
inlinevirtualinherited

Reimplemented in Slic3r::C3DPrinterOS, and Slic3r::SimplyPrint.

97{ return false; }

◆ log_out()

virtual void Slic3r::PrintHost::log_out ( ) const
inlinevirtualinherited

Reimplemented in Slic3r::C3DPrinterOS, and Slic3r::SimplyPrint.

98{}

◆ make_url()

std::string Slic3r::OctoPrint::make_url ( const std::string & path) const
protectedinherited
518{
519 if (m_host.find("http://") == 0 || m_host.find("https://") == 0) {
520 if (m_host.back() == '/') {
521 return (boost::format("%1%%2%") % m_host % path).str();
522 } else {
523 return (boost::format("%1%/%2%") % m_host % path).str();
524 }
525 } else {
526 return (boost::format("http://%1%/%2%") % m_host % path).str();
527 }
528}

References m_host.

Referenced by Slic3r::ElegooLink::elegoo_test(), Slic3r::PrusaLink::get_storage(), Slic3r::ElegooLink::make_cc2_info_url(), Slic3r::ElegooLink::make_cc2_upload_url(), test(), Slic3r::PrusaLink::test(), Slic3r::PrusaLink::test_with_method_check(), Slic3r::ElegooLink::upload_inner_with_host(), upload_inner_with_host(), and Slic3r::PrusaLink::upload_inner_with_host().

Here is the caller graph for this function:

◆ post_inner()

bool Slic3r::PrusaLink::post_inner ( PrintHostUpload upload_data,
std::string url,
const std::string & name,
ProgressFn prorgess_fn,
ErrorFn error_fn,
InfoFn info_fn ) const
privateinherited
1074{
1075 info_fn(L"set_complete_off", wxString());
1076 bool res = true;
1077 const auto upload_filename = upload_data.upload_path.filename();
1078 const auto upload_parent_path = upload_data.upload_path.parent_path();
1079
1080 Http http = Http::post(std::move(url));
1081#ifdef WIN32
1082 // "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
1083 // And when creating Http object above, libcurl automatically includes "Host" header from address it got.
1084 // Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
1085 // Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
1086 // https://www.rfc-editor.org/rfc/rfc7230#section-5.4
1087 http.header("Host", Http::get_host_header_value(m_host));
1088#endif // _WIN32
1089 set_auth(http);
1090 set_http_post_header_args(http, upload_data.post_action);
1091 http.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
1092 .form_add_file("file", upload_data.source_path.string(), upload_filename.string())
1093 .on_complete([&](std::string body, unsigned status) {
1095 // PrusaConnect message
1096 wxString widebody = wxString::FromUTF8(body);
1097 BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % widebody;
1098 std::string message = m_show_after_message ? (boost::format("%1%") % widebody).str() : std::string();
1099 if (status == 202)
1100 info_fn(L"complete_with_warning", message);
1101 else
1102 info_fn(L"complete", message);
1103 } else {
1104 // PrusaLink
1105 BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%") % name % status;
1106 info_fn(L"complete", wxString());
1107 }
1108
1109
1110 })
1111 .on_error([&](std::string body, std::string error, unsigned status) {
1112 BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
1113 error_fn(format_error(body, error, status));
1114 res = false;
1115 })
1116 .on_progress([&](Http::Progress progress, bool& cancel) {
1117 prorgess_fn(std::move(progress), cancel);
1118 if (cancel) {
1119 // Upload was canceled
1120 BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled";
1121 res = false;
1122 }
1123 })
1124#ifdef WIN32
1125 .ssl_revoke_best_effort(m_ssl_revoke_best_effort)
1126#endif
1127 .perform_sync();
1128
1129 return res;
1130}
static std::string get_host_header_value(const std::string &url)
Definition Http.cpp:1044
static Http post(std::string url)
Definition Http.cpp:891
bool m_ssl_revoke_best_effort
Definition OctoPrint.hpp:49
virtual wxString format_error(const std::string &body, const std::string &error, unsigned status) const
Definition PrintHost.cpp:202
@ http
Definition IPrinterAgent.hpp:64
@ Http
Definition PluginAuditManager.hpp:49

References Slic3r::PrintHost::format_error(), Slic3r::Http::get_host_header_value(), Slic3r::http, L, Slic3r::OctoPrint::m_host, m_show_after_message, Slic3r::OctoPrint::m_ssl_revoke_best_effort, Slic3r::Http::post(), Slic3r::PrintHostUpload::post_action, set_auth(), set_http_post_header_args(), Slic3r::PrintHostUpload::source_path, and Slic3r::PrintHostUpload::upload_path.

Referenced by upload_inner_with_host().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ put_inner()

bool Slic3r::PrusaLink::put_inner ( PrintHostUpload upload_data,
std::string url,
const std::string & name,
ProgressFn prorgess_fn,
ErrorFn error_fn,
InfoFn info_fn ) const
privateinherited
1025{
1026 info_fn(L"set_complete_off", wxString());
1027
1028 bool res = true;
1029 // Percent escape all filenames in on path and add it to the url. This is different from POST.
1030 url += "/" + escape_path_by_element(upload_data.upload_path);
1031 Http http = Http::put(std::move(url));
1032#ifdef WIN32
1033 // "Host" header is necessary here. We have resolved IP address and subsituted it into "url" variable.
1034 // And when creating Http object above, libcurl automatically includes "Host" header from address it got.
1035 // Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
1036 // Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
1037 // https://www.rfc-editor.org/rfc/rfc7230#section-5.4
1038 http.header("Host", Http::get_host_header_value(m_host));
1039#endif // _WIN32
1040 set_auth(http);
1041 // This is ugly, but works. There was an error at PrusaLink side that accepts any string at Print-After-Upload as true, thus False was also triggering print after upload.
1042 if (upload_data.post_action == PrintHostPostUploadAction::StartPrint)
1043 http.header("Print-After-Upload", "?1");
1044 http.set_put_body(upload_data.source_path)
1045 .header("Content-Type", "text/x.gcode")
1046 .header("Overwrite", "?1")
1047 .on_complete([&](std::string body, unsigned status) {
1048 wxString widebody = wxString::FromUTF8(body);
1049 BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % widebody;
1050 std::string message = m_show_after_message ? (boost::format("%1%") % widebody).str() : std::string();
1051 info_fn(L"complete", message);
1052 })
1053 .on_error([&](std::string body, std::string error, unsigned status) {
1054 BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
1055 error_fn(format_error(body, error, status));
1056 res = false;
1057 })
1058 .on_progress([&](Http::Progress progress, bool& cancel) {
1059 prorgess_fn(std::move(progress), cancel);
1060 if (cancel) {
1061 // Upload was canceled
1062 BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled";
1063 res = false;
1064 }
1065 })
1066#ifdef WIN32
1067 .ssl_revoke_best_effort(m_ssl_revoke_best_effort)
1068#endif
1069 .perform_sync();
1070
1071 return res;
1072}
static Http put(std::string url)
Definition Http.cpp:899

References Slic3r::PrintHost::format_error(), Slic3r::Http::get_host_header_value(), Slic3r::http, L, Slic3r::OctoPrint::m_host, m_show_after_message, Slic3r::OctoPrint::m_ssl_revoke_best_effort, Slic3r::PrintHostUpload::post_action, Slic3r::Http::put(), set_auth(), Slic3r::PrintHostUpload::source_path, Slic3r::StartPrint, and Slic3r::PrintHostUpload::upload_path.

Referenced by upload_inner_with_host().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_auth()

void Slic3r::PrusaLink::set_auth ( Http & http) const
overrideprotectedvirtualinherited

Reimplemented from Slic3r::OctoPrint.

581{
582 switch (m_authorization_type) {
583 case atKeyPassword:
584 http.header("X-Api-Key", get_apikey());
585 break;
586 case atUserPassword:
587 http.auth_digest(m_username, m_password);
588 break;
589 }
590
591 if (!get_cafile().empty()) {
592 http.ca_file(get_cafile());
593 }
594}
const std::string & get_cafile() const
Definition OctoPrint.hpp:37
const std::string & get_apikey() const
Definition OctoPrint.hpp:36
@ atUserPassword
Definition PrintConfig.hpp:123
@ atKeyPassword
Definition PrintConfig.hpp:123
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:273

References Slic3r::atKeyPassword, Slic3r::atUserPassword, Slic3r::empty(), Slic3r::OctoPrint::get_apikey(), Slic3r::OctoPrint::get_cafile(), Slic3r::http, m_authorization_type, m_password, and m_username.

Referenced by get_storage(), post_inner(), put_inner(), test(), and test_with_method_check().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_http_post_header_args()

void Slic3r::PrusaConnect::set_http_post_header_args ( Http & http,
PrintHostPostUploadAction post_action ) const
overrideprotectedvirtual

Reimplemented from Slic3r::PrusaLink.

1143{
1144 // Language for accept message
1145 wxString wlang = GUI::wxGetApp().current_language_code();
1146 std::string lang = GUI::format(wlang.SubString(0, 1));
1147 http.header("Accept-Language", lang);
1148 // Post action
1149 if (post_action == PrintHostPostUploadAction::StartPrint) {
1150 http.form_add("to_print", "True");
1151 } else if (post_action == PrintHostPostUploadAction::QueuePrint) {
1152 http.form_add("to_queue", "True");
1153 }
1154
1155}
std::string format(const char *fmt, TArgs &&... args)
Definition format.hpp:36

References Slic3r::GUI::format(), Slic3r::http, Slic3r::QueuePrint, and Slic3r::StartPrint.

Here is the call graph for this function:

◆ supports_multiple_printers()

virtual bool Slic3r::PrintHost::supports_multiple_printers ( ) const
inlinevirtualinherited

Reimplemented in Slic3r::Repetier.

73{ return false; }

◆ test()

bool Slic3r::PrusaLink::test ( wxString & curl_msg) const
overrideprotectedvirtualinherited

Reimplemented from Slic3r::OctoPrint.

625{
626 // Since the request is performed synchronously here,
627 // it is ok to refer to `msg` from within the closure
628 const char* name = get_name();
629
630 bool res = true;
631 auto url = make_url("api/version");
632
633 BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
634
635 auto http = Http::get(std::move(url));
636 set_auth(http);
637 http.on_error([&](std::string body, std::string error, unsigned status) {
638 BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
639 res = false;
640 msg = format_error(body, error, status);
641 })
642 .on_complete([&, this](std::string body, unsigned) {
643 BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
644
645 try {
646 std::stringstream ss(body);
647 pt::ptree ptree;
648 pt::read_json(ss, ptree);
649
650 if (!ptree.get_optional<std::string>("api")) {
651 res = false;
652 return;
653 }
654
655 const auto text = ptree.get_optional<std::string>("text");
656 res = validate_version_text(text);
657 if (!res) {
658 msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint"));
659 }
660 }
661 catch (const std::exception&) {
662 res = false;
663 msg = _L("Could not parse server response.");
664 }
665 })
666#ifdef WIN32
667 .ssl_revoke_best_effort(m_ssl_revoke_best_effort)
668 .on_ip_resolve([&](std::string address) {
669 // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
670 // Remember resolved address to be reused at successive REST API call.
671 msg = GUI::from_u8(address);
672 })
673#endif // WIN32
674 .perform_sync();
675
676 return res;
677}
static Http get(std::string url)
Definition Http.cpp:886
std::string make_url(const std::string &path) const
Definition OctoPrint.cpp:517
wxString from_u8(const std::string &str)
Definition GUI.cpp:544

References _L, Slic3r::PrintHost::format_error(), Slic3r::GUI::format_wxstr(), Slic3r::GUI::from_u8(), Slic3r::Http::get(), get_name(), Slic3r::http, Slic3r::OctoPrint::m_ssl_revoke_best_effort, Slic3r::OctoPrint::make_url(), set_auth(), and validate_version_text().

Here is the call graph for this function:

◆ test_with_method_check()

bool Slic3r::PrusaLink::test_with_method_check ( wxString & curl_msg,
bool & use_put ) const
privateinherited
787{
788 // Since the request is performed synchronously here,
789 // it is ok to refer to `msg` from within the closure
790
791 const char* name = get_name();
792
793 bool res = true;
794 auto url = make_url("api/version");
795
796 BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
797 // Here we do not have to add custom "Host" header - the url contains host filled by user and libCurl will set the header by itself.
798 auto http = Http::get(std::move(url));
799 set_auth(http);
800 http.on_error([&](std::string body, std::string error, unsigned status) {
801 BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
802 res = false;
803 msg = format_error(body, error, status);
804 })
805 .on_complete([&, this](std::string body, unsigned) {
806 BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
807
808 try {
809 std::stringstream ss(body);
810 pt::ptree ptree;
811 pt::read_json(ss, ptree);
812
813 if (!ptree.get_optional<std::string>("api")) {
814 res = false;
815 return;
816 }
817
818 const auto text = ptree.get_optional<std::string>("text");
819 res = validate_version_text(text);
820 if (!res) {
821 msg = GUI::format_wxstr(_L("Mismatched type of print host: %s"), (text ? *text : "OctoPrint"));
822 use_put = false;
823 return;
824 }
825
826 // find capabilities subtree and read upload-by-put
827 for (const auto& section : ptree) {
828 if (section.first == "capabilities") {
829 const auto put_upload = section.second.get_optional<bool>("upload-by-put");
830 if (put_upload)
831 use_put = *put_upload;
832 break;
833 }
834 }
835
836 }
837 catch (const std::exception&) {
838 res = false;
839 msg = _L("Could not parse server response.");
840 }
841 })
842#ifdef WIN32
843 .ssl_revoke_best_effort(m_ssl_revoke_best_effort)
844 .on_ip_resolve([&](std::string address) {
845 // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
846 // Remember resolved address to be reused at successive REST API call.
847 msg = GUI::from_u8(address);
848 })
849#endif // WIN32
850 .perform_sync();
851
852 return res;
853}

References _L, Slic3r::PrintHost::format_error(), Slic3r::GUI::format_wxstr(), Slic3r::GUI::from_u8(), Slic3r::Http::get(), get_name(), Slic3r::http, Slic3r::OctoPrint::m_ssl_revoke_best_effort, Slic3r::OctoPrint::make_url(), set_auth(), and validate_version_text().

Referenced by upload_inner_with_host().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ timed_out_message()

wxString Slic3r::PrintHost::timed_out_message ( )
staticprotectedinherited
223{
224 return _L("Connection timed out. Please check if the printer and computer network are functioning properly, and confirm that they are on the same network.");
225}

References _L.

Referenced by format_error(), and Slic3r::CrealityPrint::start_print().

Here is the caller graph for this function:

◆ upload()

bool Slic3r::OctoPrint::upload ( PrintHostUpload upload_data,
ProgressFn prorgess_fn,
ErrorFn error_fn,
InfoFn info_fn ) const
overridevirtualinherited

Implements Slic3r::PrintHost.

276{
277#ifndef WIN32
278 return upload_inner_with_host(std::move(upload_data), prorgess_fn, error_fn, info_fn);
279#else
280 std::string host = Http::get_host_from_url(m_host);
281
282 // decide what to do based on m_host - resolve hostname or upload to ip
283 std::vector<boost::asio::ip::address> resolved_addr;
284 boost::system::error_code ec;
285 boost::asio::ip::address host_ip = boost::asio::ip::make_address(host, ec);
286 if (!ec) {
287 resolved_addr.push_back(host_ip);
288 } else if ( GUI::get_app_config()->get_bool("allow_ip_resolve") && boost::algorithm::ends_with(host, ".local")){
289 Bonjour("octoprint")
290 .set_hostname(host)
291 .set_retries(5) // number of rounds of queries send
292 .set_timeout(1) // after each timeout, if there is any answer, the resolving will stop
293 .on_resolve([&ra = resolved_addr](const std::vector<BonjourReply>& replies) {
294 for (const auto & rpl : replies) {
295 boost::asio::ip::address ip(rpl.ip);
296 ra.emplace_back(ip);
297 BOOST_LOG_TRIVIAL(info) << "Resolved IP address: " << rpl.ip;
298 }
299 })
300 .resolve_sync();
301 }
302 if (resolved_addr.empty()) {
303 // no resolved addresses - try system resolving
304 BOOST_LOG_TRIVIAL(error) << "PrusaSlicer failed to resolve hostname " << m_host << " into the IP address. Starting upload with system resolving.";
305 return upload_inner_with_host(std::move(upload_data), prorgess_fn, error_fn, info_fn);
306 } else if (resolved_addr.size() == 1) {
307 // one address resolved - upload there
308 return upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn, error_fn, info_fn, resolved_addr.front());
309 } else if (resolved_addr.size() == 2 && resolved_addr[0].is_v4() != resolved_addr[1].is_v4()) {
310 // there are just 2 addresses and 1 is ip_v4 and other is ip_v6
311 // try sending to both. (Then if both fail, show both error msg after second try)
312 wxString error_message;
313 if (!upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn
314 , [&msg = error_message, resolved_addr](wxString error) { msg = GUI::format_wxstr("%1%: %2%", resolved_addr.front(), error); }
315 , info_fn, resolved_addr.front())
316 &&
317 !upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn
318 , [&msg = error_message, resolved_addr](wxString error) { msg += GUI::format_wxstr("\n%1%: %2%", resolved_addr.back(), error); }
319 , info_fn, resolved_addr.back())
320 ) {
321
322 error_fn(error_message);
323 return false;
324 }
325 return true;
326 } else {
327 // There are multiple addresses - user needs to choose which to use.
328 size_t selected_index = resolved_addr.size();
329 IPListDialog dialog(nullptr, boost::nowide::widen(m_host), resolved_addr, selected_index);
330 if (dialog.ShowModal() == wxID_OK && selected_index < resolved_addr.size()) {
331 return upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn, error_fn, info_fn, resolved_addr[selected_index]);
332 }
333 }
334 return false;
335#endif // WIN32
336}
static std::string get_host_from_url(const std::string &url, std::string *port=nullptr)
Definition Http.cpp:1008
virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
Definition OctoPrint.cpp:409
AppConfig * get_app_config()
Definition GUI.cpp:539

References Slic3r::GUI::format_wxstr(), Slic3r::GUI::get_app_config(), Slic3r::Http::get_host_from_url(), m_host, Slic3r::Bonjour::on_resolve(), Slic3r::Bonjour::set_hostname(), Slic3r::Bonjour::set_retries(), Slic3r::Bonjour::set_timeout(), and upload_inner_with_host().

Here is the call graph for this function:

◆ upload_inner_with_host() [1/2]

bool Slic3r::OctoPrint::upload_inner_with_host ( PrintHostUpload upload_data,
ProgressFn prorgess_fn,
ErrorFn error_fn,
InfoFn info_fn ) const
protectedvirtualinherited
410{
411 const char* name = get_name();
412
413 const auto upload_filename = upload_data.upload_path.filename();
414 const auto upload_parent_path = upload_data.upload_path.parent_path();
415
416 // If test fails, test_msg_or_host_ip contains the error message.
417 // Otherwise on Windows it contains the resolved IP address of the host.
418 wxString test_msg_or_host_ip;
419 if (!test(test_msg_or_host_ip)) {
420 error_fn(std::move(test_msg_or_host_ip));
421 return false;
422 }
423
424 std::string url;
425 bool res = true;
426
427#ifdef WIN32
428 // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
429 if (m_host.find("https://") == 0 || test_msg_or_host_ip.empty() || !GUI::get_app_config()->get_bool("allow_ip_resolve"))
430#endif // _WIN32
431 {
432 // If https is entered we assume signed ceritificate is being used
433 // IP resolving will not happen - it could resolve into address not being specified in cert
434 url = make_url("api/files/local");
435 }
436#ifdef WIN32
437 else {
438 // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
439 // Curl uses easy_getinfo to get ip address of last successful transaction.
440 // If it got the address use it instead of the stored in "host" variable.
441 // This new address returns in "test_msg_or_host_ip" variable.
442 // Solves troubles of uploades failing with name address.
443 // in original address (m_host) replace host for resolved ip
444 info_fn(L"resolve", test_msg_or_host_ip);
445 url = substitute_host(make_url("api/files/local"), GUI::into_u8(test_msg_or_host_ip));
446 BOOST_LOG_TRIVIAL(info) << "Upload address after ip resolve: " << url;
447 }
448#endif // _WIN32
449
450 const std::string plateindex = upload_data.extended("plateindex");
451 BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%, plateindex: %7%")
452 % name
453 % upload_data.source_path
454 % url
455 % upload_filename.string()
456 % upload_parent_path.string()
457 % (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
458 % (plateindex.empty() ? "-" : plateindex);
459
460 auto http = Http::post(std::move(url));
461#ifdef WIN32
462 // "Host" header is necessary here. In the workaround above (two mDNS..) we have got IP address from test connection and subsituted it into "url" variable.
463 // And when creating Http object above, libcurl automatically includes "Host" header from address it got.
464 // Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back.
465 // Not changing the host would work on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734).
466 // Also when allow_ip_resolve = 0, this is not needed, but it should not break anything if it stays.
467 // https://www.rfc-editor.org/rfc/rfc7230#section-5.4
468 http.header("Host", Http::get_host_header_value(m_host));
469#endif // _WIN32
470 set_auth(http);
471 http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
472 .form_add("path", upload_parent_path.string()); // XXX: slashes on windows ???
473 //ORCA: gcode inside a .gcode.3mf is index-coded (Metadata/plate_<N>.gcode), so the upload names the
474 // plate via a 1-based `plateindex` (see Plater::send_gcode_legacy); servers that don't use it
475 // ignore the unknown form field.
476 if (!plateindex.empty())
477 http.form_add("plateindex", plateindex);
478 http.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
479 .on_complete([&](std::string body, unsigned status) {
480 BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
481 })
482 .on_error([&](std::string body, std::string error, unsigned status) {
483 BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
484 error_fn(format_error(body, error, status));
485 res = false;
486 })
487 .on_progress([&](Http::Progress progress, bool& cancel) {
488 prorgess_fn(std::move(progress), cancel);
489 if (cancel) {
490 // Upload was canceled
491 BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
492 res = false;
493 }
494 })
495#ifdef WIN32
496 .ssl_revoke_best_effort(m_ssl_revoke_best_effort)
497#endif
498 .perform_sync();
499
500 return res;
501}
bool get_bool(const std::string &section, const std::string &key) const
Definition AppConfig.hpp:163
virtual void set_auth(Http &http) const
Definition OctoPrint.cpp:508
const char * get_name() const override
Definition OctoPrint.cpp:146
virtual bool test(wxString &curl_msg) const override
Definition OctoPrint.cpp:207
std::string into_u8(const wxString &str)
Definition GUI.cpp:549

References Slic3r::PrintHostUpload::extended(), Slic3r::PrintHost::format_error(), Slic3r::GUI::get_app_config(), Slic3r::Http::get_host_header_value(), get_name(), Slic3r::http, Slic3r::GUI::into_u8(), L, m_host, m_ssl_revoke_best_effort, make_url(), Slic3r::Http::post(), Slic3r::PrintHostUpload::post_action, set_auth(), Slic3r::PrintHostUpload::source_path, Slic3r::StartPrint, test(), and Slic3r::PrintHostUpload::upload_path.

Referenced by upload(), and Slic3r::ElegooLink::upload_inner_with_host().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ upload_inner_with_host() [2/2]

bool Slic3r::PrusaLink::upload_inner_with_host ( PrintHostUpload upload_data,
ProgressFn prorgess_fn,
ErrorFn error_fn,
InfoFn info_fn ) const
overrideprotectedinherited
969{
970 const char* name = get_name();
971
972 const auto upload_filename = upload_data.upload_path.filename();
973 const auto upload_parent_path = upload_data.upload_path.parent_path();
974
975 // If test fails, test_msg_or_host_ip contains the error message.
976 // Otherwise on Windows it contains the resolved IP address of the host.
977 wxString test_msg_or_host_ip;
978 bool use_put = false;
979 if (!test_with_method_check(test_msg_or_host_ip, use_put)) {
980 error_fn(std::move(test_msg_or_host_ip));
981 return false;
982 }
983
984 std::string url;
985 std::string storage_path = (use_put ? "api/v1/files" : "api/files");
986 storage_path += (upload_data.storage.empty() ? "/local" : upload_data.storage);
987#ifdef WIN32
988 // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
989 if (m_host.find("https://") == 0 || test_msg_or_host_ip.empty() || !GUI::get_app_config()->get_bool("allow_ip_resolve"))
990#endif // _WIN32
991 {
992 // If https is entered we assume signed ceritificate is being used
993 // IP resolving will not happen - it could resolve into address not being specified in cert
994 url = make_url(storage_path);
995 }
996#ifdef WIN32
997 else {
998 // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
999 // Curl uses easy_getinfo to get ip address of last successful transaction.
1000 // If it got the address use it instead of the stored in "host" variable.
1001 // This new address returns in "test_msg_or_host_ip" variable.
1002 // Solves troubles of uploades failing with name address.
1003 // in original address (m_host) replace host for resolved ip
1004 info_fn(L"resolve", test_msg_or_host_ip);
1005 url = substitute_host(make_url(storage_path), GUI::into_u8(test_msg_or_host_ip));
1006 BOOST_LOG_TRIVIAL(info) << "Upload address after ip resolve: " << url;
1007 }
1008#endif // _WIN32
1009
1010 BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%, method: %7%")
1011 % name
1012 % upload_data.source_path
1013 % url
1014 % upload_filename.string()
1015 % upload_parent_path.string()
1016 % (upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
1017 % (use_put ? "PUT" : "POST");
1018
1019 if (use_put)
1020 return put_inner(std::move(upload_data), std::move(url), name, prorgess_fn, error_fn, info_fn);
1021 return post_inner(std::move(upload_data), std::move(url), name, prorgess_fn, error_fn, info_fn);
1022}
boost::filesystem::path storage_path()
Definition InstanceID.cpp:100

References Slic3r::GUI::get_app_config(), get_name(), Slic3r::GUI::into_u8(), L, Slic3r::OctoPrint::m_host, Slic3r::OctoPrint::make_url(), Slic3r::PrintHostUpload::post_action, post_inner(), put_inner(), Slic3r::PrintHostUpload::source_path, Slic3r::StartPrint, Slic3r::PrintHostUpload::storage, test_with_method_check(), and Slic3r::PrintHostUpload::upload_path.

Here is the call graph for this function:

◆ validate_version_text()

bool Slic3r::PrusaLink::validate_version_text ( const boost::optional< std::string > & version_text) const
overrideprotectedvirtualinherited

Reimplemented from Slic3r::OctoPrint.

Reimplemented in Slic3r::SL1Host.

576{
577 return version_text ? (boost::starts_with(*version_text, "PrusaLink") || boost::starts_with(*version_text, "OctoPrint")) : false;
578}

Referenced by test(), and test_with_method_check().

Here is the caller graph for this function:

Member Data Documentation

◆ m_apikey

std::string Slic3r::OctoPrint::m_apikey
protectedinherited

◆ m_authorization_type

AuthorizationType Slic3r::PrusaLink::m_authorization_type
protectedinherited

Referenced by PrusaLink(), and set_auth().

◆ m_cafile

std::string Slic3r::OctoPrint::m_cafile
protectedinherited

Referenced by OctoPrint(), get_cafile(), and set_auth().

◆ m_host

◆ m_password

std::string Slic3r::PrusaLink::m_password
protectedinherited

Referenced by PrusaLink(), and set_auth().

◆ m_show_after_message

bool Slic3r::PrusaLink::m_show_after_message
privateinherited

Referenced by PrusaLink(), post_inner(), and put_inner().

◆ m_ssl_revoke_best_effort

◆ m_username

std::string Slic3r::PrusaLink::m_username
protectedinherited

Referenced by PrusaLink(), and set_auth().


The documentation for this class was generated from the following files:
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/slic3r/Utils/OctoPrint.hpp
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/slic3r/Utils/OctoPrint.cpp