fix(filament): PLA-Varianten (PLA+, Silk, Matte) korrekt erkennen und an OrcaSlicer übermitteln
All checks were successful
Nightly Build / build (push) Successful in 12m36s

- _normalize_material() normalisiert Drucker-Typen auf kanonische Keys
- _TRAY_INFO_IDX erweitert um Silk/Matte/CF-Varianten und Schreibweisen
- _default_filament_name() mappt Varianten auf korrekte Generic-Profile
- Filament-Dropdown zeigt Hersteller-Profile der jeweiligen Variante
- Material-Buttons: PLA+, PLA Silk, PLA Matte hinzugefügt

Fixes #82
This commit is contained in:
2026-07-02 21:54:42 +02:00
parent 51f22947c5
commit cd11542352
3 changed files with 93 additions and 23 deletions

View File

@@ -1,4 +1,7 @@
## Changes in this build
- Fix: wrong filament slot used in multicolor prints when T0 is unused — AMS box mapping now includes placeholder entries for all paint indices so the printer correctly matches T1/T2/T3 tool changes to the right slots (Issue #78)
- Fix: Spoolman spool-slot assignment now persisted to config.ini and restored on bridge restart
- Fix: Spoolman spool-slot assignment persistence was silently broken (NameError on config_loader swallowed by bare except) — now uses correct module reference and logs failures; per-printer isolation added so multi-printer setups no longer overwrite each other (Issue #80, PR #83 by @walterioo)
- Fix: spool dropdown in print dialog showed "[object Object]" instead of vendor name (PR #83)
- Fix: gate_spool_id in MMU object now populated from per-printer slot map instead of hardcoded -1 (PR #83)
- Fix: PLA variants (PLA+, PLA Silk, PLA Matte) now recognized as their material family — correct tray_info_idx sent to printer, correct Generic profile synced to OrcaSlicer, and vendor profiles for the variant shown in slot editor dropdown (Issue #82)
- UI: PLA+, PLA Silk and PLA Matte buttons added to slot material selector

View File

@@ -1855,23 +1855,55 @@ class KobraXBridge:
# `compatible_printers: []` (= mit allen Druckern kompatibel).
_TRAY_INFO_IDX = {
# Anycubic-eigene Kobra-X-Profile
"PLA": "GFPLA",
"PLA+": "GFPLA+",
"PLA SILK": "GFPLA Silk",
"PETG": "GFPETG",
"ABS": "GFABS",
"ASA": "GFASA",
"TPU": "GFTPU 95A",
"PVA": "GFPVA",
"PLA": "GFPLA",
"PLA+": "GFPLA+",
"PLA SILK": "GFPLA Silk",
"PLA-SILK": "GFPLA Silk",
"PLASILK": "GFPLA Silk",
"SILK PLA": "GFPLA Silk",
"PLA MATTE": "GFPLA",
"PLA-MATTE": "GFPLA",
"PLA MARBLE": "GFPLA",
"PLA WOOD": "GFPLA",
"PETG": "GFPETG",
"PETG+": "GFPETG",
"ABS": "GFABS",
"ASA": "GFASA",
"TPU": "GFTPU 95A",
"TPE": "GFTPU 95A",
"PVA": "GFPVA",
# Kein Anycubic-Kobra-X-Profil → Library-Fallback
"PLA-CF": "OGFL98",
"PETG-CF": "OGFG98",
"PA": "OGFN99",
"PA-CF": "OGFN98",
"PC": "OGFC99",
"HIPS": "OGFS98",
"PLA-CF": "OGFL98",
"PLA CF": "OGFL98",
"PETG-CF": "OGFG98",
"PETG CF": "OGFG98",
"PA": "OGFN99",
"PA-CF": "OGFN98",
"PA CF": "OGFN98",
"PC": "OGFC99",
"HIPS": "OGFS98",
}
# Normalisiert Material-Typ-Strings auf den kanonischen Key für _TRAY_INFO_IDX
# und _default_filament_name. PLA-Varianten die nicht exakt matchen fallen
# auf ihre Basisfamilie zurück (PLA+ → PLA+, PLA Matte → PLA, etc.).
@staticmethod
def _normalize_material(mat: str) -> str:
m = mat.upper().strip().replace("-", " ").replace("_", " ")
# Bekannte Varianten normalisieren
_ALIASES = {
"PLAPLUS": "PLA+", "PLA PLUS": "PLA+",
"SILK PLA": "PLA SILK", "PLASILK": "PLA SILK",
"PLA MATTE": "PLA MATTE", "PLA MARBLE": "PLA MARBLE",
"PLA WOOD": "PLA WOOD",
"TPE": "TPU",
"PETG PLUS": "PETG+",
"PA6": "PA", "PA12": "PA", "PA66": "PA",
}
if m in _ALIASES:
return _ALIASES[m]
return m
def _build_lane_data(self) -> dict:
"""Baut BBL-AMS-JSON für OrcaSlicer DevFilaSystemParser::ParseV1_0.
@@ -1910,7 +1942,7 @@ class KobraXBridge:
color_hex = color_raw[:6].upper() + "FF"
else:
color_hex = "FFFFFFFF"
material = slot.get("type", "PLA").upper()
material = self._normalize_material(slot.get("type", "PLA"))
# User-Override aus config.ini [filament_profiles].slot_N_id
# bekommt Vorrang vor dem Default-Mapping nach material-Type.
# Vendor wird mitgesendet (tray_sub_brands + filament_vendor),
@@ -2086,7 +2118,7 @@ class KobraXBridge:
for _global_index, slot in slots:
occupied = slot.get("status") == 5
gate_status.append(1 if occupied else 0)
material = (slot.get("type") or "PLA").upper() if occupied else ""
material = self._normalize_material(slot.get("type") or "PLA") if occupied else ""
gate_material.append(material)
c = slot.get("color", [0, 0, 0]) if occupied else [0, 0, 0]
# Happy Hare erwartet gate_color als RRGGBB OHNE '#' (Klipper-Limitation).
@@ -2136,8 +2168,22 @@ class KobraXBridge:
kann pro Slot eine konkrete Marke setzen wenn er das will."""
if not material:
return ""
mat = material.upper().strip()
mat = self._normalize_material(material)
profs = self._load_orca_filaments()
# Varianten-Mapping: Drucker meldet z.B. "PLA SILK", OrcaSlicer speichert
# alle Varianten unter type=PLA mit dem Variant-Namen im name-Feld.
_VARIANT_NAME = {
"PLA SILK": "Generic PLA Silk",
"PLA MATTE": "Generic PLA Matte",
"PLA+": "Generic PLA",
"PLA-CF": "Generic PLA-CF",
"PETG-CF": "Generic PETG-CF",
}
if mat in _VARIANT_NAME:
target = _VARIANT_NAME[mat]
for p in profs:
if p.get("vendor") == "Generic" and p.get("name") == target:
return p["name"]
def _match_type(p: dict) -> bool:
pt = (p.get("type") or "").upper()
return pt == mat or pt.startswith(mat + "-") or pt.startswith(mat + " ")

View File

@@ -1413,7 +1413,14 @@ function doProfileImportUpload(files){
// ── AMS Slot Edit ──
var _slotEditIndex=-1;
var _slotEditLoaded=false;
var _MAT_PRESETS=['PLA','PETG','ABS','ASA','TPU','PA','PC','HIPS'];
var _MAT_PRESETS=['PLA','PLA+','PLA SILK','PLA MATTE','PETG','ABS','ASA','TPU','PA','PC','HIPS'];
function _normalizeMat(m){
var s=m.toUpperCase().trim().replace(/-/g,' ').replace(/_/g,' ');
var aliases={'PLAPLUS':'PLA+','PLA PLUS':'PLA+','SILK PLA':'PLA SILK','PLASILK':'PLA SILK',
'PLA MATTE':'PLA','PLA MARBLE':'PLA','PLA WOOD':'PLA','TPE':'TPU',
'PETG PLUS':'PETG+','PA6':'PA','PA12':'PA','PA66':'PA'};
return aliases[s]||s;
}
var _BASE_MATERIAL_TYPES=['PLA','PETG','ABS','ASA','TPU','TPE','PA','PC','HIPS','PEI','PEEK'];
function updateSlotEditFeedButton(){
var btn=document.getElementById('btn-slot-edit-feed');
@@ -1454,10 +1461,21 @@ function _fillSlotProfileDropdown(material, currentVendor, currentName){
_loadOrcaFilaments(function(profiles){
// Type-Filter: nur Profile vom passenden material zeigen (z.B. PLA → alle PLA-Varianten)
var matU=(material||'').toUpperCase().trim();
// PLA-Varianten: Drucker meldet "PLA SILK"/"PLA+"/"PLA MATTE", OrcaSlicer
// speichert alle unter type=PLA — Namens-Keyword als Zusatzfilter.
var _PLA_VARIANT_KW={'PLA SILK':'silk','PLA+':'pla+','PLA MATTE':'matte',
'PLA MARBLE':'marble','PLA WOOD':'wood'};
var variantKw=_PLA_VARIANT_KW[matU]||null;
var baseMat=variantKw?'PLA':matU;
var matched=profiles.filter(function(p){
var pt=(p.type||'').toUpperCase();
// PLA-CF, PLA-SILK etc. zählen auch zu PLA
return matU==='' || pt===matU || pt.startsWith(matU+'-') || pt.startsWith(matU+' ');
var nameL=(p.name||'').toLowerCase();
// Basis-Typ muss passen
var typeOk=baseMat===''||pt===baseMat||pt.startsWith(baseMat+'-')||pt.startsWith(baseMat+' ');
if(!typeOk) return false;
// Bei Variante: Name muss Keyword enthalten (z.B. "silk", "matte", "pla+")
if(variantKw) return nameL.indexOf(variantKw)!==-1;
return true;
});
sel.innerHTML='<option value="">'+tr('slot_edit_profile_default')+'</option>';
// User-Profile (is_user) zuerst — eigene Optgroup '★ Eigene' an erster Stelle.
@@ -1619,11 +1637,14 @@ function openSlotEdit(i){
_renderCopyFromSlot(globalIdx);
var mat=(slot.type||'PLA').toUpperCase();
document.getElementById('slot-edit-mat').value=mat;
// Normalisieren für Button-Highlighting: PLA-Varianten auf nächsten Preset mappen
var matNorm=_normalizeMat(mat);
var btns=document.getElementById('slot-mat-btns');
btns.innerHTML=_MAT_PRESETS.map(function(m){
var active=m===mat||m===matNorm;
return '<button class="mat-preset-btn" data-mat="'+m+'" onclick="selectMatPreset(\''+m+'\')" '
+'style="padding:4px 10px;border-radius:6px;border:1px solid var(--border);cursor:pointer;font-size:12px;'
+(m===mat?'background:var(--accent);color:#fff':'background:var(--raised);color:var(--txt2)')+'">'+m+'</button>';
+(active?'background:var(--accent);color:#fff':'background:var(--raised);color:var(--txt2)')+'">'+m+'</button>';
}).join('');
// OrcaSlicer-Profil-Dropdown: aktuellen User-Override für diesen Slot
// aus /kx/filament/slots holen (enthält vendor+name+id).