fix(ams): show "?" for loaded-but-unconfigured AMS slots (#1694)

Reporter on a 3-AMS P1S saw slots labelled "Empty" even though spools
  were physically loaded - OrcaSlicer's Device view showed the same
  slots as loaded.

  Root cause: the compact label below the AMS slot circle rendered
  tray.tray_type || t('ams.slotEmpty'), falling back to "Empty" whenever
  the printer firmware hadn't been told which material is in the slot.
  getEmptySlotKind already distinguishes 'physical' (firmware confirmed
  empty via state 9/10) from 'reset' (tray_type absent but firmware
  hasn't confirmed empty - spool loaded, just unassigned). The hover
  card and circle border already used that distinction; the compact
  label did not.

  Fix: label branches on emptyKind - 'physical' keeps "Empty", 'reset'
  shows "?" matching the slicer's own convention. External / VT tray
  label is unchanged (external trays have no "configured/unconfigured"
  distinction - they're either loaded or not). SpoolBuddy AmsUnitCard
  carried the same bug and got the same fix plus a tooltip.
This commit is contained in:
maziggy
2026-06-09 08:41:46 +02:00
parent 7a9c78c32c
commit 529155413f
17 changed files with 121 additions and 74 deletions
@@ -10,6 +10,15 @@ function isTrayEmpty(tray: AMSTray): boolean {
return !tray.tray_type || tray.tray_type === '';
}
// Mirror of PrintersPage.getEmptySlotKind (#1694): 'physical' when firmware
// confirms no spool (state 9/10), 'reset' when tray_type is absent but the
// firmware hasn't confirmed empty (= spool loaded, slot just unconfigured).
function getEmptySlotKind(tray: AMSTray): 'physical' | 'reset' | null {
if (tray.tray_type) return null;
const state = (tray as { state?: number | null }).state ?? null;
return state === 9 || state === 10 ? 'physical' : 'reset';
}
function getAmsName(id: number): string {
if (id <= 3) return `AMS ${String.fromCharCode(65 + id)}`;
if (id >= 128 && id <= 135) return `AMS HT ${String.fromCharCode(65 + id - 128)}`;
@@ -147,6 +156,7 @@ interface SpoolSlotProps {
function SpoolSlot({ tray, slotIndex, isActive, fillOverride, spoolmanFill, onClick }: SpoolSlotProps) {
const isEmpty = isTrayEmpty(tray);
const emptyKind = getEmptySlotKind(tray);
const color = trayColorToCSS(tray.tray_color);
const amsFill = tray.remain !== null && tray.remain !== undefined && tray.remain >= 0 ? tray.remain : null;
// If inventory says 0% but AMS reports positive remain, prefer AMS (#676)
@@ -181,8 +191,11 @@ function SpoolSlot({ tray, slotIndex, isActive, fillOverride, spoolmanFill, onCl
</div>
{/* Material type */}
<span className="text-sm text-white/70 truncate max-w-full">
{isEmpty ? 'Empty' : tray.tray_type || 'Unknown'}
<span
className="text-sm text-white/70 truncate max-w-full"
title={emptyKind === 'reset' ? 'Spool loaded — slot not configured' : undefined}
>
{isEmpty ? (emptyKind === 'reset' ? '?' : 'Empty') : tray.tray_type || 'Unknown'}
</span>
{/* Fill level bar */}