visionA/visionA-frontend/src/lib/hardware-compat.ts
jim800121chen 051994ed54 feat(workspace): 圖片/影片/批次推論 tab + 載入模型到裝置 FlashDialog(前端)
推論工作區前端塔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>
2026-07-09 06:27:14 +08:00

62 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 硬體相容性檢查 — visionA Cloudflash 前的 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 || "";
}