瀏覽代碼

fix(frontend/hms): surface uncataloged HMS faults that carry firmware actions (#1840)

filterKnownHMSErrors and the modal-local copy gated visibility on
ERROR_DESCRIPTIONS membership. H2C 0500_809C carries IGNORE_RESUME /
PROBLEM_SOLVED_RESUME but is missing from the bundled 853-entry catalog,
so the entire error — pip, count, panel, action buttons — never rendered
even though backend captured + dispatched it correctly.

The gate isn't dead code: PrintersPageBucketing pins the post-cancel
0C00_001B junk-echo regression to it. Widen the predicate to keep
(cataloged) OR (actions.length > 0) so noise is still filtered out
while user-actionable faults always surface.

Replace the modal's inline filter with the shared helper so badge
counts and modal contents agree by construction. Fall back to
hmsErrors.unknownCode ("Unknown HMS code — see the Bambu Lab wiki
for details.") when the catalog has no entry. New key translated in
all 11 locales.

New bucketing test pins PAUSE + uncataloged-with-actions = error;
existing FAILED + uncataloged-without-actions = finished stays green.
maziggy 2 月之前
父節點
當前提交
98b4d1c854

文件差異過大導致無法顯示
+ 0 - 0
CHANGELOG.md


+ 17 - 2
frontend/src/__tests__/pages/PrintersPageBucketing.test.ts

@@ -20,7 +20,7 @@ import { describe, it, expect } from 'vitest';
 type Status = {
   connected: boolean;
   state: string | null;
-  hms_errors?: { code: string; attr: number; severity: number }[];
+  hms_errors?: { code: string; attr: number; severity: number; actions?: string[] }[];
 };
 
 type Bucket = 'printing' | 'paused' | 'finished' | 'idle' | 'offline' | 'error';
@@ -32,7 +32,8 @@ function filterKnownHMSErrors(errors: Status['hms_errors']): NonNullable<Status[
     const codeNum = parseInt(e.code.replace('0x', ''), 16) || 0;
     const module = ((e.attr >> 16) & 0xFFFF).toString(16).padStart(4, '0').toUpperCase();
     const code = (codeNum & 0xFFFF).toString(16).padStart(4, '0').toUpperCase();
-    return KNOWN_HMS_CODES.has(`${module}_${code}`);
+    if (KNOWN_HMS_CODES.has(`${module}_${code}`)) return true;
+    return (e.actions?.length ?? 0) > 0;
   });
 }
 
@@ -77,6 +78,20 @@ describe('FAILED-without-HMS bucketing', () => {
     expect(classifyPrinterStatus(cancelEcho)).toBe('finished');
   });
 
+  it('classifies PAUSE + uncataloged HMS WITH actions as "error" (#1840: H2C 0500_809C carries actions but isnt in the bundled catalog)', () => {
+    const h2cActionableFault: Status = {
+      connected: true,
+      state: 'PAUSE',
+      hms_errors: [{
+        code: '0x809c',
+        attr: 0x0500_809C,
+        severity: 3,
+        actions: ['IGNORE_RESUME', 'PROBLEM_SOLVED_RESUME'],
+      }],
+    };
+    expect(classifyPrinterStatus(h2cActionableFault)).toBe('error');
+  });
+
   it('classifies FINISH as "finished" (unchanged baseline)', () => {
     const completedPrinter: Status = { connected: true, state: 'FINISH' };
     expect(classifyPrinterStatus(completedPrinter)).toBe('finished');

+ 14 - 9
frontend/src/components/HMSErrorModal.tsx

@@ -896,12 +896,20 @@ function getShortCode(attr: number, code: number): string {
   return `${module.toString(16).padStart(4, '0').toUpperCase()}_${codeNum.toString(16).padStart(4, '0').toUpperCase()}`;
 }
 
-// Helper to filter only known HMS errors (exported for use in badge counts)
+// Helper to filter HMS errors the UI should surface (exported for use in badge counts).
+// Keeps an error if EITHER:
+//   - it's in the bundled ERROR_DESCRIPTIONS catalog (known, has a description), OR
+//   - it carries firmware actions (uncataloged but user-actionable — e.g. H2C 0500_809C
+//     with IGNORE_RESUME/PROBLEM_SOLVED_RESUME — must surface so the button can render).
+// Drops uncataloged errors WITHOUT actions: those are transient junk like the post-cancel
+// 0C00_001B echo that re-introduces the FAILED-after-cancel "1 problem forever"
+// regression — see PrintersPageBucketing.test.ts.
 export function filterKnownHMSErrors(errors: HMSError[]): HMSError[] {
   return errors.filter((error) => {
     const codeNum = parseInt(error.code.replace('0x', ''), 16) || 0;
     const shortCode = getShortCode(error.attr, codeNum);
-    return ERROR_DESCRIPTIONS[shortCode] !== undefined;
+    if (ERROR_DESCRIPTIONS[shortCode] !== undefined) return true;
+    return (error.actions?.length ?? 0) > 0;
   });
 }
 
@@ -925,12 +933,9 @@ export function HMSErrorModal({ printerName, errors, onClose, printerId, hasPerm
     },
   });
 
-  // Filter to only show errors we have descriptions for (skip unknown codes)
-  const knownErrors = errors.filter((error) => {
-    const codeNum = parseInt(error.code.replace('0x', ''), 16) || 0;
-    const shortCode = getShortCode(error.attr, codeNum);
-    return ERROR_DESCRIPTIONS[shortCode] !== undefined;
-  });
+  // Surface cataloged errors and uncataloged-but-actionable errors. Mirrors
+  // filterKnownHMSErrors so the modal and the badge counts agree.
+  const knownErrors = filterKnownHMSErrors(errors);
 
   // Close on Escape key
   useEffect(() => {
@@ -998,7 +1003,7 @@ export function HMSErrorModal({ printerName, errors, onClose, printerId, hasPerm
                 const { label, color, bgColor, buttonHoverColor, Icon } = getSeverityInfo(error.severity);
                 const codeNum = parseInt(error.code.replace('0x', ''), 16) || 0;
                 const shortCode = getShortCode(error.attr, codeNum);
-                const description = ERROR_DESCRIPTIONS[shortCode];
+                const description = ERROR_DESCRIPTIONS[shortCode] ?? t('hmsErrors.unknownCode');
                 const hmsHomeUrl = getHMSHomeUrl();
                 const displayCode = shortCode.replace('_', '-');
 

+ 1 - 0
frontend/src/i18n/locales/de.ts

@@ -2755,6 +2755,7 @@ export default {
     title: 'Fehler - {{name}}',
     noErrors: 'Keine Fehler',
     viewOnWiki: 'Im Bambu Lab Wiki ansehen',
+    unknownCode: 'Unbekannter HMS-Code — Details siehe Bambu Lab Wiki.',
     clearInstructions: 'Löschen Sie die Fehler am Drucker, um sie hier zu entfernen.',
     clearErrors: 'Fehler löschen',
     clearSuccess: 'HMS-Fehler gelöscht',

+ 1 - 0
frontend/src/i18n/locales/en.ts

@@ -2784,6 +2784,7 @@ export default {
     title: 'Errors - {{name}}',
     noErrors: 'No errors',
     viewOnWiki: 'View on Bambu Lab Wiki',
+    unknownCode: 'Unknown HMS code — see the Bambu Lab wiki for details.',
     clearInstructions: 'Clear errors on the printer to dismiss them here.',
     clearErrors: 'Clear Errors',
     clearSuccess: 'HMS errors cleared',

+ 1 - 0
frontend/src/i18n/locales/es.ts

@@ -2758,6 +2758,7 @@ export default {
     title: 'Errores - {{name}}',
     noErrors: 'No hay errores',
     viewOnWiki: 'Ver en la wiki de Bambu Lab',
+    unknownCode: 'Código HMS desconocido — consulta la wiki de Bambu Lab para más detalles.',
     clearInstructions: 'Borre los errores en la impresora para descartarlos aquí.',
     clearErrors: 'Borrar errores',
     clearSuccess: 'Errores HMS borrados',

+ 1 - 0
frontend/src/i18n/locales/fr.ts

@@ -2744,6 +2744,7 @@ export default {
     title: 'Erreurs - {{name}}',
     noErrors: 'Aucune erreur',
     viewOnWiki: 'Voir sur le Wiki Bambu Lab',
+    unknownCode: 'Code HMS inconnu — consultez le wiki Bambu Lab pour plus de détails.',
     clearInstructions: 'Effacez les erreurs sur l\'imprimante pour les retirer ici.',
     clearErrors: 'Effacer les erreurs',
     clearSuccess: 'Erreurs HMS effacées',

+ 1 - 0
frontend/src/i18n/locales/it.ts

@@ -2743,6 +2743,7 @@ export default {
     title: 'Errori - {{name}}',
     noErrors: 'Nessun errore',
     viewOnWiki: 'Vedi su Bambu Lab Wiki',
+    unknownCode: 'Codice HMS sconosciuto — consulta la wiki di Bambu Lab per i dettagli.',
     clearInstructions: 'Cancella gli errori sulla stampante per rimuoverli qui.',
     clearErrors: 'Cancella errori',
     clearSuccess: 'Errori HMS cancellati',

+ 1 - 0
frontend/src/i18n/locales/ja.ts

@@ -2755,6 +2755,7 @@ export default {
     title: 'エラー - {{name}}',
     noErrors: 'エラーなし',
     viewOnWiki: 'Bambu Lab Wikiで表示',
+    unknownCode: '不明なHMSコード — 詳細はBambu Lab Wikiを参照してください。',
     clearInstructions: 'プリンターでエラーをクリアするとここからも消えます。',
     clearErrors: 'エラーをクリア',
     clearSuccess: 'HMSエラーをクリアしました',

+ 1 - 0
frontend/src/i18n/locales/ko.ts

@@ -2605,6 +2605,7 @@ export default {
     title: '오류 - {{name}}',
     noErrors: '오류 없음',
     viewOnWiki: 'Bambu Lab 위키에서 보기',
+    unknownCode: '알 수 없는 HMS 코드 — 자세한 내용은 Bambu Lab 위키를 참조하세요.',
     clearInstructions: '오류를 해제하려면 프린터에서 오류를 지우세요.',
     clearErrors: '오류 지우기',
     clearSuccess: 'HMS 오류가 지워졌습니다',

+ 1 - 0
frontend/src/i18n/locales/pt-BR.ts

@@ -2743,6 +2743,7 @@ export default {
     title: 'Erros - {{name}}',
     noErrors: 'Nenhum erro',
     viewOnWiki: 'Ver no Bambu Lab Wiki',
+    unknownCode: 'Código HMS desconhecido — consulte o wiki da Bambu Lab para mais detalhes.',
     clearInstructions: 'Limpe os erros na impressora para descartá-los aqui.',
     clearErrors: 'Limpar Erros',
     clearSuccess: 'Erros HMS limpos',

+ 1 - 0
frontend/src/i18n/locales/tr.ts

@@ -2759,6 +2759,7 @@ export default {
     title: 'Hatalar - {{name}}',
     noErrors: 'Hata yok',
     viewOnWiki: 'Bambu Lab Wiki\'de görüntüle',
+    unknownCode: 'Bilinmeyen HMS kodu — ayrıntılar için Bambu Lab wiki sayfasına bakın.',
     clearInstructions: 'Buradan kapatmak için yazıcıdaki hataları temizleyin.',
     clearErrors: 'Hataları Temizle',
     clearSuccess: 'HMS hataları temizlendi',

+ 1 - 0
frontend/src/i18n/locales/zh-CN.ts

@@ -2743,6 +2743,7 @@ export default {
     title: '错误 - {{name}}',
     noErrors: '无错误',
     viewOnWiki: '在拓竹 Wiki 上查看',
+    unknownCode: '未知 HMS 代码 — 详情请参阅拓竹 Wiki。',
     clearInstructions: '在打印机上清除错误以在此处消除它们。',
     clearErrors: '清除错误',
     clearSuccess: 'HMS 错误已清除',

+ 1 - 0
frontend/src/i18n/locales/zh-TW.ts

@@ -2743,6 +2743,7 @@ export default {
     title: '錯誤 - {{name}}',
     noErrors: '無錯誤',
     viewOnWiki: '在拓竹 Wiki 上檢視',
+    unknownCode: '未知 HMS 代碼 — 詳情請參閱拓竹 Wiki。',
     clearInstructions: '在印表機上清除錯誤以在此處消除它們。',
     clearErrors: '清除錯誤',
     clearSuccess: 'HMS 錯誤已清除',

文件差異過大導致無法顯示
+ 0 - 0
static/assets/index-CKTYjVC_.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-ChnivgH9.js"></script>
+    <script type="module" crossorigin src="/assets/index-CKTYjVC_.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-BYzbe9TT.css">
   </head>
   <body>

部分文件因文件數量過多而無法顯示