mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-10 09:11:08 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aebc0a2979 | ||
|
|
6f0ec60d21 | ||
|
|
6b366ad8a9 | ||
|
|
3d62769664 |
@@ -3894,6 +3894,16 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
|||||||
maps.erase(j);
|
maps.erase(j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (is_placeholder) {
|
||||||
|
// Orca: an empty AMS slot is a placeholder, not a filament - never synthesize a
|
||||||
|
// preset for it. Kept here only to keep index alignment with ams_infos; the sync
|
||||||
|
// drops it so no filament slot is created or retained for an empty tray.
|
||||||
|
ams_filament_presets.push_back("");
|
||||||
|
ams_filament_colors.push_back("");
|
||||||
|
ams_filament_color_types.push_back("");
|
||||||
|
ams_multi_color_filment.push_back({});
|
||||||
|
} else if (use_map) {
|
||||||
ams_filament_presets.push_back("Generic PLA");//for unknow matieral
|
ams_filament_presets.push_back("Generic PLA");//for unknow matieral
|
||||||
auto default_unknown_color = "#CECECE";
|
auto default_unknown_color = "#CECECE";
|
||||||
ams_filament_colors.push_back(default_unknown_color);
|
ams_filament_colors.push_back(default_unknown_color);
|
||||||
@@ -3902,12 +3912,6 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
|||||||
filament_multi_color.push_back(default_unknown_color);
|
filament_multi_color.push_back(default_unknown_color);
|
||||||
}
|
}
|
||||||
ams_multi_color_filment.push_back(filament_multi_color);
|
ams_multi_color_filment.push_back(filament_multi_color);
|
||||||
} else if (is_placeholder) {
|
|
||||||
// Orca: push placeholders to keep index alignment with ams_infos
|
|
||||||
ams_filament_presets.push_back("");
|
|
||||||
ams_filament_colors.push_back("");
|
|
||||||
ams_filament_color_types.push_back("");
|
|
||||||
ams_multi_color_filment.push_back({});
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -4271,6 +4275,10 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
|||||||
result_multi_colors.push_back(
|
result_multi_colors.push_back(
|
||||||
i < ams_multi_color_filment.size() ? ams_multi_color_filment[i]
|
i < ams_multi_color_filment.size() ? ams_multi_color_filment[i]
|
||||||
: std::vector<std::string>{ams_filament_colors[i]});
|
: std::vector<std::string>{ams_filament_colors[i]});
|
||||||
|
} else if (i < ams_infos.size() && ams_infos[i].is_placeholder) {
|
||||||
|
// Orca: an empty AMS slot is a placeholder, not a filament, so drop it instead of
|
||||||
|
// keeping or filling a slot for it. The device AMS panel still shows the empty tray.
|
||||||
|
continue;
|
||||||
} else if (i < exist_presets.size()) {
|
} else if (i < exist_presets.size()) {
|
||||||
// Empty tray or beyond tray count: keep existing filament
|
// Empty tray or beyond tray count: keep existing filament
|
||||||
result_colors.push_back(exist_colors[i]);
|
result_colors.push_back(exist_colors[i]);
|
||||||
@@ -4295,8 +4303,8 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
|
|||||||
filament_color_type->values = result_color_types;
|
filament_color_type->values = result_color_types;
|
||||||
this->filament_presets = result_presets;
|
this->filament_presets = result_presets;
|
||||||
ams_multi_color_filment = result_multi_colors;
|
ams_multi_color_filment = result_multi_colors;
|
||||||
filament_map->values.resize(total, 1);
|
filament_map->values.resize(result_presets.size(), 1);
|
||||||
filament_volume_map->values.resize(total, static_cast<int>(NozzleVolumeType::nvtStandard));
|
filament_volume_map->values.resize(result_presets.size(), static_cast<int>(NozzleVolumeType::nvtStandard));
|
||||||
} else {
|
} else {
|
||||||
// BBL: existing wholesale replace
|
// BBL: existing wholesale replace
|
||||||
filament_color->values = ams_filament_colors;
|
filament_color->values = ams_filament_colors;
|
||||||
|
|||||||
@@ -441,19 +441,7 @@ int MoonrakerPrinterAgent::set_queue_on_main_fn(QueueOnMainFn fn)
|
|||||||
|
|
||||||
void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index, const std::vector<AmsTrayData>& trays)
|
void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index, const std::vector<AmsTrayData>& trays)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Look up MachineObject via DeviceManager
|
|
||||||
auto* dev_manager = GUI::wxGetApp().getDeviceManager();
|
|
||||||
if (!dev_manager) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MachineObject* obj = dev_manager->get_my_machine(device_info.dev_id);
|
|
||||||
if (!obj) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build BBL-format JSON for DevFilaSystemParser::ParseV1_0
|
// Build BBL-format JSON for DevFilaSystemParser::ParseV1_0
|
||||||
nlohmann::json ams_json = nlohmann::json::object();
|
|
||||||
nlohmann::json ams_array = nlohmann::json::array();
|
nlohmann::json ams_array = nlohmann::json::array();
|
||||||
|
|
||||||
// Calculate ams_exist_bits and tray_exist_bits
|
// Calculate ams_exist_bits and tray_exist_bits
|
||||||
@@ -461,11 +449,9 @@ void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index,
|
|||||||
unsigned long tray_exist_bits = 0;
|
unsigned long tray_exist_bits = 0;
|
||||||
|
|
||||||
for (int ams_id = 0; ams_id < ams_count; ++ams_id) {
|
for (int ams_id = 0; ams_id < ams_count; ++ams_id) {
|
||||||
ams_exist_bits |= (1 << ams_id);
|
if (ams_id < 32) {
|
||||||
|
ams_exist_bits |= (1UL << ams_id);
|
||||||
nlohmann::json ams_unit = nlohmann::json::object();
|
}
|
||||||
ams_unit["id"] = std::to_string(ams_id);
|
|
||||||
ams_unit["info"] = "0002"; // treat as AMS_LITE
|
|
||||||
|
|
||||||
nlohmann::json tray_array = nlohmann::json::array();
|
nlohmann::json tray_array = nlohmann::json::array();
|
||||||
int max_slot_in_this_ams = std::min(3, max_lane_index - ams_id * 4);
|
int max_slot_in_this_ams = std::min(3, max_lane_index - ams_id * 4);
|
||||||
@@ -481,43 +467,130 @@ void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nlohmann::json tray_json = nlohmann::json::object();
|
if (tray && tray->has_filament && slot_index < 32) {
|
||||||
tray_json["id"] = std::to_string(slot_id);
|
tray_exist_bits |= (1UL << slot_index);
|
||||||
tray_json["tag_uid"] = "0000000000000000";
|
|
||||||
|
|
||||||
if (tray && tray->has_filament) {
|
|
||||||
tray_exist_bits |= (1 << slot_index);
|
|
||||||
|
|
||||||
tray_json["tray_info_idx"] = tray->tray_info_idx;
|
|
||||||
tray_json["tray_type"] = tray->tray_type;
|
|
||||||
tray_json["tray_color"] = normalize_color_value(tray->tray_color);
|
|
||||||
|
|
||||||
// Add temperature data if provided
|
|
||||||
if (tray->bed_temp > 0) {
|
|
||||||
tray_json["bed_temp"] = std::to_string(tray->bed_temp);
|
|
||||||
}
|
|
||||||
if (tray->nozzle_temp > 0) {
|
|
||||||
tray_json["nozzle_temp_max"] = std::to_string(tray->nozzle_temp);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tray_json["tray_info_idx"] = "";
|
|
||||||
tray_json["tray_type"] = "";
|
|
||||||
tray_json["tray_color"] = "00000000";
|
|
||||||
tray_json["tray_slot_placeholder"] = "1";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tray_array.push_back(tray_json);
|
static const AmsTrayData empty_tray;
|
||||||
|
tray_array.push_back(make_ams_tray_json(std::to_string(slot_id), tray ? *tray : empty_tray));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nlohmann::json ams_unit = nlohmann::json::object();
|
||||||
|
ams_unit["id"] = std::to_string(ams_id);
|
||||||
|
// Generic unit: type 1 (AMS) maps to AMSModel::GENERIC_AMS, which drives the
|
||||||
|
// variable-lane rendering. This is not a real Bambu AMS-Lite.
|
||||||
|
ams_unit["info"] = "0001";
|
||||||
ams_unit["tray"] = tray_array;
|
ams_unit["tray"] = tray_array;
|
||||||
ams_array.push_back(ams_unit);
|
ams_array.push_back(ams_unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::build_ams_payload: Parsed " << trays.size() << " trays";
|
||||||
|
submit_ams_payload(ams_array, ams_exist_bits, tray_exist_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoonrakerPrinterAgent::build_ams_payload_grouped(const std::vector<AmsTrayData>& trays)
|
||||||
|
{
|
||||||
|
// One AMS box per extruder, best effort. Lane-data changers (AFC, recent Happy Hare) report an
|
||||||
|
// extruder per lane but no physical box layout, so a box here is a logical feed group, not a
|
||||||
|
// discovered unit. Lanes without an extruder fall back to the main extruder.
|
||||||
|
std::map<int, std::vector<const AmsTrayData*>> boxes;
|
||||||
|
for (const auto& tray : trays) {
|
||||||
|
boxes[tray.extruder_id].push_back(&tray);
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json ams_array = nlohmann::json::array();
|
||||||
|
unsigned long ams_exist_bits = 0;
|
||||||
|
unsigned long tray_exist_bits = 0;
|
||||||
|
|
||||||
|
int ams_id = 0;
|
||||||
|
for (auto& [extruder_id, lanes] : boxes) {
|
||||||
|
if (ams_id < 32) {
|
||||||
|
ams_exist_bits |= (1UL << ams_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep a deterministic slot order within the box.
|
||||||
|
std::stable_sort(lanes.begin(), lanes.end(),
|
||||||
|
[](const AmsTrayData* a, const AmsTrayData* b) { return a->slot_index < b->slot_index; });
|
||||||
|
|
||||||
|
nlohmann::json tray_array = nlohmann::json::array();
|
||||||
|
for (size_t slot = 0; slot < lanes.size(); ++slot) {
|
||||||
|
const AmsTrayData& tray = *lanes[slot];
|
||||||
|
int bit = ams_id * 4 + static_cast<int>(slot);
|
||||||
|
if (tray.has_filament && bit < 32) {
|
||||||
|
tray_exist_bits |= (1UL << bit);
|
||||||
|
}
|
||||||
|
tray_array.push_back(make_ams_tray_json(std::to_string(slot), tray));
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json ams_unit = nlohmann::json::object();
|
||||||
|
ams_unit["id"] = std::to_string(ams_id);
|
||||||
|
// info: bits 0-3 = unit type (1 = AMS, i.e. AMSModel::GENERIC_AMS), bits 8-11 = bound
|
||||||
|
// extruder id. A logical feed group is a generic unit, not a Bambu AMS-Lite.
|
||||||
|
const int info_val = 1 | ((extruder_id & 0x0F) << 8);
|
||||||
|
const char* hex_digits = "0123456789ABCDEF";
|
||||||
|
std::string info = "0000";
|
||||||
|
info[3] = hex_digits[info_val & 0x0F];
|
||||||
|
info[2] = hex_digits[(info_val >> 4) & 0x0F];
|
||||||
|
info[1] = hex_digits[(info_val >> 8) & 0x0F];
|
||||||
|
info[0] = hex_digits[(info_val >> 12) & 0x0F];
|
||||||
|
ams_unit["info"] = info;
|
||||||
|
ams_unit["tray"] = tray_array;
|
||||||
|
ams_array.push_back(ams_unit);
|
||||||
|
++ams_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::build_ams_payload_grouped: Parsed " << trays.size()
|
||||||
|
<< " trays in " << boxes.size() << " extruder box(es)";
|
||||||
|
submit_ams_payload(ams_array, ams_exist_bits, tray_exist_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json MoonrakerPrinterAgent::make_ams_tray_json(const std::string& tray_id, const AmsTrayData& tray)
|
||||||
|
{
|
||||||
|
nlohmann::json tray_json = nlohmann::json::object();
|
||||||
|
tray_json["id"] = tray_id;
|
||||||
|
tray_json["tag_uid"] = "0000000000000000";
|
||||||
|
|
||||||
|
if (tray.has_filament) {
|
||||||
|
tray_json["tray_info_idx"] = tray.tray_info_idx;
|
||||||
|
tray_json["tray_type"] = tray.tray_type;
|
||||||
|
tray_json["tray_color"] = normalize_color_value(tray.tray_color);
|
||||||
|
|
||||||
|
// Add temperature data if provided
|
||||||
|
if (tray.bed_temp > 0) {
|
||||||
|
tray_json["bed_temp"] = std::to_string(tray.bed_temp);
|
||||||
|
}
|
||||||
|
if (tray.nozzle_temp > 0) {
|
||||||
|
tray_json["nozzle_temp_max"] = std::to_string(tray.nozzle_temp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tray_json["tray_info_idx"] = "";
|
||||||
|
tray_json["tray_type"] = "";
|
||||||
|
tray_json["tray_color"] = "00000000";
|
||||||
|
tray_json["tray_slot_placeholder"] = "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
return tray_json;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoonrakerPrinterAgent::submit_ams_payload(const nlohmann::json& ams_array, unsigned long ams_exist_bits, unsigned long tray_exist_bits)
|
||||||
|
{
|
||||||
|
// Look up MachineObject via DeviceManager
|
||||||
|
auto* dev_manager = GUI::wxGetApp().getDeviceManager();
|
||||||
|
if (!dev_manager) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MachineObject* obj = dev_manager->get_my_machine(device_info.dev_id);
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Format as hex strings (matching BBL protocol)
|
// Format as hex strings (matching BBL protocol)
|
||||||
std::ostringstream ams_exist_ss;
|
std::ostringstream ams_exist_ss;
|
||||||
ams_exist_ss << std::hex << std::uppercase << ams_exist_bits;
|
ams_exist_ss << std::hex << std::uppercase << ams_exist_bits;
|
||||||
std::ostringstream tray_exist_ss;
|
std::ostringstream tray_exist_ss;
|
||||||
tray_exist_ss << std::hex << std::uppercase << tray_exist_bits;
|
tray_exist_ss << std::hex << std::uppercase << tray_exist_bits;
|
||||||
|
|
||||||
|
nlohmann::json ams_json = nlohmann::json::object();
|
||||||
ams_json["ams"] = ams_array;
|
ams_json["ams"] = ams_array;
|
||||||
ams_json["ams_exist_bits"] = ams_exist_ss.str();
|
ams_json["ams_exist_bits"] = ams_exist_ss.str();
|
||||||
ams_json["tray_exist_bits"] = tray_exist_ss.str();
|
ams_json["tray_exist_bits"] = tray_exist_ss.str();
|
||||||
@@ -528,7 +601,6 @@ void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index,
|
|||||||
|
|
||||||
// Call the parser to populate DevFilaSystem
|
// Call the parser to populate DevFilaSystem
|
||||||
DevFilaSystemParser::ParseV1_0(print_json, obj, obj->GetFilaSystem().get(), false);
|
DevFilaSystemParser::ParseV1_0(print_json, obj, obj->GetFilaSystem().get(), false);
|
||||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::build_ams_payload: Parsed " << trays.size() << " trays";
|
|
||||||
|
|
||||||
// Set printer_type so update_sync_status() can match it against the preset's printer type.
|
// Set printer_type so update_sync_status() can match it against the preset's printer type.
|
||||||
// Without this, the comparison fails and all sync badges are cleared.
|
// Without this, the comparison fails and all sync badges are cleared.
|
||||||
@@ -568,17 +640,15 @@ bool MoonrakerPrinterAgent::fetch_filament_info(std::string dev_id, FilamentSync
|
|||||||
if (fetch_moonraker_filament_data(trays, max_lane_index)) {
|
if (fetch_moonraker_filament_data(trays, max_lane_index)) {
|
||||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected Moonraker filament system with "
|
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected Moonraker filament system with "
|
||||||
<< (max_lane_index + 1) << " lanes";
|
<< (max_lane_index + 1) << " lanes";
|
||||||
int ams_count = (max_lane_index + 4) / 4;
|
build_ams_payload_grouped(trays);
|
||||||
build_ams_payload(ams_count, max_lane_index, trays);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt Happy Hare first (more widely adopted, supports more filament changers)
|
// Fall back to the Happy Hare `mmu` object for HH builds that do not publish lane_data
|
||||||
if (fetch_hh_filament_info(trays, max_lane_index)) {
|
if (fetch_hh_filament_info(trays, max_lane_index)) {
|
||||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected Happy Hare MMU with "
|
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected Happy Hare MMU with "
|
||||||
<< (max_lane_index + 1) << " gates";
|
<< (max_lane_index + 1) << " gates";
|
||||||
int ams_count = (max_lane_index + 4) / 4;
|
build_ams_payload_grouped(trays);
|
||||||
build_ams_payload(ams_count, max_lane_index, trays);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -684,6 +754,30 @@ int MoonrakerPrinterAgent::safe_json_int(const nlohmann::json& obj, const char*
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MoonrakerPrinterAgent::safe_json_int_flexible(const nlohmann::json& obj, const char* key, int default_value)
|
||||||
|
{
|
||||||
|
auto it = obj.find(key);
|
||||||
|
if (it == obj.end())
|
||||||
|
return default_value;
|
||||||
|
if (it->is_number_integer())
|
||||||
|
return it->get<int>();
|
||||||
|
if (it->is_string()) {
|
||||||
|
const std::string s = it->get<std::string>();
|
||||||
|
if (s.empty())
|
||||||
|
return default_value;
|
||||||
|
try {
|
||||||
|
std::size_t consumed = 0;
|
||||||
|
const int value = std::stoi(s, &consumed);
|
||||||
|
if (consumed != s.size()) // reject partial parses such as "2oops"
|
||||||
|
return default_value;
|
||||||
|
return value;
|
||||||
|
} catch (...) {
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
|
||||||
std::string MoonrakerPrinterAgent::safe_array_string(const nlohmann::json& arr, int idx)
|
std::string MoonrakerPrinterAgent::safe_array_string(const nlohmann::json& arr, int idx)
|
||||||
{
|
{
|
||||||
if (arr.is_array() && idx >= 0 && idx < static_cast<int>(arr.size()) && arr[idx].is_string())
|
if (arr.is_array() && idx >= 0 && idx < static_cast<int>(arr.size()) && arr[idx].is_string())
|
||||||
@@ -792,23 +886,22 @@ bool MoonrakerPrinterAgent::fetch_moonraker_filament_data(std::vector<AmsTrayDat
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract lane index from the "lane" field (tool number, 0-based)
|
// Lane tool number (0-based). AFC reports it as a string in the documented sample but it
|
||||||
std::string lane_str = safe_json_string(lane_obj, "lane");
|
// can also be a number; accept either and skip lanes without a usable index.
|
||||||
int lane_index = -1;
|
const int lane_index = safe_json_int_flexible(lane_obj, "lane", -1);
|
||||||
if (!lane_str.empty()) {
|
|
||||||
try {
|
|
||||||
lane_index = std::stoi(lane_str);
|
|
||||||
} catch (...) {
|
|
||||||
lane_index = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lane_index < 0) {
|
if (lane_index < 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
AmsTrayData tray;
|
AmsTrayData tray;
|
||||||
tray.slot_index = lane_index;
|
tray.slot_index = lane_index;
|
||||||
|
// AFC exposes the lane's extruder/toolhead as "extruder_index"; tolerate "extruder_id" as
|
||||||
|
// an alias. Unknown or negative falls back to the main extruder (best effort).
|
||||||
|
tray.extruder_id = safe_json_int_flexible(lane_obj, "extruder_index",
|
||||||
|
safe_json_int_flexible(lane_obj, "extruder_id", 0));
|
||||||
|
if (tray.extruder_id < 0) {
|
||||||
|
tray.extruder_id = 0;
|
||||||
|
}
|
||||||
tray.tray_color = safe_json_string(lane_obj, "color");
|
tray.tray_color = safe_json_string(lane_obj, "color");
|
||||||
tray.tray_type = safe_json_string(lane_obj, "material");
|
tray.tray_type = safe_json_string(lane_obj, "material");
|
||||||
tray.bed_temp = safe_json_int(lane_obj, "bed_temp");
|
tray.bed_temp = safe_json_int(lane_obj, "bed_temp");
|
||||||
@@ -913,48 +1006,41 @@ bool MoonrakerPrinterAgent::fetch_hh_filament_info(std::vector<AmsTrayData>& tra
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse gate data
|
// Parse gate data. Every gate becomes a lane so empty/unknown gates surface
|
||||||
|
// as empty AMS slots rather than vanishing. HH is single-extruder, so all
|
||||||
|
// lanes belong to feed group 0.
|
||||||
trays.clear();
|
trays.clear();
|
||||||
max_lane_index = 0;
|
|
||||||
|
|
||||||
for (int gate_idx = 0; gate_idx < num_gates; ++gate_idx) {
|
for (int gate_idx = 0; gate_idx < num_gates; ++gate_idx) {
|
||||||
// Check gate_status: -1 = unknown, 0 = empty, 1 or 2 = available
|
// Check gate_status: -1 = unknown, 0 = empty, 1 or 2 = available
|
||||||
int status = safe_array_int(gate_status, gate_idx);
|
int status = safe_array_int(gate_status, gate_idx);
|
||||||
if (status <= 0) {
|
|
||||||
continue; // Skip unknown or empty gates
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract gate data
|
|
||||||
std::string material = safe_array_string(gate_material, gate_idx);
|
std::string material = safe_array_string(gate_material, gate_idx);
|
||||||
std::string color = safe_array_string(gate_color, gate_idx);
|
std::string color = safe_array_string(gate_color, gate_idx);
|
||||||
int nozzle_temp = safe_array_int(gate_temperature, gate_idx);
|
int nozzle_temp = safe_array_int(gate_temperature, gate_idx);
|
||||||
|
|
||||||
// Skip if no material type (empty gate)
|
|
||||||
if (material.empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
AmsTrayData tray;
|
AmsTrayData tray;
|
||||||
tray.slot_index = gate_idx;
|
tray.slot_index = gate_idx;
|
||||||
tray.tray_type = material;
|
tray.extruder_id = 0;
|
||||||
tray.tray_color = color;
|
|
||||||
tray.nozzle_temp = nozzle_temp;
|
|
||||||
tray.bed_temp = 0; // HH doesn't provide bed temp in gate arrays
|
|
||||||
tray.has_filament = true;
|
|
||||||
|
|
||||||
auto* bundle = GUI::wxGetApp().preset_bundle;
|
if (status > 0 && !material.empty()) {
|
||||||
tray.tray_info_idx = bundle
|
tray.tray_type = material;
|
||||||
? bundle->filaments.filament_id_by_type(tray.tray_type)
|
tray.tray_color = color;
|
||||||
: map_filament_type_to_generic_id(tray.tray_type);
|
tray.nozzle_temp = nozzle_temp;
|
||||||
|
tray.bed_temp = 0; // HH doesn't provide bed temp in gate arrays
|
||||||
|
tray.has_filament = true;
|
||||||
|
|
||||||
|
auto* bundle = GUI::wxGetApp().preset_bundle;
|
||||||
|
tray.tray_info_idx = bundle
|
||||||
|
? bundle->filaments.filament_id_by_type(tray.tray_type)
|
||||||
|
: map_filament_type_to_generic_id(tray.tray_type);
|
||||||
|
} else {
|
||||||
|
tray.has_filament = false;
|
||||||
|
}
|
||||||
|
|
||||||
max_lane_index = std::max(max_lane_index, gate_idx);
|
|
||||||
trays.push_back(tray);
|
trays.push_back(tray);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trays.empty()) {
|
max_lane_index = num_gates - 1;
|
||||||
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_hh_filament_info: No valid HH gates found";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ protected:
|
|||||||
// Tray data for AMS payload building
|
// Tray data for AMS payload building
|
||||||
struct AmsTrayData {
|
struct AmsTrayData {
|
||||||
int slot_index = 0; // 0-based slot index
|
int slot_index = 0; // 0-based slot index
|
||||||
|
int extruder_id = 0; // Extruder this lane feeds (AFC extruder_index); 0 when unknown
|
||||||
bool has_filament = false;
|
bool has_filament = false;
|
||||||
std::string tray_type; // Material type (e.g., "PLA", "ASA")
|
std::string tray_type; // Material type (e.g., "PLA", "ASA")
|
||||||
std::string tray_color; // Raw color (#RRGGBB, 0xRRGGBB, or RRGGBBAA)
|
std::string tray_color; // Raw color (#RRGGBB, 0xRRGGBB, or RRGGBBAA)
|
||||||
@@ -99,6 +100,10 @@ protected:
|
|||||||
// Build ams JSON and call parser
|
// Build ams JSON and call parser
|
||||||
void build_ams_payload(int ams_count, int max_lane_index, const std::vector<AmsTrayData>& trays);
|
void build_ams_payload(int ams_count, int max_lane_index, const std::vector<AmsTrayData>& trays);
|
||||||
|
|
||||||
|
// Build ams JSON with one box per extruder (best effort, for lane_data-based filament
|
||||||
|
// changers such as AFC) and call the parser. Boxes are ordered by extruder id.
|
||||||
|
void build_ams_payload_grouped(const std::vector<AmsTrayData>& trays);
|
||||||
|
|
||||||
// Methods that derived classes may need to override or access
|
// Methods that derived classes may need to override or access
|
||||||
virtual bool init_device_info(const std::string& dev_id, const std::string& dev_ip, const std::string& username, const std::string& password, bool use_ssl, const std::string& port);
|
virtual bool init_device_info(const std::string& dev_id, const std::string& dev_ip, const std::string& username, const std::string& password, bool use_ssl, const std::string& port);
|
||||||
virtual bool fetch_device_info(const std::string& base_url, const std::string& api_key, MoonrakerDeviceInfo& info, std::string& error) const;
|
virtual bool fetch_device_info(const std::string& base_url, const std::string& api_key, MoonrakerDeviceInfo& info, std::string& error) const;
|
||||||
@@ -164,9 +169,18 @@ private:
|
|||||||
bool fetch_hh_filament_info(std::vector<AmsTrayData>& trays, int& max_lane_index);
|
bool fetch_hh_filament_info(std::vector<AmsTrayData>& trays, int& max_lane_index);
|
||||||
bool fetch_moonraker_filament_data(std::vector<AmsTrayData>& trays, int& max_lane_index);
|
bool fetch_moonraker_filament_data(std::vector<AmsTrayData>& trays, int& max_lane_index);
|
||||||
|
|
||||||
|
// Build one BBL tray JSON object (placeholder when the tray has no filament).
|
||||||
|
static nlohmann::json make_ams_tray_json(const std::string& tray_id, const AmsTrayData& tray);
|
||||||
|
|
||||||
|
// Wrap the AMS boxes into the payload ParseV1_0 understands and apply the pull-mode
|
||||||
|
// readiness state (printer_type, push counters, storage, module versions).
|
||||||
|
void submit_ams_payload(const nlohmann::json& ams_array, unsigned long ams_exist_bits, unsigned long tray_exist_bits);
|
||||||
|
|
||||||
// JSON helper methods
|
// JSON helper methods
|
||||||
static std::string safe_json_string(const nlohmann::json& obj, const char* key);
|
static std::string safe_json_string(const nlohmann::json& obj, const char* key);
|
||||||
static int safe_json_int(const nlohmann::json& obj, const char* key);
|
static int safe_json_int(const nlohmann::json& obj, const char* key);
|
||||||
|
// Accepts either a JSON number or a numeric string (AFC reports some fields as strings).
|
||||||
|
static int safe_json_int_flexible(const nlohmann::json& obj, const char* key, int default_value);
|
||||||
static std::string safe_array_string(const nlohmann::json& arr, int idx);
|
static std::string safe_array_string(const nlohmann::json& arr, int idx);
|
||||||
static int safe_array_int(const nlohmann::json& arr, int idx);
|
static int safe_array_int(const nlohmann::json& arr, int idx);
|
||||||
static std::string normalize_color_value(const std::string& color);
|
static std::string normalize_color_value(const std::string& color);
|
||||||
|
|||||||
Reference in New Issue
Block a user