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

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

Inheritance diagram for Slic3r::ElegooLink:
Collaboration diagram for Slic3r::ElegooLink:

Public Types

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

Public Member Functions

 ElegooLink (DynamicPrintConfig *config)
 ~ElegooLink () override=default
const char * get_name () const override
virtual bool test (wxString &curl_msg) const override
wxString get_test_ok_msg () const override
wxString get_test_failed_msg (wxString &msg) const override
bool upload (PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override
std::string get_sn () const override
bool has_auto_discovery () const override
bool can_test () const override
PrintHostPostUploadActions get_post_upload_actions () const override
bool upload (PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) 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 bool get_groups (wxArrayString &) const
virtual bool get_printers (wxArrayString &) const
virtual bool get_storage (wxArrayString &, 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 std::string get_print_host_webui (DynamicPrintConfig *config)
static PrintHost * get_print_host (DynamicPrintConfig *config)
static int get_err_code_from_body (const std::string &body)

Protected Member Functions

virtual bool validate_version_text (const boost::optional< std::string > &version_text) const override
virtual 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
virtual void set_auth (Http &http) const
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

std::string m_host
std::string m_apikey
std::string m_cafile
bool m_ssl_revoke_best_effort

Private Member Functions

bool elegoo_test (wxString &curl_msg) const
bool elegoo_cc2_test (wxString &curl_msg) const
bool print (WebSocketClient &client, std::string timeLapse, std::string heatedBedLeveling, std::string bedType, const std::string filename, ErrorFn error_fn) const
bool checkResult (WebSocketClient &client, ErrorFn error_fn) const
bool loopUpload (std::string url, PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
bool loopUploadCC2 (std::string url, const std::string &host_header, PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
bool uploadPart (Http &http, std::string md5, std::string uuid, std::string path, std::string filename, size_t filesize, size_t offset, size_t length, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
bool uploadPartCC2 (Http &http, const std::string &host_header, const std::string &token, const std::string &md5, const boost::filesystem::path &path, const std::string &filename, size_t filesize, size_t offset, size_t length, ProgressFn prorgess_fn, ErrorFn error_fn) const
std::string cc2_token () const
std::string make_cc2_info_url () const
std::string make_cc2_upload_url () const

Private Attributes

std::string m_printerModel

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

◆ ElegooLink()

Slic3r::ElegooLink::ElegooLink ( DynamicPrintConfig * config)
333 :
334 OctoPrint(config), m_printerModel(config->opt_string("printer_model")) {
335
336 }
OctoPrint(DynamicPrintConfig *config)
Definition OctoPrint.cpp:139

References Slic3r::OctoPrint::OctoPrint(), and m_printerModel.

Here is the call graph for this function:

◆ ~ElegooLink()

Slic3r::ElegooLink::~ElegooLink ( )
overridedefault

Member Function Documentation

◆ can_test()

bool Slic3r::ElegooLink::can_test ( ) const
inlineoverridevirtual

Implements Slic3r::PrintHost.

35{ return true; }

◆ cc2_token()

std::string Slic3r::ElegooLink::cc2_token ( ) const
private
397 {
398 return get_cc2_token(m_apikey);
399 }
std::string m_apikey
Definition OctoPrint.hpp:47

References Slic3r::OctoPrint::m_apikey.

Referenced by elegoo_cc2_test(), loopUploadCC2(), and make_cc2_info_url().

Here is the caller graph for this function:

◆ checkResult()

bool Slic3r::ElegooLink::checkResult ( WebSocketClient & client,
ErrorFn error_fn ) const
private
1219 {
1220 res = false;
1221 error_fn(_L("Start print timeout"));
1222 }
1223 } catch (const std::exception& e) {
1224 std::cerr << "Error: " << e.what() << std::endl;
1225 BOOST_LOG_TRIVIAL(error) << "start print error: " << e.what();
1226 error_fn(_L("Start print failed") +"\n" +GUI::from_u8(e.what()));
1227 res=false;
1228 }
1229 return res;
1230 }
1231
1232 bool ElegooLink::checkResult(WebSocketClient& client, ErrorFn error_fn) const
1233 {
1234 bool res = true;
1235 // create a random UUID generator
1236 boost::uuids::random_generator generator;
1237 // generate a UUID
1238 boost::uuids::uuid uuid = generator();
1239 std::string uuid_string = to_string(uuid);
1240 try {
1241 std::string requestID = uuid_string;
1242 auto now = std::chrono::system_clock::now();
1243 auto duration = now.time_since_epoch();
1244 auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
1245 std::string timestamp = std::to_string(milliseconds);
1246 std::string jsonString = R"({
1247 "Id":"",
1248 "Data":{
1249 "Cmd":)" +
1250 std::to_string(ElegooLinkCommand::ELEGOO_GET_STATUS) + R"(,
1251 "Data":{},
1252 "RequestID":")" +
1253 uuid_string + R"(",
1254 "MainboardID":"",
1255 "TimeStamp":)" +
1256 timestamp + R"(,
1257 "From":1
1258 }
1259 })";
1260 std::cout << "send: " << jsonString << std::endl;
1261 BOOST_LOG_TRIVIAL(info) << "start print, param: " << jsonString;
1262 bool needWrite = true;
1263 // wait 60s
1264 auto start_time = std::chrono::steady_clock::now();
1265 do {
1266 if (needWrite) {
1267 client.send(jsonString);
1268 needWrite = false;
1269 }
1270 std::string response = client.receive();
1271 std::cout << "Received: " << response << std::endl;
1272 BOOST_LOG_TRIVIAL(info) << "Received: " << response;
1273 pt::ptree root;
1274 std::istringstream is(response);
1275 pt::read_json(is, root);
1276 auto status = root.get_child_optional("Status");
1277 if (status) {
1278 auto currentStatus = status->get_child_optional("CurrentStatus");
1279 if (currentStatus) {
1280 std::vector<int> status;
1281 for (auto& item : *currentStatus) {
1282 status.push_back(item.second.get_value<int>());
#define _L(s)
Definition OBJ.cpp:34
std::function< void(wxString)> ErrorFn
Definition PrintHost.hpp:60
void send(const std::string &message)
Definition WebSocketClient.hpp:66
std::string receive(int timeout=0)
Definition WebSocketClient.hpp:71
wxString from_u8(const std::string &str)
Definition GUI.cpp:544
@ ELEGOO_GET_STATUS
Definition ElegooLink.cpp:60
std::string to_string(const Vec2crd &pt)
Definition Point.hpp:165

Referenced by loopUpload().

Here is the caller graph for this function:

◆ elegoo_cc2_test()

bool Slic3r::ElegooLink::elegoo_cc2_test ( wxString & curl_msg) const
private
478 {
479 const char* name = get_name();
480 bool res = true;
481 const auto token = cc2_token();
482 auto url = make_cc2_info_url();
483 auto http = Http::get(std::move(url));
484
485 http.header("X-Token", token);
486 http.header("Accept", "application/json");
487 http.on_error([&](std::string body, std::string error, unsigned status) {
488 BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting CC2 device info: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
489 res = false;
490 if (status == 401 || status == 403)
491 msg = format_error(body, _u8L("Invalid access code"), status);
492 else
493 msg = format_error(body, error.empty() ? _u8L("CC2 device not detected") : error, status);
494 })
495 .on_complete([&](std::string body, unsigned status) {
496 BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got CC2 device info: %2%") % name % body;
497 std::string error_message;
498 std::string serial_number;
499 if (!parse_cc2_response(body, error_message, &serial_number)) {
500 res = false;
501 msg = format_error(body, error_message.empty() ? _u8L("CC2 device not detected") : error_message, status);
502 return;
503 }
504 persist_sn(Http::get_host_header_value(m_host), token, serial_number);
505 res = true;
506 })
507#ifdef WIN32
508 .ssl_revoke_best_effort(m_ssl_revoke_best_effort)
509 .on_ip_resolve([&](std::string address) {
510 msg = GUI::from_u8(address);
511 })
512#endif // WIN32
513 .perform_sync();
514 return res;
515 }
static std::string get_host_header_value(const std::string &url)
Definition Http.cpp:1044
static Http get(std::string url)
Definition Http.cpp:886
bool m_ssl_revoke_best_effort
Definition OctoPrint.hpp:49
std::string m_host
Definition OctoPrint.hpp:46
virtual wxString format_error(const std::string &body, const std::string &error, unsigned status) const
Definition PrintHost.cpp:202
@ http
Definition IPrinterAgent.hpp:64
#define _u8L(s)
Definition I18N.hpp:11

References _u8L, cc2_token(), Slic3r::PrintHost::format_error(), Slic3r::GUI::from_u8(), Slic3r::Http::get(), Slic3r::Http::get_host_header_value(), get_name(), Slic3r::http, Slic3r::OctoPrint::m_host, Slic3r::OctoPrint::m_ssl_revoke_best_effort, and make_cc2_info_url().

Referenced by test(), and upload_inner_with_host().

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

◆ elegoo_test()

bool Slic3r::ElegooLink::elegoo_test ( wxString & curl_msg) const
private
428 {
429
430 const char *name = get_name();
431 bool res = true;
432 auto url = make_url("");
433 // 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.
434 auto http = Http::get(std::move(url));
435 set_auth(http);
436 http.on_error([&](std::string body, std::string error, unsigned status) {
437 BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
438 res = false;
439 msg = format_error(body, error, status);
440 })
441 .on_complete([&, this](std::string body, unsigned) {
442 BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
443 // Check if the response contains "ELEGOO" in any case.
444 // This is a simple check to see if the response is from an ElegooLink server.
445 std::regex re("ELEGOO", std::regex::icase);
446 std::smatch match;
447 if (std::regex_search(body, match, re)) {
448 res = true;
449 } else {
450 msg = format_error(body, _u8L("ElegooLink not detected"), 0);
451 res = false;
452 }
453 })
454#ifdef WIN32
455 .ssl_revoke_best_effort(m_ssl_revoke_best_effort)
456 .on_ip_resolve([&](std::string address) {
457 // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
458 // Remember resolved address to be reused at successive REST API call.
459 msg = GUI::from_u8(address);
460 })
461#endif // WIN32
462 .perform_sync();
463 return res;
464 }
std::string make_url(const std::string &path) const
Definition OctoPrint.cpp:517
virtual void set_auth(Http &http) const
Definition OctoPrint.cpp:508

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

Referenced by test(), and upload_inner_with_host().

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

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

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

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::ElegooLink::get_name ( ) const
overridevirtual

Implements Slic3r::PrintHost.

411{ return "ElegooLink"; }

Referenced by elegoo_cc2_test(), elegoo_test(), loopUpload(), loopUploadCC2(), uploadPart(), and uploadPartCC2().

Here is the caller graph for this function:

◆ get_post_upload_actions()

PrintHostPostUploadActions Slic3r::ElegooLink::get_post_upload_actions ( ) const
overridevirtual

Implements Slic3r::PrintHost.

413 {
414 if (classify_printer_model(m_printerModel) == ElegooPrinterType::CC2) {
416 } else {
418 }
419 }
@ None
Definition PrintHost.hpp:24
@ StartPrint
Definition PrintHost.hpp:25

References m_printerModel, Slic3r::None, and Slic3r::StartPrint.

◆ 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::ElegooLink::get_print_host_webui ( DynamicPrintConfig * config)
static
339 {
340 if (config == nullptr)
341 return {};
342
343 std::string fallback_webui = config->opt_string("print_host_webui");
344 if (fallback_webui.empty())
345 fallback_webui = config->opt_string("print_host");
346 if (!fallback_webui.empty()) {
347 const bool has_http_scheme = boost::algorithm::istarts_with(fallback_webui, "http");
348 const bool has_file_scheme = boost::algorithm::istarts_with(fallback_webui, "file:");
349
350 if (!has_http_scheme && !has_file_scheme)
351 fallback_webui = "http://" + fallback_webui;
352 }
353
354 const std::string host = config->opt_string("print_host");
355 if (host.empty())
356 return fallback_webui;
357
358 if (classify_printer_model(config->opt_string("printer_model")) != ElegooPrinterType::CC2)
359 return fallback_webui;
360
361 std::string web_path = GUI::into_u8(GUI::file_url_from_path(boost::filesystem::path(resources_dir()) / "web/elegoolink/lan_service_web/index.html"));
362 const std::string token = get_cc2_token(config->opt_string("printhost_apikey"));
363 const std::string host_ip = Http::get_host_header_value(host);
364
365 // Pass sn= so the panel can subscribe to the correct MQTT topics.
366 std::string sn = lookup_cc2_serial(config);
367 if (sn.empty()) {
368 std::string error_msg;
369 auto http = Http::get("http://" + host_ip + "/system/info?X-Token=" + escape_string(token));
370 http.timeout_connect(3).timeout_max(5);
371 http.header("X-Token", token);
372 http.header("Accept", "application/json");
373 http.on_complete([&](std::string body, unsigned /*status*/) {
374 parse_cc2_response(body, error_msg, &sn);
375 }).perform_sync();
376 if (!sn.empty())
377 persist_sn(host_ip, token, sn);
378 }
379
380 web_path += "?access_code=" + token;
381 web_path += "&ip=" + host_ip;
382 if (!sn.empty())
383 web_path += "&sn=" + sn;
384 web_path += "&id=elegoo_123456";
385
386 const std::string lang = GUI::wxGetApp().current_language_code_safe().utf8_string();
387 if (!lang.empty())
388 web_path += "&lang=" + lang;
389
390 if (GUI::get_app_config()->get_bool("developer_mode"))
391 web_path += "&dev=true";
392
393 return web_path;
394 }
wxString file_url_from_path(const boost::filesystem::path &path)
Definition GUI.cpp:569
std::string into_u8(const wxString &str)
Definition GUI.cpp:549
AppConfig * get_app_config()
Definition GUI.cpp:539
const std::string & resources_dir()
Definition utils.cpp:296

References Slic3r::GUI::file_url_from_path(), Slic3r::Http::get(), Slic3r::GUI::get_app_config(), Slic3r::Http::get_host_header_value(), Slic3r::http, Slic3r::GUI::into_u8(), Slic3r::DynamicConfig::opt_string(), and Slic3r::resources_dir().

Referenced by Slic3r::PrintHost::get_print_host_webui().

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

std::string Slic3r::ElegooLink::get_sn ( ) const
overridevirtual

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 from Slic3r::PrintHost.

422 {
423 // Panel IPC calls this on every load with a 10s timeout. Never block on HTTP
424 // here — URL sn= and dev_sn must be enough; HTTP is only for get_print_host_webui.
426 }

References Slic3r::OctoPrint::m_apikey, Slic3r::OctoPrint::m_host, and m_printerModel.

◆ get_storage()

virtual bool Slic3r::PrintHost::get_storage ( wxArrayString & ,
wxArrayString &  ) const
inlinevirtualinherited

Reimplemented in Slic3r::Moonraker, Slic3r::PrusaConnect, and Slic3r::PrusaLink.

88{ return false; }

◆ get_test_failed_msg()

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

Implements Slic3r::PrintHost.

617 {
618 return GUI::format_wxstr("%s: %s", _L("Could not connect to ElegooLink"), msg);
619 }
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::ElegooLink::get_test_ok_msg ( ) const
overridevirtual

Implements Slic3r::PrintHost.

612 {
613 return _L("Connection to ElegooLink is working correctly.");
614 }

References _L.

◆ has_auto_discovery()

bool Slic3r::ElegooLink::has_auto_discovery ( ) const
inlineoverridevirtual

Implements Slic3r::PrintHost.

34{ return false; }

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

◆ loopUpload()

bool Slic3r::ElegooLink::loopUpload ( std::string url,
PrintHostUpload upload_data,
ProgressFn prorgess_fn,
ErrorFn error_fn,
InfoFn info_fn ) const
private
798 {
799 const char* name = get_name();
800 const auto upload_filename = filename_to_utf8(upload_data.upload_path);
801 std::string source_path = path_to_utf8(upload_data.source_path);
802
803 // calc file size
804 boost::nowide::ifstream file(source_path, std::ios::binary | std::ios::ate);
805 std::streamsize size = file.tellg();
806 file.close();
807 const std::string fileSize = std::to_string(size);
808
809 // generate uuid
810 boost::uuids::random_generator generator;
811 boost::uuids::uuid uuid = generator();
812 std::string uuid_string = to_string(uuid);
813
814 std::string md5;
815 bbl_calc_md5(source_path, md5);
816
817 auto http = Http::post(url);
818#ifdef WIN32
819 // "Host" header is necessary here. In the workaround above (two mDNS..) we have got IP address from test connection and subsituted
820 // it into "url" variable. And when creating Http object above, libcurl automatically includes "Host" header from address it got.
821 // Thus "Host" is set to the resolved IP instead of host filled by user. We need to change it back. Not changing the host would work
822 // on the most cases (where there is 1 service on 1 hostname) but would break when f.e. reverse proxy is used (issue #9734). Also
823 // when allow_ip_resolve = 0, this is not needed, but it should not break anything if it stays.
824 // https://www.rfc-editor.org/rfc/rfc7230#section-5.4
825 http.header("Host", Http::get_host_header_value(m_host));
826 http.header("Accept", "application/json, text/plain, */*");
827#endif // _WIN32
828 set_auth(http);
829
830 bool res = false;
831 const int packageCount = (size + MAX_UPLOAD_PACKAGE_LENGTH - 1) / MAX_UPLOAD_PACKAGE_LENGTH;
832
833 for (size_t i = 0; i < packageCount; i++) {
834 BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2%/%3%") % name % (i + 1) % packageCount;
835 const size_t offset = MAX_UPLOAD_PACKAGE_LENGTH * i;
837 // if it is the last package, the length is the remainder of the file size divided by MAX_UPLOAD_PACKAGE_LENGTH
838 if ((i == packageCount - 1) && (size % MAX_UPLOAD_PACKAGE_LENGTH > 0)) {
840 }
841 res = uploadPart(
842 http, md5, uuid_string, source_path, upload_filename, size, offset, length,
843 [size, i, progress_fn](Http::Progress progress, bool& cancel) {
844 Http::Progress p(0, 0, size, i * MAX_UPLOAD_PACKAGE_LENGTH + progress.ulnow, progress.buffer);
845 progress_fn(p, cancel);
846 },
847 error_fn, info_fn);
848 if (!res) {
849 break;
850 }
851 }
852
853 if (res) {
854 if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) {
855 // connect to websocket, since the upload is successful, the file will be printed
856 std::string wsUrl = Http::get_host_from_url(m_host);
857 WebSocketClient client;
858 try {
859 client.connect(wsUrl, "3030", "/websocket");
860 } catch (std::exception& e) {
861 const auto errorString = std::string(e.what());
862 if (errorString.find("The WebSocket handshake was declined by the remote peer") != std::string::npos) {
863 // error_fn(
864 // _L("The number of printer connections has exceeded the limit. Please disconnect other connections, restart the "
865 // "printer and slicer, and then try again."));
866 error_fn(_L("The file has been transferred, but some unknown errors occurred. Please check the device page for the file and try to start printing again."));
867 } else {
868 error_fn(std::string("\n") + wxString::FromUTF8(e.what()));
869 }
870 return false;
871 }
872 std::string timeLapse = "0";
873 std::string heatedBedLeveling = "0";
874 std::string bedType = "0";
875 timeLapse = upload_data.extended_info["timeLapse"];
876 heatedBedLeveling = upload_data.extended_info["heatedBedLeveling"];
877 bedType = upload_data.extended_info["bedType"];
878
879 std::this_thread::sleep_for(std::chrono::seconds(1));
880 if (checkResult(client, error_fn)) {
881 // send print command
882 std::this_thread::sleep_for(std::chrono::seconds(1));
883 res = print(client, timeLapse, heatedBedLeveling, bedType, upload_filename, error_fn);
884 }else{
885 res = false;
886 }
887 }
888 }
889 return res;
890 }
static std::string get_host_from_url(const std::string &url, std::string *port=nullptr)
Definition Http.cpp:1008
static Http post(std::string url)
Definition Http.cpp:891
void connect(const std::string &host, const std::string &port, const std::string &path="/")
Definition WebSocketClient.hpp:39
std::string filename_to_utf8(const boost::filesystem::path &path)
Definition PrinterWebViewHandler.cpp:88
bool bbl_calc_md5(std::string &filename, std::string &md5_out)
Definition utils.cpp:1902
double length(const Points &pts)
Definition MultiPoint.hpp:181
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582

References _L, Slic3r::bbl_calc_md5(), Slic3r::Http::Progress::buffer, checkResult(), Slic3r::PrintHostUpload::extended_info, Slic3r::Http::get_host_from_url(), Slic3r::Http::get_host_header_value(), get_name(), Slic3r::http, Slic3r::length(), Slic3r::OctoPrint::m_host, MAX_UPLOAD_PACKAGE_LENGTH, Slic3r::offset(), Slic3r::Http::post(), Slic3r::PrintHostUpload::post_action, print(), Slic3r::OctoPrint::set_auth(), Slic3r::PrintHostUpload::source_path, Slic3r::StartPrint, Slic3r::to_string(), Slic3r::Http::Progress::ulnow, Slic3r::PrintHostUpload::upload_path, and uploadPart().

Referenced by upload_inner_with_host().

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

◆ loopUploadCC2()

bool Slic3r::ElegooLink::loopUploadCC2 ( std::string url,
const std::string & host_header,
PrintHostUpload upload_data,
ProgressFn prorgess_fn,
ErrorFn error_fn,
InfoFn info_fn ) const
private
973 {
974 BOOST_LOG_TRIVIAL(info) << get_name() << ": Uploading file to Elegoo CC2";
975 const auto upload_filename = filename_to_utf8(upload_data.upload_path);
976 std::string source_path = path_to_utf8(upload_data.source_path);
977 std::string md5;
978 bbl_calc_md5(source_path, md5);
979 std::transform(md5.begin(), md5.end(), md5.begin(), [](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
980
981 boost::nowide::ifstream file(source_path, std::ios::binary | std::ios::ate);
982 if (!file.is_open()) {
983 error_fn(_L("Failed to open file for upload."));
984 return false;
985 }
986
987 const std::streamsize size = file.tellg();
988 file.close();
989 if (size <= 0) {
990 error_fn(_L("The file is empty or could not be read."));
991 return false;
992 }
993
994 if (md5.empty()) {
995 error_fn(_L("Failed to calculate file checksum."));
996 return false;
997 }
998
999 const std::string token = cc2_token();
1000 const int packageCount = static_cast<int>((size + MAX_UPLOAD_PACKAGE_LENGTH - 1) / MAX_UPLOAD_PACKAGE_LENGTH);
1001 auto http = Http::put2(url);
1002 http.timeout_connect(30)
1003 .timeout_max(180);
1004
1005 bool res = false;
1006 for (int i = 0; i < packageCount; ++i) {
1007 const size_t offset = MAX_UPLOAD_PACKAGE_LENGTH * static_cast<size_t>(i);
1009 if (i == packageCount - 1 && size % MAX_UPLOAD_PACKAGE_LENGTH > 0)
1010 length = static_cast<size_t>(size % MAX_UPLOAD_PACKAGE_LENGTH);
1011
1012 res = uploadPartCC2(
1013 http, host_header, token, md5, source_path, upload_filename, static_cast<size_t>(size), offset, length,
1014 [size, i, progress_fn](Http::Progress progress, bool& cancel) {
1015 const size_t uploaded = static_cast<size_t>(i) * MAX_UPLOAD_PACKAGE_LENGTH + progress.ulnow;
1016 Http::Progress merged(0, 0, static_cast<size_t>(size), std::min(static_cast<size_t>(size), uploaded), progress.buffer,
1017 progress.upload_spd);
1018 progress_fn(merged, cancel);
1019 },
1020 error_fn);
1021 if (!res)
1022 break;
1023 }
1024
1025 if (res && upload_data.post_action == PrintHostPostUploadAction::StartPrint)
1026 BOOST_LOG_TRIVIAL(info) << get_name() << ": CC2 upload completed; start print is not supported.";
1027
1028 (void) info_fn;
1029 return res;
1030 }
static Http put2(std::string url)
Definition Http.cpp:907

References _L, Slic3r::bbl_calc_md5(), Slic3r::Http::Progress::buffer, cc2_token(), get_name(), Slic3r::http, Slic3r::length(), MAX_UPLOAD_PACKAGE_LENGTH, Slic3r::offset(), Slic3r::PrintHostUpload::post_action, Slic3r::Http::put2(), Slic3r::PrintHostUpload::source_path, Slic3r::StartPrint, Slic3r::Http::Progress::ulnow, Slic3r::PrintHostUpload::upload_path, Slic3r::Http::Progress::upload_spd, and uploadPartCC2().

Referenced by upload_inner_with_host().

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

◆ make_cc2_info_url()

std::string Slic3r::ElegooLink::make_cc2_info_url ( ) const
private
402 {
403 return make_url("system/info?X-Token=" + escape_string(cc2_token()));
404 }

References cc2_token(), and Slic3r::OctoPrint::make_url().

Referenced by elegoo_cc2_test().

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

◆ make_cc2_upload_url()

std::string Slic3r::ElegooLink::make_cc2_upload_url ( ) const
private
407 {
408 return make_url("upload");
409 }

References Slic3r::OctoPrint::make_url().

Referenced by upload_inner_with_host().

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

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

◆ print()

bool Slic3r::ElegooLink::print ( WebSocketClient & client,
std::string timeLapse,
std::string heatedBedLeveling,
std::string bedType,
const std::string filename,
ErrorFn error_fn ) const
private
1101 {
1102
1103 // convert bedType to 0 or 1, 0 is PTE, 1 is PC
1104 if (bedType == std::to_string((int)BedType::btPC)){
1105 bedType = "1";
1106 }else{
1107 bedType = "0";
1108 }
1109 bool res = false;
1110 // create a random UUID generator
1111 boost::uuids::random_generator generator;
1112 // generate a UUID
1113 boost::uuids::uuid uuid = generator();
1114 std::string uuid_string = to_string(uuid);
1115 try {
1116 std::string requestID = uuid_string;
1117 auto now = std::chrono::system_clock::now();
1118 auto duration = now.time_since_epoch();
1119 auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
1120 std::string timestamp = std::to_string(milliseconds);
1121 std::string jsonString = R"({
1122 "Id":"",
1123 "Data":{
1124 "Cmd":)"+std::to_string(ElegooLinkCommand::ELEGOO_START_PRINT)+R"(,
1125 "Data":{
1126 "Filename":"/local/)" + filename + R"(",
1127 "StartLayer":0,
1128 "Calibration_switch":)" + heatedBedLeveling + R"(,
1129 "PrintPlatformType":)" + bedType + R"(,
1130 "Tlp_Switch":)" + timeLapse + R"(
1131 },
1132 "RequestID":")"+ uuid_string + R"(",
1133 "MainboardID":"",
1134 "TimeStamp":)" + timestamp + R"(,
1135 "From":1
1136 }
1137 })";
1138 std::cout << "send: " << jsonString << std::endl;
1139 BOOST_LOG_TRIVIAL(info) << "start print, param: " << jsonString;
1140 client.send(jsonString);
1141 // wait 30s
1142 auto start_time = std::chrono::steady_clock::now();
1143 do{
1144 std::string response = client.receive();
1145 std::cout << "Received: " << response << std::endl;
1146 BOOST_LOG_TRIVIAL(info) << "Received: " << response;
1147
1148 //sample response
1149 // {"Id":"979d4C788A4a78bC777A870F1A02867A","Data":{"Cmd":128,"Data":{"Ack":1},"RequestID":"5223de52cc7642ae8d7924f9dd46f6ad","MainboardID":"1c7319d30105041800009c0000000000","TimeStamp":1735032553},"Topic":"sdcp/response/1c7319d30105041800009c0000000000"}
1150 pt::ptree root;
1151 std::istringstream is(response);
1152 pt::read_json(is, root);
1153
1154 auto data = root.get_child_optional("Data");
1155 if(!data){
1156 BOOST_LOG_TRIVIAL(info) << "wait for start print response";
1157 continue;
1158 }
1159 auto cmd = data->get_optional<int>("Cmd");
1160 if(!cmd){
1161 BOOST_LOG_TRIVIAL(info) << "wait for start print response";
1162 continue;
1163 }
1165 auto _ack = data->get_optional<int>("Data.Ack");
1166 if(!_ack){
1167 BOOST_LOG_TRIVIAL(error) << "start print failed, ack not found";
1168 error_fn(_L("Error code not found"));
1169 break;
1170 }
1171 auto ack = static_cast<ElegooLinkStartPrintAck>(*_ack);
1173 res = true;
1174 }else{
1175 res = false;
1176 BOOST_LOG_TRIVIAL(error) << "start print failed: ack: " << ack;
1177 wxString error_message = "";
1178 switch(ack){
1180 {
1181 error_message =_L("The printer is busy, please check the device page for the file and try to start printing again.");
1182 break;
1183 }
1185 {
1186 error_message =_(L("The file is lost, please check and try again."));
1187 break;
1188 }
1190 {
1191 error_message =_(L("The file is corrupted, please check and try again."));
1192 break;
1193 }
1197 {
1198 error_message =_(L("Transmission abnormality, please check and try again."));
1199 break;
1200 }
1202 {
1203 error_message =_(L("The file does not match the printer, please check and try again."));
1204 break;
1205 }
1206 default:
1207 {
1208 error_message =_L("Unknown error");
1209 break;
1210 }
1211 }
1212
1213 error_message += " " + wxString::Format(_L("Error code: %d"),ack);
1214 error_fn(error_message);
#define _(s)
Definition 3mf.cpp:306
#define L(s)
Definition ExtrusionEntity.cpp:25
ElegooLinkStartPrintAck
Definition ElegooLink.cpp:68
@ SDCP_PRINT_CTRL_ACK_INVALID_RESOLUTION
Definition ElegooLink.cpp:74
@ SDCP_PRINT_CTRL_ACK_FILEIO_FAILED
Definition ElegooLink.cpp:73
@ SDCP_PRINT_CTRL_ACK_OK
Definition ElegooLink.cpp:69
@ SDCP_PRINT_CTRL_ACK_NOT_FOUND
Definition ElegooLink.cpp:71
@ SDCP_PRINT_CTRL_ACK_UNKNOW_FORMAT
Definition ElegooLink.cpp:75
@ SDCP_PRINT_CTRL_ACK_MD5_FAILED
Definition ElegooLink.cpp:72
@ SDCP_PRINT_CTRL_ACK_UNKNOW_MODEL
Definition ElegooLink.cpp:76
@ SDCP_PRINT_CTRL_ACK_BUSY
Definition ElegooLink.cpp:70
@ btPC
Definition PrintConfig.hpp:509
@ ELEGOO_START_PRINT
Definition ElegooLink.cpp:64

References _, _L, Slic3r::btPC, Slic3r::ELEGOO_START_PRINT, L, Slic3r::SDCP_PRINT_CTRL_ACK_BUSY, Slic3r::SDCP_PRINT_CTRL_ACK_FILEIO_FAILED, Slic3r::SDCP_PRINT_CTRL_ACK_INVALID_RESOLUTION, Slic3r::SDCP_PRINT_CTRL_ACK_MD5_FAILED, Slic3r::SDCP_PRINT_CTRL_ACK_NOT_FOUND, Slic3r::SDCP_PRINT_CTRL_ACK_OK, Slic3r::SDCP_PRINT_CTRL_ACK_UNKNOW_FORMAT, Slic3r::SDCP_PRINT_CTRL_ACK_UNKNOW_MODEL, and Slic3r::to_string().

Referenced by loopUpload().

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

◆ set_auth()

void Slic3r::OctoPrint::set_auth ( Http & http) const
protectedvirtualinherited

Reimplemented in Slic3r::PrusaLink.

509{
510 http.header("X-Api-Key", m_apikey);
511
512 if (!m_cafile.empty()) {
513 http.ca_file(m_cafile);
514 }
515}

References Slic3r::http, m_apikey, and m_cafile.

Referenced by Slic3r::ElegooLink::elegoo_test(), Slic3r::ElegooLink::loopUpload(), test(), and upload_inner_with_host().

Here is the caller 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::ElegooLink::test ( wxString & curl_msg) const
overridevirtual

Reimplemented from Slic3r::OctoPrint.

465 {
467 case ElegooPrinterType::Other:
468 return OctoPrint::test(curl_msg);
469 case ElegooPrinterType::CC2:
470 return elegoo_cc2_test(curl_msg);
471 case ElegooPrinterType::CC:
472 return elegoo_test(curl_msg);
473 }
474 return false;
475 }
virtual bool test(wxString &curl_msg) const override
Definition OctoPrint.cpp:207

References elegoo_cc2_test(), elegoo_test(), m_printerModel, and Slic3r::OctoPrint::test().

Here is the call 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() [1/2]

bool Slic3r::ElegooLink::upload ( PrintHostUpload upload_data,
ProgressFn prorgess_fn,
ErrorFn error_fn,
InfoFn info_fn ) const
override
1033 {
1034 #ifndef WIN32
1035 return upload_inner_with_host(std::move(upload_data), prorgess_fn, error_fn, info_fn);
1036 #else
1037 std::string host = Http::get_host_from_url(m_host);
1038
1039 // decide what to do based on m_host - resolve hostname or upload to ip
1040 std::vector<boost::asio::ip::address> resolved_addr;
1041 boost::system::error_code ec;
1042 boost::asio::ip::address host_ip = boost::asio::ip::make_address(host, ec);
1043 if (!ec) {
1044 resolved_addr.push_back(host_ip);
1045 } else if ( GUI::get_app_config()->get_bool("allow_ip_resolve") && boost::algorithm::ends_with(host, ".local")){
1046 Bonjour("octoprint")
1047 .set_hostname(host)
1048 .set_retries(5) // number of rounds of queries send
1049 .set_timeout(1) // after each timeout, if there is any answer, the resolving will stop
1050 .on_resolve([&ra = resolved_addr](const std::vector<BonjourReply>& replies) {
1051 for (const auto & rpl : replies) {
1052 boost::asio::ip::address ip(rpl.ip);
1053 ra.emplace_back(ip);
1054 BOOST_LOG_TRIVIAL(info) << "Resolved IP address: " << rpl.ip;
1055 }
1056 })
1057 .resolve_sync();
1058 }
1059 if (resolved_addr.empty()) {
1060 // no resolved addresses - try system resolving
1061 BOOST_LOG_TRIVIAL(error) << "ElegooLink failed to resolve hostname " << m_host << " into the IP address. Starting upload with system resolving.";
1062 return upload_inner_with_host(std::move(upload_data), prorgess_fn, error_fn, info_fn);
1063 } else if (resolved_addr.size() == 1) {
1064 // one address resolved - upload there
1065 return upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn, error_fn, info_fn, resolved_addr.front());
1066 } else if (resolved_addr.size() == 2 && resolved_addr[0].is_v4() != resolved_addr[1].is_v4()) {
1067 // there are just 2 addresses and 1 is ip_v4 and other is ip_v6
1068 // try sending to both. (Then if both fail, show both error msg after second try)
1069 wxString error_message;
1070 if (!upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn
1071 , [&msg = error_message, resolved_addr](wxString error) { msg = GUI::format_wxstr("%1%: %2%", resolved_addr.front(), error); }
1072 , info_fn, resolved_addr.front())
1073 &&
1074 !upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn
1075 , [&msg = error_message, resolved_addr](wxString error) { msg += GUI::format_wxstr("\n%1%: %2%", resolved_addr.back(), error); }
1076 , info_fn, resolved_addr.back())
1077 ) {
1078
1079 error_fn(error_message);
1080 return false;
1081 }
1082 return true;
1083 } else {
1084 // There are multiple addresses - user needs to choose which to use.
1085 size_t selected_index = resolved_addr.size();
1086 IPListDialog dialog(nullptr, boost::nowide::widen(m_host), resolved_addr, selected_index);
1087 if (dialog.ShowModal() == wxID_OK && selected_index < resolved_addr.size()) {
1088 return upload_inner_with_resolved_ip(std::move(upload_data), prorgess_fn, error_fn, info_fn, resolved_addr[selected_index]);
1089 }
1090 }
1091 return false;
1092 #endif // WIN32
1093 }

References Slic3r::GUI::format_wxstr(), Slic3r::GUI::get_app_config(), Slic3r::Http::get_host_from_url(), Slic3r::OctoPrint::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() [2/2]

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}
virtual bool upload_inner_with_host(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
Definition OctoPrint.cpp:409

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::ElegooLink::upload_inner_with_host ( PrintHostUpload upload_data,
ProgressFn prorgess_fn,
ErrorFn error_fn,
InfoFn info_fn ) const
overrideprotectedvirtual
658 {
659 const auto printer_type = classify_printer_model(m_printerModel);
660
661 if (printer_type == ElegooPrinterType::Other)
662 return OctoPrint::upload_inner_with_host(std::move(upload_data), prorgess_fn, error_fn, info_fn);
663
664 if (printer_type == ElegooPrinterType::CC2) {
665 wxString cc2_msg;
666 if (!elegoo_cc2_test(cc2_msg)) {
667 error_fn(std::move(cc2_msg));
668 return false;
669 }
670
671 std::string url;
672#ifdef WIN32
673 if (m_host.find("https://") == 0 || cc2_msg.empty() || !GUI::get_app_config()->get_bool("allow_ip_resolve"))
674#endif // _WIN32
675 {
676 url = make_cc2_upload_url();
677 }
678#ifdef WIN32
679 else {
680 info_fn(L"resolve", cc2_msg);
681 url = substitute_host(make_cc2_upload_url(), GUI::into_u8(cc2_msg));
682 BOOST_LOG_TRIVIAL(info) << "CC2 upload address after ip resolve: " << url;
683 }
684#endif // _WIN32
685
686 return loopUploadCC2(url, Http::get_host_header_value(m_host), std::move(upload_data), prorgess_fn, error_fn, info_fn);
687 }
688
689 wxString legacy_msg;
690 if(!elegoo_test(legacy_msg)){
691 error_fn(std::move(legacy_msg));
692 return false;
693 }
694
695 std::string url;
696 #ifdef WIN32
697 // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
698 if (m_host.find("https://") == 0 || legacy_msg.empty() || !GUI::get_app_config()->get_bool("allow_ip_resolve"))
699 #endif // _WIN32
700 {
701 // If https is entered we assume signed ceritificate is being used
702 // IP resolving will not happen - it could resolve into address not being specified in cert
703 url = make_url("uploadFile/upload");
704 }
705 #ifdef WIN32
706 else {
707 // Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
708 // Curl uses easy_getinfo to get ip address of last successful transaction.
709 // If it got the address use it instead of the stored in "host" variable.
710 // This new address returns in "test_msg_or_host_ip" variable.
711 // Solves troubles of uploades failing with name address.
712 // in original address (m_host) replace host for resolved ip
713 info_fn(L"resolve", legacy_msg);
714 url = substitute_host(make_url("uploadFile/upload"), GUI::into_u8(legacy_msg));
715 BOOST_LOG_TRIVIAL(info) << "Upload address after ip resolve: " << url;
716 }
717 #endif // _WIN32
718
719 bool res = loopUpload(url, upload_data, prorgess_fn, error_fn, info_fn);
720 return res;
721 }
bool get_bool(const std::string &section, const std::string &key) const
Definition AppConfig.hpp:163

References elegoo_cc2_test(), elegoo_test(), Slic3r::GUI::get_app_config(), Slic3r::Http::get_host_header_value(), Slic3r::GUI::into_u8(), L, loopUpload(), loopUploadCC2(), Slic3r::OctoPrint::m_host, m_printerModel, make_cc2_upload_url(), Slic3r::OctoPrint::make_url(), and Slic3r::OctoPrint::upload_inner_with_host().

Referenced by upload().

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

◆ upload_inner_with_host() [2/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}
const char * get_name() const override
Definition OctoPrint.cpp:146

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:

◆ uploadPart()

bool Slic3r::ElegooLink::uploadPart ( Http & http,
std::string md5,
std::string uuid,
std::string path,
std::string filename,
size_t filesize,
size_t offset,
size_t length,
ProgressFn prorgess_fn,
ErrorFn error_fn,
InfoFn info_fn ) const
private
730 {
731 const char* name = get_name();
732 bool result = false;
733 http.form_clear();
734 http.form_add("Check", "1")
735 .form_add("S-File-MD5", md5)
736 .form_add("Offset", std::to_string(offset))
737 .form_add("Uuid", uuid)
738 .form_add("TotalSize", std::to_string(filesize))
739 .form_add_file("File", path, filename, offset, length)
740 .on_complete([&](std::string body, unsigned status) {
741 BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
742 if (status == 200) {
743 try {
744 pt::ptree root;
745 std::istringstream is(body);
746 pt::read_json(is, root);
747 std::string code = root.get<std::string>("code");
748 if (code == "000000") {
749 // info_fn(L"complete", wxString());
750 result = true;
751 } else {
752 // get error messages
753 pt::ptree messages = root.get_child("messages");
754 std::string error_message = (boost::format(_u8L("Error code: %1%")) % code).str() + "\n";
755 for (pt::ptree::value_type& message : messages) {
756 std::string field = message.second.get<std::string>("field");
757 std::string msg = message.second.get<std::string>("message");
758 error_message += field + ":" + msg + "\n";
759 }
760 error_fn(wxString::FromUTF8(error_message));
761 }
762 } catch (...) {
763 BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error parsing response: %2%") % name % body;
764 error_fn(_L("Error parsing response"));
765 }
766 } else {
767 error_fn(format_error(body, _u8L("Upload failed"), status));
768 }
769 })
770 .on_error([&](std::string body, std::string error, unsigned status) {
771 BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status %
772 body;
773 error_fn(format_error(body, error, status));
774 })
775 .on_progress([&](Http::Progress progress, bool& cancel) {
776 // If upload is finished, do not call progress_fn
777 // on_complete will be called after some time, so we do not need to call it here
778 // Because some devices will call on_complete after the upload progress reaches 100%,
779 // so we need to process it here, based on on_complete
780 if (progress.ultotal == progress.ulnow) {
781 // Upload is finished
782 return;
783 }
784 prorgess_fn(std::move(progress), cancel);
785 if (cancel) {
786 // Upload was canceled
787 BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
788 }
789 })
790#ifdef WIN32
791 .ssl_revoke_best_effort(m_ssl_revoke_best_effort)
792#endif
793 .perform_sync();
794 return result;
795 }

References _L, _u8L, Slic3r::PrintHost::format_error(), get_name(), Slic3r::http, Slic3r::length(), Slic3r::OctoPrint::m_ssl_revoke_best_effort, Slic3r::offset(), Slic3r::Http::Progress::ulnow, and Slic3r::Http::Progress::ultotal.

Referenced by loopUpload().

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

◆ uploadPartCC2()

bool Slic3r::ElegooLink::uploadPartCC2 ( Http & http,
const std::string & host_header,
const std::string & token,
const std::string & md5,
const boost::filesystem::path & path,
const std::string & filename,
size_t filesize,
size_t offset,
size_t length,
ProgressFn prorgess_fn,
ErrorFn error_fn ) const
private
903 {
904 const char* name = get_name();
905
906 boost::nowide::ifstream file(path, std::ios::binary);
907 if (!file.is_open()) {
908 error_fn(_L("Failed to open file for upload."));
909 return false;
910 }
911
912 file.seekg(static_cast<std::streamoff>(offset), std::ios::beg);
913 std::string chunk(length, '\0');
914 file.read(chunk.data(), static_cast<std::streamsize>(length));
915 if (!file && static_cast<size_t>(file.gcount()) != length) {
916 error_fn(_L("Failed to read file chunk for upload."));
917 return false;
918 }
919 chunk.resize(static_cast<size_t>(file.gcount()));
920
921 const size_t end_offset = offset + chunk.size() - 1;
922 const auto range = std::string("bytes ") + std::to_string(offset) + "-" + std::to_string(end_offset) + "/" + std::to_string(filesize);
923
924 bool result = false;
925 http.headers_reset();
926 if (!host_header.empty())
927 http.header("Host", host_header);
928 http.header("Accept", "application/json")
929 .header("Content-Type", "application/octet-stream")
930 .header("Content-Length", std::to_string(chunk.size()))
931 .header("Content-Range", range)
932 .header("X-File-Name", filename)
933 .header("X-File-MD5", md5)
934 .header("X-Token", token)
935 .set_post_body(chunk)
936 .on_complete([&](std::string body, unsigned status) {
937 BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: CC2 chunk uploaded: HTTP %2%: %3%") % name % status % body;
938 std::string error_message;
939 if (!parse_cc2_response(body, error_message)) {
940 error_fn(format_error(body, error_message.empty() ? _u8L("CC2 upload failed") : error_message, status));
941 return;
942 }
943 result = true;
944 })
945 .on_error([&](std::string body, std::string error, unsigned status) {
946 BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading CC2 chunk: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
947 if (status == 401 || status == 403)
948 error_fn(format_error(body, _u8L("Invalid access code"), status));
949 else
950 error_fn(format_error(body, error.empty() ? _u8L("CC2 upload failed") : error, status));
951 })
952 .on_progress([&](Http::Progress progress, bool& cancel) {
953 if (progress.ultotal == progress.ulnow)
954 return;
955 prorgess_fn(std::move(progress), cancel);
956 if (cancel)
957 BOOST_LOG_TRIVIAL(info) << name << ": CC2 upload canceled";
958 })
959#ifdef WIN32
960 .ssl_revoke_best_effort(m_ssl_revoke_best_effort)
961#endif
962 .perform_sync();
963
964 return result;
965 }
auto range(Cont &&cont)
Definition libslic3r.h:405

References _L, _u8L, Slic3r::PrintHost::format_error(), get_name(), Slic3r::http, Slic3r::length(), Slic3r::OctoPrint::m_ssl_revoke_best_effort, Slic3r::offset(), Slic3r::range(), Slic3r::Http::Progress::ulnow, and Slic3r::Http::Progress::ultotal.

Referenced by loopUploadCC2().

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

◆ validate_version_text()

bool Slic3r::ElegooLink::validate_version_text ( const boost::optional< std::string > & version_text) const
overrideprotectedvirtual

Reimplemented from Slic3r::OctoPrint.

724 {
725 return true;
726 }

Member Data Documentation

◆ m_apikey

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

◆ m_cafile

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

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

◆ m_host

◆ m_printerModel

std::string Slic3r::ElegooLink::m_printerModel
private

◆ m_ssl_revoke_best_effort


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