feat(local-tool): 首次啟動自動安裝 Kneron WinUSB driver
行為改變:Wails app 首次啟動會在 venv 就緒後、spawn server 前自動呼叫 libwdi 安裝 Kneron WinUSB driver。使用者不再需要手動點「安裝 USB Driver」 按鈕(按鈕保留供失敗後重試用)。 實作: - startServer() step 1.5 新增 ensureDriverInstalled() 呼叫 - 用 <dataDir>/.driver-installed 記號檔避免每次啟動都彈 UAC - 失敗不擋 server 啟動,只寫 log,使用者可稍後手動重試 - 新增 app-level log helper appLog() 寫到 <dataDir>/logs/wails.log (Wails Windows 以 windowsgui subsystem build,os.Stderr 指向 null device, 沒有這個檔使用者看不到 startup 期間的 debug 訊息) - 手動 InstallKneronDriver binding 成功時也寫記號檔 使用者移除 .driver-installed 檔就能強制重裝(例如 Windows 更新把 driver 弄壞時)。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d1ff55005a
commit
3355a096b8
@ -332,7 +332,68 @@ func (a *App) InstallKneronDriver() error {
|
|||||||
pyBin = resolved
|
pyBin = resolved
|
||||||
}
|
}
|
||||||
|
|
||||||
return installKneronWinUSBDriver(pyBin)
|
if err := installKneronWinUSBDriver(pyBin); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 手動安裝成功後也寫記號檔(之後就不會再自動彈 UAC)
|
||||||
|
a.markDriverInstalled()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// appLog 把一行訊息寫到 <dataDir>/logs/wails.log 以及 os.Stderr。
|
||||||
|
// Wails Windows app 以 windowsgui subsystem build,os.Stderr 指向 null device,
|
||||||
|
// 沒有這個檔使用者就看不到 startup 期間的 debug 訊息。
|
||||||
|
func (a *App) appLog(format string, args ...interface{}) {
|
||||||
|
msg := fmt.Sprintf("["+time.Now().Format("15:04:05")+"] "+format, args...)
|
||||||
|
fmt.Fprintln(os.Stderr, msg) // dev 模式 / macOS / Linux 會看到
|
||||||
|
if a.dataDir == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logsDir := filepath.Join(a.dataDir, "logs")
|
||||||
|
_ = os.MkdirAll(logsDir, 0o755)
|
||||||
|
f, err := os.OpenFile(filepath.Join(logsDir, "wails.log"),
|
||||||
|
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
fmt.Fprintln(f, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureDriverInstalled 在首次 startup 時自動安裝 Kneron WinUSB driver。
|
||||||
|
//
|
||||||
|
// 用 `<dataDir>/.driver-installed` 記號檔避免每次啟動都彈 UAC:
|
||||||
|
// - 檔案存在 → 跳過(之前已安裝過,假設仍有效)
|
||||||
|
// - 檔案不存在 → 呼叫 installKneronWinUSBDriver → 成功後寫記號檔
|
||||||
|
//
|
||||||
|
// 失敗時寫 stderr 但不回 error(讓 startup 流程繼續走,使用者之後可手動重試)。
|
||||||
|
//
|
||||||
|
// 使用者移除 `.driver-installed` 檔就能強制重裝(例如 Windows 更新把 driver 弄壞時)。
|
||||||
|
func (a *App) ensureDriverInstalled(pyBin string) error {
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
return nil // macOS / Linux 不需要
|
||||||
|
}
|
||||||
|
marker := filepath.Join(a.dataDir, ".driver-installed")
|
||||||
|
if fileExists(marker) {
|
||||||
|
a.appLog("driver 記號檔存在,跳過自動安裝:%s", marker)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
a.appLog("首次啟動:自動安裝 Kneron WinUSB driver(會彈出 UAC 提權視窗,請點「是」)")
|
||||||
|
if err := installKneronWinUSBDriver(pyBin); err != nil {
|
||||||
|
a.appLog("driver 自動安裝失敗(非致命):%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.markDriverInstalled()
|
||||||
|
a.appLog("driver 自動安裝完成,記號檔已建立:%s", marker)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// markDriverInstalled 寫 `.driver-installed` 記號檔,內容為時間戳供 debug。
|
||||||
|
func (a *App) markDriverInstalled() {
|
||||||
|
marker := filepath.Join(a.dataDir, ".driver-installed")
|
||||||
|
content := fmt.Sprintf("installed at %s\n", time.Now().Format(time.RFC3339))
|
||||||
|
_ = os.WriteFile(marker, []byte(content), 0o644)
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
@ -351,6 +412,15 @@ func (a *App) startServer() error {
|
|||||||
a.pythonModeR = pyMode
|
a.pythonModeR = pyMode
|
||||||
a.mu.Unlock()
|
a.mu.Unlock()
|
||||||
|
|
||||||
|
// 1.5. 首次啟動自動安裝 Kneron WinUSB driver(Windows only,macOS/Linux no-op)
|
||||||
|
// 失敗不擋 server 啟動 —— 使用者之後可手動點「安裝 USB Driver」按鈕重試。
|
||||||
|
// 用 .driver-installed 記號檔避免每次都跑。
|
||||||
|
if !a.mockMode && pyBin != "" {
|
||||||
|
if err := a.ensureDriverInstalled(pyBin); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "[visiona-local] driver auto-install failed (非致命,可於 UI 手動重試):", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2. 找 port
|
// 2. 找 port
|
||||||
port, err := pickPort(defaultPreferredPort)
|
port, err := pickPort(defaultPreferredPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user