From edc8355c2377ff23aef169a397e3b349cd7c1c11 Mon Sep 17 00:00:00 2001 From: jim800121chen Date: Wed, 25 Feb 2026 01:03:33 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=20PRD=20v2.3=20+=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20TDD=20v1.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PRD: 新增 B4.5 已實作額外功能(i18n、深色模式、多來源推論、Dashboard 等 9 項) - TDD: 新增技術設計文件,涵蓋前後端架構、通訊協定、Driver 抽象層、Camera Pipeline Co-Authored-By: Claude Opus 4.6 --- docs/PRD-Integrated.md | 101 +- docs/TDD.md | 2018 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2115 insertions(+), 4 deletions(-) create mode 100644 docs/TDD.md diff --git a/docs/PRD-Integrated.md b/docs/PRD-Integrated.md index 7854abe..33ef3cf 100644 --- a/docs/PRD-Integrated.md +++ b/docs/PRD-Integrated.md @@ -8,9 +8,9 @@ |------|------| | 文件名稱 | 邊緣 AI 開發平台 PRD | | 產品名稱 | (暫未定名,以下稱「本平台」) | -| 版本 | v2.2 | -| 日期 | 2026-02-11 | -| 狀態 | 初稿 | +| 版本 | v2.3 | +| 日期 | 2026-02-24 | +| 狀態 | 更新中 | --- @@ -34,6 +34,7 @@ - [B3.7 硬體支援路線圖](#b37-硬體支援路線圖) - [B3.8 硬體技術規格](#b38-硬體技術規格) - [B4. MVP 功能規格](#b4-mvp-功能規格) + - [B4.5 已實作的額外功能](#b45-已實作的額外功能) - [B5. 功能路線圖(Post-MVP)](#b5-功能路線圖post-mvp) - [B6. 商業模式與策略](#b6-商業模式與策略) @@ -1084,6 +1085,98 @@ Kneron Dongle Arduino 開發板 非 Kneron 晶片 | **互動控制** | Confidence Threshold 滑桿、開始/停止推論 | | **裝置日誌** | 原始串列通訊日誌(可展開查看) | +### B4.5 已實作的額外功能 + +> 以下功能為 MVP 開發過程中額外實作,超出原始 PRD 範圍。共 9 項功能。 + +#### F5 — 多來源推論輸入 + +| 項目 | 規格 | +|------|------| +| **概述** | 除了攝影機之外,支援上傳圖片和影片作為推論輸入來源 | +| **輸入來源** | 攝影機(即時)、圖片上傳(JPG/PNG)、影片上傳(MP4/AVI/MOV)、影片 URL(直接連結、YouTube、RTSP) | +| **圖片推論** | 上傳圖片至後端,送入裝置 NPU 推論後回傳結果 | +| **影片推論** | 上傳影片或提供 URL,後端逐幀擷取送入裝置推論,結果透過 WebSocket 即時串流 | +| **YouTube 支援** | 透過 yt-dlp 解析 YouTube URL 取得直接影片連結,再進行影片推論 | +| **UI 設計** | Tab 式切換(攝影機 / 圖片 / 影片),影片 tab 內再切換上傳檔案或貼上 URL | + +#### F6 — 多語系支援(i18n) + +| 項目 | 規格 | +|------|------| +| **概述** | 完整的前端多語系支援,支援英文與繁體中文切換 | +| **支援語言** | English (en)、繁體中文 (zh-TW) | +| **實作方式** | 自訂 TypeScript i18n 系統,不依賴外部框架(無 next-intl / react-i18next) | +| **翻譯字典** | 約 120+ 翻譯 key,按功能分區(common, nav, dashboard, devices, models, camera, inference, settings, errors) | +| **型別安全** | `TranslationKey` 型別由 `Paths` 遞迴產生,提供所有 key 的自動完成,缺 key 時 TypeScript 報錯 | +| **切換方式** | Settings 頁面語言下拉選單,切換後即時生效(無需重新整理) | +| **持久化** | 語言設定透過 Zustand persist 儲存至 localStorage | + +#### F7 — 深色模式 + +| 項目 | 規格 | +|------|------| +| **概述** | 支援淺色和深色主題切換 | +| **實作方式** | ThemeSync 元件監聽 Zustand store 的 theme 設定,同步更新 `` 的 `class` 屬性(`dark`) | +| **CSS 方案** | 使用 Tailwind CSS 的 `dark:` variant,搭配 CSS custom properties | +| **切換方式** | Settings 頁面主題選擇 | +| **持久化** | 主題設定透過 Zustand persist 儲存至 localStorage | + +#### F8 — 儀表板(Dashboard) + +| 項目 | 規格 | +|------|------| +| **概述** | 首頁儀表板提供平台總覽,包含統計卡片、快速操作和即時資訊 | +| **統計卡片** | 模型數量、裝置數量、已連線裝置數、燒錄次數 | +| **快速操作** | 「瀏覽模型」、「管理裝置」、「上傳模型」按鈕,快速導航到對應頁面 | +| **活動時間線** | 顯示最近 10 筆系統活動(模型上傳/刪除、裝置連線/斷線、燒錄開始/完成/錯誤) | +| **已連線裝置** | 即時顯示目前已連線的裝置列表及狀態 | + +#### F9 — 裝置偏好設定 + +| 項目 | 規格 | +|------|------| +| **概述** | 每台裝置可設定自訂名稱和備註 | +| **自訂名稱** | alias 欄位,例如「實驗室裝置 #1」 | +| **備註** | 自由文字備註欄位 | +| **持久化** | 透過 Zustand persist 儲存至 localStorage,以 deviceId 為 key | + +#### F10 — 模型比較 + +| 項目 | 規格 | +|------|------| +| **概述** | 模型庫內可選取最多 3 個模型進行並排比較 | +| **比較面向** | 準確率、FPS、模型大小、延遲、任務類型 | +| **UI 設計** | Dialog 式比較表,含指標長條圖視覺化 | +| **操作方式** | 在模型列表勾選後點擊「比較」按鈕 | + +#### F11 — 自訂模型上傳 + +| 項目 | 規格 | +|------|------| +| **概述** | 使用者可上傳自訂 .nef 模型檔案到模型庫 | +| **上傳資訊** | 模型檔案(.nef)、模型名稱、描述、任務類型、標籤、輸入尺寸、量化方式 | +| **驗證** | 必填欄位驗證(檔案、名稱、任務類型、標籤) | +| **模型管理** | 自訂模型可在模型詳情頁刪除 | + +#### F12 — 設定頁面 + +| 項目 | 規格 | +|------|------| +| **概述** | 集中管理應用程式設定 | +| **伺服器設定** | 顯示 API URL 和 WebSocket URL(唯讀) | +| **外觀設定** | 主題切換(淺色/深色)、語言切換(EN/zh-TW) | +| **關於** | 顯示平台版本資訊 | +| **恢復預設** | 一鍵恢復所有設定至預設值 | + +#### F13 — 裝置健康狀態與連線歷史 + +| 項目 | 規格 | +|------|------| +| **概述** | 裝置詳情頁顯示健康狀態和連線歷史 | +| **健康狀態** | 狀態、韌體版本、運行時間、最後活動時間 | +| **連線歷史** | 按時間排列的連線/斷線事件紀錄 | + --- ## B5. 功能路線圖(Post-MVP) @@ -1270,4 +1363,4 @@ Phase 3 — 進階功能(長期差異化) --- -*文件版本:v2.2 | 日期:2026-02-11 | 狀態:初稿* +*文件版本:v2.3 | 日期:2026-02-24 | 狀態:更新中* diff --git a/docs/TDD.md b/docs/TDD.md new file mode 100644 index 0000000..bf8f493 --- /dev/null +++ b/docs/TDD.md @@ -0,0 +1,2018 @@ +# 邊緣 AI 開發平台 — 技術設計文件(TDD) + +--- + +## 文件資訊 + +| 項目 | 內容 | +|------|------| +| 文件名稱 | 邊緣 AI 開發平台 TDD | +| 對應 PRD | PRD-Integrated.md v2.2 | +| 版本 | v1.2 | +| 日期 | 2026-02-24 | +| 狀態 | 更新中 | + +--- + +## 目錄 + +- [1. 技術架構總覽](#1-技術架構總覽) +- [2. 前端架構設計](#2-前端架構設計) +- [3. 後端架構設計(本地端 Backend Server)](#3-後端架構設計本地端-backend-server) +- [4. 前後端通訊設計](#4-前後端通訊設計) +- [5. 裝置通訊抽象層設計](#5-裝置通訊抽象層設計) +- [6. 模型庫設計](#6-模型庫設計) +- [7. 資料模型設計](#7-資料模型設計) +- [8. MVP 功能模組對照](#8-mvp-功能模組對照) +- [8.5 已實作額外功能技術對照](#85-已實作額外功能技術對照) +- [9. 開發環境與工具鏈](#9-開發環境與工具鏈) +- [10. 專案目錄結構](#10-專案目錄結構) +- [11. 部署與打包策略](#11-部署與打包策略) +- [12. 安全性考量](#12-安全性考量) +- [13. 效能目標](#13-效能目標) +- [14. 測試策略](#14-測試策略) +- [15. 開發階段規劃](#15-開發階段規劃) + +--- + +## 1. 技術架構總覽 + +### 1.1 整體架構圖 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ 使用者電腦 │ +│ │ +│ ┌─────────────────────────┐ HTTP/WS ┌───────────────────────────┐ │ +│ │ 前端 Web App │◄═══════════►│ 本地端 Backend Server │ │ +│ │ (瀏覽器) │ :3721 │ (Go 單一執行檔) │ │ +│ │ │ │ │ │ +│ │ Next.js (React) │ │ ┌──────────────────────┐ │ │ +│ │ + shadcn/ui │ │ │ HTTP REST API │ │ │ +│ │ + Tailwind CSS │ │ │ WebSocket Server │ │ │ +│ │ + Zustand │ │ │ Static File Server │ │ │ +│ │ │ │ └──────────┬───────────┘ │ │ +│ │ Features: │ │ │ │ │ +│ │ ├── 模型庫瀏覽/篩選 │ │ ┌──────────┴───────────┐ │ │ +│ │ ├── 裝置狀態面板 │ │ │ Device Manager │ │ │ +│ │ ├── 燒錄進度 UI │ │ │ ├── USB 監聽 │ │ │ +│ │ ├── Camera 即時預覽 │ │ │ ├── Driver Registry │ │ │ +│ │ ├── 推論結果疊加顯示 │ │ │ └── Session Manager │ │ │ +│ │ └── Canvas 邊界框繪製 │ │ ├────────────────────────┤ │ │ +│ └─────────────────────────┘ │ │ Camera Manager │ │ │ +│ │ │ ├── Webcam 擷取 │ │ │ +│ │ │ ├── Frame → Device │ │ │ +│ │ │ └── MJPEG/WS 串流 │ │ │ +│ │ └──────────┬───────────┘ │ │ +│ │ │ USB │ │ +│ ┌──────────────┐ │ ▼ │ │ +│ │ USB Webcam │─── Video ──────────────►│ ┌──────────────────────┐ │ │ +│ │ (UVC) │ │ │ Device Drivers │ │ │ +│ └──────────────┘ │ │ ├── Kneron KL720 │ │ │ +│ │ │ ├── Kneron KL730 │ │ │ +│ │ │ └── (擴充) │ │ │ +│ │ └──────────┬───────────┘ │ │ +│ └─────────────┼─────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────┐ │ +│ │ Edge Device │ │ +│ │ (Kneron Dongle) │ │ +│ └──────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + │ HTTPS(選用,Post-MVP) + ▼ +┌──────────────────┐ +│ 雲端服務(選用) │ +│ ● 模型儲存庫 │ +│ ● 使用者帳號 │ +└──────────────────┘ +``` + +### 1.2 技術選型總覽 + +| 層面 | 技術選型 | 版本 | 選型理由 | +|------|---------|------|---------| +| **前端框架** | Next.js (React) | 15.x | SSG 輸出、生態系成熟、元件庫豐富 | +| **UI 框架** | shadcn/ui + Tailwind CSS | latest | 可客製化、不鎖定、輕量 | +| **狀態管理** | Zustand | 5.x | 輕量、適合即時資料流、無 boilerplate | +| **後端語言** | Go | 1.22+ | 跨平台編譯、單一 binary、串列埠函式庫成熟 | +| **HTTP 框架** | Gin 或 Echo | latest | 高效能、middleware 豐富 | +| **WebSocket** | gorilla/websocket | latest | Go 生態最成熟的 WS 實作 | +| **串列埠** | go.bug.st/serial | latest | 跨平台、純 Go 實作 | +| **USB 偵測** | gousb / 平台原生 | latest | USB 裝置偵測與熱插拔 | +| **Kneron SDK** | Kneron PLUS SDK (C API) | 1.3.x | 透過 CGo 呼叫或 subprocess | +| **打包工具** | GoReleaser | latest | 跨平台 binary 打包與發佈 | +| **Camera 擷取** | OpenCV (gocv) / ffmpeg | latest | 後端擷取 webcam 影像,送至裝置推論 | + +### 1.3 設計原則 + +1. **本地優先 (Local-first)**:所有核心功能在本地端完成,不依賴雲端 +2. **Driver 抽象化**:從第一天就設計裝置抽象層,確保未來硬體擴展不需重構 +3. **前後端分離**:前端透過 REST + WebSocket 與後端通訊,後端同時提供靜態檔案服務 +4. **零依賴安裝**:使用者只需下載一個執行檔,前端 build 後嵌入 binary +5. **漸進式複雜度**:UI 預設簡潔,進階功能可展開 +6. **Camera-in-the-loop**:Camera 影像擷取由後端負責,送至 AI 硬體推論後將結果與畫面同步回傳前端 + +--- + +## 2. 前端架構設計 + +### 2.1 技術棧 + +``` +Next.js 15.x (App Router) +├── React 19 +├── TypeScript 5.x +├── Tailwind CSS 4.x +├── shadcn/ui (元件庫) +├── Zustand 5.x (狀態管理) +├── Recharts (圖表) +├── Lucide Icons (圖示) +└── next export (靜態輸出,嵌入 Go binary) +``` + +### 2.2 為什麼選 Next.js + React + +| 考量面向 | Next.js (React) | Nuxt (Vue) | SvelteKit | 評估 | +|---------|:-:|:-:|:-:|------| +| 元件生態系 | ★★★★★ | ★★★★ | ★★★ | React 的 Dashboard/資料視覺化元件最豐富 | +| 靜態輸出 | ★★★★★ | ★★★★ | ★★★★ | `next export` 成熟,可直接嵌入 Go binary | +| WebSocket 整合 | ★★★★★ | ★★★★ | ★★★★ | 搭配 Zustand 管理即時資料流 | +| Canvas/繪圖 | ★★★★★ | ★★★★ | ★★★ | 邊界框繪製、影像疊加等需求,React Canvas 生態最成熟 | +| 團隊熟悉度 | 考量中 | 考量中 | 考量中 | 需確認 | +| 長期維護性 | ★★★★★ | ★★★★ | ★★★ | React 社群最大,招募最容易 | + +### 2.3 頁面結構 + +``` +/ (首頁 / Dashboard) +├── /models ← Step 1:模型庫 +│ ├── /models?type=... ← 篩選結果 +│ └── /models/[id] ← 模型詳情 +├── /devices ← Step 2:裝置管理 +│ └── /devices/[id] ← 裝置詳情/燒錄 +├── /workspace ← Step 3:即時推論工作區 +│ └── /workspace/[deviceId] ← 單一裝置推論介面 +└── /settings ← 設定 +``` + +### 2.4 元件架構 + +``` +src/ +├── app/ # Next.js App Router +│ ├── layout.tsx # 全域 Layout (側邊欄 + 頂部列) +│ ├── page.tsx # Dashboard / 首頁 +│ ├── models/ +│ │ ├── page.tsx # 模型庫列表 +│ │ └── [id]/page.tsx # 模型詳情 +│ ├── devices/ +│ │ ├── page.tsx # 裝置列表 +│ │ └── [id]/page.tsx # 裝置詳情 + 燒錄 +│ └── workspace/ +│ └── [deviceId]/page.tsx # 即時推論工作區 +│ +├── components/ +│ ├── ui/ # shadcn/ui 基礎元件 +│ │ ├── button.tsx +│ │ ├── card.tsx +│ │ ├── dialog.tsx +│ │ ├── progress.tsx +│ │ ├── slider.tsx +│ │ └── ... +│ │ +│ ├── layout/ # 版面元件 +│ │ ├── sidebar.tsx # 側邊導覽列 +│ │ ├── header.tsx # 頂部列 (裝置狀態指示) +│ │ └── connection-status.tsx # Server 連線狀態 +│ │ +│ ├── models/ # 模型庫相關元件 +│ │ ├── model-card.tsx # 模型卡片 +│ │ ├── model-grid.tsx # 模型卡片網格 +│ │ ├── model-filters.tsx # 篩選面板 +│ │ ├── model-detail.tsx # 模型詳情 +│ │ └── model-compatibility.tsx # 硬體相容性徽章 +│ │ +│ ├── devices/ # 裝置相關元件 +│ │ ├── device-list.tsx # 裝置列表 +│ │ ├── device-card.tsx # 裝置卡片 (狀態、類型) +│ │ ├── device-status.tsx # 裝置即時狀態 +│ │ ├── flash-dialog.tsx # 燒錄確認對話框 +│ │ └── flash-progress.tsx # 燒錄進度條 +│ │ +│ ├── inference/ # 推論結果相關元件 +│ │ ├── inference-panel.tsx # 推論主面板 +│ │ ├── classification-result.tsx # 分類結果 (長條圖) +│ │ ├── detection-canvas.tsx # 物件偵測 Canvas (邊界框) +│ │ ├── performance-metrics.tsx # FPS / 延遲指標 +│ │ ├── confidence-slider.tsx # Confidence Threshold 滑桿 +│ │ └── device-log.tsx # 裝置日誌 (展開/收合) +│ │ +│ └── camera/ # Camera 相關元件 +│ ├── camera-feed.tsx # Camera 即時影像顯示 (MJPEG/WS) +│ ├── camera-overlay.tsx # 推論結果疊加層 (Canvas: 邊界框+標籤) +│ ├── camera-controls.tsx # Camera 控制面板 (選擇/解析度/開關) +│ ├── camera-selector.tsx # Camera 裝置選擇下拉選單 +│ └── camera-inference-view.tsx # Camera + 推論整合檢視 (主元件) +│ +│ ├── dashboard/ # 儀表板元件 +│ │ ├── activity-timeline.tsx # 活動時間線 +│ │ └── connected-devices-list.tsx # 已連線裝置列表 +│ │ +│ ├── theme-sync.tsx # 主題同步(Zustand → class) +│ └── lang-sync.tsx # 語言同步(Zustand → lang) +│ +├── stores/ # Zustand 狀態管理 +│ ├── device-store.ts # 裝置狀態 (列表、連線、狀態) +│ ├── model-store.ts # 模型庫狀態 (列表、篩選、選取) +│ ├── inference-store.ts # 推論結果狀態 (即時資料) +│ ├── camera-store.ts # Camera 狀態 (選擇、解析度、串流狀態) +│ └── flash-store.ts # 燒錄狀態 (進度、錯誤) +│ ├── settings-store.ts # 設定狀態 (主題、語言、persist) +│ ├── activity-store.ts # 活動記錄 (時間線事件) +│ └── device-preferences-store.ts # 裝置偏好 (alias、notes、persist) +│ +├── hooks/ # 自訂 Hooks +│ ├── use-websocket.ts # WebSocket 連線管理 +│ ├── use-device-events.ts # 裝置插拔事件 +│ ├── use-inference-stream.ts # 推論結果串流 +│ ├── use-camera-stream.ts # Camera 影像串流訂閱 +│ └── use-flash-progress.ts # 燒錄進度訂閱 +│ +├── lib/ # 工具函式 +│ ├── api.ts # REST API client +│ ├── i18n/ # 多語系模組 +│ │ ├── types.ts # TranslationDict 介面 + Paths 型別 +│ │ ├── en.ts # 英文翻譯字典 +│ │ ├── zh-TW.ts # 繁體中文翻譯字典 +│ │ └── index.ts # useTranslation / getTranslation +│ ├── toast.ts # Toast 通知工具 +│ ├── ws.ts # WebSocket client +│ └── constants.ts # 常數定義 +│ +└── types/ # TypeScript 型別定義 + ├── device.ts # 裝置相關型別 + ├── model.ts # 模型相關型別 + ├── inference.ts # 推論結果型別 + └── api.ts # API 回應型別 +``` + +### 2.5 狀態管理設計 (Zustand) + +```typescript +// stores/device-store.ts +interface DeviceState { + devices: Device[]; + selectedDevice: Device | null; + connectionStatus: 'connected' | 'disconnected' | 'connecting'; + + // Actions + setDevices: (devices: Device[]) => void; + selectDevice: (id: string) => void; + updateDeviceStatus: (id: string, status: DeviceStatus) => void; +} + +// stores/inference-store.ts +interface InferenceState { + isRunning: boolean; + results: InferenceResult[]; // 最近 N 筆結果 + latestResult: InferenceResult | null; + fps: number; + latency: number; + confidenceThreshold: number; + + // Actions + addResult: (result: InferenceResult) => void; + setConfidenceThreshold: (value: number) => void; + clearResults: () => void; + startInference: () => void; + stopInference: () => void; +} + +// stores/flash-store.ts +interface FlashState { + isFlashing: boolean; + progress: number; // 0-100 + stage: 'idle' | 'preparing' | 'transferring' | 'verifying' | 'rebooting' | 'done' | 'error'; + error: string | null; + + // Actions + startFlash: (deviceId: string, modelId: string) => void; + updateProgress: (progress: number, stage: string) => void; + setError: (error: string) => void; + reset: () => void; +} +``` + +### 2.6 Camera 狀態管理與元件設計 + +#### Camera Store + +```typescript +// stores/camera-store.ts +interface CameraState { + cameras: CameraDevice[]; // 可用的 Camera 列表 + selectedCameraId: string | null; // 目前選擇的 Camera + isStreaming: boolean; // 是否正在串流 + resolution: { width: number; height: number }; + streamUrl: string | null; // MJPEG 串流 URL + + // Actions + setCameras: (cameras: CameraDevice[]) => void; + selectCamera: (id: string) => void; + setResolution: (width: number, height: number) => void; + setStreaming: (streaming: boolean) => void; + setStreamUrl: (url: string | null) => void; +} + +interface CameraDevice { + id: string; + name: string; // e.g., "FaceTime HD Camera", "USB Camera" + index: number; // OpenCV camera index + resolutions: { width: number; height: number }[]; // 支援的解析度 +} +``` + +#### Camera 推論整合檢視元件 + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Camera Inference View │ +│ ┌────────────────────────────────┐ ┌─────────────────────┐│ +│ │ │ │ 推論結果面板 ││ +│ │ Camera Feed │ │ ││ +│ │ (MJPEG 串流) │ │ 🏷 person 92% ││ +│ │ │ │ 🏷 car 85% ││ +│ │ ┌─────────┐ │ │ 🏷 dog 23% ││ +│ │ │ person │ ← 邊界框疊加 │ │ ││ +│ │ │ 92% │ (Canvas Layer) │ │ ─────────────────── ││ +│ │ └─────────┘ │ │ FPS: 24.3 ││ +│ │ │ │ Latency: 38ms ││ +│ │ │ │ Model: YOLOv5-face ││ +│ └────────────────────────────────┘ │ ││ +│ ┌────────────────────────────────┐ │ Threshold: ──●──── ││ +│ │ Camera: [FaceTime HD ▼] │ │ 0.65 ││ +│ │ Resolution: [640x480 ▼] │ │ ││ +│ │ [▶ Start] [⏹ Stop] │ │ [📋 Log ▼] ││ +│ └────────────────────────────────┘ └─────────────────────┘│ +└──────────────────────────────────────────────────────────────┘ +``` + +#### Camera Feed 元件設計 + +```typescript +// components/camera/camera-feed.tsx +// 顯示後端 MJPEG 串流的影像 + +interface CameraFeedProps { + streamUrl: string; // MJPEG stream URL from backend + onFrameSize?: (w: number, h: number) => void; +} + +// 實作方式: tag 直接載入 MJPEG stream URL +// MJPEG 是 motion JPEG,瀏覽器原生支援,無需特殊解碼 +// + + +// components/camera/camera-overlay.tsx +// 在 Camera Feed 上方疊加推論結果的 Canvas 層 + +interface CameraOverlayProps { + width: number; + height: number; + detections: DetectionResult[]; // 邊界框 + 標籤 + classifications: ClassResult[]; // 分類結果 + confidenceThreshold: number; +} + +// 使用 Canvas 2D API 繪製: +// - 邊界框 (不同顏色對應不同類別) +// - 類別標籤 + 信心分數 +// - 僅顯示 confidence > threshold 的結果 +``` + +### 2.7 WebSocket Hook 設計 + +```typescript +// hooks/use-websocket.ts +function useWebSocket(url: string, options?: { + onMessage?: (data: any) => void; + onConnect?: () => void; + onDisconnect?: () => void; + reconnect?: boolean; + reconnectInterval?: number; +}) { + // 自動連線、斷線重連、心跳檢測 + // 回傳: { send, disconnect, status } +} + +// hooks/use-inference-stream.ts +function useInferenceStream(deviceId: string) { + const ws = useWebSocket(`ws://localhost:3721/ws/devices/${deviceId}/inference`, { + onMessage: (data) => { + useInferenceStore.getState().addResult(data); + } + }); + return ws; +} + +// hooks/use-camera-stream.ts +function useCameraStream(cameraId: string) { + const { setStreamUrl, setStreaming } = useCameraStore(); + + const start = async (deviceId: string) => { + // POST /api/camera/start → 啟動後端 camera 擷取 + 推論 pipeline + // 取得 MJPEG stream URL + const res = await api.post(`/api/camera/start`, { cameraId, deviceId }); + setStreamUrl(res.data.streamUrl); + setStreaming(true); + }; + + const stop = async () => { + await api.post(`/api/camera/stop`); + setStreamUrl(null); + setStreaming(false); + }; + + return { start, stop }; +} +``` + +### 2.7 靜態輸出策略 + +前端使用 `next export` 產生純靜態檔案(HTML/CSS/JS),嵌入 Go binary: + +``` +next build → out/ 目錄 → Go embed → 單一執行檔 +``` + +Go 端使用 `embed.FS` 提供靜態檔案服務: + +```go +//go:embed frontend/out/* +var frontendFS embed.FS + +func main() { + // 將前端靜態檔案掛載到 HTTP server + http.Handle("/", http.FileServer(http.FS(frontendFS))) +} +``` + +--- + +## 3. 後端架構設計(本地端 Backend Server) + +### 3.1 模組架構 + +``` +server/ +├── main.go # 入口點 +│ +├── cmd/ # CLI 命令 +│ └── root.go # 啟動 server、版本資訊 +│ +├── internal/ +│ ├── api/ # HTTP API 層 +│ │ ├── router.go # 路由定義 +│ │ ├── middleware/ # 中間件 (CORS, logging) +│ │ ├── handlers/ +│ │ │ ├── device_handler.go # /api/devices 端點 +│ │ │ ├── model_handler.go # /api/models 端點 +│ │ │ └── system_handler.go # /api/system 端點 +│ │ └── ws/ +│ │ ├── hub.go # WebSocket 連線管理 +│ │ ├── inference_ws.go # 推論結果 WS 端點 +│ │ ├── flash_ws.go # 燒錄進度 WS 端點 +│ │ └── device_events_ws.go # 裝置事件 WS 端點 +│ │ +│ ├── device/ # 裝置管理核心 +│ │ ├── manager.go # Device Manager (偵測、生命週期) +│ │ ├── registry.go # Driver Registry +│ │ ├── session.go # 裝置 Session 管理 +│ │ └── types.go # 裝置相關型別 +│ │ +│ ├── driver/ # 裝置驅動層 +│ │ ├── interface.go # DeviceDriver 介面定義 +│ │ ├── kneron/ +│ │ │ ├── kl720_driver.go # KL720 Dongle 驅動 (MVP) +│ │ │ ├── kl730_driver.go # KL730 / KNEO Pi 驅動 (Phase 1) +│ │ │ └── kneron_sdk.go # Kneron PLUS SDK 封裝 +│ │ └── mock/ +│ │ └── mock_driver.go # 模擬驅動 (開發/測試用) +│ │ +│ ├── model/ # 模型管理 +│ │ ├── repository.go # 模型中繼資料儲存 +│ │ ├── downloader.go # 模型檔案下載 +│ │ └── types.go # 模型相關型別 +│ │ +│ ├── flash/ # 燒錄服務 +│ │ ├── service.go # 燒錄流程控制 +│ │ └── progress.go # 進度追蹤與回報 +│ │ +│ ├── inference/ # 推論服務 +│ │ ├── service.go # 推論流程控制 +│ │ ├── parser.go # 推論結果解析 +│ │ └── stream.go # 結果串流管理 +│ │ +│ ├── camera/ # Camera 管理 +│ │ ├── manager.go # Camera Manager (偵測、開啟、串流) +│ │ ├── capture.go # 影像擷取 (OpenCV/ffmpeg) +│ │ ├── mjpeg.go # MJPEG 串流 HTTP handler +│ │ └── pipeline.go # Camera → Device 推論 Pipeline +│ │ +│ └── config/ # 設定管理 +│ └── config.go # 設定檔讀取、預設值 +│ +├── pkg/ # 可重用套件 +│ ├── serial/ # 串列埠工具 +│ │ └── serial.go # go.bug.st/serial 封裝 +│ ├── usb/ # USB 工具 +│ │ └── monitor.go # USB 裝置熱插拔監聽 +│ └── logger/ # 日誌工具 +│ └── logger.go +│ +└── frontend/ # 嵌入的前端靜態檔案 + └── out/ # next export 輸出 +``` + +### 3.2 核心模組設計 + +#### 3.2.1 Device Manager + +```go +// internal/device/manager.go + +type DeviceManager struct { + registry *DriverRegistry + sessions map[string]*DeviceSession + usbMonitor *usb.Monitor + eventBus chan DeviceEvent + mu sync.RWMutex +} + +// 核心方法 +func (m *DeviceManager) Start() error // 啟動 USB 監聽 +func (m *DeviceManager) Stop() // 停止 +func (m *DeviceManager) ListDevices() []DeviceInfo // 列出裝置 +func (m *DeviceManager) GetDevice(id string) (*DeviceInfo, error) +func (m *DeviceManager) Connect(id string) error // 連線 +func (m *DeviceManager) Disconnect(id string) error // 斷線 +func (m *DeviceManager) Flash(id string, modelPath string, progressCh chan<- FlashProgress) error +func (m *DeviceManager) StartInference(id string, resultCh chan<- InferenceResult) error +func (m *DeviceManager) StopInference(id string) error +func (m *DeviceManager) Events() <-chan DeviceEvent // 裝置事件串流 +``` + +#### 3.2.2 Driver Registry + +```go +// internal/device/registry.go + +type DriverRegistry struct { + drivers []DriverFactory +} + +type DriverFactory struct { + Name string + Match func(usbInfo USBDeviceInfo) bool // VID/PID 比對 + Create func(usbInfo USBDeviceInfo) (DeviceDriver, error) +} + +func (r *DriverRegistry) Register(factory DriverFactory) +func (r *DriverRegistry) FindDriver(usbInfo USBDeviceInfo) (DeviceDriver, error) +``` + +#### 3.2.3 WebSocket Hub + +```go +// internal/api/ws/hub.go + +type Hub struct { + rooms map[string]map[*Client]bool // roomId → clients + register chan *Subscription + unregister chan *Subscription + broadcast chan *Message +} + +type Subscription struct { + Client *Client + Room string // e.g., "inference:device-123", "flash:device-123" +} + +// 前端訂閱特定裝置的推論結果或燒錄進度 +// Hub 負責將 DeviceManager 的事件分發到對應的 WebSocket 客戶端 +``` + +### 3.3 Kneron SDK 整合策略 + +由於 Kneron PLUS SDK 是 C/C++ API,有兩種整合方式: + +| 方式 | 優點 | 缺點 | 建議 | +|------|------|------|------| +| **CGo 直接呼叫** | 效能好、整合緊密 | 交叉編譯複雜、需 C 編譯環境 | Phase 1+ | +| **Python subprocess** | 開發快、隔離好 | 需安裝 Python、效能略差 | MVP 階段 | + +**MVP 建議方案:** 使用 Python subprocess 呼叫 Kneron PLUS Python SDK,透過 JSON 通訊。後續再以 CGo 替換。 + +```go +// internal/driver/kneron/kneron_sdk.go + +type KneronSDK struct { + pythonPath string + scriptDir string +} + +func (sdk *KneronSDK) LoadModel(deviceIndex int, nefPath string) error { + // 呼叫 Python script: kp_load_model_from_file() + cmd := exec.Command(sdk.pythonPath, filepath.Join(sdk.scriptDir, "load_model.py"), + "--device-index", strconv.Itoa(deviceIndex), + "--nef-path", nefPath, + ) + // 解析 JSON 輸出 +} + +func (sdk *KneronSDK) StartInference(deviceIndex int, resultCh chan<- InferenceResult) error { + // 啟動長時間運行的 Python 推論 process + // 透過 stdout JSON lines 串流結果 +} +``` + +### 3.4 Mock Driver(開發/測試用) + +為了在沒有實體硬體的情況下開發和測試前後端整合: + +```go +// internal/driver/mock/mock_driver.go + +type MockDriver struct { + deviceType string + connected bool +} + +func (d *MockDriver) Detect() bool { return true } + +func (d *MockDriver) Flash(binary []byte, progressCh chan<- FlashProgress) error { + // 模擬燒錄進度:每 200ms 增加 5% + for i := 0; i <= 100; i += 5 { + progressCh <- FlashProgress{Percent: i, Stage: "transferring"} + time.Sleep(200 * time.Millisecond) + } + return nil +} + +func (d *MockDriver) ReadInference() (*InferenceResult, error) { + // 回傳模擬的推論結果 + return &InferenceResult{ + Type: "classification", + Results: []ClassResult{ + {Label: "person", Confidence: 0.92}, + {Label: "car", Confidence: 0.05}, + }, + LatencyMs: 28, + }, nil +} +``` + +### 3.5 Camera Manager 設計 + +KL720 Dongle 本身沒有攝影機,推論流程是:**電腦端 Webcam 擷取影像 → 透過 USB 送到 KL720 NPU 推論 → 推論結果回傳 → 前端顯示影像 + 疊加邊界框**。 + +Camera 相關的影像擷取由 **後端 Go Server** 負責(而非前端瀏覽器),原因: + +| 方案 | 優點 | 缺點 | 決策 | +|------|------|------|------| +| **後端擷取 (Go/OpenCV)** | 直接將 frame 送到裝置推論,無需前端→後端傳輸影像;可背景執行 | 需 OpenCV 或 ffmpeg 依賴 | **採用** | +| 前端擷取 (getUserMedia) | 不需後端處理影像 | 影像需從瀏覽器→後端→裝置,延遲高;關頁面斷流 | 不採用 | + +#### Camera-Inference Pipeline 架構 + +``` +┌──────────┐ USB/UVC ┌─────────────────────────────────────────────────┐ +│ Webcam │───────────────►│ Go Backend Server │ +│ (UVC) │ frame │ │ +└──────────┘ │ ┌─────────────┐ frame ┌─────────────────┐ │ + │ │ Camera │──────────►│ Inference │ │ + │ │ Manager │ │ Pipeline │ │ + │ │ │ │ │ │ + │ │ OpenCV / │ result │ frame → resize │ │ + │ │ gocv │◄──────────│ → send to USB │ │ + │ │ │ │ → read result │ │ + │ └──────┬──────┘ └────────┬────────┘ │ + │ │ │ │ + │ MJPEG stream WS push │ + │ (HTTP /api/camera/stream) (inference) │ + │ │ │ │ + └─────────┼───────────────────────────┼──────────┘ + │ │ + ▼ ▼ + ┌─────────────────────────────────────────────────┐ + │ Browser (React) │ + │ │ + │ ┌──────────────────┐ ┌──────────────────────┐│ + │ │ MJPEG │ │ overlay ││ + │ │ Camera 即時影像 │ │ 邊界框 + 標籤 ││ + │ │ │ │ (InferenceResult) ││ + │ └──────────────────┘ └──────────────────────┘│ + └─────────────────────────────────────────────────┘ +``` + +#### Camera Manager 實作 + +```go +// internal/camera/manager.go + +type CameraManager struct { + cameras []CameraInfo + capture *gocv.VideoCapture // OpenCV camera capture + streaming bool + frameCh chan gocv.Mat // 影像 frame channel + mu sync.Mutex +} + +type CameraInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Index int `json:"index"` // OpenCV camera index (0, 1, 2...) + Width int `json:"width"` + Height int `json:"height"` +} + +// 列出可用 Camera +func (m *CameraManager) ListCameras() ([]CameraInfo, error) { + // 嘗試開啟 index 0, 1, 2... 偵測可用 camera +} + +// 開啟 Camera +func (m *CameraManager) Open(index int, width, height int) error { + m.capture = gocv.OpenVideoCapture(index) + m.capture.Set(gocv.VideoCaptureFrameWidth, float64(width)) + m.capture.Set(gocv.VideoCaptureFrameHeight, float64(height)) +} + +// 關閉 Camera +func (m *CameraManager) Close() error + +// 讀取一幀 +func (m *CameraManager) ReadFrame() (gocv.Mat, error) +``` + +#### Camera-Inference Pipeline + +```go +// internal/camera/pipeline.go + +type InferencePipeline struct { + camera *CameraManager + device driver.DeviceDriver + resultCh chan *driver.InferenceResult + frameCh chan []byte // JPEG encoded frames for MJPEG + running bool + mu sync.Mutex +} + +// 核心推論迴圈 +func (p *InferencePipeline) Run(ctx context.Context) error { + for { + select { + case <-ctx.Done(): + return nil + default: + // 1. 從 Camera 擷取一幀 + frame, err := p.camera.ReadFrame() + + // 2. 編碼為 JPEG → 送到 MJPEG 串流 (給前端顯示) + jpegBuf := encodeJPEG(frame) + p.frameCh <- jpegBuf + + // 3. 前處理 (resize 到模型輸入大小) + resized := preprocess(frame, modelInputWidth, modelInputHeight) + + // 4. 送到裝置推論 (透過 Kneron PLUS SDK) + result, err := p.device.RunInference(resized) + + // 5. 推論結果送到 WebSocket (給前端疊加邊界框) + p.resultCh <- result + } + } +} +``` + +#### MJPEG 串流 HTTP Handler + +```go +// internal/camera/mjpeg.go + +// MJPEG 是 motion JPEG 串流格式,瀏覽器 tag 原生支援 +// Content-Type: multipart/x-mixed-replace; boundary=frame +// +// 前端只需: + +func (h *MJPEGHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "multipart/x-mixed-replace; boundary=frame") + w.Header().Set("Cache-Control", "no-cache") + + for { + select { + case <-r.Context().Done(): + return + case jpegFrame := <-h.frameCh: + // 寫入 MJPEG boundary + JPEG data + fmt.Fprintf(w, "--frame\r\n") + fmt.Fprintf(w, "Content-Type: image/jpeg\r\n") + fmt.Fprintf(w, "Content-Length: %d\r\n\r\n", len(jpegFrame)) + w.Write(jpegFrame) + fmt.Fprintf(w, "\r\n") + w.(http.Flusher).Flush() + } + } +} +``` + +#### Driver 介面擴充 — Camera 推論 + +```go +// 在 DeviceDriver 介面中新增 Camera 推論方法 + +type DeviceDriver interface { + // ... 原有方法 ... + + // Camera 推論(送入影像 frame,回傳推論結果) + RunInference(imageData []byte) (*InferenceResult, error) + + // 取得模型所需的輸入尺寸 + GetInputSize() (width int, height int, error) +} +``` + +#### 影像擷取技術選型 + +| 方案 | 優點 | 缺點 | 評估 | +|------|------|------|------| +| **gocv (OpenCV Go binding)** | 功能完整、影像處理能力強、Camera 支援好 | 需安裝 OpenCV C 函式庫 (可靜態連結) | **MVP 推薦** | +| ffmpeg (透過 subprocess) | 不需 CGo、格式支援廣 | 啟動慢、控制粒度粗 | 備選 | +| v4l2 (Linux) / AVFoundation (macOS) | 零依賴、效能最佳 | 需各平台實作、開發量大 | 不建議 MVP | + +> **注意:** 使用 gocv 會引入 OpenCV 依賴,影響「單一執行檔零依賴」的目標。 +> 緩解策略:靜態連結 OpenCV 或提供帶 OpenCV 的安裝包,或 MVP 先使用 ffmpeg subprocess 方案。 + +--- + +## 4. 前後端通訊設計 + +### 4.1 REST API 規格 + +#### 裝置管理 API + +``` +GET /api/devices + 回應: { devices: Device[] } + 說明: 列出所有已偵測到的裝置 + +GET /api/devices/:id + 回應: { device: DeviceDetail } + 說明: 取得單一裝置詳細資訊 + +POST /api/devices/:id/connect + 回應: { success: boolean } + 說明: 建立與裝置的連線 + +POST /api/devices/:id/disconnect + 回應: { success: boolean } + 說明: 中斷裝置連線 + +POST /api/devices/:id/flash + 請求: { modelId: string } + 回應: { taskId: string } + 說明: 開始燒錄流程(進度透過 WebSocket 回報) + +GET /api/devices/:id/status + 回應: { status: DeviceStatus } + 說明: 取得裝置當前狀態 + +POST /api/devices/:id/inference/start + 回應: { success: boolean } + 說明: 啟動推論(結果透過 WebSocket 串流) + +POST /api/devices/:id/inference/stop + 回應: { success: boolean } + 說明: 停止推論 +``` + +#### 模型管理 API + +``` +GET /api/models + 查詢: ?type=classification&hardware=kl720&q=face + 回應: { models: ModelSummary[], total: number } + 說明: 列出模型,支援篩選與搜尋 + +GET /api/models/:id + 回應: { model: ModelDetail } + 說明: 取得模型詳細資訊 + +POST /api/models/:id/download + 回應: { taskId: string, status: "downloading" | "cached" } + 說明: 下載模型到本地快取 +``` + +#### 媒體管理 API + +``` +POST /api/media/upload/image + 請求: multipart/form-data (file + deviceId) + 回應: { success: boolean, filename: string } + 說明: 上傳圖片檔案作為推論輸入 + +POST /api/media/upload/video + 請求: multipart/form-data (file + deviceId) + 回應: { success: boolean, filename: string } + 說明: 上傳影片檔案作為推論輸入 + +POST /api/media/url + 請求: { url: string, deviceId: string } + 回應: { success: boolean, resolvedUrl?: string } + 說明: 提供影片 URL 作為推論輸入,支援直接連結、YouTube(yt-dlp 解析)、RTSP +``` + +#### 自訂模型上傳 API + +``` +POST /api/models/upload + 請求: multipart/form-data (file + metadata JSON) + 回應: { success: boolean, model: ModelSummary } + 說明: 上傳自訂 .nef 模型 + +DELETE /api/models/:id + 回應: { success: boolean } + 說明: 刪除自訂模型 +``` + +#### Camera API + +``` +GET /api/camera/list + 回應: { cameras: CameraDevice[] } + 說明: 列出所有可用的 Camera 裝置 + +POST /api/camera/start + 請求: { cameraId: string, deviceId: string, resolution?: { width, height } } + 回應: { streamUrl: string, inputSize: { width, height } } + 說明: 啟動 Camera 擷取 + 推論 Pipeline + 回傳 MJPEG 串流 URL 供前端 顯示 + +POST /api/camera/stop + 回應: { success: boolean } + 說明: 停止 Camera 擷取與推論 + +GET /api/camera/stream + 查詢: ?id=0 + 回應: multipart/x-mixed-replace (MJPEG 串流) + 說明: Camera 即時影像 MJPEG 串流端點 + 前端直接 即可顯示 +``` + +#### 系統 API + +``` +GET /api/system/info + 回應: { version: string, platform: string, uptime: number } + 說明: Server 系統資訊 + +GET /api/system/health + 回應: { status: "ok" } + 說明: 健康檢查 +``` + +### 4.2 WebSocket 端點 + +``` +ws://localhost:3721/ws/devices/events + 訊息格式: { event: "connected" | "disconnected" | "updated", device: Device } + 說明: 裝置插拔與狀態變更事件 + +ws://localhost:3721/ws/devices/:id/inference + 訊息格式: InferenceResult (JSON) + 說明: 即時推論結果串流 + +ws://localhost:3721/ws/devices/:id/flash-progress + 訊息格式: { percent: number, stage: string, message?: string } + 說明: 燒錄進度即時更新 + +ws://localhost:3721/ws/devices/:id/log + 訊息格式: { timestamp: string, level: string, message: string } + 說明: 裝置通訊日誌 +``` + +### 4.3 資料流圖 + +#### Camera 推論資料流 (MVP 核心流程) + +``` +Webcam Go Server Browser (React) + │ │ │ + │ UVC frame │ │ + ├────────────────────►│ │ + │ (video capture) │ │ + │ │ ┌──────────────────────┐ │ + │ │ │ Camera-Inference │ │ + │ │ │ Pipeline │ │ + │ │ │ │ │ + │ │ │ 1. capture frame │ │ + │ │ │ 2. encode JPEG ──────┼──── MJPEG stream ───────►│ + │ │ │ │ (HTTP multipart) │ 顯示 + │ │ │ 3. resize frame │ │ Camera 畫面 + │ │ │ 4. send to device ───┼──► KL720 Dongle │ + │ │ │ │ (USB bulk transfer) │ + │ │ │ 5. read result ◄─────┼─── NPU inference │ + │ │ │ 6. push result ──────┼──── WebSocket ──────────►│ + │ │ │ │ { detections, fps } │ Canvas overlay + │ │ └──────────────────────┘ │ 邊界框繪製 + │ │ │ + │ │ ┌───────────────┤ + │ │ │ 疊加結果到 │ + │ │ │ Camera 畫面上 │ + │ │ └───────────────┘ +``` + +#### 裝置端直接推論資料流 (無 Camera) + +``` +Device (MCU) Go Server Browser (React) + │ │ │ + │ Serial/USB data │ │ + ├─────────────────────────────►│ │ + │ (raw inference output) │ │ + │ │ parse & structure │ + │ ├──────┐ │ + │ │ │ InferenceResult │ + │ │◄─────┘ │ + │ │ │ + │ │ WebSocket push │ + │ ├───────────────────────────────►│ + │ │ { type, results, latency } │ + │ │ │ Zustand store + │ │ ├──────┐ + │ │ │ │ update + │ │ │◄─────┘ + │ │ │ + │ │ │ React re-render + │ │ ├──────┐ + │ │ │ │ UI update + │ │ │◄─────┘ +``` + +### 4.4 通用 API 回應格式 + +```typescript +// 成功 +{ + "success": true, + "data": { ... } +} + +// 錯誤 +{ + "success": false, + "error": { + "code": "DEVICE_NOT_FOUND", + "message": "裝置未找到", + "detail": "未偵測到 ID 為 'abc123' 的裝置,請確認 USB 連接。" + } +} +``` + +### 4.5 錯誤代碼 + +| 代碼 | HTTP | 說明 | +|------|------|------| +| `DEVICE_NOT_FOUND` | 404 | 裝置未找到 | +| `DEVICE_NOT_CONNECTED` | 409 | 裝置未連線 | +| `DEVICE_BUSY` | 409 | 裝置正在燒錄或推論中 | +| `MODEL_NOT_FOUND` | 404 | 模型未找到 | +| `MODEL_INCOMPATIBLE` | 400 | 模型與裝置不相容 | +| `FLASH_FAILED` | 500 | 燒錄失敗 | +| `FLASH_TIMEOUT` | 408 | 燒錄逾時 | +| `INFERENCE_ERROR` | 500 | 推論錯誤 | +| `SERIAL_ERROR` | 500 | 串列埠通訊錯誤 | +| `CAMERA_NOT_FOUND` | 404 | Camera 裝置未找到 | +| `CAMERA_BUSY` | 409 | Camera 已被其他 pipeline 使用 | +| `CAMERA_OPEN_FAILED` | 500 | Camera 開啟失敗 | +| `PIPELINE_ALREADY_RUNNING` | 409 | 推論 pipeline 已在執行中 | + +--- + +## 5. 裝置通訊抽象層設計 + +### 5.1 Driver 介面 + +```go +// internal/driver/interface.go + +package driver + +// DeviceDriver 定義所有裝置驅動必須實作的介面 +type DeviceDriver interface { + // 基本資訊 + Info() DeviceInfo + + // 連線管理 + Connect() error + Disconnect() error + IsConnected() bool + + // 燒錄 + Flash(modelPath string, progressCh chan<- FlashProgress) error + + // 推論 + StartInference() error + StopInference() error + ReadInference() (*InferenceResult, error) + + // 命令 + SendCommand(cmd string) (string, error) + + // 系統 + GetSystemInfo() (*SystemInfo, error) + GetModelInfo() (*ModelInfo, error) +} + +// DeviceInfo 裝置基本資訊 +type DeviceInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Type string `json:"type"` // "kneron_kl720", "kneron_kl730", ... + Port string `json:"port"` // 串列埠路徑 or USB device path + VendorID uint16 `json:"vendorId"` + ProductID uint16 `json:"productId"` + Status DeviceStatus `json:"status"` + FirmwareVer string `json:"firmwareVersion,omitempty"` +} + +type DeviceStatus string + +const ( + StatusDetected DeviceStatus = "detected" // 已偵測,未連線 + StatusConnecting DeviceStatus = "connecting" // 連線中 + StatusConnected DeviceStatus = "connected" // 已連線 + StatusFlashing DeviceStatus = "flashing" // 燒錄中 + StatusInferencing DeviceStatus = "inferencing" // 推論中 + StatusError DeviceStatus = "error" // 錯誤 + StatusDisconnected DeviceStatus = "disconnected" // 已斷線 +) + +// FlashProgress 燒錄進度 +type FlashProgress struct { + Percent int `json:"percent"` // 0-100 + Stage string `json:"stage"` // preparing, transferring, verifying, rebooting, done + Message string `json:"message,omitempty"` +} + +// InferenceResult 推論結果 +type InferenceResult struct { + Type string `json:"type"` // "classification" | "detection" + Timestamp int64 `json:"timestamp"` // Unix ms + LatencyMs float64 `json:"latencyMs"` + + // 分類結果 + Classifications []ClassResult `json:"classifications,omitempty"` + + // 偵測結果 + Detections []DetectionResult `json:"detections,omitempty"` +} + +type ClassResult struct { + Label string `json:"label"` + Confidence float64 `json:"confidence"` // 0.0 - 1.0 +} + +type DetectionResult struct { + Label string `json:"label"` + Confidence float64 `json:"confidence"` + BBox BBox `json:"bbox"` +} + +type BBox struct { + X float64 `json:"x"` // 左上角 X (0.0-1.0, 歸一化) + Y float64 `json:"y"` // 左上角 Y + Width float64 `json:"width"` // 寬 + Height float64 `json:"height"` // 高 +} +``` + +### 5.2 Kneron KL720 Driver 實作策略 + +```go +// internal/driver/kneron/kl720_driver.go + +type KL720Driver struct { + info DeviceInfo + sdk *KneronSDK // Kneron PLUS SDK 封裝 + connected bool + inferring bool + mu sync.Mutex +} + +// USB 裝置識別 +var KL720USBFilter = USBFilter{ + VendorID: 0x3231, // Kneron VID (需確認實際值) + ProductID: 0x0200, // KL720 PID (需確認實際值) +} + +func (d *KL720Driver) Connect() error { + d.mu.Lock() + defer d.mu.Unlock() + + // 1. 透過 Kneron PLUS SDK 初始化裝置 + // 2. 取得裝置資訊 (kp_get_system_info) + // 3. 設定連線狀態 + return d.sdk.Connect(d.info.Port) +} + +func (d *KL720Driver) Flash(modelPath string, progressCh chan<- FlashProgress) error { + // 1. 驗證 NEF 檔案 + // 2. 進入 bootloader / DFU 模式 + // 3. 透過 kp_load_model_from_file() 上傳 NEF + // 4. 回報進度 + // 5. 驗證 CRC + // 6. 重啟裝置 +} + +func (d *KL720Driver) ReadInference() (*InferenceResult, error) { + // 透過 Kneron PLUS SDK 讀取推論結果 + // 解析 Kneron 格式 → 統一 InferenceResult +} +``` + +### 5.3 Driver 註冊流程 + +```go +// main.go or cmd/root.go + +func setupDrivers(registry *device.DriverRegistry) { + // 註冊 Kneron KL720 Driver + registry.Register(device.DriverFactory{ + Name: "kneron_kl720", + Match: func(info USBDeviceInfo) bool { + return info.VendorID == 0x3231 && info.ProductID == 0x0200 + }, + Create: func(info USBDeviceInfo) (DeviceDriver, error) { + return kneron.NewKL720Driver(info) + }, + }) + + // 註冊 Mock Driver(開發模式) + if config.DevMode { + registry.Register(device.DriverFactory{ + Name: "mock", + Match: func(info USBDeviceInfo) bool { return true }, + Create: func(info USBDeviceInfo) (DeviceDriver, error) { + return mock.NewMockDriver(), nil + }, + }) + } +} +``` + +--- + +## 6. 模型庫設計 + +### 6.1 模型中繼資料結構 + +```typescript +// types/model.ts + +interface Model { + id: string; + name: string; + description: string; + thumbnail: string; // 縮圖 URL + + // 分類 + taskType: "classification" | "detection" | "segmentation"; + categories: string[]; // ["face", "vehicle", "industrial"] + + // 技術規格 + framework: string; // "kneron_nef" | "onnx" | "tflite" + inputSize: { width: number; height: number }; + modelSize: number; // bytes + quantization: "int8" | "float16" | "float32"; + + // 效能指標 + accuracy: number; // 0-100 (%) + latencyMs: number; // 推論延遲 + fps: number; // 在目標硬體上的 FPS + + // 相容性 + supportedHardware: string[]; // ["kneron_kl720", "kneron_kl730"] + + // 模型內容 + labels: string[]; // 類別標籤列表 + sampleImages: string[]; // 範例圖片 + + // 元資料 + version: string; + author: string; + license: string; + createdAt: string; + updatedAt: string; +} +``` + +### 6.2 MVP 模型庫資料來源 + +MVP 階段使用本地 JSON 檔案作為模型中繼資料儲存: + +``` +server/data/ +├── models.json # 模型中繼資料列表 +└── models/ # 模型檔案目錄 + ├── kneron-face-detect-v1.nef + ├── kneron-person-detect-v1.nef + └── ... +``` + +後續可擴展為: +- 遠端模型庫 API +- 社群模型市集 +- Kneron 官方模型庫整合 + +### 6.3 模型篩選邏輯 + +```go +// internal/model/repository.go + +type ModelFilter struct { + TaskType string `query:"type"` // classification, detection + Hardware string `query:"hardware"` // kneron_kl720 + Category string `query:"category"` // face, vehicle + Query string `query:"q"` // 關鍵字搜尋 + SortBy string `query:"sortBy"` // name, accuracy, size + SortOrder string `query:"order"` // asc, desc + Page int `query:"page"` + PageSize int `query:"pageSize"` +} + +func (r *Repository) List(filter ModelFilter) ([]ModelSummary, int, error) { + // 1. 載入所有模型中繼資料 + // 2. 依篩選條件過濾 + // 3. 排序 + // 4. 分頁 + // 5. 回傳結果與總數 +} +``` + +--- + +## 7. 資料模型設計 + +### 7.1 核心型別對照 + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ 前端 (TypeScript) 後端 (Go) │ +│ │ +│ Device ←→ device.DeviceInfo │ +│ DeviceStatus ←→ device.DeviceStatus │ +│ Model ←→ model.Model │ +│ ModelSummary ←→ model.ModelSummary │ +│ InferenceResult ←→ driver.InferenceResult │ +│ ClassResult ←→ driver.ClassResult │ +│ DetectionResult ←→ driver.DetectionResult │ +│ FlashProgress ←→ driver.FlashProgress │ +│ DeviceEvent ←→ device.DeviceEvent │ +└──────────────────────────────────────────────────────────────────┘ +``` + +### 7.2 本地儲存 + +MVP 階段不使用資料庫,使用檔案系統: + +``` +~/.edge-ai-platform/ # 應用資料目錄 +├── config.json # 使用者設定 +├── models/ # 模型檔案快取 +│ ├── kneron-face-detect-v1.nef +│ └── ... +└── logs/ # 日誌 + └── server.log +``` + +--- + +## 8. MVP 功能模組對照 + +### PRD 功能 → 技術實作對照 + +| PRD 功能 (B4) | 前端元件 | 後端模組 | API | 優先級 | +|--------------|---------|---------|-----|:------:| +| **F1 模型庫瀏覽** | `ModelGrid`, `ModelCard`, `ModelFilters` | `model/repository` | `GET /api/models` | P0 | +| **F1 模型篩選** | `ModelFilters` (TaskType, Hardware, Search) | `model/repository.List(filter)` | `GET /api/models?type=&q=` | P0 | +| **F1 模型詳情** | `ModelDetail`, `ModelCompatibility` | `model/repository` | `GET /api/models/:id` | P0 | +| **F1 模型下載** | 下載按鈕 + 進度 | `model/downloader` | `POST /api/models/:id/download` | P0 | +| **F2 裝置偵測** | `DeviceList`, `DeviceCard` | `device/manager`, `usb/monitor` | `GET /api/devices` + WS | P0 | +| **F2 裝置連線** | `DeviceStatus` | `device/manager.Connect()` | `POST /api/devices/:id/connect` | P0 | +| **F2 裝置熱插拔** | `useDeviceEvents` hook | `usb/monitor` | `ws://.../ws/devices/events` | P1 | +| **F3 燒錄** | `FlashDialog`, `FlashProgress` | `flash/service`, `driver` | `POST .../flash` + WS progress | P0 | +| **F3 燒錄進度** | `FlashProgress` (進度條) | `flash/progress` | `ws://.../flash-progress` | P0 | +| **F4 推論串流** | `InferencePanel` | `inference/service`, `driver` | WS `/inference` | P0 | +| **F4 分類結果** | `ClassificationResult` (長條圖) | `inference/parser` | InferenceResult JSON | P0 | +| **F4 偵測結果** | `DetectionCanvas` (邊界框) | `inference/parser` | InferenceResult JSON | P1 | +| **F4 效能指標** | `PerformanceMetrics` (FPS, 延遲) | `inference/service` | 含在 InferenceResult 內 | P0 | +| **F4 Threshold** | `ConfidenceSlider` | 前端過濾 | 不需 API(前端處理) | P1 | +| **F4 裝置日誌** | `DeviceLog` | `api/ws/device_events_ws` | `ws://.../log` | P1 | +| **F5 Camera 列表** | `CameraSelector` | `camera/manager.ListCameras()` | `GET /api/camera/list` | P0 | +| **F5 Camera 串流** | `CameraFeed` (MJPEG ``) | `camera/mjpeg`, `camera/capture` | `GET /api/camera/stream` | P0 | +| **F5 Camera 推論** | `CameraInferenceView` | `camera/pipeline` | `POST /api/camera/start` | P0 | +| **F5 推論疊加** | `CameraOverlay` (Canvas 邊界框) | `inference/service` | WS `/inference` | P0 | +| **F5 Camera 控制** | `CameraControls` (解析度/開關) | `camera/manager` | `POST /api/camera/stop` | P1 | + +### 8.5 已實作額外功能技術對照 + +以下為 MVP 開發期間實作但不在原始 PRD 範圍內的功能,及其技術實作對照。 + +#### 8.5.1 多來源推論輸入(F5) + +| 前端元件 | 後端模組 | API | +|---------|---------|-----| +| `SourceSelector` (Tab: camera/image/video) | `camera/manager` | `POST /api/camera/start` | +| Image upload button | `api/handlers/media_handler` | `POST /api/media/upload/image` | +| Video upload button | `api/handlers/media_handler` | `POST /api/media/upload/video` | +| URL input + submit | `api/handlers/media_handler` | `POST /api/media/url` | + +**影片 URL 處理流程:** + +``` +URL 輸入 → 後端分類 URL 類型: +├── 直接連結 (.mp4 等) → 直接開啟影片串流 +├── YouTube URL → yt-dlp 解析取得直接連結 → 開啟影片串流 +└── 無效 URL → 回傳錯誤 +``` + +#### 8.5.2 多語系支援 i18n(F6) + +**架構設計:** + +``` +src/lib/i18n/ +├── types.ts # TranslationDict 介面 + Paths 遞迴型別 +├── en.ts # 英文字典(~120 keys) +├── zh-TW.ts # 繁體中文字典 +└── index.ts # useTranslation() hook + getTranslation() helper +``` + +| 使用場景 | API | 說明 | +|---------|-----|------| +| React 元件 | `useTranslation()` | Hook,語言切換時自動 re-render | +| Zustand Store | `getTranslation()` | 非 hook 存取器,用於 store action 中的 toast 訊息 | +| 參數插值 | `t('key', { n: 5 })` | 支援 `{param}` 格式的字串替換 | + +**型別安全:** `TranslationKey` 由 `Paths` 遞迴產生,所有 `t()` 呼叫都有 key 自動完成,新增 key 時兩份語言檔必須同步更新否則 TypeScript 報錯。 + +**同步元件:** +- `LangSync`:監聽 settings store 的 `language`,同步更新 `` 屬性 +- `ThemeSync`:監聽 settings store 的 `theme`,同步更新 `` 的 `class` 屬性(加/移除 `dark`) + +#### 8.5.3 深色模式(F7) + +| 元件 | 機制 | +|------|------| +| `ThemeSync` | Zustand `settings-store.theme` → `document.documentElement.classList.toggle('dark')` | +| Tailwind CSS | 使用 `dark:` variant,搭配 CSS custom properties 定義色彩 | +| Settings UI | 淺色/深色切換按鈕,persist 至 localStorage | + +#### 8.5.4 儀表板 Dashboard(F8) + +| 前端元件 | 資料來源 | 說明 | +|---------|---------|------| +| `app/page.tsx` | `model-store`, `device-store`, `activity-store` | 統計卡片 + 快速操作 | +| `ActivityTimeline` | `activity-store` | 最近 10 筆活動,含圖示和相對時間 | +| `ConnectedDevicesList` | `device-store` | 已連線裝置即時列表 | + +**Activity Store 事件類型:** `model_upload`, `model_delete`, `device_connect`, `device_disconnect`, `flash_start`, `flash_complete`, `flash_error` + +#### 8.5.5 裝置偏好設定(F9) + +| Store | 持久化 | 資料結構 | +|-------|--------|---------| +| `device-preferences-store` | Zustand persist → localStorage | `Record` | + +#### 8.5.6 模型比較(F10) + +| 前端元件 | 功能 | +|---------|------| +| `ModelComparisonDialog` | 最多 3 模型並排比較 | +| 比較指標 | 準確率、FPS、模型大小、延遲、任務類型 | +| 視覺化 | 指標長條圖(百分比 bar) | + +#### 8.5.7 設定頁面(F12) + +| 區塊 | 內容 | +|------|------| +| 伺服器設定 | API URL、WebSocket URL(唯讀顯示) | +| 外觀 | 主題切換(淺色/深色)、語言切換(EN/zh-TW) | +| 關於 | 版本資訊 | +| 操作 | 恢復預設值 | + +**Settings Store:** `settings-store.ts` 管理 `theme` ('light' | 'dark') 和 `language` ('en' | 'zh-TW'),persist 至 localStorage。 + +--- + +## 9. 開發環境與工具鏈 + +### 9.1 前端開發環境 + +| 工具 | 版本 | 用途 | +|------|------|------| +| Node.js | 20 LTS | JavaScript runtime | +| pnpm | 9.x | 套件管理(比 npm 快、磁碟效率高) | +| TypeScript | 5.x | 型別安全 | +| ESLint | 9.x | 程式碼品質 | +| Prettier | 3.x | 程式碼格式化 | + +### 9.2 後端開發環境 + +| 工具 | 版本 | 用途 | +|------|------|------| +| Go | 1.22+ | 後端語言 | +| golangci-lint | latest | Go linter | +| Air | latest | Go 熱重載(開發用) | +| GoReleaser | latest | 跨平台打包 | + +### 9.3 開發流程 + +``` +開發模式 (Development): +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ Next.js Dev │ │ Go Server │ │ Device / Mock │ +│ (pnpm dev) │ │ (air / go run) │ │ │ +│ localhost:3000 │───►│ localhost:3721 │───►│ USB / Serial │ +│ │ │ │ │ │ +│ HMR 熱重載 │ │ 熱重載 (Air) │ │ Mock Driver │ +└─────────────────┘ └──────────────────┘ └─────────────────┘ + +生產模式 (Production): +┌─────────────────────────────────────────────┐ +│ 單一執行檔 │ +│ │ +│ Go Server (localhost:3721) │ +│ ├── REST API (/api/...) │ +│ ├── WebSocket (/ws/...) │ +│ ├── Static (/ → 嵌入的前端 build) │ +│ └── Device Communication (USB/Serial) │ +└─────────────────────────────────────────────┘ +``` + +### 9.4 開發模式代理設定 + +```javascript +// next.config.js +module.exports = { + async rewrites() { + return [ + { + source: '/api/:path*', + destination: 'http://localhost:3721/api/:path*', + }, + // WebSocket 透過環境變數指定 + ]; + }, +}; +``` + +--- + +## 10. 專案目錄結構 + +``` +edge-ai-platform/ +├── README.md +├── Makefile # 建置腳本 +├── .goreleaser.yml # 跨平台打包設定 +│ +├── server/ # Go 後端 +│ ├── go.mod +│ ├── go.sum +│ ├── main.go +│ ├── cmd/ +│ ├── internal/ +│ │ ├── api/ +│ │ ├── device/ +│ │ ├── driver/ +│ │ ├── model/ +│ │ ├── flash/ +│ │ ├── inference/ +│ │ └── config/ +│ ├── pkg/ +│ │ ├── serial/ +│ │ ├── usb/ +│ │ └── logger/ +│ ├── data/ # 內建模型中繼資料 +│ │ └── models.json +│ ├── scripts/ # Kneron SDK Python 腳本 +│ │ ├── load_model.py +│ │ ├── start_inference.py +│ │ └── requirements.txt +│ └── frontend/ # 嵌入用前端 build +│ └── out/ +│ +├── frontend/ # Next.js 前端 +│ ├── package.json +│ ├── pnpm-lock.yaml +│ ├── next.config.js +│ ├── tailwind.config.ts +│ ├── tsconfig.json +│ ├── components.json # shadcn/ui 設定 +│ ├── src/ +│ │ ├── app/ +│ │ ├── components/ +│ │ ├── stores/ +│ │ ├── hooks/ +│ │ ├── lib/ +│ │ └── types/ +│ └── public/ +│ └── images/ +│ +├── docs/ # 文件 +│ ├── api.md # API 文件 +│ └── driver-development.md # Driver 開發指南 +│ +└── .github/ # CI/CD + └── workflows/ + ├── ci.yml + └── release.yml +``` + +--- + +## 11. 部署與打包策略 + +### 11.1 建置流程 + +``` +1. 前端建置 + pnpm build && pnpm export + → frontend/out/ (靜態檔案) + +2. 複製前端到 Go embed 目錄 + cp -r frontend/out server/frontend/out + +3. Go 編譯 (含嵌入前端) + cd server && go build -o edge-ai-platform + +4. 或使用 GoReleaser 跨平台打包 + goreleaser release --snapshot +``` + +### 11.2 目標平台 + +| 平台 | 架構 | 輸出檔案 | 安裝方式 | +|------|------|---------|---------| +| macOS (Apple Silicon) | darwin/arm64 | `edge-ai-platform-darwin-arm64` | `.dmg` 或直接執行 | +| macOS (Intel) | darwin/amd64 | `edge-ai-platform-darwin-amd64` | `.dmg` 或直接執行 | +| Windows | windows/amd64 | `edge-ai-platform.exe` | `.msi` 安裝包 | +| Linux | linux/amd64 | `edge-ai-platform-linux-amd64` | `.deb` / `.rpm` / 直接執行 | + +### 11.3 GoReleaser 設定 + +```yaml +# .goreleaser.yml +builds: + - id: edge-ai-platform + dir: server + binary: edge-ai-platform + goos: + - darwin + - linux + - windows + goarch: + - amd64 + - arm64 + env: + - CGO_ENABLED=0 + ldflags: + - -s -w -X main.version={{.Version}} + +archives: + - format: tar.gz + format_overrides: + - goos: windows + format: zip +``` + +### 11.4 Makefile + +```makefile +.PHONY: dev dev-frontend dev-backend build clean + +# 開發模式 +dev: dev-frontend dev-backend + +dev-frontend: + cd frontend && pnpm dev + +dev-backend: + cd server && air + +# 建置 +build: build-frontend build-backend + +build-frontend: + cd frontend && pnpm build && pnpm export + cp -r frontend/out server/frontend/out + +build-backend: build-frontend + cd server && go build -o ../dist/edge-ai-platform + +# 跨平台打包 +release: + goreleaser release --snapshot --clean + +clean: + rm -rf dist/ frontend/out/ server/frontend/out/ +``` + +--- + +## 12. 安全性考量 + +### 12.1 本地 Server 安全 + +| 風險 | 緩解措施 | +|------|---------| +| 外部存取本地 Server | 僅綁定 `127.0.0.1`,拒絕外部連線 | +| 跨站請求偽造 (CSRF) | 驗證 `Origin` header | +| 路徑穿越 (Path Traversal) | 模型檔案路徑白名單、嚴格驗證 | +| 命令注入 | 不將使用者輸入直接傳入 shell 命令 | +| CORS | 嚴格限制為 `localhost` 來源 | +| WebSocket 安全 | 驗證 Origin header | + +### 12.2 裝置通訊安全 + +| 風險 | 緩解措施 | +|------|---------| +| 惡意韌體 | 驗證 NEF 檔案完整性(CRC/Hash) | +| USB 攻擊面 | 僅處理已知 VID/PID 的裝置 | +| 串列埠注入 | 嚴格解析裝置回應格式 | + +--- + +## 13. 效能目標 + +### 13.1 回應時間目標(對應 PRD 驗收標準) + +| 指標 | 目標 | PRD 來源 | +|------|------|---------| +| 裝置偵測延遲 | USB 接上 < 5 秒出現在列表 | B2.3 驗收標準 | +| 燒錄進度更新 | < 500ms 更新一次 | B2.3 | +| 推論結果延遲 | < 200ms(裝置到前端) | B2.4 驗收標準 | +| 首次推論顯示 | 燒錄完成後 < 5 秒 | B2.4 驗收標準 | +| 前端頁面載入 | < 2 秒 | 一般標準 | +| API 回應時間 | < 100ms(列表/詳情) | 一般標準 | +| 模型搜尋 | 使用者可在 30 秒內找到模型 | B2.2 驗收標準 | + +### 13.2 資源使用 + +| 指標 | 目標 | +|------|------| +| Server 記憶體 | < 100 MB(空閒)/ < 200 MB(推論串流中) | +| Server CPU | < 5%(空閒)/ < 15%(推論串流中) | +| 執行檔大小 | < 50 MB(含前端) | + +--- + +## 14. 測試策略 + +### 14.1 前端測試 + +| 類型 | 工具 | 覆蓋範圍 | +|------|------|---------| +| 單元測試 | Vitest | 工具函式、Store 邏輯 | +| 元件測試 | Vitest + Testing Library | UI 元件渲染、互動 | +| E2E 測試 | Playwright | 完整使用者流程 | + +### 14.2 後端測試 + +| 類型 | 工具 | 覆蓋範圍 | +|------|------|---------| +| 單元測試 | Go testing | Driver 介面、解析器、業務邏輯 | +| 整合測試 | Go testing + httptest | API 端點、WebSocket | +| Mock 測試 | Mock Driver | 無硬體環境的完整流程測試 | + +### 14.3 關鍵測試場景 + +``` +模型庫: +├── 模型列表載入與顯示 +├── 篩選功能(單選、多選、組合) +├── 搜尋功能 +└── 模型詳情頁內容正確性 + +裝置管理: +├── 裝置偵測(插入 USB) +├── 裝置移除(拔出 USB) +├── 多裝置同時連線 +└── 裝置斷線重連 + +燒錄: +├── 正常燒錄流程(進度 0→100%) +├── 燒錄中斷線(錯誤處理) +├── 模型不相容(預先阻止) +└── 燒錄逾時 + +推論: +├── 分類結果即時顯示 +├── 偵測結果邊界框繪製 +├── 高頻率更新不卡頓 +├── Confidence Threshold 過濾 +└── 推論啟停控制 + +Camera: +├── Camera 列表偵測 +├── Camera 開啟與 MJPEG 串流 +├── Camera 切換(多 Camera 支援) +├── Camera → Device 推論 Pipeline +├── MJPEG 串流 + 邊界框疊加同步 +├── Camera 關閉後正確釋放資源 +└── 不同解析度切換 +``` + +--- + +## 15. 開發階段規劃 + +### 15.1 MVP 開發階段 + +``` +Phase 0 — 基礎建設 (Scaffold) +━━━━━━━━━━━━━━━━━━━━━━━━━━━ +├── 初始化前端專案 (Next.js + shadcn/ui + Tailwind) +├── 初始化後端專案 (Go module + 專案結構) +├── 設定開發工具鏈 (ESLint, Prettier, golangci-lint) +├── 實作 Mock Driver +├── 前後端通訊基礎 (REST + WebSocket) +└── CI 設定 + +Phase 1 — 模型庫 (Step 1) +━━━━━━━━━━━━━━━━━━━━━━━━ +├── 後端: 模型中繼資料 API (GET /api/models, GET /api/models/:id) +├── 後端: 模型篩選與搜尋 +├── 前端: 模型庫頁面 (ModelGrid + ModelCard) +├── 前端: 篩選面板 (ModelFilters) +├── 前端: 模型詳情頁 (ModelDetail) +└── 測試: 模型 API + 前端元件 + +Phase 2 — 裝置管理 (Step 2) +━━━━━━━━━━━━━━━━━━━━━━━━━━ +├── 後端: USB 裝置偵測與熱插拔 +├── 後端: Driver 抽象層 + Mock Driver 完善 +├── 後端: 裝置 API (GET /api/devices, POST connect/disconnect) +├── 後端: WebSocket 裝置事件 +├── 前端: 裝置列表頁面 (DeviceList + DeviceCard) +├── 前端: 裝置狀態即時更新 +└── 測試: 裝置偵測 + WebSocket 事件 + +Phase 3 — 燒錄 (Step 2 續) +━━━━━━━━━━━━━━━━━━━━━━━━━ +├── 後端: 燒錄服務 (Flash Service) +├── 後端: 燒錄進度 WebSocket +├── 後端: Kneron SDK 整合 (Python subprocess) +├── 前端: 燒錄對話框 + 進度條 +├── 前端: 錯誤處理 UI +└── 測試: 燒錄流程 (Mock + 實機) + +Phase 4 — Camera 與即時推論 (Step 3) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +├── 後端: Camera Manager (偵測、開啟、擷取) +├── 後端: MJPEG 串流 HTTP handler +├── 後端: Camera-Inference Pipeline (frame → device → result) +├── 後端: 推論結果串流 (WebSocket) +├── 後端: 推論結果解析器 +├── 前端: Camera 選擇器 + 控制面板 +├── 前端: Camera Feed 元件 (MJPEG ) +├── 前端: 推論結果 Canvas 疊加層 (邊界框 + 標籤) +├── 前端: Camera + 推論整合檢視 (CameraInferenceView) +├── 前端: 分類結果長條圖 (Recharts) +├── 前端: 效能指標顯示 (FPS, 延遲) +├── 前端: Confidence Threshold 滑桿 +├── 前端: 裝置日誌面板 +└── 測試: Camera 串流 + 推論疊加 + UI 效能 + +Phase 5 — 整合與打包 +━━━━━━━━━━━━━━━━━━━ +├── 前端 build 嵌入 Go binary +├── 跨平台打包 (GoReleaser) +├── 安裝包製作 (dmg, msi, deb) +├── E2E 測試 (完整使用者流程) +├── 效能測試與最佳化 +└── 文件撰寫 +``` + +### 15.2 技術風險與緩解 + +| 風險 | 嚴重度 | 緩解策略 | +|------|:------:|---------| +| Kneron PLUS SDK 整合困難 | 高 | 先用 Python subprocess;確保 Mock Driver 可用於平行開發 | +| 跨平台串列埠差異 | 中 | 使用成熟的 `go.bug.st/serial`;各平台 CI 測試 | +| Kneron USB VID/PID 未確認 | 中 | 取得實體硬體後確認;Mock Driver 先行開發 | +| WebSocket 高頻推論資料效能 | 中 | 前端節流(throttle);二進位格式(MessagePack)備選 | +| OpenCV (gocv) 依賴與跨平台 | 中 | 靜態連結 OpenCV 或使用 ffmpeg subprocess 作為 fallback;Docker 建置環境 | +| Camera 擷取延遲 | 中 | MJPEG 串流減少前端解碼負擔;後端控制 frame rate (可調 15-30 FPS) | +| Go embed 前端檔案大小 | 低 | Next.js 靜態輸出已最佳化;gzip 壓縮 | +| CGo 交叉編譯 | 中 | MVP 用 Python subprocess 避免;Phase 1+ 再評估 CGo | + +### 15.3 開發模式與 Mock 策略 + +為了在沒有實體 Kneron Dongle 的情況下進行完整的前後端開發: + +``` +啟動方式: + edge-ai-platform --mock # 使用 Mock Driver + edge-ai-platform --mock-devices 3 # 模擬 3 台裝置 + +Mock Driver 行為: + ● 偵測: 模擬 1-N 台虛擬裝置 + ● 連線: 立即成功 + ● 燒錄: 模擬 10 秒燒錄過程(進度遞增) + ● 推論: 每 100ms 產生隨機推論結果 + ├── 分類: 在 5 個類別中隨機分配信心分數 + └── 偵測: 隨機位置的 1-3 個邊界框 + +Mock Camera 行為: + ● Camera 列表: 回傳 1 台虛擬 Camera ("Mock Camera 0") + ● MJPEG 串流: 產生帶有時間戳的測試圖卡影像 (color bars / gradient) + ● Camera 推論: 以測試圖卡影像送入 Mock Driver,回傳隨機推論結果 + ● 用途: 在沒有實體 Camera 和 Dongle 的環境下完整測試前後端整合 +``` + +--- + +## 附錄 + +### A. 預設連接埠 + +| 服務 | 預設埠 | 說明 | +|------|:------:|------| +| Go Server (HTTP + WS + Static) | 3721 | 所有服務共用 | +| Next.js Dev Server | 3000 | 僅開發模式 | + +### B. 環境變數 + +| 變數 | 預設值 | 說明 | +|------|--------|------| +| `PORT` | `3721` | Server 監聽埠 | +| `HOST` | `127.0.0.1` | Server 綁定地址 | +| `LOG_LEVEL` | `info` | 日誌等級 (debug/info/warn/error) | +| `DEV_MODE` | `false` | 開發模式 (啟用 Mock Driver) | +| `MOCK_DEVICES` | `1` | Mock 裝置數量 | +| `DATA_DIR` | `~/.edge-ai-platform` | 應用資料目錄 | +| `PYTHON_PATH` | `python3` | Python 路徑 (Kneron SDK) | + +### C. 相依套件清單 + +**前端 (package.json):** + +```json +{ + "dependencies": { + "next": "^15.0.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "zustand": "^5.0.0", + "recharts": "^2.12.0", + "lucide-react": "^0.400.0", + "clsx": "^2.0.0", + "tailwind-merge": "^2.0.0" + }, + "devDependencies": { + "typescript": "^5.5.0", + "tailwindcss": "^4.0.0", + "@types/react": "^19.0.0", + "vitest": "^2.0.0", + "@testing-library/react": "^16.0.0", + "playwright": "^1.45.0", + "eslint": "^9.0.0", + "prettier": "^3.0.0" + } +} +``` + +**後端 (go.mod 主要依賴):** + +``` +go.bug.st/serial // 串列埠通訊 +github.com/gin-gonic/gin // HTTP 框架 (或 echo) +github.com/gorilla/websocket // WebSocket +github.com/google/gousb // USB 裝置偵測 +gocv.io/x/gocv // OpenCV Go binding (Camera 擷取) +github.com/fsnotify/fsnotify // 檔案監聽 +github.com/spf13/cobra // CLI 框架 +github.com/spf13/viper // 設定管理 +go.uber.org/zap // 結構化日誌 +``` + +--- + +*文件版本:v1.2 | 日期:2026-02-24 | 狀態:更新中*