mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 21:42:43 +00:00
build: clear eleven single-site clang-cl warning categories Each of these is the last site left in its category, and every one is the compiler saying it cannot tell what the code meant. Nothing here changes defined behavior. - OrcaSlicer_app_msvc.cpp printed a DWORD with %d - StackWalker.cpp ran delete[] through an LPVOID - ToolOrdering.cpp used a bare ; as a deliberate skip loop's body - WipeTower.cpp had finish_block_tcr = finish_block_tcr, so the branch that reached it did nothing. Folding the condition into the enclosing if leaves the other branch untouched - GCodeProcessor.cpp had an else binding to the inner if while the outer if carried no braces - AmsMappingPopupUpdate.cpp wrote >= 1 || <= 3 where its own comment says && - CalibrationWizardPresetPage.cpp left max_decimal_length unset through a pair of conditions that cover every value but not visibly so - DevManager.cpp bound map elements to pair<K, V> rather than pair<const K, V>, copying every one - SyncAmsInfoDialog.cpp had extraneous parentheses around a comparison - Http.cpp had if (speed > 0.01) speed = speed;. speed now starts at 0 as well, because curl_easy_getinfo leaves the target untouched when it fails and the value reaches Progress either way - SnapmakerPrinterAgent.cpp truncated npos into an unsigned int, so the != npos guard was always true. A colour with no # still yields 0, because the wrap produced 0 as well Nine categories go to zero. -Wtautological-overlap-compare and -Wsometimes-uninitialized reach zero when #15583 merges their second site.
229 lines
9.2 KiB
C++
229 lines
9.2 KiB
C++
#include "SnapmakerPrinterAgent.hpp"
|
|
#include "Http.hpp"
|
|
#include "libslic3r/PresetBundle.hpp"
|
|
#include "slic3r/GUI/GUI_App.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;
|
|
}
|
|
|
|
std::string find_closest_color_preset_by_vendor_and_type(const PresetCollection& filaments,
|
|
const std::string& vendor_name,
|
|
const std::string& filament_type,
|
|
const std::string& color_rgba)
|
|
{
|
|
std::string best_match_id = "";
|
|
int best_color_distance = 0xffffffff;
|
|
|
|
for (const auto& p : filaments.get_presets()) {
|
|
if (p.is_visible && p.is_compatible &&
|
|
// Filament profile must be detached from parent to be considered for matching
|
|
filaments.get_preset_base(p) == &p && p.config.opt_string("filament_vendor", 0u) == vendor_name &&
|
|
p.config.opt_string("filament_type", 0u) == filament_type) {
|
|
// The printer returns RGBA in the format RRGGBBAA, but profiles store color as #RRGGBB,
|
|
// so we must remove # and ignore alpha channel for distance calculation
|
|
unsigned int target_color_value = std::stoul(color_rgba.substr(0, color_rgba.length() - 2), nullptr, 16);
|
|
|
|
std::string p_color = p.config.opt_string("default_filament_colour", 0u);
|
|
unsigned int p_color_value;
|
|
if (!p_color.empty()) {
|
|
size_t hash_pos = p_color.find("#");
|
|
p_color_value = std::stoul(p_color.substr(hash_pos != std::string::npos ? hash_pos + 1 : 0), nullptr, 16);
|
|
} else {
|
|
// Default to black if no color specified in profile. Assume other profiles might be a closer color match.
|
|
// Could be a problem if the target color is also black and there exist a specific profile for that type, vendor and color
|
|
// combination.
|
|
p_color_value = 0;
|
|
}
|
|
|
|
// Calculate Euclidean color distance in RGB space
|
|
int dr = ((target_color_value & 0xff) - (p_color_value & 0xff));
|
|
int dg = (((target_color_value >> 8) & 0xff) - ((p_color_value >> 8) & 0xff));
|
|
int db = (((target_color_value >> 16) & 0xff) - ((p_color_value >> 16) & 0xff));
|
|
unsigned int distance = dr * dr + dg * dg + db * db;
|
|
|
|
if (distance < best_color_distance) {
|
|
best_color_distance = distance;
|
|
best_match_id = p.filament_id;
|
|
}
|
|
}
|
|
}
|
|
return best_match_id;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
SnapmakerPrinterAgent::SnapmakerPrinterAgent(std::string log_dir) : MoonrakerPrinterAgent(std::move(log_dir)) {}
|
|
|
|
AgentInfo SnapmakerPrinterAgent::get_agent_info_static()
|
|
{
|
|
return AgentInfo{"snapmaker", "Snapmaker", 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 == "SNAPSPEED" || sub == "HS")
|
|
return base + " HIGH SPEED";
|
|
if (sub == "SILK")
|
|
return base + " SILK";
|
|
if (sub == "WOOD")
|
|
return base + " WOOD";
|
|
if (sub == "MATTE")
|
|
return base + " MATTE";
|
|
if (sub == "MARBLE")
|
|
return base + " MARBLE";
|
|
|
|
// 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>{});
|
|
auto filament_vendor = ptc.value("filament_vendor", 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_color = safe_at(filament_color, i, default_color);
|
|
|
|
auto* bundle = GUI::wxGetApp().preset_bundle;
|
|
// Try to find a matching preset for this filament based on vendor, type and color.
|
|
// If not found, default to traditional search by type only or generic type mapping.
|
|
if (bundle) {
|
|
std::string vendor = safe_at(filament_vendor, i, empty_str);
|
|
std::string filament_id = find_closest_color_preset_by_vendor_and_type(bundle->filaments, vendor, tray.tray_type,
|
|
tray.tray_color);
|
|
|
|
if (!filament_id.empty()) {
|
|
tray.tray_info_idx = filament_id;
|
|
BOOST_LOG_TRIVIAL(warning) << "Filament sync: Found manufacturer-specific profile for slot " << i << ": "
|
|
<< filament_id;
|
|
} else {
|
|
tray.tray_info_idx = bundle->filaments.filament_id_by_type(tray.tray_type);
|
|
}
|
|
} else {
|
|
tray.tray_info_idx = map_filament_type_to_generic_id(tray.tray_type);
|
|
}
|
|
|
|
// 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
|