Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Creality Print support #6928

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ static t_config_enum_values s_keys_map_PrintHostType {
{ "prusalink", htPrusaLink },
{ "prusaconnect", htPrusaConnect },
{ "octoprint", htOctoPrint },
{ "crealityprint", htCrealityPrint },
{ "duet", htDuet },
{ "flashair", htFlashAir },
{ "astrobox", htAstroBox },
Expand Down Expand Up @@ -3399,6 +3400,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("repetier");
def->enum_values.push_back("mks");
def->enum_values.push_back("esp3d");
def->enum_values.push_back("crealityprint");
def->enum_values.push_back("obico");
def->enum_values.push_back("flashforge");
def->enum_values.push_back("simplyprint");
Expand All @@ -3411,6 +3413,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back("Repetier");
def->enum_labels.push_back("MKS");
def->enum_labels.push_back("ESP3D");
def->enum_labels.push_back("CrealityPrint");
def->enum_labels.push_back("Obico");
def->enum_labels.push_back("Flashforge");
def->enum_labels.push_back("SimplyPrint");
Expand Down
2 changes: 1 addition & 1 deletion src/libslic3r/PrintConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ enum class FuzzySkinType {
};

enum PrintHostType {
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htObico, htFlashforge, htSimplyPrint
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htCrealityPrint, htObico, htFlashforge, htSimplyPrint
};

enum AuthorizationType {
Expand Down
2 changes: 2 additions & 0 deletions src/slic3r/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,8 @@ set(SLIC3R_GUI_SOURCES
Utils/MKS.hpp
Utils/ESP3D.cpp
Utils/ESP3D.hpp
Utils/CrealityPrint.cpp
Utils/CrealityPrint.hpp
Utils/WxFontUtils.cpp
Utils/WxFontUtils.hpp
Utils/Duet.cpp
Expand Down
234 changes: 234 additions & 0 deletions src/slic3r/Utils/CrealityPrint.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
#include "CrealityPrint.hpp"

#include <algorithm>
#include <sstream>
#include <exception>
#include <boost/format.hpp>
#include <boost/foreach.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/asio.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/nowide/convert.hpp>

#include <curl/curl.h>
#include <wx/progdlg.h>

#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/format.hpp"
#include "Http.hpp"
#include "libslic3r/AppConfig.hpp"
#include "Bonjour.hpp"
#include "slic3r/GUI/BonjourDialog.hpp"

#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <cstdlib>
#include <iostream>
#include <string>

#include <fstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using std::to_string;

namespace beast = boost::beast;
namespace http = beast::http;
namespace websocket = beast::websocket;
namespace net = boost::asio;
using tcp = boost::asio::ip::tcp;

namespace fs = boost::filesystem;
namespace pt = boost::property_tree;

namespace Slic3r {

CrealityPrint::CrealityPrint(DynamicPrintConfig* config) :
m_host(config->opt_string("print_host")),
m_web_ui(config->opt_string("print_host_webui")),
m_cafile(config->opt_string("printhost_cafile")),
m_port(config->opt_string("printhost_port")),
m_apikey(config->opt_string("printhost_apikey")),
m_ssl_revoke_best_effort(config->opt_bool("printhost_ssl_ignore_revoke"))
{}

const char* CrealityPrint::get_name() const { return "Creality Print"; }

std::string CrealityPrint::get_host() const {
return m_host;
}
void CrealityPrint::set_auth(Http& http) const
{
http.header("Authorization", "Bearer " + m_apikey);
if (!m_cafile.empty()) {
http.ca_file(m_cafile);
}
}

wxString CrealityPrint::get_test_ok_msg() const { return _(L("Connected to CrealityPrint successfully!")); }

wxString CrealityPrint::get_test_failed_msg(wxString& msg) const
{
return GUI::format_wxstr("%s: %s", _L("Could not connect to CrealityPrint"), msg.Truncate(256));
}

bool CrealityPrint::test(wxString& msg) const
{
bool res = true;
const char* name = get_name();
auto url = make_url("info");

BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
// 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.
auto http = Http::get(std::move(url));
set_auth(http);
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status %
body;
res = false;
msg = format_error(body, error, status);
})
.on_complete([&, this](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
})
#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();

return res;
}

PrintHostPostUploadActions CrealityPrint::get_post_upload_actions() const {
return PrintHostPostUploadAction::StartPrint;
}

bool CrealityPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
{
const char* name = get_name();
const auto upload_filename = upload_data.upload_path.filename();
const auto upload_parent_path = upload_data.upload_path.parent_path();
wxString test_msg;
if (!test(test_msg)) {
error_fn(std::move(test_msg));
return false;
}

bool res = true;
auto url = make_url("upload/" + safe_filename(upload_filename.string()));

auto http = Http::post(url); // std::move(url));
set_auth(http);
http.form_add("path", upload_parent_path.string())
.form_add_file("file", upload_data.source_path.string(), upload_filename.string())

.on_complete([&](std::string body, unsigned status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;

if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) {
start_print(safe_filename(upload_filename.string()));
}
})
.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file to %2%: %3%, HTTP %4%, body: `%5%`") % name % url % 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) << name << ": Upload canceled";
res = false;
}
})
#ifdef WIN32
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
#endif
.perform_sync();
return res;
}

std::string CrealityPrint::make_url(const std::string &path) const
{
if (m_host.find("http://") == 0 || m_host.find("https://") == 0) {
if (m_host.back() == '/') {
return (boost::format("%1%%2%") % m_host % path).str();
} else {
return (boost::format("%1%/%2%") % m_host % path).str();
}
} else {
return (boost::format("http://%1%/%2%") % m_host % path).str();
}
}

std::string CrealityPrint::safe_filename(const std::string &filename) const
{
std::string safe_filename = filename;
std::replace(safe_filename.begin(), safe_filename.end(), ' ', '_');

return safe_filename;
}

void CrealityPrint::start_print(const std::string &filename) const
{
try {
std::string host = m_host;
auto const port = "9999";

json j2 = {
{ "method", "set" },
{
"params", {
{ "opGcodeFile", "printprt:/usr/data/printer_data/gcodes/" + filename }
}
}
};

net::io_context ioc;

tcp::resolver resolver{ioc};
websocket::stream<tcp::socket> ws{ioc};

auto const results = resolver.resolve(host, port);

auto ep = net::connect(ws.next_layer(), results);

host += ':' + std::to_string(ep.port());

ws.set_option(websocket::stream_base::decorator(
[](websocket::request_type& req)
{
req.set(http::field::user_agent,
std::string(BOOST_BEAST_VERSION_STRING) +
" websocket-client-coro");
}));

ws.handshake(host, "/");

ws.write(net::buffer(to_string(j2)));

beast::flat_buffer buffer;

ws.read(buffer);

ws.close(websocket::close_code::normal);
} catch(std::exception const& e) {
std::cerr << "Error: " << e.what() << std::endl;
}

}

}
49 changes: 49 additions & 0 deletions src/slic3r/Utils/CrealityPrint.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#ifndef slic3r_CrealityPrint_hpp_
#define slic3r_CrealityPrint_hpp_

#include <string>
#include <wx/string.h>
#include <boost/optional.hpp>
#include <boost/asio/ip/address.hpp>

#include "PrintHost.hpp"
#include "libslic3r/PrintConfig.hpp"

namespace Slic3r {

class DynamicPrintConfig;
class Http;
class CrealityPrint : public PrintHost
{
public:
CrealityPrint(DynamicPrintConfig* config);
~CrealityPrint() override = default;

const char* get_name() const override;
virtual bool can_test() const { return true; };
std::string get_host() const override;
bool has_auto_discovery() const override { return true; }

wxString get_test_ok_msg() const override;
wxString get_test_failed_msg(wxString& msg) const override;
virtual bool test(wxString& curl_msg) const override;
PrintHostPostUploadActions get_post_upload_actions() const;
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override;

protected:
virtual void set_auth(Http& http) const;
private:
std::string m_host;
std::string m_port;
std::string m_apikey;
std::string m_cafile;
std::string m_web_ui;
bool m_ssl_revoke_best_effort;

std::string make_url(const std::string& path) const;
void start_print(const std::string& path) const;
std::string safe_filename(const std::string& filename) const;
};
} // namespace Slic3r

#endif
2 changes: 1 addition & 1 deletion src/slic3r/Utils/ESP3D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ bool ESP3D::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn
http.header("Connection", "keep-alive")
.form_add_file("file", upload_data.source_path, short_name)
.on_complete([&](std::string body, unsigned status) {
// check for OK
// check for OK
if (upload_data.post_action == PrintHostPostUploadAction::StartPrint) {
wxString errormsg;
res = start_print(errormsg, short_name);
Expand Down
2 changes: 2 additions & 0 deletions src/slic3r/Utils/PrintHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "Repetier.hpp"
#include "MKS.hpp"
#include "ESP3D.hpp"
#include "CrealityPrint.hpp"
#include "../GUI/PrintHostDialogs.hpp"
#include "../GUI/MainFrame.hpp"
#include "Obico.hpp"
Expand Down Expand Up @@ -60,6 +61,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
case htPrusaConnect: return new PrusaConnect(config);
case htMKS: return new MKS(config);
case htESP3D: return new ESP3D(config);
case htCrealityPrint: return new CrealityPrint(config);
case htObico: return new Obico(config);
case htFlashforge: return new Flashforge(config);
case htSimplyPrint: return new SimplyPrint(config);
Expand Down