Files
OrcaSlicer/src/slic3r/Utils/UltiMaker.cpp
T
HanifKoh 4895bc03b4 Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced (#16099)
* Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced

Generated with include-what-you-use and applied conservatively. Only OrcaSlicer's own headers, the ones under src/ and tests/, are removed or forward-declared; standard-library and third-party includes are left alone. An include is removed only when both the Release and the Debug configuration leave it unused, never from inside a conditional block, and never from a file with platform-specific blocks, which only gain includes. Files whose only use of a header sits behind a feature or debug macro (libvgcode's OpenGL ES and marker code, the ARACHNE/TESTS_EXPORT_SVGS debug output) keep their includes.

clonable_ptr.hpp gains #pragma once; it had no include guard and was only safe while Config.hpp was its sole includer.

* Remove Unused Project Includes From Files With Platform-Specific Code

A Linux include-what-you-use run cannot see the code inside _WIN32, __APPLE__ or __linux__ blocks, so its verdict is only taken where nothing the removed header declares, directly or through what it includes, is named inside those blocks. Removals also have to hold in both the Release and Debug configuration and never touch a line inside a conditional block.

* Restore the libslic3r Precompiled Header and Direct Includes Lost in the Platform Pass

The platform-file pass treated pchheader.hpp as an ordinary header and
emptied it, and left GUI_Preview.hpp and 14 other files relying on
headers they no longer reached directly.

* Restore MainFrame.hpp in ParamsDialog.cpp for the Windows-Only Reparent Call

* Include Headers That Files Reached Through Ones the Cleanup Removed

* Drop Includes Duplicated by the Cleanup or by Main's Own Additions

* Leave PreciseSeam.cpp as Main Has It After the Precise Seam Rework
2026-10-05 16:47:17 +08:00

667 lines
23 KiB
C++

#include "UltiMaker.hpp"
#include <algorithm>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <cstdio>
#include <cassert>
#include <ctime>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <utility>
#include <exception>
#include <sstream>
#include <fstream>
#include <wx/frame.h>
#include <wx/event.h>
#include <wx/progdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/string.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>
#include "PrintHost.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "Http.hpp"
#include <iostream>
#include <stdio.h>
#include <string>
namespace fs = boost::filesystem;
namespace pt = boost::property_tree;
namespace Slic3r {
UltiMaker::UltiMaker(DynamicPrintConfig *config) :
m_host(config->opt_string("print_host")),
host(config->opt_string("print_host")),
m_api_username(config->opt_string("printhost_user")),
m_api_password(config->opt_string("printhost_password")),
m_ssl_revoke_best_effort(config->opt_bool("printhost_ssl_ignore_revoke"))
{}
const char* UltiMaker::get_name() const { return "UltiMaker"; }
// Modified from OctoPrint::test in OctoPrint.cpp
bool UltiMaker::test(wxString &msg) const
{
// Returns true on success, false on error.
// If called with specific arg, just generate the auth creds.
if (msg == "generate_auth_creds") {
BOOST_LOG_TRIVIAL(debug) << "UltiMaker: test called with generate_auth_creds! Generating the auth credentials.";
return this->generate_auth_creds(msg);
}
// Since the request is performed synchronously here,
// it is ok to refer to `msg` from within the closure
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: Attempting to test machine");
const char* name = get_name();
bool res = true;
auto url = (boost::format("http://%1%/api/v1/system/variant") % host).str();
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Get system variant at: %2%") % name % url;
auto http = Http::get(std::move(url));
set_auth(http);
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting system variant: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
res = false;
msg = format_error(body, error, status);
})
.on_complete([name, &res](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got system variant: %2%") % name % body;
// Validate that response is correct ("UltiMaker 3", "UltiMaker 3 extended" or "UltiMaker S5")
res = (boost::starts_with(body, "\"Ultimaker 3") || body == "\"Ultimaker S5\"");
})
#ifdef WIN32
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
.on_ip_resolve([&](std::string address) {
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
// Remember resolved address to be reused at successive REST API call.
msg = GUI::from_u8(address);
})
#endif // WIN32
.perform_sync();
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Testing auth.") % name;
std::string auth_result = test_auth();
if ( !boost::starts_with(auth_result,"OK") ) { msg = auth_result; res = false; };
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Auth test result: %2%") % name % auth_result;
return res;
}
wxString UltiMaker::get_test_ok_msg () const
{
return _("Connection to UltiMaker works correctly.");
}
wxString UltiMaker::get_test_failed_msg (wxString &msg) const
{
return GUI::from_u8((boost::format("%s: %s")
% _utf8("Could not connect to UltiMaker")
% std::string(msg.ToUTF8())).str());
}
#pragma region Auth
// Modified from PrusaLink::set_auth in OctoPrint.cpp
void UltiMaker::set_auth(Http& http) const
{
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: set_auth, m_api_username of %1% and m_api_password of %2%") % get_api_username() % get_api_password();
http.auth_digest(m_api_username, m_api_password);
}
bool UltiMaker::has_auth_creds() const{
// True if has authentication credentials, false otherwise
return (!m_api_username.empty()) && (!m_api_password.empty());
}
bool UltiMaker::is_authorized() const{
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: is_authorized() checking if creds are valid.");
const char* name = get_name();
bool rtn = false;
auto url = (boost::format("http://%1%/api/v1/auth/verify") % host).str();
auto http = Http::get(std::move(url));
set_auth(http);
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error verifying credentials: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
if (status == 403 || body == "{\"message\": \"Authorization required.\"}") {
rtn = false;
}
})
.on_complete([&](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: url completed without error: %1%") % url;
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got credential verification: %2%") % name % body;
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: [auth] rtn=%2%") % name % rtn; // DEBUG
try {
// Validate that response is correct
rtn = (boost::starts_with(body, "\"ok\"") || body == "{\"message\": \"ok\"}");
}
catch (const std::exception &) {
rtn = false;
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: is_authorized Caught error") % name;
}
})
#ifdef WIN32
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
.on_ip_resolve([&](std::string address) {
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
// Remember resolved address to be reused at successive REST API call.
// msg = GUI::from_u8(address);
})
#endif // WIN32
.perform_sync();
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: [auth] rtn=%2%") % name % rtn;
return rtn;
}
std::string UltiMaker::auth_status() const{
// returns 'authorized', 'unauthorized', or 'waiting', or 'ERROR: unknown value' on a different output
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: auth_status() verifying credentials.");
const char* name = get_name();
std::string rtn = "ERROR: unknown value";
auto url = (boost::format("http://%1%/api/v1/auth/check/%2%") % host % m_api_username).str();
auto http = Http::get(std::move(url));
set_auth(http);
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error verifying credentials: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
})
.on_complete([&](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: url completed without error: %1%") % url;
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got credential verification: %2%") % name % body;
try {
if (body == "{\"message\": \"unknown\"}") { rtn = "waiting";}
if (body == "{\"message\": \"authorized\"}" ) {rtn = "authorized";}
if (body == "{\"message\": \"unauthorized\"}") {rtn = "unauthorized";}
}
catch (const std::exception &) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: auth_status Caught error") % name;
}
})
#ifdef WIN32
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
.on_ip_resolve([&](std::string address) {
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
// Remember resolved address to be reused at successive REST API call.
// msg = GUI::from_u8(address);
})
#endif // WIN32
.perform_sync();
if (boost::starts_with(rtn, "ERROR")) { BOOST_LOG_TRIVIAL(warning) << boost::format("%1%: [auth][auth_status] Unknown value encountered in api/v1/auth/check") % name;}
return rtn;
}
bool UltiMaker::generate_auth_creds(wxString &msg) const {
// Returns true if no error, false otherwise.
BOOST_LOG_TRIVIAL(debug) << "UltiMaker: generate_auth_creds called!";
bool ret = true;
/*
The auth POST request returns json of the form
{
"id": "string",
"key": "string"
}
where id is the m_api_username and key is the m_api_password
*/
const std::string name = get_name();
std::string application = "OrcaSlicer";
std::string application_key = "application";
std::string user = "OrcaSlicer";
std::string user_key = "user";
std::string exclusionKey = "OrcaSlicer";
std::string exclusionKey_key = "OrcaSlicer";
auto url = (boost::format("http://%1%/api/v1/auth/request") % host).str();
auto http = Http::post(std::move(url));
// Note: the body/form cannot be blank or OrcaSlicer's Http library crashes on perform_sync()
http.form_clear();
http.mime_form_add_text(application_key,application);
http.mime_form_add_text(user_key,user);
http.mime_form_add_text(exclusionKey_key,exclusionKey);
http.on_error([name, &msg, &ret](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error generating credentials: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
msg = "Error generating credentials! See the log for details.";
ret = false;
})
.on_complete([name, &msg, &ret](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: body: %2%") % name % body;
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: POST request completed without error") % name;
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got credential verification: %2%") % name % body;
std::stringstream sin(body);
boost::property_tree::ptree pt;
try {
// Parse JSON into property tree
boost::property_tree::read_json(sin, pt);
// Extract the keys from the json
std::string id = pt.get<std::string>("id", "");
std::string key = pt.get<std::string>("key", "");
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: Got id and key. ID=%1%") % id;
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: KEY=%1%") % key;
if (id.empty() || key.empty()) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Either ID or KEY is empty.") % name;
msg = GUI::from_u8("ERROR: Either ID or KEY is empty. ID=" + id + " KEY=" + key);
ret = false;
} else {
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: ID and KEY are both present. Setting message appropriately.") % name;
msg = GUI::from_u8("Username: " + id + " Password: " + key);
}
}
catch (const std::exception & e) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: generate_auth_creds Caught error") % name;
BOOST_LOG_TRIVIAL(error) << "Json parse err: " << e.what();
ret = false;
}
})
#ifdef WIN32
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
.on_ip_resolve([&](std::string address) {
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
// Remember resolved address to be reused at successive REST API call.
// msg = GUI::from_u8(address);
})
#endif // WIN32
.perform_sync();
// Wait for user to authorize on the physical machine
BOOST_LOG_TRIVIAL(debug) << "UltiMaker: generate_auth_creds done! ret=" << ret;
return ret;
}
std::string UltiMaker::test_auth() const {
/*
Used in UltiMaker::test(), creates creds if they don't already
exist, and returns various error strings if errors.
Returns "OK" if no errors and creds are valid.
*/
const char* name = get_name();
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Testing for presence of auth creds...") % name;
// Auth credentials are alreay existing (user-entered)
if (has_auth_creds()) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Has auth credentials. Testing validity...") % name;
std::string auth_stat = auth_status();
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: auth_status returned %2%") % name % auth_stat;
if (auth_stat == "waiting") {
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Auth credentials are waiting for user approval at the physical machine.") % name;
return "Authentication creds waiting on approval; Please approve access via the dialog box on the printer's screen.";
} else if (auth_stat == "authorized" && is_authorized()) {
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Auth credentials are valid. Returning OK") % name;
return "OK";
} else {
BOOST_LOG_TRIVIAL(warning) << boost::format("%1%: Auth credentials are INVALID. auth_status returned %2%") % name % auth_stat;
return "Invalid authentication credentials";
}
} else { // Auth credentials do not exist
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Auth credentials do NOT already exist. Returning error string.") % name;
return "Error: No authentication credentials found!";
}
}
#pragma endregion Auth
int UltiMaker::getPrintTime(std::string filepath) const {
// Get the total print time in seconds from the provided gcode filepath
std::ifstream file_in(filepath);
std::string line;
int ret = 0;
bool found = false;
int h = 0;
int m = 0;
int s = 0;
if (file_in.is_open()){
while (getline(file_in, line) && !found) {
if (boost::starts_with(line, "; estimated printing time (normal mode) =")) {
if (sscanf(line.c_str(), "; estimated printing time (normal mode) = %dh %dm %ds", &h, &m, &s) == 3) {
ret = 3600*h + 60*m + s;
BOOST_LOG_TRIVIAL(warning) << boost::format("UltiMaker: Hours=%1% Minutes=%2% Seconds=%3% Total Seconds=%4%") % h % m % s % ret;
found = true;
} else if (sscanf(line.c_str(), "; estimated printing time (normal mode) = %dm %ds", &m, &s) == 2) {
ret = 60*m + s;
BOOST_LOG_TRIVIAL(warning) << boost::format("UltiMaker: No Hours, Minutes=%1% Seconds=%2% Total Seconds=%3%") % m % s % ret;
found = true;
} else {
BOOST_LOG_TRIVIAL(warning) << boost::format("UltiMaker: ERROR: sscanf error while getting total print time, no vars assigned.");
}
}
}
} else {
BOOST_LOG_TRIVIAL(error) << boost::format("UltiMaker postprocessing ERROR: file %1% or file %2% was unable to open!") % filepath % (filepath+".temp");
return 0;
}
if (!found) {
BOOST_LOG_TRIVIAL(error) << boost::format("UltiMaker: Print time estimate not found!!");
}
file_in.close();
return ret;
}
bool UltiMaker::makeGriffinCompatible(std::string filepath) const {
// Modifies the header of the given file path to remove the double colon
// at the timestamp because >1 colons in a line makes UltiMakers crash.
// Opening and modifying files code was modified from https://www.w3resource.com/cpp-exercises/file-handling/cpp-file-handling-exercise-6.php
std::string file_temp = filepath+".temp";
std::ifstream file_in(filepath);
std::ofstream file_out(file_temp);
std::string line;
bool write_on = false;
if (file_in.is_open() && file_out.is_open()){
while (getline(file_in, line)) {
if (write_on) {
if (boost::starts_with(line, "; generated by OrcaSlicer")) {
boost::replace_all(line, ":", "-"); // Replace the colons in the date with dashes to prevent printer crash.
}
// UM machines need total print time in seconds.
if (boost::starts_with(line, ";PRINT.TIME:")) {
file_out << ";PRINT.TIME:"<< getPrintTime(filepath) << "\n";
} else {
file_out << line << "\n"; // Write the modified line to the output file
}
} else if (boost::starts_with(line, ";START_OF_HEADER")) {
write_on = true;
file_out << line << "\n";
}
}
} else {
BOOST_LOG_TRIVIAL(error) << boost::format("UltiMaker: file %1% or file %2% was unable to open!") % filepath % (filepath+".temp");
return false;
}
if (!write_on) {
BOOST_LOG_TRIVIAL(warning) << boost::format("UltiMaker: while postprocessing to make compatible with Griffin, headers not found!");
}
file_in.close();
file_out.close();
// Now replace the upload file with the temp one
std::remove(filepath.c_str());
std::rename(file_temp.c_str(), filepath.c_str());
// boost::filesystem::copy_file(file_temp, filepath); // replacement of the above line to keep in /tmp/
return true;
}
bool UltiMaker::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
{
// Preprocess the file so the machine doesn't crash
if (makeGriffinCompatible(upload_data.source_path.string())) {
BOOST_LOG_TRIVIAL(info) << "UltiMaker: [upload] Griffin compatibility appeared successful.";
} else {
BOOST_LOG_TRIVIAL(warning) << "UltiMaker: [upload] Griffin compatibility FAILED.";
return false;
}
wxString connect_msg;
auto connectionType = connect(connect_msg);
if (connectionType == ConnectionType::error) {
BOOST_LOG_TRIVIAL(warning) << boost::format("UltiMaker: [upload] connectionType is of type error!");
error_fn(std::move(connect_msg));
return false;
}
bool res = true;
bool dsf = (connectionType == ConnectionType::dsf);
auto upload_cmd = get_upload_url(upload_data.upload_path.string(), connectionType);
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: Uploading file %1%, filepath: %2%, post_action: %3%, command: %4%")
% upload_data.source_path
% upload_data.upload_path
% int(upload_data.post_action)
% upload_cmd;
auto http = (dsf ? Http::put(std::move(upload_cmd)) : Http::post(std::move(upload_cmd)));
http.form_add("jobname",upload_data.upload_path.string());
http.form_add_file("file", upload_data.source_path, upload_data.upload_path.string());
set_auth(http);
http.on_complete([&](std::string body, unsigned status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: File uploaded: HTTP %1%: %2%") % status % body;
int err_code = dsf ? (status == 201 ? 0 : 1) : get_err_code_from_body(body);
if (err_code != 0) {
BOOST_LOG_TRIVIAL(error) << boost::format("UltiMaker: Request completed but error code was received: %1%") % err_code;
error_fn(format_error(body, L("Unknown error occurred"), 0));
res = false;
} else if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) {
wxString errormsg;
res = start_print(errormsg, upload_data.upload_path.string(), connectionType);
if (! res) {
error_fn(std::move(errormsg));
}
}
})
.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("UltiMaker: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
error_fn(format_error(body, error, status));
res = false;
})
.on_progress([&](Http::Progress progress, bool &cancel) {
prorgess_fn(std::move(progress), cancel);
if (cancel) {
// Upload was canceled
BOOST_LOG_TRIVIAL(info) << "UltiMaker: Upload canceled";
res = false;
}
})
.perform_sync();
disconnect(connectionType);
return res;
}
UltiMaker::ConnectionType UltiMaker::connect(wxString &msg) const
{
BOOST_LOG_TRIVIAL(info) << boost::format("UltiMaker: Attempting to connect");
auto res = ConnectionType::error;
auto url = get_connect_url();
auto http = Http::get(std::move(url));
set_auth(http);
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: Connecting to url %1%") % url;
http.on_error([&](std::string body, std::string error, unsigned status) {
auto dsfUrl = get_connect_url();
auto dsfHttp = Http::get(std::move(dsfUrl));
dsfHttp.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("UltiMaker: Error connecting: %1%, HTTP %2%, body: `%3%`") % error % status % body;
msg = format_error(body, error, status);
})
.on_complete([&](std::string body, unsigned) {
res = ConnectionType::dsf;
})
.perform_sync();
})
.on_complete([&](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: Got: %1%") % body;
int err_code = get_err_code_from_body(body);
switch (err_code) {
case 0:
res = ConnectionType::rrf;
break;
case 1:
msg = format_error(body, L("Wrong password"), 0);
break;
case 2:
msg = format_error(body, L("Could not get resources to create a new connection"), 0);
break;
default:
msg = format_error(body, L("Unknown error occurred"), 0);
break;
}
})
.perform_sync();
return res;
}
void UltiMaker::disconnect(ConnectionType connectionType) const
{
// we don't need to disconnect from DSF or if it failed anyway
if (connectionType != ConnectionType::rrf) {
return;
}
auto url = (boost::format("%1%rr_disconnect")
% get_base_url()).str();
auto http = Http::get(std::move(url));
http.on_error([&](std::string body, std::string error, unsigned status) {
// we don't care about it, if disconnect is not working UltiMaker will disconnect automatically after some time
BOOST_LOG_TRIVIAL(error) << boost::format("UltiMaker: Error disconnecting: %1%, HTTP %2%, body: `%3%`") % error % status % body;
})
.perform_sync();
}
#pragma region Get Constants
std::string UltiMaker::get_upload_url(const std::string &filename, ConnectionType connectionType) const
{
assert(connectionType != ConnectionType::error);
if (connectionType == ConnectionType::dsf) {
return (boost::format("%1%/machine/file/gcodes/%2%")
% get_base_url()
% Http::url_encode(filename)).str();
} else {
return get_base_url()+"/print_job";
}
}
std::string UltiMaker::get_status_url() const
{
return (boost::format("%1%/printer/status") % get_base_url()).str();
}
std::string UltiMaker::get_connect_url() const
{
return (boost::format("%1%/printer/status") % get_base_url()).str();
}
std::string UltiMaker::get_base_url() const
{
if (host.find("http://") == 0 || host.find("https://") == 0) {
if (host.back() == '/') {
return host;
} else {
return (boost::format("%1%/api/v1") % host).str();
}
} else {
return (boost::format("http://%1%/api/v1") % host).str();
}
}
std::string UltiMaker::timestamp_str() const
{
enum { BUFFER_SIZE = 32 };
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
char buffer[BUFFER_SIZE];
std::strftime(buffer, BUFFER_SIZE, "time=%Y-%m-%dT%H:%M:%S", &tm);
return std::string(buffer);
}
#pragma endregion Get Constants
bool UltiMaker::start_print(wxString &msg, const std::string &filename, ConnectionType connectionType) const
{
assert(connectionType != ConnectionType::error);
bool res = false;
bool dsf = (connectionType == ConnectionType::dsf);
auto url = dsf
? (boost::format("%1%machine/code") % get_base_url()).str()
: (boost::format("%1%rr_gcode?gcode=M32%%20\"0:/gcodes/%2%\"") % get_base_url() % Http::url_encode(filename)).str();
auto http = (dsf ? Http::post(std::move(url)) : Http::get(std::move(url)));
set_auth(http);
if (dsf) {
http.set_post_body(
(boost::format("M32 \"0:/gcodes/%1%\"") % filename).str()
);
}
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("UltiMaker: Error starting print: %1%, HTTP %2%, body: `%3%`") % error % status % body;
msg = format_error(body, error, status);
})
.on_complete([&](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("UltiMaker: Got: %1%") % body;
res = true;
})
.perform_sync();
return res;
}
}