Просмотр исходного кода

Prefer the selected printer when no preset matches the plate's material (#3234)

maziggy 2 недель назад
Родитель
Сommit
8977c9e647

+ 57 - 0
frontend/src/__tests__/utils/slicePresetPicker3234.test.ts

@@ -0,0 +1,57 @@
+import { describe, expect, it } from 'vitest';
+
+import type { UnifiedPreset, UnifiedPresetsBySlot, UnifiedPresetsResponse } from '../../api/client';
+import { pickFilamentForSlot } from '../../utils/slicePresetPicker';
+import { buildCompatibilityIndex } from '../../utils/slicerPrinterMatch';
+
+// #3234 made the sidecar list every filament the slicer ships and resolve the
+// material of all of them. Before, a few presets with no known material kept
+// the no-such-material fallback on the right printer by accident; with those
+// gone, the fallback has to look at the printer itself.
+
+const index = buildCompatibilityIndex({ 'Bambu Lab X1 Carbon': 'X1C', 'Bambu Lab A1': 'A1' });
+const X1C = 'Bambu Lab X1 Carbon 0.4 nozzle';
+const A1 = 'Bambu Lab A1 0.4 nozzle';
+
+function empty(): UnifiedPresetsBySlot {
+  return { printer: [], process: [], filament: [] };
+}
+
+function tiers(perTier: Partial<Record<'local' | 'orca_cloud' | 'cloud' | 'standard', Partial<UnifiedPreset>[]>>) {
+  const build = (source: 'local' | 'orca_cloud' | 'cloud' | 'standard') => ({
+    ...empty(),
+    filament: (perTier[source] ?? []).map((e) => ({ id: e.name as string, source, ...e })) as UnifiedPreset[],
+  });
+  return {
+    local: build('local'),
+    orca_cloud: build('orca_cloud'),
+    cloud: build('cloud'),
+    standard: build('standard'),
+    cloud_status: 'ok',
+    orca_cloud_status: 'ok',
+  } as UnifiedPresetsResponse;
+}
+
+const pick = (by: UnifiedPresetsResponse, type: string, printer = X1C, color = '') =>
+  pickFilamentForSlot(by, { type, color }, printer, index)?.id;
+
+describe('pickFilamentForSlot — no preset of the plate material (#3234)', () => {
+  it('falls back to a preset the selected printer can take', () => {
+    // Alphabetically first and scored the same, but for another printer: the
+    // slicer would refuse the slice outright.
+    const by = tiers({
+      standard: [
+        { name: 'Bambu ABS @BBL A1', filament_type: 'ABS', compatible_printers: [A1] },
+        { name: 'Bambu PLA Basic @BBL X1C', filament_type: 'PLA', compatible_printers: [X1C] },
+      ],
+    });
+    expect(pick(by, 'XYZ')).toBe('Bambu PLA Basic @BBL X1C');
+  });
+
+  it('still returns something when only other printers have presets', () => {
+    const by = tiers({
+      standard: [{ name: 'Bambu ABS @BBL A1', filament_type: 'ABS', compatible_printers: [A1] }],
+    });
+    expect(pick(by, 'XYZ')).toBe('Bambu ABS @BBL A1');
+  });
+});

+ 10 - 1
frontend/src/utils/slicePresetPicker.ts

@@ -227,6 +227,7 @@ export function pickFilamentForSlot(
   let bestCompatible: { ref: PresetRef; score: number } | null = null;
   let bestMismatch: { ref: PresetRef; score: number } | null = null;
   let bestWrongType: { ref: PresetRef; score: number } | null = null;
+  let bestWrongTypeMismatch: { ref: PresetRef; score: number } | null = null;
   for (const tier of SLICE_MODAL_TIER_ORDER) {
     for (const p of by[tier].filament) {
       let score = 0;
@@ -240,7 +241,14 @@ export function pickFilamentForSlot(
       score += TIER_BONUS[tier];
       const ref = { source: p.source, id: p.id };
       if (statesDifferentMaterial(p, reqType)) {
-        if (bestWrongType == null || score > bestWrongType.score) {
+        // Still split by printer: with no preset of the plate's material at
+        // all, a wrong-printer pick is refused by the slicer outright, so it
+        // must not beat one the selected printer can take.
+        if (presetCompatibility(p, 'filament', printerName, compatIndex) === 'mismatch') {
+          if (bestWrongTypeMismatch == null || score > bestWrongTypeMismatch.score) {
+            bestWrongTypeMismatch = { ref, score };
+          }
+        } else if (bestWrongType == null || score > bestWrongType.score) {
           bestWrongType = { ref, score };
         }
       } else if (presetCompatibility(p, 'filament', printerName, compatIndex) === 'mismatch') {
@@ -258,6 +266,7 @@ export function pickFilamentForSlot(
   // user can see and change in the dropdown than a null the modal renders as
   // an empty slot, which is what shipped before the partition existed.
   if (bestWrongType != null) return bestWrongType.ref;
+  if (bestWrongTypeMismatch != null) return bestWrongTypeMismatch.ref;
   // Final fallback when there are no filament presets at all (empty
   // registry) — pickDefault returns null in that case too, but keeping the
   // call mirrors the rest of the picker logic for shape consistency.