mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-25 18:00:57 +00:00
WIP fix for filament type syncing
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
#include "slic3r/Utils/AmsPayload.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include <algorithm>
|
||||
@@ -146,6 +147,7 @@ void AMSMaterialsSetting::create_panel_normal(wxWindow* parent)
|
||||
#else
|
||||
m_comboBox_filament = new ::ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_COMBOX_WIDTH, 0, nullptr, wxCB_READONLY);
|
||||
#endif
|
||||
m_comboBox_filament->SetClientData(&m_comboBox_from_printer);
|
||||
|
||||
m_sizer_filament->Add(m_comboBox_filament, 1, wxALIGN_CENTER, 0);
|
||||
|
||||
@@ -483,10 +485,7 @@ void AMSMaterialsSetting::on_select_reset(wxCommandEvent& event) {
|
||||
m_filament_type = "";
|
||||
long nozzle_temp_min_int = 0;
|
||||
long nozzle_temp_max_int = 0;
|
||||
wxColour color = *wxWHITE;
|
||||
char col_buf[10];
|
||||
sprintf(col_buf, "%02X%02X%02X00", (int)color.Red(), (int)color.Green(), (int)color.Blue());
|
||||
std::string color_str; // reset use empty string
|
||||
std::string color_str; // empty; command_ams_filament_settings maps it to the empty-tray color
|
||||
|
||||
std::string selected_ams_id;
|
||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
||||
@@ -503,7 +502,7 @@ void AMSMaterialsSetting::on_select_reset(wxCommandEvent& event) {
|
||||
|
||||
if (obj) {
|
||||
if(m_is_third){
|
||||
obj->command_ams_filament_settings(ams_id, slot_id, ams_filament_id, ams_setting_id, std::string(col_buf), m_filament_type, nozzle_temp_min_int,
|
||||
obj->command_ams_filament_settings(ams_id, slot_id, ams_filament_id, ams_setting_id, color_str, m_filament_type, nozzle_temp_min_int,
|
||||
nozzle_temp_max_int);
|
||||
}
|
||||
|
||||
@@ -674,7 +673,7 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
|
||||
|
||||
wxString nozzle_temp_max = m_input_nozzle_max->GetTextCtrl()->GetValue();
|
||||
|
||||
long nozzle_temp_min_int, nozzle_temp_max_int;
|
||||
long nozzle_temp_min_int = 0, nozzle_temp_max_int = 0;
|
||||
nozzle_temp_min.ToLong(&nozzle_temp_min_int);
|
||||
nozzle_temp_max.ToLong(&nozzle_temp_max_int);
|
||||
wxColour color = m_clr_picker->m_colour;
|
||||
@@ -878,12 +877,22 @@ void AMSMaterialsSetting::on_clr_picker(wxMouseEvent &event)
|
||||
}
|
||||
|
||||
std::vector<wxColour> ams_colors;
|
||||
obj->GetFilaSystem()->CollectAmsColors(ams_colors);
|
||||
// A fixed-vocabulary printer can only show its own palette; anything else
|
||||
// would offer a color its screen cannot hold.
|
||||
const FilamentMetadataValues metadata = filament_metadata_values(obj->get_dev_id());
|
||||
const bool constrained = metadata.constrained && !metadata.colors.empty();
|
||||
if (constrained) {
|
||||
for (const std::string& color : metadata.colors)
|
||||
ams_colors.push_back(DevAmsTray::decode_color(color));
|
||||
} else {
|
||||
obj->GetFilaSystem()->CollectAmsColors(ams_colors);
|
||||
}
|
||||
|
||||
wxPoint img_pos = m_clr_picker->ClientToScreen(wxPoint(0, 0));
|
||||
wxPoint popup_pos(img_pos.x - m_color_picker_popup.GetSize().x - FromDIP(95), img_pos.y - FromDIP(65));
|
||||
m_color_picker_popup.Position(popup_pos, wxSize(0, 0));
|
||||
m_color_picker_popup.set_ams_colours(ams_colors);
|
||||
m_color_picker_popup.set_constrained(constrained);
|
||||
m_color_picker_popup.set_def_colour(m_clr_picker->m_colour);
|
||||
m_color_picker_popup.Popup();
|
||||
}
|
||||
@@ -1008,8 +1017,12 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi
|
||||
if (alias.empty())
|
||||
continue;
|
||||
|
||||
filament_items.push_back(alias);
|
||||
_collect_filament_info(alias, *filament_it, query_filament_vendors, query_filament_types);
|
||||
// A printer with a fixed vocabulary only offers the types it can
|
||||
// hold, so an incompatible preset never reaches the list.
|
||||
if (!filament_material_compatible(obj->get_dev_id(), query_filament_types[alias].ToStdString()))
|
||||
continue;
|
||||
filament_items.push_back(alias);
|
||||
|
||||
FilamentInfos filament_infos;
|
||||
filament_infos.filament_id = filament_it->filament_id;
|
||||
@@ -1208,7 +1221,7 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi
|
||||
}
|
||||
|
||||
m_comboBox_filament->Set(filament_items);
|
||||
m_comboBox_filament->SetClientData(new int(1));
|
||||
m_comboBox_from_printer = 1;
|
||||
m_comboBox_filament->SetSelection(selection_idx);
|
||||
post_select_event(selection_idx);
|
||||
|
||||
@@ -1245,8 +1258,7 @@ void AMSMaterialsSetting::on_select_cali_result(wxCommandEvent &evt)
|
||||
void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
{
|
||||
// Get the flag whether to open the filament setting dialog from the device page
|
||||
int* from_printer = static_cast<int*>(m_comboBox_filament->GetClientData());
|
||||
const bool initial_printer_selection = from_printer && *from_printer == 1;
|
||||
const bool initial_printer_selection = m_comboBox_from_printer == 1;
|
||||
|
||||
m_filament_type = "";
|
||||
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
|
||||
@@ -1330,7 +1342,7 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
m_input_k_val->GetTextCtrl()->SetValue(wxEmptyString);
|
||||
m_input_n_val->GetTextCtrl()->SetValue(wxEmptyString);
|
||||
}
|
||||
m_comboBox_filament->SetClientData(new int(0));
|
||||
m_comboBox_from_printer = 0;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
@@ -1417,7 +1429,7 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
|
||||
m_comboBox_cali_result->Set(items);
|
||||
if (ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID) {
|
||||
if (from_printer && (*from_printer == 1)) {
|
||||
if (m_comboBox_from_printer == 1) {
|
||||
for (auto slot : obj->vt_slot) {
|
||||
if (slot.id == std::to_string(ams_id))
|
||||
cali_select_idx = CalibUtils::get_selected_calib_idx(m_pa_profile_items, slot.cali_idx);
|
||||
@@ -1434,7 +1446,7 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (from_printer && (*from_printer == 1)) {
|
||||
if (m_comboBox_from_printer == 1) {
|
||||
DevAmsTray* selected_tray = this->obj->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id));
|
||||
if (!selected_tray)
|
||||
{
|
||||
@@ -1476,7 +1488,7 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt)
|
||||
}
|
||||
}
|
||||
|
||||
m_comboBox_filament->SetClientData(new int(0));
|
||||
m_comboBox_from_printer = 0;
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::on_dpi_changed(const wxRect &suggested_rect)
|
||||
@@ -1738,6 +1750,7 @@ ColorPickerPopup::ColorPickerPopup(wxWindow* parent)
|
||||
cp->set_selected(false);
|
||||
cp->SetBackgroundColour(StateColor::darkModeColorFor(wxColour(238,238,238)));
|
||||
m_color_pickers.push_back(cp);
|
||||
m_def_color_pickers.push_back(cp);
|
||||
fg_sizer->Add(cp, 0, wxALL, FromDIP(3));
|
||||
cp->Bind(wxEVT_LEFT_DOWN, [this, cp](auto& e) {
|
||||
set_def_colour(cp->m_colour);
|
||||
@@ -1750,27 +1763,27 @@ ColorPickerPopup::ColorPickerPopup(wxWindow* parent)
|
||||
}
|
||||
|
||||
wxBoxSizer* m_sizer_other = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto m_title_other = new wxStaticText(m_def_color_box, wxID_ANY, _L("Other Color"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_title_other = new wxStaticText(m_def_color_box, wxID_ANY, _L("Other Color"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_title_other->SetFont(::Label::Body_14);
|
||||
m_title_other->SetBackgroundColour(wxColour(238, 238, 238));
|
||||
m_sizer_other->Add(m_title_other, 0, wxALL, 5);
|
||||
auto other_line = new wxPanel(m_def_color_box, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
|
||||
other_line->SetMinSize(wxSize(-1, 1));
|
||||
other_line->SetMaxSize(wxSize(-1, 1));
|
||||
other_line->SetBackgroundColour(wxColour("#CECECE"));
|
||||
m_sizer_other->Add(other_line, 1, wxALIGN_CENTER, 0);
|
||||
m_other_line = new wxPanel(m_def_color_box, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
|
||||
m_other_line->SetMinSize(wxSize(-1, 1));
|
||||
m_other_line->SetMaxSize(wxSize(-1, 1));
|
||||
m_other_line->SetBackgroundColour(wxColour("#CECECE"));
|
||||
m_sizer_other->Add(m_other_line, 1, wxALIGN_CENTER, 0);
|
||||
|
||||
//custom color
|
||||
wxBoxSizer* m_sizer_custom = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto m_title_custom = new wxStaticText(m_def_color_box, wxID_ANY, _L("Custom Color"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_title_custom = new wxStaticText(m_def_color_box, wxID_ANY, _L("Custom Color"), wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_title_custom->SetFont(::Label::Body_14);
|
||||
m_title_custom->SetBackgroundColour(wxColour(238, 238, 238));
|
||||
auto custom_line = new wxPanel(m_def_color_box, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
|
||||
custom_line->SetBackgroundColour(wxColour("#CECECE"));
|
||||
custom_line->SetMinSize(wxSize(-1, 1));
|
||||
custom_line->SetMaxSize(wxSize(-1, 1));
|
||||
m_custom_line = new wxPanel(m_def_color_box, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
|
||||
m_custom_line->SetBackgroundColour(wxColour("#CECECE"));
|
||||
m_custom_line->SetMinSize(wxSize(-1, 1));
|
||||
m_custom_line->SetMaxSize(wxSize(-1, 1));
|
||||
m_sizer_custom->Add(m_title_custom, 0, wxALL, 5);
|
||||
m_sizer_custom->Add(custom_line, 1, wxALIGN_CENTER, 0);
|
||||
m_sizer_custom->Add(m_custom_line, 1, wxALIGN_CENTER, 0);
|
||||
|
||||
m_custom_cp = new StaticBox(m_def_color_box);
|
||||
m_custom_cp->SetSize(FromDIP(60), FromDIP(25));
|
||||
@@ -1910,6 +1923,22 @@ void ColorPickerPopup::set_ams_colours(std::vector<wxColour> ams)
|
||||
Fit();
|
||||
}
|
||||
|
||||
// A printer with a fixed vocabulary only shows its own palette: the default
|
||||
// swatches and the custom picker would offer colors its screen cannot hold.
|
||||
void ColorPickerPopup::set_constrained(bool constrained)
|
||||
{
|
||||
const bool show = !constrained;
|
||||
m_title_other->Show(show);
|
||||
m_other_line->Show(show);
|
||||
for (ColorPicker* cp : m_def_color_pickers)
|
||||
cp->Show(show);
|
||||
m_title_custom->Show(show);
|
||||
m_custom_line->Show(show);
|
||||
m_custom_cp->Show(show);
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void ColorPickerPopup::set_def_colour(wxColour col)
|
||||
{
|
||||
m_def_col = col;
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "DeviceManager.hpp"
|
||||
#include "wx/clrpicker.h"
|
||||
#include "wx/colourdata.h"
|
||||
#include "wx/panel.h"
|
||||
#include "wx/stattext.h"
|
||||
#include "Widgets/RadioBox.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/RoundedRectangle.hpp"
|
||||
@@ -77,12 +79,20 @@ public:
|
||||
std::vector<wxColour> m_ams_colors;
|
||||
std::vector<ColorPicker*> m_color_pickers;
|
||||
std::vector<ColorPicker*> m_ams_color_pickers;
|
||||
// Sections a fixed-vocabulary printer hides in set_constrained: its own
|
||||
// palette is the only color its screen can show.
|
||||
std::vector<ColorPicker*> m_def_color_pickers;
|
||||
wxStaticText* m_title_other{nullptr};
|
||||
wxPanel* m_other_line{nullptr};
|
||||
wxStaticText* m_title_custom{nullptr};
|
||||
wxPanel* m_custom_line{nullptr};
|
||||
|
||||
public:
|
||||
ColorPickerPopup(wxWindow* parent);
|
||||
~ColorPickerPopup() {};
|
||||
void on_custom_clr_picker(wxMouseEvent& event);
|
||||
void set_ams_colours(std::vector<wxColour> ams);
|
||||
void set_constrained(bool constrained);
|
||||
void set_def_colour(wxColour col);
|
||||
void paintEvent(wxPaintEvent& evt);
|
||||
virtual void OnDismiss() wxOVERRIDE;
|
||||
@@ -123,6 +133,10 @@ public:
|
||||
std::string ams_setting_id;
|
||||
|
||||
bool m_is_third;
|
||||
|
||||
// Marks whether the filament combo was seeded from the printer's own tray.
|
||||
// Stored as a member so the combo's client data never owns an allocation.
|
||||
int m_comboBox_from_printer{0};
|
||||
// Orca: view-only mode (laser/cut). When set, the dialog is inspectable but every
|
||||
// editing control is disabled and no command is sent.
|
||||
bool m_view_only = false;
|
||||
|
||||
@@ -20,23 +20,31 @@ static int _hex_digit_to_int(const char c) { return (c >= '0' && c <= '9') ? c -
|
||||
|
||||
wxColour DevAmsTray::decode_color(const std::string &color)
|
||||
{
|
||||
std::array<int, 4> ret = {0, 0, 0, 0};
|
||||
// 6 hex digits (RRGGBB) and 8 (RRGGBBAA) are both legal on the wire;
|
||||
// normalize_ams_color pads the short form, so accept it here too.
|
||||
const size_t hex_digits = (color.size() == 6 || color.size() == 8) ? color.size() : 0;
|
||||
if (hex_digits == 0) return wxColour(255, 255, 255, 255);
|
||||
|
||||
std::array<int, 4> ret = {0, 0, 0, 255};
|
||||
const char * c = color.data();
|
||||
if (color.size() == 8) {
|
||||
for (size_t j = 0; j < 4; ++j) {
|
||||
int digit1 = _hex_digit_to_int(*c++);
|
||||
int digit2 = _hex_digit_to_int(*c++);
|
||||
if (digit1 == -1 || digit2 == -1) break;
|
||||
ret[j] = static_cast<float>(digit1 * 16 + digit2);
|
||||
}
|
||||
} else { return wxColour(255, 255, 255, 255); }
|
||||
for (size_t j = 0; j < hex_digits / 2; ++j) {
|
||||
int digit1 = _hex_digit_to_int(*c++);
|
||||
int digit2 = _hex_digit_to_int(*c++);
|
||||
if (digit1 == -1 || digit2 == -1) break;
|
||||
ret[j] = digit1 * 16 + digit2;
|
||||
}
|
||||
|
||||
return wxColour(ret[0], ret[1], ret[2], ret[3]);
|
||||
}
|
||||
|
||||
void DevAmsTray::UpdateColorFromStr(const std::string& color)
|
||||
{
|
||||
if (color.empty()) return;
|
||||
// An empty color clears, exactly like a payload that omits the key.
|
||||
if (color.empty()) {
|
||||
wx_color = wxColour();
|
||||
this->color.clear();
|
||||
return;
|
||||
}
|
||||
if (this->color != color)
|
||||
{
|
||||
wx_color = "#" + wxString::FromUTF8(color);
|
||||
@@ -729,7 +737,8 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
}
|
||||
else
|
||||
{
|
||||
curr_tray->color = "";
|
||||
// Absent and empty carry the same meaning.
|
||||
curr_tray->UpdateColorFromStr(std::string());
|
||||
}
|
||||
if (tray_it->contains("nozzle_temp_max"))
|
||||
{
|
||||
|
||||
@@ -565,6 +565,37 @@ bool MachineObject::ams_filament_ack_failed(const nlohmann::json& jj, std::strin
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MachineObject::ams_filament_native_sync_pending(const nlohmann::json& jj)
|
||||
{
|
||||
return jj.contains("native_sync") && jj["native_sync"].is_string() &&
|
||||
jj["native_sync"].get<std::string>() == "pending";
|
||||
}
|
||||
|
||||
bool MachineObject::ams_filament_connector_only(const nlohmann::json& jj)
|
||||
{
|
||||
return jj.contains("native_sync") && jj["native_sync"].is_string() &&
|
||||
jj["native_sync"].get<std::string>() == "connector_only";
|
||||
}
|
||||
|
||||
void MachineObject::fill_ams_filament_ack_tray(DevAmsTray& tray, const nlohmann::json& jj)
|
||||
{
|
||||
// Every field is optional: `jj` is a mutable copy, so a bare operator[]
|
||||
// would insert null and the get<> below would throw type_error.
|
||||
if (jj.contains("nozzle_temp_max") && jj["nozzle_temp_max"].is_number())
|
||||
tray.nozzle_temp_max = std::to_string(jj["nozzle_temp_max"].get<int>());
|
||||
if (jj.contains("nozzle_temp_min") && jj["nozzle_temp_min"].is_number())
|
||||
tray.nozzle_temp_min = std::to_string(jj["nozzle_temp_min"].get<int>());
|
||||
if (jj.contains("tray_color") && jj["tray_color"].is_string())
|
||||
tray.color = jj["tray_color"].get<std::string>();
|
||||
if (jj.contains("tray_info_idx") && jj["tray_info_idx"].is_string())
|
||||
tray.setting_id = jj["tray_info_idx"].get<std::string>();
|
||||
if (jj.contains("tray_type") || jj.contains("tray_info_idx")) {
|
||||
const std::string type = (jj.contains("tray_type") && jj["tray_type"].is_string()) ? jj["tray_type"].get<std::string>() : tray.m_fila_type;
|
||||
tray.m_fila_type = setting_id_to_type(tray.setting_id, type);
|
||||
}
|
||||
tray.set_hold_count();
|
||||
}
|
||||
|
||||
PrinterArch MachineObject::get_printer_arch() const
|
||||
{
|
||||
return DevPrinterConfigUtil::get_printer_arch(printer_type);
|
||||
@@ -1791,8 +1822,10 @@ int MachineObject::command_ams_filament_settings(int ams_id, int slot_id, std::s
|
||||
j["print"]["tray_id"] = tag_tray_id;
|
||||
j["print"]["tray_info_idx"] = filament_id;
|
||||
j["print"]["setting_id"] = setting_id;
|
||||
// format "FFFFFFFF" RGBA
|
||||
j["print"]["tray_color"] = tray_color;
|
||||
// Always present: Bambu firmware echoes it into the tray handler, and a
|
||||
// slim request would make that echo throw. An empty value becomes the
|
||||
// protocol's own empty-tray color.
|
||||
j["print"]["tray_color"] = tray_color.empty() ? std::string("00000000") : tray_color;
|
||||
j["print"]["nozzle_temp_min"] = nozzle_temp_min;
|
||||
j["print"]["nozzle_temp_max"] = nozzle_temp_max;
|
||||
j["print"]["tray_type"] = tray_type;
|
||||
@@ -4257,6 +4290,10 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
if (!ack_reason.empty())
|
||||
text += wxString::FromUTF8(": ") + wxString::FromUTF8(ack_reason);
|
||||
GUI::wxGetApp().push_notification(this, text);
|
||||
} else if (ams_filament_native_sync_pending(jj)) {
|
||||
GUI::wxGetApp().push_notification(this, _L("Filament settings are saved and will be applied when the printer reconnects."));
|
||||
} else if (ams_filament_connector_only(jj)) {
|
||||
GUI::wxGetApp().push_notification(this, _L("Filament settings were saved by OrcaSonar but could not be applied to the printer."));
|
||||
}
|
||||
|
||||
if (!ack_failed && jj["ams_id"].is_number()) {
|
||||
@@ -4267,39 +4304,15 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
}
|
||||
if (ams_id == 255 && tray_id == VIRTUAL_TRAY_MAIN_ID && !vt_slot.empty()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "ams_filament_setting, parse tray info";
|
||||
vt_slot[0].nozzle_temp_max = std::to_string(jj["nozzle_temp_max"].get<int>());
|
||||
vt_slot[0].nozzle_temp_min = std::to_string(jj["nozzle_temp_min"].get<int>());
|
||||
vt_slot[0].color = jj["tray_color"].get<std::string>();
|
||||
vt_slot[0].setting_id = jj["tray_info_idx"].get<std::string>();
|
||||
//vt_tray.type = jj["tray_type"].get<std::string>();
|
||||
vt_slot[0].m_fila_type = setting_id_to_type(vt_slot[0].setting_id, jj["tray_type"].get<std::string>());
|
||||
fill_ams_filament_ack_tray(vt_slot[0], jj);
|
||||
vt_slot[0].is_exists = true;
|
||||
// delay update
|
||||
vt_slot[0].set_hold_count();
|
||||
} else {
|
||||
auto ams = m_fila_system->GetAmsById(std::to_string(ams_id));
|
||||
if (ams) {
|
||||
auto tray_it = ams->GetTrays().find(std::to_string(tray_id));
|
||||
if (tray_it != ams->GetTrays().end()) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "ams_filament_setting, parse tray info";
|
||||
tray_it->second->nozzle_temp_max = std::to_string(jj["nozzle_temp_max"].get<int>());
|
||||
tray_it->second->nozzle_temp_min = std::to_string(jj["nozzle_temp_min"].get<int>());
|
||||
//tray_it->second->type = jj["tray_type"].get<std::string>();
|
||||
tray_it->second->color = jj["tray_color"].get<std::string>();
|
||||
|
||||
/*tray_it->second->cols.clear();
|
||||
if (jj.contains("cols")) {
|
||||
if (jj["cols"].is_array()) {
|
||||
for (auto it = jj["cols"].begin(); it != jj["cols"].end(); it++) {
|
||||
tray_it->second->cols.push_back(it.value().get<std::string>());
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
tray_it->second->setting_id = jj["tray_info_idx"].get<std::string>();
|
||||
tray_it->second->m_fila_type = setting_id_to_type(tray_it->second->setting_id, jj["tray_type"].get<std::string>());
|
||||
// delay update
|
||||
tray_it->second->set_hold_count();
|
||||
fill_ams_filament_ack_tray(*tray_it->second, jj);
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(warning) << "ams_filament_setting, can not find in trayList, tray_id=" << tray_id;
|
||||
}
|
||||
@@ -5972,14 +5985,11 @@ void MachineObject::check_ams_filament_valid()
|
||||
if (curr_tray->setting_id.size() == 8 && curr_tray->setting_id[0] == 'P' && filament_list.find(curr_tray->setting_id) == filament_list.end()) {
|
||||
if (checked_filament.find(curr_tray->setting_id) != checked_filament.end()) {
|
||||
need_checked_filament_id[nozzle_diameter_str].insert(curr_tray->setting_id);
|
||||
wxColour color = *wxWHITE;
|
||||
char col_buf[10];
|
||||
sprintf(col_buf, "%02X%02X%02XFF", (int) color.Red(), (int) color.Green(), (int) color.Blue());
|
||||
try {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << __LINE__ << " ams settings_id is not exist in filament_list and reset, ams_id: " << ams_id << " tray_id"
|
||||
<< slot_id << "filament_id: " << curr_tray->setting_id;
|
||||
|
||||
command_ams_filament_settings(std::stoi(ams_id), std::stoi(slot_id), "", "", std::string(col_buf), "", 0, 0);
|
||||
command_ams_filament_settings(std::stoi(ams_id), std::stoi(slot_id), "", "", "00000000", "", 0, 0);
|
||||
continue;
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << __LINE__ << " stoi error and ams_id: " << ams_id << " tray_id" << slot_id;
|
||||
@@ -6034,12 +6044,9 @@ void MachineObject::check_ams_filament_valid()
|
||||
if (vt_tray.setting_id.size() == 8 && vt_tray.setting_id[0] == 'P' && filament_list.find(vt_tray.setting_id) == filament_list.end()) {
|
||||
if (checked_filament.find(vt_tray.setting_id) != checked_filament.end()) {
|
||||
need_checked_filament_id[nozzle_diameter_str].insert(vt_tray.setting_id);
|
||||
wxColour color = *wxWHITE;
|
||||
char col_buf[10];
|
||||
sprintf(col_buf, "%02X%02X%02XFF", (int) color.Red(), (int) color.Green(), (int) color.Blue());
|
||||
try {
|
||||
BOOST_LOG_TRIVIAL(info) << "vt_tray.setting_id is not exist in filament_list and reset vt_tray and the filament_id is: " << vt_tray.setting_id;
|
||||
command_ams_filament_settings(vt_id, 0, "", "", std::string(col_buf), "", 0, 0);
|
||||
command_ams_filament_settings(vt_id, 0, "", "", "00000000", "", 0, 0);
|
||||
continue;
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << __LINE__ << " stoi error and tray_id" << vt_tray.id;
|
||||
|
||||
@@ -170,6 +170,11 @@ public:
|
||||
|
||||
// Orca: an OPCP failure ack (result != "success"). Fills reason when present.
|
||||
static bool ams_filament_ack_failed(const nlohmann::json& jj, std::string& reason);
|
||||
static bool ams_filament_native_sync_pending(const nlohmann::json& jj);
|
||||
static bool ams_filament_connector_only(const nlohmann::json& jj);
|
||||
// Orca: apply one ams_filament_setting echo to a tray. A slim ack omits
|
||||
// fields; an unguarded get here would throw and drop the whole push frame.
|
||||
static void fill_ams_filament_ack_tray(DevAmsTray& tray, const nlohmann::json& jj);
|
||||
|
||||
// Orca: these DeviceCore module accessors are unwired on the read side — axis/chamber/status
|
||||
// are fed every MQTT push but no GUI consumer reads them yet, and for calib/upgrade the inline
|
||||
|
||||
@@ -4616,6 +4616,12 @@ void StatusPanel::on_filament_edit(wxCommandEvent &event)
|
||||
filament = tray->sub_brands;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No tray state for this slot: never show the previous slot's.
|
||||
m_filament_setting_dlg->set_empty_color(AMS_MATERIALS_SETTING_DEF_COLOUR);
|
||||
m_filament_setting_dlg->ams_filament_id = "";
|
||||
}
|
||||
|
||||
m_filament_setting_dlg->Move(wxPoint(current_position_x, current_position_y));
|
||||
m_filament_setting_dlg->Popup(filament, sn_number, temp_min, temp_max, k_val, n_val);
|
||||
|
||||
@@ -89,7 +89,8 @@ std::string map_filament_type_to_generic_id(const std::string& filament_type)
|
||||
if (it == type_to_ofl_family.end())
|
||||
return UNKNOWN_FILAMENT_ID;
|
||||
|
||||
if (auto* bundle = GUI::wxGetApp().preset_bundle) {
|
||||
auto* bundle = wxTheApp != nullptr ? GUI::wxGetApp().preset_bundle : nullptr;
|
||||
if (bundle) {
|
||||
const Preset* preset = bundle->filaments.find_preset("Generic " + it->second + " @System");
|
||||
if (preset != nullptr && preset->is_system && !preset->filament_id.empty())
|
||||
return preset->filament_id;
|
||||
@@ -270,7 +271,7 @@ LaneDataFetch read_moonraker_lane_data(const std::string& origin,
|
||||
|
||||
void resolve_tray_info_idx(std::vector<AmsTrayData>& trays)
|
||||
{
|
||||
auto* bundle = GUI::wxGetApp().preset_bundle;
|
||||
auto* bundle = wxTheApp != nullptr ? GUI::wxGetApp().preset_bundle : nullptr;
|
||||
for (auto& tray : trays) {
|
||||
// Absent lanes render as placeholders; resolving an empty type is busy
|
||||
// work at best and could bind a bogus id.
|
||||
@@ -280,9 +281,15 @@ void resolve_tray_info_idx(std::vector<AmsTrayData>& trays)
|
||||
// what is still empty so the generic fallback cannot overwrite it.
|
||||
if (!tray.tray_info_idx.empty())
|
||||
continue;
|
||||
tray.tray_info_idx = bundle
|
||||
? bundle->filaments.filament_id_by_type(tray.tray_type)
|
||||
: map_filament_type_to_generic_id(tray.tray_type);
|
||||
if (bundle) {
|
||||
std::string filament_id;
|
||||
if (bundle->filaments.filament_id_by_type(tray.tray_type, filament_id))
|
||||
tray.tray_info_idx = std::move(filament_id);
|
||||
} else {
|
||||
const std::string filament_id = map_filament_type_to_generic_id(tray.tray_type);
|
||||
if (filament_id != UNKNOWN_FILAMENT_ID)
|
||||
tray.tray_info_idx = filament_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,6 +395,7 @@ struct AmsDeviceCaps
|
||||
bool ops_known = false;
|
||||
bool has_ams = false;
|
||||
bool filament_slots = false;
|
||||
FilamentMetadataValues metadata;
|
||||
};
|
||||
static std::map<std::string, AmsDeviceCaps> g_ams_caps;
|
||||
|
||||
@@ -447,6 +455,84 @@ bool has_filament_slots(const std::string& dev_id)
|
||||
return it != g_ams_caps.end() && it->second.filament_slots;
|
||||
}
|
||||
|
||||
void register_filament_metadata(const std::string& dev_id, const FilamentMetadataValues& values)
|
||||
{
|
||||
if (dev_id.empty())
|
||||
return;
|
||||
std::lock_guard<std::mutex> lock(g_ams_state_mutex);
|
||||
g_ams_caps[dev_id].metadata = values;
|
||||
}
|
||||
|
||||
FilamentMetadataValues filament_metadata_values(const std::string& dev_id)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_ams_state_mutex);
|
||||
auto it = g_ams_caps.find(dev_id);
|
||||
if (it == g_ams_caps.end())
|
||||
return {};
|
||||
return it->second.metadata;
|
||||
}
|
||||
|
||||
static bool material_prefix(const std::string& value, const char* family)
|
||||
{
|
||||
const std::string prefix(family);
|
||||
if (value == prefix)
|
||||
return true;
|
||||
if (value.size() <= prefix.size() || value.compare(0, prefix.size(), prefix) != 0)
|
||||
return false;
|
||||
const char next = value[prefix.size()];
|
||||
return next == ' ' || next == '+' || next == '-' || next == '/';
|
||||
}
|
||||
|
||||
static bool qidi_pa_material(const std::string& value)
|
||||
{
|
||||
return material_prefix(value, "PA") || material_prefix(value, "PA12") ||
|
||||
material_prefix(value, "PAHT") || material_prefix(value, "ULTRAPA");
|
||||
}
|
||||
|
||||
// Keep distinct material families separate; only known variants share a stem.
|
||||
std::string normalize_qidi_material_family(const std::string& value)
|
||||
{
|
||||
const std::string upper = boost::algorithm::to_upper_copy(boost::algorithm::trim_copy(value));
|
||||
if (material_prefix(upper, "PLA-CF")) return "PLA-CF";
|
||||
if (material_prefix(upper, "PPA-CF")) return "PPA-CF";
|
||||
if (material_prefix(upper, "PC-ABS")) return "PC";
|
||||
if (material_prefix(upper, "PCTG")) return "PCTG";
|
||||
if (material_prefix(upper, "PPA")) return "PPA";
|
||||
if (material_prefix(upper, "PLA")) return "PLA";
|
||||
if (material_prefix(upper, "ABS")) return "ABS";
|
||||
if (material_prefix(upper, "PETG")) return "PETG";
|
||||
if (material_prefix(upper, "TPU")) return "TPU";
|
||||
if (material_prefix(upper, "ASA")) return "ASA";
|
||||
if (qidi_pa_material(upper) || material_prefix(upper, "NYLON")) return "PA";
|
||||
if (material_prefix(upper, "PC")) return "PC";
|
||||
if (material_prefix(upper, "PVA")) return "PVA";
|
||||
return upper;
|
||||
}
|
||||
|
||||
bool filament_material_compatible(const std::string& dev_id, const std::string& preset_type)
|
||||
{
|
||||
const FilamentMetadataValues values = filament_metadata_values(dev_id);
|
||||
if (!values.constrained)
|
||||
return true;
|
||||
if (boost::algorithm::trim_copy(preset_type).empty())
|
||||
return true; // a reply that never carried a type must not hide presets
|
||||
std::vector<std::string> parts;
|
||||
boost::algorithm::split(parts, preset_type, boost::algorithm::is_any_of(";"), boost::token_compress_on);
|
||||
for (const std::string& part : parts) {
|
||||
const std::string type = boost::algorithm::trim_copy(part);
|
||||
if (type.empty())
|
||||
continue;
|
||||
const std::string category = normalize_qidi_material_family(type);
|
||||
for (const std::string& declared : values.materials) {
|
||||
if (boost::algorithm::iequals(boost::algorithm::trim_copy(declared), type))
|
||||
return true;
|
||||
if (normalize_qidi_material_family(declared) == category)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void clear_ams_caps(const std::string& dev_id)
|
||||
{
|
||||
if (dev_id.empty())
|
||||
|
||||
@@ -113,6 +113,26 @@ bool ams_caps_known(const std::string& dev_id);
|
||||
void register_filament_slots(const std::string& dev_id, bool has_slots);
|
||||
bool has_filament_slots(const std::string& dev_id);
|
||||
|
||||
// The values a fixed-vocabulary printer can hold for filament metadata, from
|
||||
// protocol.filament_metadata.<driver>.values. Materials are the strings the
|
||||
// status projection reports and colors are RRGGBBAA. `constrained` is true only
|
||||
// when every writing driver declared values: one free-form driver keeps the
|
||||
// full picker available.
|
||||
struct FilamentMetadataValues
|
||||
{
|
||||
bool constrained = false;
|
||||
std::vector<std::string> materials;
|
||||
std::vector<std::string> colors;
|
||||
};
|
||||
void register_filament_metadata(const std::string& dev_id, const FilamentMetadataValues& values);
|
||||
FilamentMetadataValues filament_metadata_values(const std::string& dev_id);
|
||||
std::string normalize_qidi_material_family(const std::string& material);
|
||||
// Whether a preset's filament type can be represented by the device's declared
|
||||
// materials: an exact type, or the material category the two names share. An
|
||||
// unconstrained or unanswered device accepts everything, and a type the reply
|
||||
// never carried is kept rather than hidden.
|
||||
bool filament_material_compatible(const std::string& dev_id, const std::string& preset_type);
|
||||
|
||||
// Forget a device's declared capabilities, so a reconnect starts from "no
|
||||
// reply yet" instead of a stale declaration.
|
||||
void clear_ams_caps(const std::string& dev_id);
|
||||
|
||||
@@ -686,6 +686,43 @@ void OrcaPrinterAgent::register_ams_capabilities(const std::string& dev_id, cons
|
||||
has_slots = slots_it->get<bool>();
|
||||
}
|
||||
register_filament_slots(dev_id, has_slots);
|
||||
|
||||
// filament_metadata.<driver>.values publishes what a fixed-vocabulary
|
||||
// driver can hold. It constrains the picker only when every writing driver
|
||||
// declared values: one free-form driver (Happy Hare, AFC) keeps the full
|
||||
// color picker and preset list available. Registered on every reply so a
|
||||
// later one without the key clears a stale palette.
|
||||
FilamentMetadataValues metadata;
|
||||
const auto meta_it = proto_it->find("filament_metadata");
|
||||
if (meta_it != proto_it->end() && meta_it->is_object() && !meta_it->empty()) {
|
||||
const auto collect = [](std::vector<std::string>& out, const nlohmann::json& values, const char* key) {
|
||||
const auto arr = values.find(key);
|
||||
if (arr == values.end() || !arr->is_array())
|
||||
return;
|
||||
for (const auto& v : *arr) {
|
||||
if (!v.is_string())
|
||||
continue;
|
||||
const std::string entry = v.get<std::string>();
|
||||
if (std::find(out.begin(), out.end(), entry) == out.end())
|
||||
out.push_back(entry);
|
||||
}
|
||||
};
|
||||
bool all_constrained = true;
|
||||
for (auto driver = meta_it->begin(); driver != meta_it->end() && all_constrained; ++driver) {
|
||||
const auto values_it = driver.value().find("values");
|
||||
if (values_it == driver.value().end() || !values_it->is_object() || values_it->empty()) {
|
||||
all_constrained = false;
|
||||
break;
|
||||
}
|
||||
collect(metadata.materials, *values_it, "material");
|
||||
collect(metadata.colors, *values_it, "color");
|
||||
}
|
||||
if (all_constrained && (!metadata.materials.empty() || !metadata.colors.empty()))
|
||||
metadata.constrained = true;
|
||||
else
|
||||
metadata = FilamentMetadataValues{};
|
||||
}
|
||||
register_filament_metadata(dev_id, metadata);
|
||||
}
|
||||
|
||||
void OrcaPrinterAgent::deliver_to_sink(const std::string& dev_id, const std::string& payload, bool local)
|
||||
@@ -1299,9 +1336,9 @@ std::string OrcaPrinterAgent::canonicalize_ams_payload(const std::string& dev_id
|
||||
if (cmd.empty() || (cmd.rfind("ams_", 0) != 0 && cmd != "auto_stop_ams_dry"))
|
||||
return json_str;
|
||||
|
||||
// filament_setting is exempt from the ams_ops union: it persists
|
||||
// connector state through the filament-slot model, so filament_slots
|
||||
// alone advertises it (OrcaSonar OPCP §7.8).
|
||||
// filament_setting is exempt from the ams_ops union: filament_slots
|
||||
// advertises the canonical slot write, while native metadata support is
|
||||
// reported separately by the driver (OrcaSonar OPCP §7.8).
|
||||
auto op_allowed = [&dev_id](const std::string& o) {
|
||||
if (o == "filament_setting")
|
||||
return !ams_caps_known(dev_id) || has_filament_slots(dev_id);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "QidiPrinterAgent.hpp"
|
||||
#include "Http.hpp"
|
||||
#include "IPrinterAgent.hpp"
|
||||
#include "slic3r/Utils/AmsPayload.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
@@ -507,26 +508,7 @@ std::string QidiPrinterAgent::infer_series_id(const std::string& model_id, const
|
||||
|
||||
std::string QidiPrinterAgent::normalize_filament_type(const std::string& filament_type)
|
||||
{
|
||||
const std::string upper = trim_and_upper(filament_type);
|
||||
|
||||
if (upper.find("PLA") != std::string::npos)
|
||||
return "PLA";
|
||||
if (upper.find("ABS") != std::string::npos)
|
||||
return "ABS";
|
||||
if (upper.find("PETG") != std::string::npos)
|
||||
return "PETG";
|
||||
if (upper.find("TPU") != std::string::npos)
|
||||
return "TPU";
|
||||
if (upper.find("ASA") != std::string::npos)
|
||||
return "ASA";
|
||||
if (upper.find("PA") != std::string::npos || upper.find("NYLON") != std::string::npos)
|
||||
return "PA";
|
||||
if (upper.find("PC") != std::string::npos)
|
||||
return "PC";
|
||||
if (upper.find("PVA") != std::string::npos)
|
||||
return "PVA";
|
||||
|
||||
return upper;
|
||||
return normalize_qidi_material_family(filament_type);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -285,6 +285,56 @@ TEST_CASE("AMS filament setting acks expose OPCP failures", "[DeviceManager]")
|
||||
CHECK_FALSE(MachineObject::ams_filament_ack_failed(plain, reason));
|
||||
}
|
||||
|
||||
TEST_CASE("AMS filament acknowledgements report native sync state", "[DeviceManager]")
|
||||
{
|
||||
CHECK(MachineObject::ams_filament_native_sync_pending(json::parse(R"({"native_sync":"pending"})")));
|
||||
CHECK_FALSE(MachineObject::ams_filament_native_sync_pending(json::parse(R"({"native_sync":"applied"})")));
|
||||
CHECK(MachineObject::ams_filament_connector_only(json::parse(R"({"native_sync":"connector_only"})")));
|
||||
CHECK_FALSE(MachineObject::ams_filament_connector_only(json::parse(R"({"native_sync":"not_applicable"})")));
|
||||
}
|
||||
|
||||
// Every tray field in the echo is optional: an unguarded get would throw
|
||||
// type_error into parse_json's frame-wide catch and drop the rest of the push.
|
||||
TEST_CASE("A slim AMS filament acknowledgement leaves the tray untouched", "[DeviceManager]")
|
||||
{
|
||||
DevAmsTray tray("0");
|
||||
tray.color = "FF0000FF";
|
||||
tray.nozzle_temp_min = "190";
|
||||
tray.m_fila_type = "PLA";
|
||||
|
||||
CHECK_NOTHROW(MachineObject::fill_ams_filament_ack_tray(
|
||||
tray, json::parse(R"({"command":"ams_filament_setting","result":"success","errno":0})")));
|
||||
CHECK(tray.color == "FF0000FF");
|
||||
CHECK(tray.nozzle_temp_min == "190");
|
||||
CHECK(tray.m_fila_type == "PLA");
|
||||
|
||||
CHECK_NOTHROW(MachineObject::fill_ams_filament_ack_tray(
|
||||
tray, json::parse(R"({"tray_color":"0000FFFF","tray_type":"ABS","tray_info_idx":"GFA01","nozzle_temp_min":210,"nozzle_temp_max":240})")));
|
||||
CHECK(tray.color == "0000FFFF");
|
||||
CHECK(tray.setting_id == "GFA01");
|
||||
CHECK(tray.nozzle_temp_min == "210");
|
||||
CHECK(tray.nozzle_temp_max == "240");
|
||||
CHECK(tray.m_fila_type == "ABS");
|
||||
}
|
||||
|
||||
// An echo that carries no tray fields at all must leave the frame alive: the
|
||||
// frame-wide catch would silently swallow every later message in the push.
|
||||
TEST_CASE("An AMS filament acknowledgement without tray fields does not abort the frame", "[DeviceManager]")
|
||||
{
|
||||
MachineObject machine(nullptr, nullptr, "test", "test-device", "127.0.0.1");
|
||||
|
||||
const json ams = json::parse(R"({"ams":{"tray_exist_bits":"1","ams":[
|
||||
{ "id": "0", "info": "00000001", "tray": [ { "id": "0" } ] } ]}})");
|
||||
DevFilaSystemParser::ParseV1_0(ams, &machine, machine.GetFilaSystem().get(), false);
|
||||
REQUIRE(machine.GetFilaSystem()->GetAmsTray("0", "0") != nullptr);
|
||||
|
||||
CHECK_NOTHROW(machine.parse_json("lan", R"({"print":{"command":"ams_filament_setting","result":"success","errno":0,"ams_id":0,"tray_id":0}})", false));
|
||||
|
||||
const DevAmsTray* tray = machine.GetFilaSystem()->GetAmsTray("0", "0");
|
||||
REQUIRE(tray != nullptr);
|
||||
CHECK(tray->color.empty());
|
||||
}
|
||||
|
||||
// An ack that targets a tray but omits tray_id must not abort the frame: it
|
||||
// falls back to slot 0 instead of an unguarded get on a missing key.
|
||||
TEST_CASE("An AMS filament ack without a tray_id targets slot 0", "[DeviceManager]")
|
||||
|
||||
@@ -351,6 +351,15 @@ TEST_CASE("OrcaPrinterAgent rewrites Bambu ams_* payloads onto the canonical Orc
|
||||
CHECK(!out["print"].contains("tray_id"));
|
||||
CHECK(out["print"]["tray_type"] == "PLA");
|
||||
|
||||
// tray_color is the one field this sync exists to carry: it must survive
|
||||
// the funnel, both as a real color and as the empty-tray marker a reset
|
||||
// sends.
|
||||
out = nlohmann::json::parse(canon("dev-c1", R"({"print":{"command":"ams_filament_setting","ams_id":1,"slot_id":2,"tray_id":2,"tray_type":"PLA","tray_color":"00FF00FF","tray_info_idx":"GFL01"}})"));
|
||||
CHECK(out["print"]["tray_color"] == "00FF00FF");
|
||||
out = nlohmann::json::parse(canon("dev-c1", R"({"print":{"command":"ams_filament_setting","ams_id":1,"slot_id":2,"tray_id":2,"tray_type":"","tray_info_idx":"","tray_color":"00000000"}})"));
|
||||
CHECK(out["print"]["tray_color"] == "00000000");
|
||||
CHECK(out["print"]["tray_type"] == "");
|
||||
|
||||
// Wide-box dual form: both addressings resolve to one slot server-side.
|
||||
out = nlohmann::json::parse(canon("dev-c1", R"({"print":{"command":"ams_filament_setting","ams_id":1,"slot_id":5,"tray_type":"PLA"}})"));
|
||||
CHECK(out["print"]["ams_id"] == 1);
|
||||
@@ -471,6 +480,73 @@ TEST_CASE("filament slot writes remain independent of FMS", "[OrcaPrinterAgent]"
|
||||
CHECK(unsupported);
|
||||
}
|
||||
|
||||
// A fixed-vocabulary printer publishes the values it can hold, so the dialog
|
||||
// offers only those: an exact type and its variants pass the filter, a material
|
||||
// the printer lacks does not. A free-form driver and an unanswered device stay
|
||||
// unconstrained.
|
||||
TEST_CASE("A printer publishes its filament metadata values", "[OrcaPrinterAgent]") {
|
||||
Probe agent("/tmp");
|
||||
agent.deliver_to_sink("dev-qidi",
|
||||
R"({"info":{"command":"get_capabilities","capabilities":{"protocol":{
|
||||
"filament_metadata":{"qidi_box":{"read":["material","color"],"write":["material","color"],
|
||||
"values":{"material":["PLA","PETG"],"color":["FF0000FF","00FF00FF"]}}}}}}})",
|
||||
/*local=*/true);
|
||||
|
||||
const Slic3r::FilamentMetadataValues values = Slic3r::filament_metadata_values("dev-qidi");
|
||||
CHECK(values.constrained);
|
||||
CHECK(values.materials.size() == 2);
|
||||
CHECK(values.colors.size() == 2);
|
||||
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi", "PLA"));
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi", "PETG"));
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi", "PLA+"));
|
||||
CHECK_FALSE(Slic3r::filament_material_compatible("dev-qidi", "HIPS"));
|
||||
|
||||
CHECK(Slic3r::normalize_qidi_material_family("PCTG") == "PCTG");
|
||||
CHECK(Slic3r::normalize_qidi_material_family("PPA") == "PPA");
|
||||
CHECK(Slic3r::normalize_qidi_material_family("PLA-CF") == "PLA-CF");
|
||||
CHECK(Slic3r::normalize_qidi_material_family("PA12-CF") == "PA");
|
||||
CHECK(Slic3r::normalize_qidi_material_family("PC-ABS-FR") == "PC");
|
||||
|
||||
agent.deliver_to_sink("dev-qidi-polymer",
|
||||
R"({"info":{"command":"get_capabilities","capabilities":{"protocol":{
|
||||
"filament_metadata":{"qidi_box":{"read":["material","color"],"write":["material","color"],
|
||||
"values":{"material":["PC","PA"],"color":["FF0000FF"]}}}}}}})",
|
||||
/*local=*/true);
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi-polymer", "PC"));
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi-polymer", "PA Nylon"));
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi-polymer", "PA-CF"));
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi-polymer", "PA12-CF"));
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi-polymer", "PAHT-CF"));
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi-polymer", "PAHT-GF"));
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi-polymer", "PAHT-S"));
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi-polymer", "UltraPA"));
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi-polymer", "UltraPA-CF25"));
|
||||
CHECK(Slic3r::filament_material_compatible("dev-qidi-polymer", "PC-ABS-FR"));
|
||||
CHECK_FALSE(Slic3r::filament_material_compatible("dev-qidi-polymer", "PCTG"));
|
||||
CHECK_FALSE(Slic3r::filament_material_compatible("dev-qidi-polymer", "PPA"));
|
||||
|
||||
agent.deliver_to_sink("dev-happyhare",
|
||||
R"({"info":{"command":"get_capabilities","capabilities":{"protocol":{
|
||||
"filament_metadata":{"happy_hare":{"read":["material","color"],"write":["material","color"]}}}}}})",
|
||||
/*local=*/true);
|
||||
CHECK_FALSE(Slic3r::filament_metadata_values("dev-happyhare").constrained);
|
||||
CHECK(Slic3r::filament_material_compatible("dev-happyhare", "HIPS"));
|
||||
|
||||
CHECK(Slic3r::filament_material_compatible("dev-unanswered", "HIPS"));
|
||||
}
|
||||
|
||||
TEST_CASE("An unknown lane material does not bind to an arbitrary filament id", "[OrcaPrinterAgent]") {
|
||||
AmsTrayData tray;
|
||||
tray.has_filament = true;
|
||||
tray.tray_type = "Unlisted polymer family";
|
||||
std::vector<AmsTrayData> trays{tray};
|
||||
|
||||
Slic3r::resolve_tray_info_idx(trays);
|
||||
|
||||
CHECK(trays[0].tray_info_idx.empty());
|
||||
}
|
||||
|
||||
// A box wider than 4 slots is shown as several 4-tray units, so the BBL tray id
|
||||
// a panel reports must split into that unit's own (ams_id, slot_id). A flat lane
|
||||
// would be the tray id, which is not the layout lane for a wide box
|
||||
|
||||
Reference in New Issue
Block a user