推論工作區前端塔3 + flash 前端(i18n/device-detail 交纏、一批 commit)。 塔3(圖片/影片/批次 tab、複用塊1/2 顯示管線 0 重造): - media.ts 上傳 helper(XHR image/video/batch)+ media-uploader + media-tab - workspace-client 3 tab 打開 + activeTab 互斥 camera/media WS - video seek 用後端 durationSeconds 換算(脫離 fps 耦合、Reviewer M-1 修正) - 補 tab 切換 WS reset 測試(S-1) - Reviewer 2 輪通過 flash 前端(載入模型到裝置、補「進工作區前置」缺口,照 POC 移植): - flash-dialog + flash-progress(選 model + 相容性檢查 + WS 進度) - flash-store(throw-based ApiError) - use-flash-progress(onOpen 才 POST 防 race + hasStartedRef 防重連重複) - hardware-compat(targetChip 單值、寧鬆勿嚴、UX 警示非安全閘) - device-detail-client 掛 FlashDialog(gate 未改、flash 完 fetchDevice 解鎖) - Reviewer 0C/0M 契約:same-origin cookie、WS/stream/upload URL 相對路徑無 token、i18n 雙語對稱、 設計 token 不裸色值。全前端 tsc/eslint clean、塔3 30 test + flash 45 test 綠。 (全套 11 failed 全屬 conversion-store 既有 flaky、未觸碰) follow-up:flash WS 逾時提示(Mi)、--success token(design)、conversion-store flaky 釐清。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
62 lines
2.8 KiB
TypeScript
62 lines
2.8 KiB
TypeScript
/**
|
||
* 硬體相容性檢查 — visionA Cloud(flash 前的 UX 早警示)
|
||
*
|
||
* 移植自 POC edge-ai-platform/frontend/src/lib/hardware-compat.ts(唯讀參考、非搬 code)。
|
||
*
|
||
* ⚠️ 與 POC 的關鍵契約差異(見 flash-model-load-mapping.md §2.3-C):
|
||
* - POC model 用 `supportedHardware: string[]`(多值陣列),比對「device 晶片 ∈ 陣列」。
|
||
* - visionA model 只有 `targetChip: TargetChip`(單值,見 model-store.ts:33),故改為單值比對。
|
||
*
|
||
* 定位(重要):**這只是前端 UX 早警示**,避免使用者選了不相容 model 才 POST 被拒。
|
||
* 真正的權威把關在 local agent(`flash/service.go isCompatible`)——即使前端誤放行,
|
||
* agent 仍會擋。因此前端邏輯**寧鬆勿嚴**(無法判定時預設相容),避免誤擋合法組合。
|
||
*
|
||
* device.type 來源多樣(local agent 回報值 / 後端正規化值),可能是:
|
||
* - `kneron_kl520` / `kneron_kl720` …(driver type 前綴)
|
||
* - `KL520` / `kl520`(大小寫混用)
|
||
* 這裡統一正規化到小寫晶片代號(`kl520`…),與 `TargetChip` 對齊後比對。
|
||
*/
|
||
|
||
import type { TargetChip } from "@/stores/model-store";
|
||
|
||
/**
|
||
* 把 device.type 正規化成小寫晶片代號(如 `kl520`)。
|
||
* 無法辨識時回傳原字串的小寫,交由呼叫端寬鬆處理(寧鬆勿嚴)。
|
||
*/
|
||
export function getChipFromDeviceType(deviceType: string): string {
|
||
if (!deviceType) return "";
|
||
const lower = deviceType.toLowerCase();
|
||
// 抓出 kl 後接 3 碼數字的晶片代號(涵蓋 `kneron_kl520` / `KL520` / `kl-520` 等變體)。
|
||
const match = lower.match(/kl\s*[-_]?\s*(\d{3})/);
|
||
if (match) return `kl${match[1]}`;
|
||
return lower;
|
||
}
|
||
|
||
/**
|
||
* 判斷 model(單一 targetChip)是否與 device 相容。
|
||
*
|
||
* 寧鬆勿嚴規則(前端只做早警示、權威在 agent):
|
||
* - model.targetChip 為 `unknown` → 視為相容(無法判定,不擋)。
|
||
* - device.type 無法辨識出晶片代號 → 視為相容(無法判定,不擋)。
|
||
* - 兩者都可辨識 → 嚴格比對晶片代號是否相同。
|
||
*/
|
||
export function isModelCompatible(
|
||
targetChip: TargetChip,
|
||
deviceType: string,
|
||
): boolean {
|
||
if (targetChip === "unknown") return true;
|
||
const deviceChip = getChipFromDeviceType(deviceType);
|
||
// device 端無法辨識晶片 → 不擋(交給 agent 權威把關)。
|
||
if (!deviceChip.startsWith("kl")) return true;
|
||
return deviceChip === targetChip;
|
||
}
|
||
|
||
/**
|
||
* 給 UI 顯示用的裝置晶片代號(大寫,如 `KL520`);無法辨識時回傳原字串。
|
||
*/
|
||
export function getHardwareLabel(deviceType: string): string {
|
||
const chip = getChipFromDeviceType(deviceType);
|
||
if (chip.startsWith("kl")) return chip.toUpperCase();
|
||
return deviceType || "";
|
||
}
|