# visionA Local Tool — 功能規格文件 > 版本:v0.1.0-dev | 最後更新:2026-04-16 | 對應 commit:`9793a2e` ## 概述 visionA Local Tool 是 Kneron KL520 / KL720 邊緣 AI 推論硬體的**本機桌面應用**,讓使用者在完全離線的環境下,透過 USB 連接 Kneron 裝置,進行影像 / 影片 / 攝影機即時推論。 架構為 **Wails 桌面 App(Go + Vanilla JS 控制台)+ Go HTTP Server + Next.js Web UI(瀏覽器)**。Wails 控制台負責 server lifecycle 管理,瀏覽器 Web UI 負責裝置操作、模型管理、推論工作區。 ### 三平台支援 | 平台 | 安裝格式 | Build target | |------|---------|-------------| | macOS (x86_64) | `.dmg` | `make dmg` | | Windows (x64) | `.exe` (Inno Setup) | `make exe` | | Linux (x86_64) | `.AppImage` | `make appimage` | ### 完全離線安裝 所有依賴(Python runtime、wheels、ffmpeg、ffprobe、.nef 模型)全部 vendor 進 installer,安裝和執行過程**不需要網路連線**。 --- ## 1. Wails 控制台(桌面應用程式視窗) ### 1.1 啟動流程 — 5 階段進度面板 | 階段 | 名稱 | 做什麼 | |------|------|--------| | 1 | 初始化控制台 | 建立 dataDir、舊資料遷移、single-instance lock、IPC server、首次啟動 seed(複製內建模型到 user dataDir) | | 2 | 檢查 Python 執行環境 | 偵測系統 Python → 或解壓內建 Python runtime(首次啟動 ~1-3 分鐘)→ 建 venv → pip install wheels(numpy/opencv/KneronPLUS 等 9 個套件) | | 3 | 啟動本機伺服器 | spawn `visiona-local-server` subprocess → 等 health check 通過(首次啟動 Windows Defender 掃描可能需 1-2 分鐘) | | 4 | 偵測 Kneron 裝置 | 對 `/api/devices` 發一次 GET 確認 server 可 serve 業務 endpoint | | 5 | 開啟瀏覽器 | 自動開啟系統預設瀏覽器到 `http://127.0.0.1:3721`(可在設定中關閉) | - **全屏 splash**:app 啟動最一開始顯示 logo + spinner 全屏 overlay,收到第一個 stage event 後切換到 5 階段面板 - **每階段細步文案**:Go 端在每個 sub-step emit `startup:stage-detail` event,前端即時顯示當前正在做什麼(例如「正在解壓 Python runtime」「等待伺服器健康檢查通過」) - **啟動完成自動收合**:5 階段全部完成後面板收合成一行 summary「✓ 啟動完成 · 點此展開檢視」,使用者點擊可展開歷史紀錄 - **重啟 / 重試時重新展開**:按「重新啟動伺服器」或「重試」會重置面板並完整重跑 5 階段 - **前端 snapshot 補漏**:前端 init 完成後呼叫 `GetStartupSnapshot()` 補上 race window 中漏掉的 stage events,避免順序亂跳 - **Web UI 連線指示燈**:Stage 6「等待 Web UI 連線」從面板隱藏,改為 header 的 Web UI 連線指示燈(綠燈 = 已連線、黃燈 pulse = 等待中) ### 1.2 Timeout 機制 | 類型 | 時長 | 行為 | |------|------|------| | 每階段 soft timeout | 20 秒 | emit「正在重試...」提示,**不中斷流程** | | 整體 hard timeout | 5 分鐘(300 秒) | 超過則 fail 進 Error state | | 首次 bootstrap pause | 無上限 | Stage 1 seed / Stage 2 Python bootstrap / Stage 3 waitHealthy 期間暫停 hard timeout 計時,不算進 5 分鐘 budget | ### 1.3 控制台功能 - **狀態列**:顯示 server 狀態(閒置 / 啟動中 / 執行中 / 停止中 / 已停止 / 錯誤)、port、PID、uptime、Web UI 連線狀態 - **主要控制**:在瀏覽器開啟 / 啟動 / 管理(停止 / 重新啟動 / 開啟 log 資料夾) - **Log 面板**:server 即時 log 顯示、自動跟隨最新、關鍵字過濾、層級過濾(All / INFO / WARN / ERROR)、清空 / 複製 / 匯出 / 開啟 log 資料夾 - **Settings**:啟動時自動開啟瀏覽器(macOS/Windows 預設 ON、Linux 預設 OFF)、語言切換(繁體中文 / English / Auto)、關於資訊 - **Shutdown modal**:server 停止時顯示「正在停止伺服器…」overlay,15 秒 watchdog 自動 hide,可用 Esc / 點 backdrop 手動關閉 - **Error 處理**:server 啟動失敗 → 紅 banner「伺服器無法啟動」+ 重試按鈕;runtime crash → OS 原生通知 + Error state - **i18n 雙語**:繁體中文 + English,全 UI 元素(含 stage 細步文案、設定、錯誤訊息) ### 1.4 Single-instance 保護 - 用 `visiona-local.lock` 檔案鎖 + PID 存活檢查 - 第二次啟動偵測到已有 instance → 嘗試 IPC raise 把現有視窗提到前景 → 自己 quietly exit - Stale lock(PID 已死)自動清理並取得新 lock --- ## 2. Web UI(瀏覽器介面) ### 2.1 頁面路由 | 路由 | 功能 | |------|------| | `/` | 儀表板 — 已連接裝置列表、活動時間軸、統計卡片 | | `/devices` | 裝置列表 — 掃描 / 連接 / 斷開 Kneron USB 裝置 | | `/devices/[id]` | 裝置詳細 — 狀態、健康度、韌體版本、連線日誌、設定 | | `/models` | 模型庫 — 7 個預設 .nef 模型(KL520 × 4 + KL720 × 3)+ 上傳自訂模型 | | `/models/[id]` | 模型詳細 — metadata、支援硬體、效能數據 | | `/workspace` | 工作區 — 選裝置 → 選模型 → 選來源 → 推論 | | `/workspace/[deviceId]` | 單一裝置工作區 — 推論操作主介面 | | `/settings` | 設定 — 語言、主題、其他偏好 | ### 2.2 推論來源 | 來源 | 說明 | |------|------| | 攝影機 (Camera) | USB / IP Camera 即時串流推論 | | 圖片 (Image) | 上傳單張 JPG/PNG 做單次推論 | | 影片 (Video) | 上傳 MP4/AVI/MOV/MPEG/MPG 做逐幀推論(ffmpeg 解碼) | | 批次圖片 (Batch Images) | 上傳多張圖片逐張推論 | ### 2.3 推論結果顯示 - **MJPEG 即時串流**:`/api/camera/stream` 提供多客戶端 multipart MJPEG 串流 - **Canvas overlay**:bounding box + label + confidence 即時疊加 - **推論面板**:FPS、平均延遲、信心度門檻調整、分類結果展示 - **影片進度**:目前幀 / 總幀、seek 跳轉 - **批次進度**:已處理 / 總張數、縮圖預覽 ### 2.4 模型管理 - **7 個預設模型**(NEF 格式、INT8 量化) - **上傳自訂 .nef 模型**:`POST /api/models/upload` - **刪除已上傳模型**:`DELETE /api/models/:id` - **模型篩選**:按任務類型(object_detection / classification)、硬體(KL520 / KL720)、關鍵字 - **模型對比工具**:並排比較兩個模型的規格 ### 2.5 裝置管理 - **掃描 USB 裝置**:偵測已連接的 Kneron KL520 / KL720 - **連接 / 斷開**:連接時自動載入 firmware(KL520 USB Boot 流程) - **韌體更新**:flash .nef 到裝置 + 進度 WebSocket 即時回報 - **WinUSB 驅動安裝**(Windows only):首次啟動自動透過 KneronPLUS SDK libwdi 安裝,需 UAC 提權 ### 2.6 離線覆蓋層 - Server 關閉 / 崩潰時,瀏覽器 tab 顯示 Offline Overlay - `role=alertdialog` + focus trap + WebSocket 重連機制 - `boot-id` 偵測 server restart → 自動 reload(含 reload loop guard) --- ## 3. 預設模型 ### KL520(4 個) | 模型 ID | 名稱 | 任務 | 輸入 | FPS | |---------|------|------|------|-----| | kl520-yolov5-detection | YOLOv5 Detection | 物件偵測 | 640×640 | ~20 | | kl520-fcos-detection | FCOS Detection | 物件偵測 | 512×512 | ~22 | | kl520-ssd-face-detection | SSD Face Detection | 人臉偵測 | 320×240 | ~100 | | kl520-tiny-yolov3 | Tiny YOLOv3 | 物件偵測 | 416×416 | ~28 | ### KL720(3 個) | 模型 ID | 名稱 | 任務 | 輸入 | FPS | |---------|------|------|------|-----| | kl720-yolov5-detection | YOLOv5 Detection | 物件偵測 | 640×640 | ~33 | | kl720-resnet18-classification | ResNet18 Classification | 分類(1000 類)| 224×224 | ~100 | | kl720-fcos-detection | FCOS Detection | 物件偵測 | 512×512 | ~33 | --- ## 4. 內嵌依賴 | 依賴 | 版本 | 用途 | 授權 | |------|------|------|------| | Python | 3.12.9 (python-build-standalone) | Kneron Python bridge | PSF | | numpy | 2.4.4 | 影像處理 | BSD | | opencv-python-headless | 4.10.0 | 影像處理 | Apache-2.0 | | KneronPLUS | 2.0.0 | Kneron SDK Python binding | Kneron | | pyusb | 1.3.1 | USB 裝置存取 | BSD | | requests | 2.33.1 | HTTP 工具 | Apache-2.0 | | ffmpeg | 自建 LGPL v3 decoder-only | 影片解碼(mp4/avi/mov/mpeg/mpg)| LGPL v3 | | ffprobe | 同上 | 影片 metadata 提取 | LGPL v3 | ### ffmpeg LGPL 合規 macOS 版為自行編譯的最小 decoder-only build(~5.7MB),只啟用必要的 demuxer/decoder/parser/filter,**無 --enable-gpl / libx264 / libx265**,符合 LGPL v3。Windows/Linux 使用 BtbN 官方 LGPL 預編譯 binary。 --- ## 5. Server API 摘要 ### 系統 | Method | Path | 說明 | |--------|------|------| | GET | `/api/system/health` | 健康檢查(高頻輪詢用,無 log) | | GET | `/api/system/info` | 版本、平台、uptime | | GET | `/api/system/metrics` | Go runtime 記憶體統計 | | GET | `/api/system/deps` | 外部依賴檢查(python/ffmpeg/ffprobe) | | GET | `/api/system/boot-id` | server 啟動 ID(偵測 restart 用) | | POST | `/api/system/restart` | 重啟 server | | POST | `/api/system/install-driver` | 安裝 Kneron USB 驅動(Windows only) | | POST | `/api/system/shutdown-notify` | 廣播關機通知到 WebSocket clients | ### 模型 | Method | Path | 說明 | |--------|------|------| | GET | `/api/models` | 列出所有模型(支援 filter) | | GET | `/api/models/:id` | 取得單一模型詳細 | | POST | `/api/models/upload` | 上傳自訂 .nef 模型 | | DELETE | `/api/models/:id` | 刪除已上傳模型 | ### 裝置 | Method | Path | 說明 | |--------|------|------| | GET | `/api/devices` | 列出已偵測裝置 | | POST | `/api/devices/scan` | 掃描 USB 裝置 | | GET | `/api/devices/:id` | 取得裝置詳細 | | POST | `/api/devices/:id/connect` | 連接裝置 | | POST | `/api/devices/:id/disconnect` | 斷開裝置 | | POST | `/api/devices/:id/flash` | 燒錄韌體 | | POST | `/api/devices/:id/inference/start` | 啟動推論 | | POST | `/api/devices/:id/inference/stop` | 停止推論 | ### 媒體 | Method | Path | 說明 | |--------|------|------| | GET | `/api/camera/list` | 列出可用攝影機 | | POST | `/api/camera/start` | 啟動攝影機推論 pipeline | | POST | `/api/camera/stop` | 停止 pipeline | | GET | `/api/camera/stream` | MJPEG 即時串流 | | POST | `/api/media/upload/image` | 上傳單張圖片推論 | | POST | `/api/media/upload/video` | 上傳影片逐幀推論 | | POST | `/api/media/upload/batch-images` | 批次上傳圖片推論 | | GET | `/api/media/batch-images/:index` | 取得批次中特定幀 | | POST | `/api/media/seek` | 影片 seek 跳轉 | ### WebSocket | Path | 說明 | |------|------| | `/ws/devices/events` | 裝置連接 / 斷開事件 | | `/ws/devices/:id/flash-progress` | 韌體更新進度 | | `/ws/devices/:id/inference` | 推論結果即時串流 | | `/ws/server-logs` | server log 即時廣播 | | `/ws/system` | 系統事件(含 shutdown-imminent) | --- ## 6. M8 重構變更紀錄(2026-04-14 ~ 2026-04-16) 以下是 M8 重構後的所有修復和改動,按時間排序: ### 架構變更(R5 決策實作) | Commit | 變更 | |--------|------| | `8cd5751` | **M8 重構 — Wails 控制台 + 瀏覽器 Web UI**:原 Wails 內嵌 Next.js 改為「Wails 控制台管理 server lifecycle + 瀏覽器開 Web UI」。砍 yt-dlp(M8-1)、砍 Mock 模式(M8-2)、ffmpeg LGPL vendor(M8-3)、ServerController v2 state machine(M8-4)、啟動階段管線 6 stage pipeline(M8-4b)、Wails 控制台 vanilla JS UI(M8-5)、Offline Overlay(M8-7)、CORS middleware(M8-8)、shutdown broadcast(MAJ-4)、Boot-ID + tab 重連(M8-9) | ### Bug 修復 | Commit | 問題 | 修法 | |--------|------|------| | `d7cddf3` | **預設模型永遠載入 0 個** — server 預設 dataDir 算錯,models.json 找不到 | 拆 `builtInDataDir`(read-only bundle)vs `dataDir`(writable user home),新增 `resolveBuiltInDataDir` + `findFirstExisting` helper,跨 macOS/Windows/Linux AppImage 五路徑自動偵測 | | `a6cd1c1` | **Wails 控制台一打開就看到 Settings + shutdown-modal** — CSS `.modal-backdrop { display: flex }` 覆蓋 user agent `[hidden] { display: none }` | 全域 `[hidden] { display: none !important; }` | | `a209470` | **Windows 乾淨環境啟動失敗(Stage 2 Python bootstrap > 60s)** | StartupPipeline 加 `PauseHardTimeout` / `ResumeHardTimeout` API,首次 bootstrap 不算入 hard timeout budget | | `c649a81` | **Stage 3 waitHealthy 也被 hard timeout 幹掉** | waitHealthy 冷啟動時也 pause;healthCheckTimeout 30s → 180s | | `35db6c8` | **shutdown-modal「正在停止伺服器」popup 卡死 + Wails app stderr 在 Windows 上看不到** | 前端 15s watchdog + Esc/backdrop 可關 + stopGraceful hardBailout timer + appLog 覆蓋啟動流程關鍵訊息到 wails.log | | `d946561` | **Stage 順序亂跳(Stage 1 等待中但 Stage 2 完成)** | 新增 `GetStartupSnapshot()` binding,前端 init 後拉 snapshot 補漏 events + monotonic status rank 避免倒退 | | `ad9beab` | **Restart / Stop+Start 後 5 階段面板不更新** | `startInternal` 進場偵測 pipeline 已完成/已失敗 → 自動 `rebuildStartupPipeline` 重建,`didRebuild` flag 分流 Stage 5 openBrowser | | `fbd585a` | **Linux build 找不到 webkit2gtk-4.0** | Makefile `wails-linux` 自動偵測 4.1 → `-tags webkit2_41`;bootstrap-linux.sh 也做版本偵測 | | `485a2e0` | **Linux build 找不到 appimagetool** | bootstrap-linux.sh 新增自動下載安裝 appimagetool | ### 功能改善 | Commit | 變更 | |--------|------| | `dd35b56` | regen Wails bindings(M8 漏提的 binding 產物) | | `9c9e005` | Stage 3 sub-step 細步進度 + 啟動完成後面板可收合 | | `ff5cab6` | hard timeout 放寬 + 全 stage 細步 detail emit(13 個 i18n keys)+ Stage 1 seed pause | | `f5655e3` | hard timeout → 5 分鐘(300s)+ Stage 6 隱藏到 header 連線指示燈 + 全屏 splash overlay | | `9793a2e` | models.json 從 15 筆精簡為 7 筆(只保留有實體 .nef 的模型) | ### 開發工具 / Bug A | Commit | 變更 | |--------|------| | `a209470` | `killStaleServerOnPort` 改用 `ps -o args=` 識別 `go run` 產生的 server(開發/Reviewer 測試用) | --- ## 7. 資料目錄 | 平台 | 位置 | |------|------| | macOS | `~/Library/Application Support/visiona-local/` | | Windows | `%APPDATA%\visiona-local\` | | Linux | `~/.config/visiona-local/` 或 `$XDG_DATA_HOME/visiona-local/` | ### 目錄結構 ``` visiona-local/ ├── visiona-local.lock # single-instance 鎖 ├── visiona-local.ipc-port # server port(供 raise 用) ├── visiona-local.wails-ipc-port # Wails IPC port ├── preferences.json # 使用者偏好(autoOpenBrowser / locale) ├── models.json # 預設模型 catalog(首次啟動從 bundle seed) ├── nef/ # 預設 .nef 模型檔(首次啟動從 bundle seed) │ ├── kl520/ │ └── kl720/ ├── custom-models/ # 使用者上傳的自訂模型 ├── runtime/ # Python runtime(首次啟動解壓) │ ├── python/ │ └── venv/ ├── logs/ │ ├── server.stdout.log # server subprocess stdout │ ├── server.stderr.log # server subprocess stderr │ └── wails.log # Wails app (appLog) 啟動流程日誌 └── .first-ws-connected # WebSocket sentinel file(啟動階段用) ``` --- ## 8. Build 指引 ### macOS ```bash cd local-tool make vendor-sync # 下載 Python + wheels + 驗證 ffmpeg LGPL make payload-macos # 準備 payload/darwin/ make dmg # → dist/visiona-local.dmg (~163MB) ``` ### Windows(需在 Windows 機器上) ```powershell cd local-tool .\scripts\bootstrap-windows.ps1 # 安裝 Go / Node / pnpm / Wails / Python / MSYS2 make exe # → dist/visiona-local-*-windows-x64.exe ``` ### Linux(Ubuntu 22.04+) ```bash cd local-tool bash scripts/bootstrap-linux.sh # 安裝所有依賴 + 自動 build # 或手動: make vendor-python-linux vendor-wheels-linux vendor-ffmpeg-linux make payload-linux make appimage # → dist/visiona-local-*-linux-x64.AppImage ``` --- ## 9. 除錯指引 ### Windows 啟動問題 1. 檢查 `%APPDATA%\visiona-local\logs\wails.log` - 第一行應有 `fix marker: d946561+`(確認 build 版本) - Stage 1-5 的細步文案會記錄每個 sub-step 2. 檢查 `server.stdout.log` / `server.stderr.log` 3. 如需更詳細的 Wails 端 log,從命令列啟動: ``` "C:\Program Files\visiona-local\visiona-local.exe" > %TEMP%\wails-debug.txt 2>&1 ``` ### 常見問題 | 症狀 | 可能原因 | 解法 | |------|---------|------| | 啟動 Stage 3 timeout | Windows Defender 首次掃 .exe | 等待,有 pause 機制不會 fail;或加白名單 | | 第二次啟動閃黑窗消失 | 前一次 crash 的 lock 殘留 | 刪 `visiona-local.lock` | | 瀏覽器看不到裝置 | USB 權限(Linux)/ WinUSB driver 沒裝 | Linux: `sudo bash install-udev.sh`;Windows: 從 UI 點「安裝 Kneron USB 驅動程式」 | | 模型庫是空的 | 首次啟動 seed 失敗 | 檢查 `wails.log` 的 seed 訊息;或手動複製 `models.json + nef/` 從 installer 到 user dataDir |