mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-07 15:51:08 +00:00
fix: harden printer-agent async callbacks and detached fetch threads
This commit is contained in:
@@ -1461,9 +1461,10 @@ int MoonrakerPrinterAgent::handle_request(const std::string& dev_id, const std::
|
||||
}
|
||||
response["print"]["param"] = gcode;
|
||||
|
||||
send_gcode_async(dev_id, gcode, [this, dev_id, response](bool success) mutable {
|
||||
MessageRouter router = make_message_router(dev_id);
|
||||
send_gcode_async(dev_id, gcode, [router, response](bool success) mutable {
|
||||
response["print"]["result"] = success ? "success" : "failed";
|
||||
dispatch_message(dev_id, response.dump());
|
||||
router.deliver(response.dump());
|
||||
});
|
||||
return BAMBU_NETWORK_SUCCESS;
|
||||
}
|
||||
@@ -2905,6 +2906,36 @@ nlohmann::json MoonrakerPrinterAgent::build_print_payload_locked() const
|
||||
return payload;
|
||||
}
|
||||
|
||||
void MoonrakerPrinterAgent::MessageRouter::deliver(std::string payload) const
|
||||
{
|
||||
auto dispatch = [local = local_fn, cloud = cloud_fn, dev = dev_id, payload = std::move(payload)]() {
|
||||
if (local) {
|
||||
local(dev, payload);
|
||||
return;
|
||||
}
|
||||
if (cloud) {
|
||||
cloud(dev, payload);
|
||||
}
|
||||
};
|
||||
|
||||
if (queue_fn) {
|
||||
queue_fn(dispatch);
|
||||
} else {
|
||||
dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
MoonrakerPrinterAgent::MessageRouter MoonrakerPrinterAgent::make_message_router(const std::string& dev_id) const
|
||||
{
|
||||
MessageRouter router;
|
||||
router.dev_id = dev_id;
|
||||
std::lock_guard<std::recursive_mutex> lock(state_mutex);
|
||||
router.local_fn = on_local_message_fn;
|
||||
router.cloud_fn = on_message_fn;
|
||||
router.queue_fn = queue_on_main_fn;
|
||||
return router;
|
||||
}
|
||||
|
||||
void MoonrakerPrinterAgent::dispatch_message(const std::string& dev_id, const std::string& payload)
|
||||
{
|
||||
OnMessageFn local_fn;
|
||||
|
||||
@@ -209,6 +209,20 @@ private:
|
||||
void announce_printhost_device();
|
||||
void dispatch_local_connect(int state, const std::string& dev_id, const std::string& msg);
|
||||
void dispatch_printer_connected(const std::string& dev_id);
|
||||
|
||||
// Self-contained snapshot of the message callbacks. Async completions capture this
|
||||
// by value so they never dereference `this` after the agent may have been destroyed.
|
||||
struct MessageRouter
|
||||
{
|
||||
OnMessageFn local_fn;
|
||||
OnMessageFn cloud_fn;
|
||||
QueueOnMainFn queue_fn;
|
||||
std::string dev_id;
|
||||
|
||||
void deliver(std::string payload) const;
|
||||
};
|
||||
|
||||
MessageRouter make_message_router(const std::string& dev_id) const;
|
||||
void dispatch_message(const std::string& dev_id, const std::string& payload);
|
||||
void start_status_stream(const std::string& dev_id, ConnectionSettings connection);
|
||||
void stop_status_stream();
|
||||
|
||||
@@ -53,6 +53,14 @@ struct InFlightGuard
|
||||
~InFlightGuard() { if (counter) counter->fetch_sub(1, std::memory_order_relaxed); }
|
||||
};
|
||||
|
||||
// nlohmann::json::value() returns the default only when the key is absent; a present but
|
||||
// null/wrong-typed value throws. Firmware JSON is untrusted, so read defensively.
|
||||
int read_int_or(const nlohmann::json& obj, const std::string& key, int fallback)
|
||||
{
|
||||
auto it = obj.find(key);
|
||||
return (it != obj.end() && it->is_number_integer()) ? it->get<int>() : fallback;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
const std::string QidiPrinterAgent_VERSION = "0.0.1";
|
||||
@@ -98,37 +106,45 @@ bool QidiPrinterAgent::fetch_filament_info(std::string dev_id, FilamentSyncMode
|
||||
|
||||
InFlightGuard guard{filament_fetch_in_flight};
|
||||
std::thread([this, guard = std::move(guard), connection = std::move(connection), model_id, model_name]() mutable {
|
||||
std::string error;
|
||||
try {
|
||||
std::string error;
|
||||
|
||||
// 1. Fetch device info and infer series_id
|
||||
std::string series_id;
|
||||
{
|
||||
MoonrakerDeviceInfo info;
|
||||
if (fetch_device_info(connection, info, error)) {
|
||||
series_id = infer_series_id(info.model_id, info.dev_name);
|
||||
// 1. Fetch device info and infer series_id
|
||||
std::string series_id;
|
||||
{
|
||||
MoonrakerDeviceInfo info;
|
||||
if (fetch_device_info(connection, info, error)) {
|
||||
series_id = infer_series_id(info.model_id, info.dev_name);
|
||||
}
|
||||
}
|
||||
if (series_id.empty()) {
|
||||
// Fall back to the configured Orca model if Moonraker doesn't expose a usable identifier.
|
||||
series_id = infer_series_id(model_id, model_name);
|
||||
}
|
||||
}
|
||||
if (series_id.empty()) {
|
||||
// Fall back to the configured Orca model if Moonraker doesn't expose a usable identifier.
|
||||
series_id = infer_series_id(model_id, model_name);
|
||||
}
|
||||
|
||||
// 2. Fetch filament dictionary
|
||||
QidiFilamentDict dict;
|
||||
if (!fetch_filament_dict(connection, dict, error)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "QidiPrinterAgent::fetch_filament_info: Failed to fetch filament dict: " << error;
|
||||
}
|
||||
// 2. Fetch filament dictionary
|
||||
QidiFilamentDict dict;
|
||||
if (!fetch_filament_dict(connection, dict, error)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "QidiPrinterAgent::fetch_filament_info: Failed to fetch filament dict: " << error;
|
||||
}
|
||||
|
||||
// 3. Fetch slot info and build AmsTrayData directly
|
||||
std::vector<AmsTrayData> trays;
|
||||
int box_count = 0;
|
||||
if (!fetch_slot_info(connection, dict, series_id, trays, box_count, error)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "QidiPrinterAgent::fetch_filament_info: Failed to fetch slot info: " << error;
|
||||
return;
|
||||
}
|
||||
// 3. Fetch slot info and build AmsTrayData directly
|
||||
std::vector<AmsTrayData> trays;
|
||||
int box_count = 0;
|
||||
if (!fetch_slot_info(connection, dict, series_id, trays, box_count, error)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "QidiPrinterAgent::fetch_filament_info: Failed to fetch slot info: " << error;
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Build the AMS payload
|
||||
build_ams_payload(box_count, box_count * 4 - 1, trays);
|
||||
// 4. Build the AMS payload
|
||||
build_ams_payload(box_count, box_count * 4 - 1, trays);
|
||||
} catch (const std::exception& e) {
|
||||
// why: an exception escaping a detached thread is std::terminate, and firmware
|
||||
// JSON is untrusted; mirror run_command_worker and swallow it here.
|
||||
BOOST_LOG_TRIVIAL(error) << "QidiPrinterAgent::fetch_filament_info: unhandled exception: " << e.what();
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << "QidiPrinterAgent::fetch_filament_info: unhandled exception";
|
||||
}
|
||||
}).detach();
|
||||
return true;
|
||||
}
|
||||
@@ -257,7 +273,7 @@ bool QidiPrinterAgent::fetch_slot_info(const ConnectionSettings& connection,
|
||||
if (!parse_slot_response(response_body, status, variables, error))
|
||||
return false;
|
||||
|
||||
box_count = variables.value("box_count", 1);
|
||||
box_count = read_int_or(variables, "box_count", 1);
|
||||
if (box_count < 0) {
|
||||
box_count = 0;
|
||||
}
|
||||
@@ -280,9 +296,9 @@ bool QidiPrinterAgent::fetch_slot_info(const ConnectionSettings& connection,
|
||||
tray.slot_index = i;
|
||||
|
||||
// Read slot variables
|
||||
const int color_index = variables.value("color_slot" + std::to_string(i), 1);
|
||||
const int filament_type = variables.value("filament_slot" + std::to_string(i), 1);
|
||||
const int vendor_type = variables.value("vendor_slot" + std::to_string(i), 0);
|
||||
const int color_index = read_int_or(variables, "color_slot" + std::to_string(i), 1);
|
||||
const int filament_type = read_int_or(variables, "filament_slot" + std::to_string(i), 1);
|
||||
const int vendor_type = read_int_or(variables, "vendor_slot" + std::to_string(i), 0);
|
||||
|
||||
// Check filament presence via runout sensor
|
||||
std::string box_stepper_key = "box_stepper slot" + std::to_string(i);
|
||||
@@ -290,7 +306,7 @@ bool QidiPrinterAgent::fetch_slot_info(const ConnectionSettings& connection,
|
||||
if (status.contains(box_stepper_key)) {
|
||||
auto& box_stepper = status[box_stepper_key];
|
||||
if (box_stepper.contains("runout_button") && !box_stepper["runout_button"].is_null()) {
|
||||
int runout_button = box_stepper["runout_button"].template get<int>();
|
||||
const int runout_button = read_int_or(box_stepper, "runout_button", 0);
|
||||
tray.has_filament = (runout_button == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,58 @@ struct InFlightGuard
|
||||
~InFlightGuard() { if (counter) counter->fetch_sub(1, std::memory_order_relaxed); }
|
||||
};
|
||||
|
||||
// nlohmann::json::value() returns the default only when the key is absent; a present but
|
||||
// null/wrong-typed value throws. Firmware JSON is untrusted, so read defensively.
|
||||
int read_int_or(const nlohmann::json& obj, const char* key, int fallback)
|
||||
{
|
||||
auto it = obj.find(key);
|
||||
return (it != obj.end() && it->is_number_integer()) ? it->get<int>() : fallback;
|
||||
}
|
||||
|
||||
std::vector<std::string> read_string_array_or(const nlohmann::json& obj, const char* key)
|
||||
{
|
||||
auto it = obj.find(key);
|
||||
if (it == obj.end() || !it->is_array())
|
||||
return {};
|
||||
std::vector<std::string> out;
|
||||
out.reserve(it->size());
|
||||
for (const auto& v : *it)
|
||||
out.push_back(v.is_string() ? v.get<std::string>() : std::string{});
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<bool> read_bool_array_or(const nlohmann::json& obj, const char* key)
|
||||
{
|
||||
auto it = obj.find(key);
|
||||
if (it == obj.end() || !it->is_array())
|
||||
return {};
|
||||
std::vector<bool> out;
|
||||
out.reserve(it->size());
|
||||
for (const auto& v : *it)
|
||||
out.push_back(v.is_boolean() ? v.get<bool>() : false);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Parse a hex colour, stopping at the first non-hex character. std::stoul throws on
|
||||
// empty/non-hex input, which must not escape the detached fetch thread.
|
||||
unsigned int parse_hex_color(const std::string& hex)
|
||||
{
|
||||
unsigned int value = 0;
|
||||
for (char c : hex) {
|
||||
int digit;
|
||||
if (c >= '0' && c <= '9')
|
||||
digit = c - '0';
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
digit = c - 'a' + 10;
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
digit = c - 'A' + 10;
|
||||
else
|
||||
break;
|
||||
value = (value << 4) | static_cast<unsigned int>(digit);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// 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)
|
||||
@@ -68,14 +120,16 @@ std::string find_closest_color_preset_by_vendor_and_type(const PresetCollection&
|
||||
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);
|
||||
// so we must remove # and ignore alpha channel for distance calculation. Firmware
|
||||
// colours are untrusted; parse_hex_color tolerates empty/non-hex instead of throwing.
|
||||
unsigned int target_color_value =
|
||||
parse_hex_color(color_rgba.substr(0, color_rgba.size() >= 2 ? color_rgba.size() - 2 : 0));
|
||||
|
||||
std::string p_color = p.config.opt_string("default_filament_colour", 0u);
|
||||
unsigned int p_color_value;
|
||||
unsigned int p_color_value = 0;
|
||||
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);
|
||||
size_t hash_pos = p_color.find("#");
|
||||
p_color_value = parse_hex_color(p_color.substr(hash_pos != std::string::npos ? hash_pos + 1 : 0));
|
||||
} 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
|
||||
@@ -186,121 +240,132 @@ bool SnapmakerPrinterAgent::fetch_filament_info(std::string dev_id, FilamentSync
|
||||
|
||||
InFlightGuard guard{filament_fetch_in_flight};
|
||||
std::thread([this, guard = std::move(guard), base_url, api_key]() {
|
||||
const std::string url = join_url(base_url, "/printer/objects/query?print_task_config&filament_detect");
|
||||
try {
|
||||
const std::string url = join_url(base_url, "/printer/objects/query?print_task_config&filament_detect");
|
||||
|
||||
std::string response_body;
|
||||
bool success = false;
|
||||
std::string http_error;
|
||||
std::string response_body;
|
||||
bool success = false;
|
||||
std::string http_error;
|
||||
|
||||
auto http = Http::get(url);
|
||||
if (!api_key.empty()) {
|
||||
http.header("X-Api-Key", 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
auto http = Http::get(url);
|
||||
if (!api_key.empty()) {
|
||||
http.header("X-Api-Key", 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 {
|
||||
tray.tray_info_idx = bundle->filaments.filament_id_by_type(tray.tray_type);
|
||||
http_error = "HTTP error: " + std::to_string(status);
|
||||
}
|
||||
} else {
|
||||
tray.tray_info_idx = map_filament_type_to_generic_id(tray.tray_type);
|
||||
}
|
||||
})
|
||||
.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();
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "SnapmakerPrinterAgent::fetch_filament_info: HTTP request failed: " << http_error;
|
||||
return;
|
||||
}
|
||||
|
||||
trays.emplace_back(std::move(tray));
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
build_ams_payload(1, slot_count - 1, trays);
|
||||
// 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;
|
||||
}
|
||||
|
||||
auto& ptc = json["result"]["status"]["print_task_config"];
|
||||
|
||||
// Read parallel arrays from print_task_config
|
||||
auto filament_exist = read_bool_array_or(ptc, "filament_exist");
|
||||
auto filament_type = read_string_array_or(ptc, "filament_type");
|
||||
auto filament_sub_type = read_string_array_or(ptc, "filament_sub_type");
|
||||
auto filament_color = read_string_array_or(ptc, "filament_color_rgba");
|
||||
auto filament_vendor = read_string_array_or(ptc, "filament_vendor");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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 = "NONE";
|
||||
if (auto vendor_it = nfc_slot.find("VENDOR"); vendor_it != nfc_slot.end() && vendor_it->is_string()) {
|
||||
vendor = vendor_it->get<std::string>();
|
||||
}
|
||||
if (vendor != "NONE" && !vendor.empty()) {
|
||||
tray.bed_temp = read_int_or(nfc_slot, "BED_TEMP", 0);
|
||||
tray.nozzle_temp = read_int_or(nfc_slot, "FIRST_LAYER_TEMP", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trays.emplace_back(std::move(tray));
|
||||
}
|
||||
|
||||
build_ams_payload(1, slot_count - 1, trays);
|
||||
} catch (const std::exception& e) {
|
||||
// why: an exception escaping a detached thread is std::terminate, and firmware
|
||||
// JSON is untrusted; mirror run_command_worker and swallow it here.
|
||||
BOOST_LOG_TRIVIAL(error) << "SnapmakerPrinterAgent::fetch_filament_info: unhandled exception: " << e.what();
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << "SnapmakerPrinterAgent::fetch_filament_info: unhandled exception";
|
||||
}
|
||||
}).detach();
|
||||
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user