mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-14 09:02:06 +00:00
Add SnapmakerPrinterAgent with filament sync support
Introduce SnapmakerPrinterAgent as a MoonrakerPrinterAgent subclass that handles Snapmaker UI -specific filament detection
This commit is contained in:
@@ -608,6 +608,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
Utils/OrcaPrinterAgent.hpp
|
||||
Utils/QidiPrinterAgent.cpp
|
||||
Utils/QidiPrinterAgent.hpp
|
||||
Utils/SnapmakerPrinterAgent.cpp
|
||||
Utils/SnapmakerPrinterAgent.hpp
|
||||
Utils/MoonrakerPrinterAgent.cpp
|
||||
Utils/MoonrakerPrinterAgent.hpp
|
||||
Utils/BBLCloudServiceAgent.cpp
|
||||
|
||||
@@ -723,12 +723,18 @@ bool MoonrakerPrinterAgent::fetch_filament_info(std::string dev_id)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MoonrakerPrinterAgent::trim_and_upper(const std::string& input)
|
||||
{
|
||||
std::string result = input;
|
||||
boost::trim(result);
|
||||
std::transform(result.begin(), result.end(), result.begin(),
|
||||
[](unsigned char c) { return static_cast<char>(std::toupper(c)); });
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string MoonrakerPrinterAgent::map_filament_type_to_generic_id(const std::string& filament_type)
|
||||
{
|
||||
std::string upper = filament_type;
|
||||
boost::trim(upper);
|
||||
std::transform(upper.begin(), upper.end(), upper.begin(),
|
||||
[](unsigned char c) { return static_cast<char>(std::toupper(c)); });
|
||||
const std::string upper = trim_and_upper(filament_type);
|
||||
|
||||
// Map to OrcaFilamentLibrary preset IDs (compatible with all printers)
|
||||
// Source: resources/profiles/OrcaFilamentLibrary/filament/
|
||||
|
||||
@@ -70,8 +70,8 @@ public:
|
||||
int set_queue_on_main_fn(QueueOnMainFn fn) override;
|
||||
|
||||
// Pull-mode agent (on-demand filament sync)
|
||||
virtual FilamentSyncMode get_filament_sync_mode() const override { return FilamentSyncMode::pull; }
|
||||
virtual bool fetch_filament_info(std::string dev_id) override;
|
||||
FilamentSyncMode get_filament_sync_mode() const override { return FilamentSyncMode::pull; }
|
||||
bool fetch_filament_info(std::string dev_id) override;
|
||||
|
||||
protected:
|
||||
struct MoonrakerDeviceInfo
|
||||
@@ -115,6 +115,9 @@ protected:
|
||||
std::string sanitize_filename(const std::string& filename);
|
||||
std::string join_url(const std::string& base_url, const std::string& path) const;
|
||||
|
||||
// Trim whitespace and convert to uppercase
|
||||
static std::string trim_and_upper(const std::string& input);
|
||||
|
||||
// Map filament type to OrcaFilamentLibrary preset ID for AMS sync compatibility
|
||||
static std::string map_filament_type_to_generic_id(const std::string& filament_type);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "BBLPrinterAgent.hpp"
|
||||
#include "OrcaPrinterAgent.hpp"
|
||||
#include "QidiPrinterAgent.hpp"
|
||||
#include "SnapmakerPrinterAgent.hpp"
|
||||
#include "MoonrakerPrinterAgent.hpp"
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <map>
|
||||
@@ -96,6 +97,7 @@ void NetworkAgentFactory::register_all_agents()
|
||||
{
|
||||
register_agent<OrcaPrinterAgent>();
|
||||
register_agent<QidiPrinterAgent>();
|
||||
register_agent<SnapmakerPrinterAgent>();
|
||||
register_agent<MoonrakerPrinterAgent>();
|
||||
|
||||
// BBLPrinterAgent takes no constructor args, so register manually
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "nlohmann/json.hpp"
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
|
||||
@@ -293,9 +292,7 @@ void QidiPrinterAgent::parse_filament_sections(const std::string& content, std::
|
||||
|
||||
std::string QidiPrinterAgent::map_filament_type_to_setting_id(const std::string& filament_type)
|
||||
{
|
||||
std::string upper = filament_type;
|
||||
boost::trim(upper);
|
||||
std::transform(upper.begin(), upper.end(), upper.begin(), [](unsigned char c) { return static_cast<char>(std::toupper(c)); });
|
||||
const std::string upper = trim_and_upper(filament_type);
|
||||
|
||||
if (upper == "PLA") {
|
||||
return "QD_1_0_1";
|
||||
@@ -351,10 +348,7 @@ std::string QidiPrinterAgent::infer_series_id(const std::string& model_id, const
|
||||
|
||||
std::string QidiPrinterAgent::normalize_filament_type(const std::string& filament_type)
|
||||
{
|
||||
std::string trimmed = filament_type;
|
||||
boost::trim(trimmed);
|
||||
std::string upper = trimmed;
|
||||
std::transform(upper.begin(), upper.end(), upper.begin(), [](unsigned char c) { return static_cast<char>(std::toupper(c)); });
|
||||
const std::string upper = trim_and_upper(filament_type);
|
||||
|
||||
if (upper.find("PLA") != std::string::npos)
|
||||
return "PLA";
|
||||
@@ -373,7 +367,7 @@ std::string QidiPrinterAgent::normalize_filament_type(const std::string& filamen
|
||||
if (upper.find("PVA") != std::string::npos)
|
||||
return "PVA";
|
||||
|
||||
return trimmed;
|
||||
return upper;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
157
src/slic3r/Utils/SnapmakerPrinterAgent.cpp
Normal file
157
src/slic3r/Utils/SnapmakerPrinterAgent.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#include "SnapmakerPrinterAgent.hpp"
|
||||
#include "Http.hpp"
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* SNAPMAKER_AGENT_VERSION = "0.0.1";
|
||||
|
||||
// Safely access a parallel array by index, returning a fallback if out of bounds.
|
||||
template<typename T>
|
||||
T safe_at(const std::vector<T>& vec, int index, const T& fallback)
|
||||
{
|
||||
return (index >= 0 && index < static_cast<int>(vec.size())) ? vec[index] : fallback;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
SnapmakerPrinterAgent::SnapmakerPrinterAgent(std::string log_dir) : MoonrakerPrinterAgent(std::move(log_dir)) {}
|
||||
|
||||
AgentInfo SnapmakerPrinterAgent::get_agent_info_static()
|
||||
{
|
||||
return AgentInfo{"snapmaker", "Snapmaker Printer Agent", SNAPMAKER_AGENT_VERSION, "Snapmaker printer agent"};
|
||||
}
|
||||
|
||||
std::string SnapmakerPrinterAgent::combine_filament_type(const std::string& type, const std::string& sub_type)
|
||||
{
|
||||
const std::string base = trim_and_upper(type);
|
||||
const std::string sub = trim_and_upper(sub_type);
|
||||
|
||||
if (base.empty())
|
||||
return "PLA";
|
||||
|
||||
if (sub.empty() || sub == "NONE")
|
||||
return base;
|
||||
|
||||
if (sub == "CF")
|
||||
return base + "-CF";
|
||||
if (sub == "GF")
|
||||
return base + "-GF";
|
||||
if (sub == "SILK")
|
||||
return base + " SILK";
|
||||
if (sub == "SNAPSPEED" || sub == "HS")
|
||||
return base + " HIGH SPEED";
|
||||
|
||||
// Unrecognized sub-type (brand names like Polylite, Basic, etc.) -- use base type only
|
||||
return base;
|
||||
}
|
||||
|
||||
bool SnapmakerPrinterAgent::fetch_filament_info(std::string dev_id)
|
||||
{
|
||||
std::string url = join_url(device_info.base_url, "/printer/objects/query?print_task_config&filament_detect");
|
||||
|
||||
std::string response_body;
|
||||
bool success = false;
|
||||
std::string http_error;
|
||||
|
||||
auto http = Http::get(url);
|
||||
if (!device_info.api_key.empty()) {
|
||||
http.header("X-Api-Key", device_info.api_key);
|
||||
}
|
||||
http.timeout_connect(5)
|
||||
.timeout_max(10)
|
||||
.on_complete([&](std::string body, unsigned status) {
|
||||
if (status == 200) {
|
||||
response_body = body;
|
||||
success = true;
|
||||
} else {
|
||||
http_error = "HTTP error: " + std::to_string(status);
|
||||
}
|
||||
})
|
||||
.on_error([&](std::string body, std::string err, unsigned status) {
|
||||
http_error = err;
|
||||
if (status > 0) {
|
||||
http_error += " (HTTP " + std::to_string(status) + ")";
|
||||
}
|
||||
})
|
||||
.perform_sync();
|
||||
|
||||
if (!success) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "SnapmakerPrinterAgent::fetch_filament_info: HTTP request failed: " << http_error;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto json = nlohmann::json::parse(response_body, nullptr, false, true);
|
||||
if (json.is_discarded()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "SnapmakerPrinterAgent::fetch_filament_info: Invalid JSON response";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Navigate to result.status.print_task_config
|
||||
if (!json.contains("result") || !json["result"].contains("status") ||
|
||||
!json["result"]["status"].contains("print_task_config")) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "SnapmakerPrinterAgent::fetch_filament_info: Missing print_task_config in response";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& ptc = json["result"]["status"]["print_task_config"];
|
||||
|
||||
// Read parallel arrays from print_task_config
|
||||
auto filament_exist = ptc.value("filament_exist", std::vector<bool>{});
|
||||
auto filament_type = ptc.value("filament_type", std::vector<std::string>{});
|
||||
auto filament_sub_type = ptc.value("filament_sub_type", std::vector<std::string>{});
|
||||
auto filament_color = ptc.value("filament_color_rgba", std::vector<std::string>{});
|
||||
|
||||
const int slot_count = static_cast<int>(filament_exist.size());
|
||||
if (slot_count == 0) {
|
||||
BOOST_LOG_TRIVIAL(info) << "SnapmakerPrinterAgent::fetch_filament_info: No filament slots reported";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read NFC filament_detect data for temperature info (optional)
|
||||
nlohmann::json nfc_info;
|
||||
if (json["result"]["status"].contains("filament_detect") &&
|
||||
json["result"]["status"]["filament_detect"].contains("info")) {
|
||||
nfc_info = json["result"]["status"]["filament_detect"]["info"];
|
||||
}
|
||||
|
||||
static const std::string empty_str;
|
||||
static const std::string default_color = "FFFFFFFF";
|
||||
|
||||
std::vector<AmsTrayData> trays;
|
||||
trays.reserve(slot_count);
|
||||
|
||||
for (int i = 0; i < slot_count; ++i) {
|
||||
AmsTrayData tray;
|
||||
tray.slot_index = i;
|
||||
tray.has_filament = filament_exist[i];
|
||||
|
||||
if (tray.has_filament) {
|
||||
tray.tray_type = combine_filament_type(safe_at(filament_type, i, empty_str),
|
||||
safe_at(filament_sub_type, i, empty_str));
|
||||
tray.tray_info_idx = map_filament_type_to_generic_id(tray.tray_type);
|
||||
tray.tray_color = safe_at(filament_color, i, default_color);
|
||||
|
||||
// Extract NFC temperature data if available
|
||||
if (nfc_info.is_array() && i < static_cast<int>(nfc_info.size()) && nfc_info[i].is_object()) {
|
||||
auto& nfc_slot = nfc_info[i];
|
||||
std::string vendor = nfc_slot.value("VENDOR", "NONE");
|
||||
if (vendor != "NONE" && !vendor.empty()) {
|
||||
tray.bed_temp = nfc_slot.value("BED_TEMP", 0);
|
||||
tray.nozzle_temp = nfc_slot.value("FIRST_LAYER_TEMP", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trays.emplace_back(std::move(tray));
|
||||
}
|
||||
|
||||
build_ams_payload(1, slot_count - 1, trays);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
25
src/slic3r/Utils/SnapmakerPrinterAgent.hpp
Normal file
25
src/slic3r/Utils/SnapmakerPrinterAgent.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "MoonrakerPrinterAgent.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class SnapmakerPrinterAgent final : public MoonrakerPrinterAgent
|
||||
{
|
||||
public:
|
||||
explicit SnapmakerPrinterAgent(std::string log_dir);
|
||||
~SnapmakerPrinterAgent() override = default;
|
||||
|
||||
static AgentInfo get_agent_info_static();
|
||||
AgentInfo get_agent_info() override { return get_agent_info_static(); }
|
||||
|
||||
bool fetch_filament_info(std::string dev_id) override;
|
||||
|
||||
private:
|
||||
// Combine filament_type + filament_sub_type into a unified type string
|
||||
static std::string combine_filament_type(const std::string& type, const std::string& sub_type);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
Reference in New Issue
Block a user