From 20b00b25c60dd46a8575fa95f4bf9bd1a7fbc259 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Mon, 27 Apr 2026 19:29:43 +0200 Subject: [PATCH] CrealityPrint: add multi-color print protocol for K2 Plus For supported models, start_print() sends colorMatch (filament-to-slot mapping) followed by multiColorPrint (with optional calibration via enableSelfTest) instead of the legacy opGcodeFile command. The dialog passes color mapping and calibration settings through extended_info. Signed-off-by: Igor Mammedov --- v4: - Skip "path" form field in multipart upload for multi-color printers (extra field breaks K2 Plus firmware parser) - Use fire-and-forget ws.write() for colorMatch and multiColorPrint instead of ws_send_and_read() (printer sends response asynchronously) --- src/slic3r/Utils/CrealityPrint.cpp | 80 +++++++++++++++++++++++++----- src/slic3r/Utils/CrealityPrint.hpp | 3 +- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/src/slic3r/Utils/CrealityPrint.cpp b/src/slic3r/Utils/CrealityPrint.cpp index 8174a2c6bb..06b2bf4154 100644 --- a/src/slic3r/Utils/CrealityPrint.cpp +++ b/src/slic3r/Utils/CrealityPrint.cpp @@ -140,15 +140,16 @@ bool CrealityPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, 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()) + if (!supports_multi_color_print()) + http.form_add("path", upload_parent_path.string()); + http.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) { wxString errormsg; - if (!start_print(errormsg, safe_filename(upload_filename.string()))) { + if (!start_print(errormsg, safe_filename(upload_filename.string()), upload_data.extended_info)) { error_fn(std::move(errormsg)); res = false; } @@ -278,28 +279,81 @@ std::string CrealityPrint::query_boxes_info() const } } -bool CrealityPrint::start_print(wxString &msg, const std::string &filename) const +bool CrealityPrint::start_print(wxString &msg, const std::string &filename, const std::map& extended_info) const { try { + const std::string gcode_path = "/mnt/UDISK/printer_data/gcodes/" + filename; + net::io_context ioc; websocket::stream ws{ioc}; ws_connect(ioc, ws, m_host, "9999"); - json j2 = { - { "method", "set" }, - { - "params", { - { "opGcodeFile", "printprt:/usr/data/printer_data/gcodes/" + filename } + if (supports_multi_color_print()) { + // Build colorMatch list from the mapping provided by the dialog + json color_list = json::array(); + for (int i = 0; ; i++) { + auto it = extended_info.find("colorMatch_" + std::to_string(i)); + if (it == extended_info.end()) + break; + // Value format: "toolId\ttype\tcolor\tboxId\tmaterialId" + auto val = it->second; + std::vector parts; + std::istringstream iss(val); + std::string part; + while (std::getline(iss, part, '\t')) + parts.push_back(part); + if (parts.size() >= 5) { + color_list.push_back({ + {"id", parts[0]}, + {"type", parts[1]}, + {"color", parts[2]}, + {"boxId", std::stoi(parts[3])}, + {"materialId", std::stoi(parts[4])} + }); } } - }; - ws.write(net::buffer(to_string(j2))); + json color_match = { + {"method", "set"}, + {"params", { + {"colorMatch", { + {"path", gcode_path}, + {"list", color_list} + }} + }} + }; + ws.write(net::buffer(to_string(color_match))); - beast::flat_buffer buffer; + // Read enableSelfTest from extended_info, default to 0 (calibration off) + int enable_self_test = 0; + auto it = extended_info.find("enableSelfTest"); + if (it != extended_info.end()) + enable_self_test = std::stoi(it->second); - ws.read(buffer); + json multi_color_print = { + {"method", "set"}, + {"params", { + {"multiColorPrint", { + {"gcode", gcode_path}, + {"enableSelfTest", enable_self_test} + }} + }} + }; + ws.write(net::buffer(to_string(multi_color_print))); + } else { + json cmd = { + {"method", "set"}, + {"params", { + {"opGcodeFile", "printprt:/usr/data/printer_data/gcodes/" + filename} + }} + }; + ws.write(net::buffer(to_string(cmd))); + beast::flat_buffer buffer; + ws.read(buffer); + } + + ws.close(websocket::close_code::normal); return true; } catch(std::exception const& e) { BOOST_LOG_TRIVIAL(error) << "CrealityPrint: Error starting print: " << e.what(); diff --git a/src/slic3r/Utils/CrealityPrint.hpp b/src/slic3r/Utils/CrealityPrint.hpp index 7952ae692e..88fb8ac71c 100644 --- a/src/slic3r/Utils/CrealityPrint.hpp +++ b/src/slic3r/Utils/CrealityPrint.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_CrealityPrint_hpp_ #define slic3r_CrealityPrint_hpp_ +#include #include #include #include @@ -44,7 +45,7 @@ private: mutable std::string m_model; std::string make_url(const std::string& path) const; - bool start_print(wxString& msg, const std::string& path) const; + bool start_print(wxString& msg, const std::string& filename, const std::map& extended_info) const; std::string safe_filename(const std::string& filename) const; void query_model() const; };