Browse Source

fix(ams): HT slot shows "Generic" after configuring custom preset (#1053)

  After configuring an AMS-HT slot with a custom cloud preset, the slot
  card and Configure modal kept showing "Generic PLA" even though the
  printer and slicer had the correct preset. The `/slot-presets` response
  keyed HT entries at `ams_id * 4 + tray_id = 512`, but frontend lookups
  used `ams_id` directly (128 on PrintersPage via getGlobalTrayId, 64 on
  SpoolBuddy via a one-off formula). All three agreed for regular AMS, so
  the mismatch only surfaced on HT — the saved preset never reached the
  UI and the render fell through to `tray.tray_type`.

  Backend now keys via a helper that mirrors frontend `getGlobalTrayId`.
  SpoolBuddy's AMS page switches to the shared helper. Regression test
  covers regular, HT, and external slot keys.
maziggy 1 month ago
parent
commit
87a5aa36e9

File diff suppressed because it is too large
+ 1 - 0
CHANGELOG.md


+ 10 - 1
backend/app/api/routes/printers.py

@@ -1725,6 +1725,15 @@ async def start_calibration(
 # ============================================================================
 
 
+def _slot_preset_key(ams_id: int, tray_id: int) -> int:
+    # Mirrors frontend getGlobalTrayId (amsHelpers.ts): AMS-HT (128-135) is keyed
+    # by ams_id since each unit has a single slot and shares its global ID with
+    # the unit itself. Regular AMS and external (255) use ams_id*4+tray_id.
+    if 128 <= ams_id <= 135:
+        return ams_id
+    return ams_id * 4 + tray_id
+
+
 @router.get("/{printer_id}/slot-presets")
 async def get_slot_presets(
     printer_id: int,
@@ -1736,7 +1745,7 @@ async def get_slot_presets(
     mappings = result.scalars().all()
 
     return {
-        mapping.ams_id * 4 + mapping.tray_id: {
+        _slot_preset_key(mapping.ams_id, mapping.tray_id): {
             "ams_id": mapping.ams_id,
             "tray_id": mapping.tray_id,
             "preset_id": mapping.preset_id,

+ 30 - 0
backend/tests/unit/test_slot_preset_key.py

@@ -0,0 +1,30 @@
+"""Unit tests for slot-preset key derivation.
+
+Regression coverage for #1053: the backend's get_slot_presets response
+must use the same keying scheme as the frontend's getGlobalTrayId
+(amsHelpers.ts) so that AMS-HT mappings round-trip correctly.
+"""
+
+from backend.app.api.routes.printers import _slot_preset_key
+
+
+def test_regular_ams_uses_global_tray_id():
+    assert _slot_preset_key(0, 0) == 0
+    assert _slot_preset_key(0, 3) == 3
+    assert _slot_preset_key(1, 1) == 5
+    assert _slot_preset_key(2, 2) == 10
+    assert _slot_preset_key(3, 3) == 15
+
+
+def test_ams_ht_keyed_by_ams_id():
+    # AMS-HT is single-slot and shares its global tray id with the unit id;
+    # frontend getGlobalTrayId(amsId, 0, false) returns amsId for 128-135.
+    assert _slot_preset_key(128, 0) == 128
+    assert _slot_preset_key(129, 0) == 129
+    assert _slot_preset_key(135, 0) == 135
+
+
+def test_external_spool_uses_multiplied_id():
+    # External (ams_id=255) matches PrintersPage lookup: 255 * 4 + tray_id.
+    assert _slot_preset_key(255, 0) == 1020
+    assert _slot_preset_key(255, 1) == 1021

+ 1 - 1
frontend/src/pages/spoolbuddy/SpoolBuddyAmsPage.tsx

@@ -291,7 +291,7 @@ export function SpoolBuddyAmsPage() {
   }, [effectiveTrayNow]);
 
   const handleAmsSlotClick = useCallback((amsId: number, trayId: number, tray: AMSTray | null) => {
-    const globalTrayId = amsId >= 128 ? (amsId - 128) * 4 + trayId + 64 : amsId * 4 + trayId;
+    const globalTrayId = getGlobalTrayId(amsId, trayId, false);
     const slotPreset = slotPresets?.[globalTrayId];
     const mappedExtruderId = amsExtruderMap[String(amsId)];
     const normalizedId = amsId >= 128 ? amsId - 128 : amsId;

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-DZZn3lqr.js


+ 1 - 1
static/index.html

@@ -26,7 +26,7 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-DFkuTMRb.js"></script>
+    <script type="module" crossorigin src="/assets/index-DZZn3lqr.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-CkAOuJaW.css">
   </head>
   <body>

Some files were not shown because too many files changed in this diff