Translate filament ids at the printer boundary

Orca content-addresses every system filament, Bambu's included, but a printer,
its AMS and its vendor's cloud know only that vendor's own catalog ids. The
printer agent now translates between the two: outbound MQTT and FTP traffic, the
AMS mapping sent with a print job, and the ids written into a 3mf bound for the
printer all leave in the printer's own ids, while status messages, loaded
projects and SD-card prints arrive in Orca's. An id with no mapping passes
through unchanged, and an agent whose printers already speak Orca's ids
translates nothing at all.

Bambu's map is generated from BambuStudio's own shipped bundle; a missing or
unreadable file leaves every lookup an identity rather than taking the app down.
The profile check validates the map's shape, and profile CI now runs on the paths
that can change it. docs/HLSD/filament_id.md records the places the map
deliberately does not reach.
This commit is contained in:
SoftFever
2026-09-06 20:53:11 +08:00
parent ec207e67a3
commit 4aa0e1d60b
23 changed files with 652 additions and 78 deletions
+11 -4
View File
@@ -1511,6 +1511,10 @@ void AMSDryCtrWin::update_filament_guide_info(DevAms* dev_ams)
m_temperature_input->GetValue().ToLong(&input_temp);
bool can_start = true;
// "GFA00" is Bambu's PLA id; GetFilamentDryingPreset is keyed by our OF ids.
auto* agent = wxGetApp().getAgent();
const std::string pla_filament_id = agent ? agent->to_orca_filament_id("GFA00") : std::string("GFA00");
int slot_count = 0, empty_count = 0;
for (auto& tray_pair : dev_ams->GetTrays()) {
if (!tray_pair.second) {
@@ -1526,13 +1530,15 @@ void AMSDryCtrWin::update_filament_guide_info(DevAms* dev_ams)
wxString filament_type = tray_pair.second->get_display_filament_type();
DevFilamentDryingPreset preset;
if (filament_type.IsEmpty()) {
auto fallback_preset = DevUtilBackend::GetFilamentDryingPreset("GFA00");
auto fallback_preset = DevUtilBackend::GetFilamentDryingPreset(pla_filament_id);
if (!fallback_preset) continue; // no PLA preset (e.g. the id map is missing): skip, don't throw
preset = fallback_preset.value();
filament_type = "?";
} else if (preset_opt.has_value()) {
preset = preset_opt.value();
} else {
auto fallback_preset = DevUtilBackend::GetFilamentDryingPreset("GFA00");
auto fallback_preset = DevUtilBackend::GetFilamentDryingPreset(pla_filament_id);
if (!fallback_preset) continue;
preset = fallback_preset.value();
}
std::string icon_path = "dev_ams_dry_ctr_enable";
@@ -1683,9 +1689,10 @@ int AMSDryCtrWin::update_filament_list(DevAms* dev_ams, MachineObject* obj)
// Select recommended drying temperature and default filament
float min_dry_temp = std::numeric_limits<float>::max();
std::string default_filament_id = "GFA00";
auto* agent = wxGetApp().getAgent();
std::string default_filament_id = agent ? agent->to_orca_filament_id("GFA00") : std::string("GFA00"); // compared against m_tray_ids[i].filament_id (our OF ids) below
bool has_ready = false;
const auto fallback_preset = DevUtilBackend::GetFilamentDryingPreset("GFA00");
const auto fallback_preset = DevUtilBackend::GetFilamentDryingPreset(default_filament_id);
for (const auto& tray_pair : dev_ams->GetTrays()) {
if (!tray_pair.second || !tray_pair.second->is_tray_info_ready()) continue;
has_ready = true;
+8 -2
View File
@@ -815,7 +815,10 @@ void AMSMaterialsSetting::set_color(wxColour color)
fila_color.m_colors.insert(color);
fila_color.EndSet(m_clr_picker->ctype);
auto clr_query = GUI::wxGetApp().get_filament_color_code_query();
m_clr_name->SetLabelText(clr_query->GetFilaColorName(ams_filament_id, fila_color));
// ams_filament_id is our OF id; GetFilaColorName looks up filaments_color_codes.json,
// downloaded from Bambu and keyed by the printer's own ids, so translate for this lookup only.
auto* agent = GUI::wxGetApp().getAgent();
m_clr_name->SetLabelText(clr_query->GetFilaColorName(agent ? agent->from_orca_filament_id(ams_filament_id) : ams_filament_id, fila_color));
}
void AMSMaterialsSetting::set_empty_color(wxColour color)
@@ -836,7 +839,10 @@ void AMSMaterialsSetting::set_colors(std::vector<wxColour> colors)
for (const auto& clr : colors) { fila_color.m_colors.insert(clr); }
fila_color.EndSet(m_clr_picker->ctype);
auto clr_query = GUI::wxGetApp().get_filament_color_code_query();
m_clr_name->SetLabelText(clr_query->GetFilaColorName(ams_filament_id, fila_color));
// ams_filament_id is our OF id; GetFilaColorName looks up filaments_color_codes.json,
// downloaded from Bambu and keyed by the printer's own ids, so translate for this lookup only.
auto* agent = GUI::wxGetApp().getAgent();
m_clr_name->SetLabelText(clr_query->GetFilaColorName(agent ? agent->from_orca_filament_id(ams_filament_id) : ams_filament_id, fila_color));
}
}
+33 -11
View File
@@ -62,10 +62,16 @@ std::string decompose_basic_type_from_source(size_t source_config_idx,
auto& project_config = wxGetApp().preset_bundle->project_config;
if (auto* filament_id_opt = project_config.option<ConfigOptionStrings>("filament_id")) {
if (source_config_idx < filament_id_opt->values.size()) {
const std::string& filament_id = filament_id_opt->values[source_config_idx];
if (filament_id == kDecomposePetgFilamentId)
// Dead in practice: "filament_id" is not in PresetBundle's s_project_options, so this
// option() lookup (create=false) always returns null and the block never runs. Kept as
// found, with the translation the values would need: they would be our OF ids, and the
// two constants are the printer's own ids.
auto* agent = wxGetApp().getAgent();
const std::string& orca_filament_id = filament_id_opt->values[source_config_idx];
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(orca_filament_id) : orca_filament_id;
if (printer_filament_id == kDecomposePetgFilamentId)
return kDecomposePetgBasicType;
if (filament_id == kDecomposePlaFilamentId)
if (printer_filament_id == kDecomposePlaFilamentId)
return kDecomposePlaBasicType;
}
}
@@ -82,9 +88,14 @@ std::string decompose_basic_type_from_source(size_t source_config_idx,
std::string decompose_basic_filament_id(const std::string& basic_type)
{
if (basic_type == kDecomposePetgBasicType)
return kDecomposePetgFilamentId;
return kDecomposePlaFilamentId;
// The result becomes DecomposeOfficialComponent::filament_id, which the rest of this file
// reads as one of our OF ids (translating back before it compares against the printer's
// ids), so translate on the way out; kDecompose*FilamentId itself stays the printer-side
// literal. The only place that would carry it further, project_config's "filament_id", is
// dead code: that key is not in PresetBundle's s_project_options.
const std::string printer_filament_id = basic_type == kDecomposePetgBasicType ? kDecomposePetgFilamentId : kDecomposePlaFilamentId;
auto* agent = wxGetApp().getAgent();
return agent ? agent->to_orca_filament_id(printer_filament_id) : printer_filament_id;
}
void set_created_standard_component_metadata(size_t config_idx, const DecomposeOfficialComponent& component)
@@ -98,8 +109,11 @@ void set_created_standard_component_metadata(size_t config_idx, const DecomposeO
}
}
const std::string type = component.filament_id == kDecomposePetgFilamentId ? kDecomposePetgShortType :
component.filament_id == kDecomposePlaFilamentId ? kDecomposePlaShortType : "";
// component.filament_id is our OF id; the two constants are the printer's own ids.
auto* agent = wxGetApp().getAgent();
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(component.filament_id) : component.filament_id;
const std::string type = printer_filament_id == kDecomposePetgFilamentId ? kDecomposePetgShortType :
printer_filament_id == kDecomposePlaFilamentId ? kDecomposePlaShortType : "";
if (!type.empty()) {
if (auto* type_opt = project_config.option<ConfigOptionStrings>("filament_type")) {
while (type_opt->values.size() <= config_idx)
@@ -151,7 +165,12 @@ DecomposeOfficialComponent lookup_decompose_official_component(
continue;
if (item.contains("fila_color") && item["fila_color"].is_array() && !item["fila_color"].empty())
result.color_hex = decompose_normalize_color_hex(item["fila_color"][0].get<std::string>());
result.filament_id = item.value("fila_id", result.filament_id);
// fila_id from this shipped, Bambu-keyed color table is a printer-side id; translate it so
// result.filament_id stays an OF id like the rest of this struct (the fallback default,
// result.filament_id, is already OF and passes through unchanged).
const std::string fila_id = item.value("fila_id", result.filament_id);
auto* agent = wxGetApp().getAgent();
result.filament_id = agent ? agent->to_orca_filament_id(fila_id) : fila_id;
return result;
}
}
@@ -211,8 +230,11 @@ int find_existing_decompose_component(
auto* type_opt = project_config.option<ConfigOptionStrings>("filament_type");
const PresetBundle& preset_bundle = *wxGetApp().preset_bundle;
const size_t num_physical = physical_colors.size();
const std::string expected_basic_type = component.filament_id == kDecomposePetgFilamentId ? kDecomposePetgBasicType :
component.filament_id == kDecomposePlaFilamentId ? kDecomposePlaBasicType : "";
// component.filament_id is our OF id; the two constants are the printer's own ids.
auto* agent = wxGetApp().getAgent();
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(component.filament_id) : component.filament_id;
const std::string expected_basic_type = printer_filament_id == kDecomposePetgFilamentId ? kDecomposePetgBasicType :
printer_filament_id == kDecomposePlaFilamentId ? kDecomposePlaBasicType : "";
const std::string expected_short_type = expected_basic_type == kDecomposePetgBasicType ? kDecomposePetgShortType :
expected_basic_type == kDecomposePlaBasicType ? kDecomposePlaShortType : "";
const std::string expected_preset_part = expected_basic_type.empty() ? "" : std::string(kDecomposeBambuPresetPrefix) + expected_basic_type;
@@ -241,8 +241,11 @@ void check_filaments(const DevFilaBlacklist::CheckFilamentInfo& check_info, DevF
std::set<std::string> white_fila_ids = filament_item.contains("white_fila_ids") ? filament_item["white_fila_ids"].get<std::set<std::string>>() : std::set<std::string>();
if (!white_fila_ids.empty() && !check_info.fila_id.empty())
{
auto it = std::find_if(white_fila_ids.begin(), white_fila_ids.end(), [&check_info](const std::string& white_fila_id) {
return white_fila_id == check_info.fila_id;
// check_info.fila_id is our OF id; white_fila_ids in filaments_blacklist.json holds the printer's own.
auto* agent = Slic3r::GUI::wxGetApp().getAgent();
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(check_info.fila_id) : check_info.fila_id;
auto it = std::find_if(white_fila_ids.begin(), white_fila_ids.end(), [&printer_filament_id](const std::string& white_fila_id) {
return white_fila_id == printer_filament_id;
});
if (it != white_fila_ids.end()) { continue; }
}
+12 -3
View File
@@ -5,6 +5,7 @@
// TODO: remove this include
#include "slic3r/GUI/DeviceManager.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "DevUtil.h"
#include "DevUtilBackend.h"
@@ -95,7 +96,12 @@ std::string DevAmsTray::get_filament_type()
if (m_fila_type == "Sup.ABS") { return "ABS-S"; }
if (m_fila_type == "Support W") { return "PLA-S"; }
if (m_fila_type == "Support G") { return "PA-S"; }
if (m_fila_type == "Support") { if (setting_id == "GFS00") { m_fila_type = "PLA-S"; } else if (setting_id == "GFS01") { m_fila_type = "PA-S"; } else { return "PLA-S"; } }
// setting_id is our OF id; GFS00/GFS01 are the printer's own support-filament ids.
if (m_fila_type == "Support") {
auto* agent = GUI::wxGetApp().getAgent();
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(setting_id) : setting_id;
if (printer_filament_id == "GFS00") { m_fila_type = "PLA-S"; } else if (printer_filament_id == "GFS01") { m_fila_type = "PA-S"; } else { return "PLA-S"; }
}
return m_fila_type;
}
@@ -654,11 +660,14 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
curr_tray->setting_id = (*tray_it)["tray_info_idx"].get<std::string>();
//std::string type = (*tray_it)["tray_type"].get<std::string>();
std::string type = MachineObject::setting_id_to_type(curr_tray->setting_id, (*tray_it)["tray_type"].get<std::string>());
if (curr_tray->setting_id == "GFS00")
// curr_tray->setting_id is our OF id; GFS00/GFS01 are the printer's own support-filament ids.
auto* agent = GUI::wxGetApp().getAgent();
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(curr_tray->setting_id) : curr_tray->setting_id;
if (printer_filament_id == "GFS00")
{
curr_tray->m_fila_type = "PLA-S";
}
else if (curr_tray->setting_id == "GFS01")
else if (printer_filament_id == "GFS01")
{
curr_tray->m_fila_type = "PA-S";
}
+8 -3
View File
@@ -110,7 +110,9 @@ bool Slic3r::is_stringing_prone_filament(const std::string& filament_id, float n
if (filament_id.empty()) return false;
const auto* set = pick_stringing_set(nozzle_diameter);
if (!set) return false;
return set->count(filament_id) > 0;
// filament_id is one of our content-addressed OF ids; the table above is keyed by the printer's own.
auto* agent = Slic3r::GUI::wxGetApp().getAgent();
return set->count(agent ? agent->from_orca_filament_id(filament_id) : filament_id) > 0;
}
wxString Slic3r::get_stage_string(int stage)
@@ -5048,10 +5050,13 @@ DevAmsTray MachineObject::parse_vt_tray(json vtray)
vt_tray.setting_id = vtray["tray_info_idx"].get<std::string>();
//std::string type = vtray["tray_type"].get<std::string>();
std::string type = setting_id_to_type(vt_tray.setting_id, vtray["tray_type"].get<std::string>());
if (vt_tray.setting_id == "GFS00") {
// vt_tray.setting_id is our OF id (translated on the way in); the two support ids below are the printer's own.
auto* agent = GUI::wxGetApp().getAgent();
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(vt_tray.setting_id) : vt_tray.setting_id;
if (printer_filament_id == "GFS00") {
vt_tray.m_fila_type = "PLA-S";
}
else if (vt_tray.setting_id == "GFS01") {
else if (printer_filament_id == "GFS01") {
vt_tray.m_fila_type = "PA-S";
}
else {
+9
View File
@@ -8823,6 +8823,11 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (wipe_tower_y_opt)
file_wipe_tower_y = *wipe_tower_y_opt;
if (auto* agent = wxGetApp().getAgent()) {
if (auto* ids = config.opt<ConfigOptionStrings>("filament_ids"))
for (std::string& id : ids->values)
id = agent->to_orca_filament_id(id);
}
preset_bundle->load_config_model(filename.string(), std::move(config), file_version);
ConfigOption* bed_type_opt = preset_bundle->project_config.option("curr_bed_type");
@@ -18669,6 +18674,8 @@ int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy
nozzle_diameter_str = nozzle_diameter_option->serialize();
std::string printer_model_id = preset_bundle.printers.get_edited_preset().get_printer_type(&preset_bundle);
// The printer reads slice_info.config and knows only its own catalog ids.
auto* id_agent = preset_bundle.is_bbl_vendor() ? wxGetApp().getAgent() : nullptr;
for (int i = 0; i < plate_data_list.size(); i++) {
PlateData *plate_data = plate_data_list[i];
@@ -18678,6 +18685,8 @@ int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy
std::string display_filament_type;
it->type = cfg.get_filament_type(display_filament_type, it->id);
it->filament_id = filament_id_opt ? filament_id_opt->get_at(it->id) : "";
if (id_agent)
it->filament_id = id_agent->from_orca_filament_id(it->filament_id);
it->color = filament_color ? filament_color->get_at(it->id) : "#FFFFFF";
// save filament info used in curr plate
int index = p->partplate_list.get_curr_plate_index();
+7 -2
View File
@@ -873,10 +873,15 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset
auto fila_type = Preset::remove_suffix_modified(GetValue().ToUTF8().data());
bool is_official = boost::algorithm::starts_with(fila_type, "Bambu");
if (is_official) {
// Get filament_id from filament_presets
// Get filament_id from filament_presets. FilamentPickerDialog looks up
// filaments_color_codes.json, which is downloaded from Bambu and keyed by the
// printer's own ids, so translate our OF id (the "GFA00" fallback is already one).
const std::string& preset_name = m_preset_bundle->filament_presets[m_filament_idx];
const Preset* selected_preset = m_collection->find_preset(preset_name);
wxString fila_id = selected_preset ? wxString::FromUTF8(selected_preset->filament_id) : "GFA00";
auto* agent = wxGetApp().getAgent();
wxString fila_id = "GFA00";
if (selected_preset)
fila_id = wxString::FromUTF8(agent ? agent->from_orca_filament_id(selected_preset->filament_id) : selected_preset->filament_id);
FilamentColor fila_color = get_cur_color_info();
// Show filament picker dialog
+13 -3
View File
@@ -3845,8 +3845,12 @@ int SelectMachineDialog::update_print_required_data(Slic3r::DynamicPrintConfig c
m_required_data_config = config;
m_required_data_model = model;
//m_required_data_plate_data_list = plate_data_list;
auto* agent = wxGetApp().getAgent();
for (auto i = 0; i < plate_data_list.size(); i++) {
if (!plate_data_list[i]->gcode_file.empty()) {
if (agent)
for (auto& info : plate_data_list[i]->slice_filaments_info)
info.filament_id = agent->to_orca_filament_id(info.filament_id);
m_required_data_plate_data_list.push_back(plate_data_list[i]);
}
}
@@ -5051,8 +5055,11 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_)
const auto& warning_tpu_filaments =
DevPrinterConfigUtil::get_value_from_config<std::vector<std::string>>(obj_->printer_type, "auto_on_cali_warning_tpu_filaments");
if (!warning_tpu_filaments.empty()) {
auto* agent = wxGetApp().getAgent();
for (const auto& fila : m_ams_mapping_result) {
if (std::find(warning_tpu_filaments.begin(), warning_tpu_filaments.end(), fila.filament_id) != warning_tpu_filaments.end()) {
// fila.filament_id is our OF id; the printer config list holds the printer's own.
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(fila.filament_id) : fila.filament_id;
if (std::find(warning_tpu_filaments.begin(), warning_tpu_filaments.end(), printer_filament_id) != warning_tpu_filaments.end()) {
show_status(PrintDialogStatus::PrintStatusTPUUnsuggestCali,
{ _L("If 'Dynamic Flow Calibration' is set to Auto/On, the system will use the manual calibration value or the default value and skip the flow calibration process. You can perform a manual flow calibration for TPU filament on the 'Calibration' page.") });
break;
@@ -5208,9 +5215,12 @@ bool SelectMachineDialog::can_support_pa_auto_cali()
std::vector<std::string> unsupport_auto_cali_filaments = DevPrinterConfigUtil::get_unsupport_auto_cali_filaments(obj->printer_type);
if (!unsupport_auto_cali_filaments.empty()) {
auto* agent = wxGetApp().getAgent();
auto iter = std::find_if(m_filaments.begin(), m_filaments.end(),
[&unsupport_auto_cali_filaments](const FilamentInfo &item) {
auto iter = std::find(unsupport_auto_cali_filaments.begin(), unsupport_auto_cali_filaments.end(), item.filament_id);
[&unsupport_auto_cali_filaments, agent](const FilamentInfo &item) {
// item.filament_id is our OF id; the printer config list holds the printer's own.
const std::string printer_filament_id = agent ? agent->from_orca_filament_id(item.filament_id) : item.filament_id;
auto iter = std::find(unsupport_auto_cali_filaments.begin(), unsupport_auto_cali_filaments.end(), printer_filament_id);
return iter != unsupport_auto_cali_filaments.end();
});