| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- """Utility functions for converting between filament_id and setting_id formats, and shared filament constants.
- Bambu printers use two ID formats for filament presets:
- - **filament_id** (aka tray_info_idx): e.g. "GFL05", "GFG02", "GFA00"
- Reported by printer firmware (RFID tags, AMS status).
- - **setting_id**: e.g. "GFSL05", "GFSG02", "GFSA00"
- Used by BambuStudio / Bambu Cloud API to resolve presets.
- For most official Bambu filaments the only difference is an "S" inserted after
- "GF". Two kinds do not follow that rule (#3387):
- - SUNLU: setting_id "GFSNLS03_04", filament_id "GFSNL03" -- a filament_id
- that itself starts with "GFS" plus a letter.
- - About thirty P1/X1-series presets whose filament_id is another one
- altogether ("GFSA05_10", Bambu PLA Matte @BBL P1P, is "GFA01", while every
- other "GFSA05" is Bambu PLA Silk, "GFA05"). Those are listed by full
- setting_id in ``filament_id_tables.SETTING_ID_FILAMENT_IDS``.
- User presets (starting with "P") use the same ID in both contexts.
- """
- import re
- from backend.app.utils.filament_id_tables import ORCA_FILAMENT_IDS, SETTING_ID_FILAMENT_IDS
- # SUNLU's ids in Bambu's catalogue: filament_id "GFSNL03", setting_id "GFSNLS03".
- _SUNLU_FILAMENT_ID = re.compile(r"GFSNL(\d+)")
- _SUNLU_SETTING_ID = re.compile(r"GFSNLS(\d+)")
- # OrcaSlicer's own filament ids ("OFMUWNkp"); Orca maps them to Bambu's at the
- # printer boundary with the table ORCA_FILAMENT_IDS is generated from.
- _ORCA_FILAMENT_ID = re.compile(r"OF[0-9A-Za-z]{6}")
- MATERIAL_TEMPS: dict[str, tuple[int, int]] = {
- "PLA": (190, 230),
- "PETG": (220, 260),
- "ABS": (240, 270),
- "ASA": (240, 270),
- "TPU": (200, 240),
- "PA": (260, 290),
- "PC": (250, 280),
- "PVA": (190, 210),
- "PLA-CF": (210, 240),
- "PETG-CF": (240, 270),
- "PA-CF": (270, 300),
- }
- # Bambu's generic preset per material, for a slot whose spool has no preset of
- # its own. Every assignment path and the Configure dialog read this one table;
- # a material missing here goes to the printer with an empty tray_info_idx (#3273).
- GENERIC_FILAMENT_IDS: dict[str, str] = {
- "PLA": "GFL99",
- "PETG": "GFG99",
- "ABS": "GFB99",
- "ASA": "GFB98",
- "PC": "GFC99",
- "PA": "GFN99",
- "NYLON": "GFN99",
- "TPU": "GFU99",
- "PVA": "GFS99",
- "HIPS": "GFS98",
- "PLA-CF": "GFL98",
- "PETG-CF": "GFG98",
- "PA-CF": "GFN98",
- "PETG HF": "GFG96",
- "PLA SILK": "GFL96",
- "PLA HIGH SPEED": "GFL95",
- "PCTG": "GFG97",
- "PE": "GFP99",
- "PP": "GFP97",
- }
- # The generics the slot-reuse check replaces instead of carrying forward:
- # a slot holding one of these gets the generic for the new spool's own
- # material. Deliberately the set from before the table above grew (#3273), so
- # a slot set to Generic PLA Silk or PLA High Speed still keeps it for a
- # preset-less PLA spool, as it always has.
- GENERIC_IDS_REPLACED_ON_REUSE: frozenset[str] = frozenset(
- {
- "GFL99",
- "GFL98",
- "GFG99",
- "GFG98",
- "GFG96",
- "GFB99",
- "GFB98",
- "GFC99",
- "GFN99",
- "GFN98",
- "GFU99",
- "GFS99",
- "GFS98",
- }
- )
- def filament_id_to_setting_id(filament_id: str) -> str:
- """Convert filament_id → setting_id (e.g. "GFL05" → "GFSL05").
- - Already a setting_id ("GFSL05", "GFSS99", "GFSNLS03") → returned
- unchanged; a support filament_id ("GFS99") still converts, and so does
- SUNLU's ("GFSNL03" → "GFSNLS03").
- - User presets ("P…") → returned unchanged.
- - Empty / unknown → returned unchanged.
- """
- if not filament_id:
- return filament_id
- # User presets start with "P" - leave unchanged
- if filament_id.startswith("P"):
- return filament_id
- sunlu = _SUNLU_FILAMENT_ID.fullmatch(filament_id)
- if sunlu:
- return f"GFSNLS{sunlu.group(1)}"
- # Official Bambu presets: GFx## -> GFSx##
- if filament_id.startswith("GF") and len(filament_id) >= 4:
- # Already a setting_id: "GFS" then the family letter ("GFSL05").
- # Support filaments are family S, so their filament_id starts with
- # "GFS" too -- but a digit follows it ("GFS99" is Generic PVA, whose
- # setting_id is "GFSS99").
- if filament_id[2] == "S" and filament_id[3].isalpha():
- return filament_id
- return f"GFS{filament_id[2:]}"
- return filament_id
- def setting_id_to_filament_id(setting_id: str) -> str:
- """Convert setting_id → filament_id (e.g. "GFSL05_07" → "GFL05"), with
- any version suffix stripped.
- - A setting_id Bambu gives a filament_id the rule does not lead to → that
- filament_id, looked up by the full id, suffix included (#3387).
- - Already a filament_id ("GFL05", a support one like "GFS99", or SUNLU's
- "GFSNL03") → returned unchanged.
- - User presets ("P…") → returned without the suffix.
- - Empty / unknown → returned without the suffix.
- """
- if not setting_id:
- return setting_id
- listed = SETTING_ID_FILAMENT_IDS.get(setting_id)
- if listed:
- return listed
- return derived_filament_id(setting_id.split("_")[0])
- def derived_filament_id(base: str) -> str:
- """The filament_id the naming rule gives for a suffix-free setting_id,
- without the table of exceptions. ``scripts/generate_filament_id_tables.py``
- lists exactly the presets where this differs from Bambu's own id."""
- sunlu = _SUNLU_SETTING_ID.fullmatch(base)
- if sunlu:
- return f"GFSNL{sunlu.group(1)}"
- if _SUNLU_FILAMENT_ID.fullmatch(base):
- return base
- # Setting_id format: GFSx## -> GFx## (remove the "S"). A digit after
- # "GFS" means a support filament_id, not a setting_id (see above).
- if base.startswith("GFS") and len(base) >= 5 and base[3].isalpha():
- return f"GF{base[3:]}"
- return base
- def orca_to_bambu_filament_id(filament_id: str) -> str:
- """The Bambu filament_id for an OrcaSlicer one ("OFMUWNkp" → "GFSNL03").
- OrcaSlicer's system presets carry ids of their own; the printer only knows
- Bambu's. An Orca id the table does not know returns ``""`` -- sent to the
- printer it would resolve nowhere. Any other id is returned unchanged.
- """
- if _ORCA_FILAMENT_ID.fullmatch(filament_id or ""):
- return ORCA_FILAMENT_IDS.get(filament_id, "")
- return filament_id
- def normalize_slicer_filament(slicer_filament: str | None) -> tuple[str, str]:
- """Normalize a slicer_filament value into (tray_info_idx, setting_id).
- The slicer_filament field on a spool can be stored in either format:
- - filament_id: "GFL05" (from RFID tag scan)
- - setting_id: "GFSL05" or "GFSL05_07" (from cloud preset picker)
- Returns (tray_info_idx, setting_id) with version suffixes stripped.
- """
- raw = slicer_filament or ""
- if not raw:
- return ("", "")
- # Strip version suffix (e.g. "GFSL05_07" → "GFSL05"); the filament_id is
- # read from the full id, since the suffix can decide it (#3387).
- base = raw.split("_")[0] if "_" in raw else raw
- tray_info_idx = setting_id_to_filament_id(raw)
- sid = filament_id_to_setting_id(base)
- return (tray_info_idx, sid)
|