From d1ff55005ab0e0eb842bb585e4623b08f93b3f2e Mon Sep 17 00:00:00 2001 From: jim800121chen Date: Sun, 12 Apr 2026 07:38:43 +0800 Subject: [PATCH] =?UTF-8?q?debug(local-tool):=20driver=20install=20verbose?= =?UTF-8?q?=20=E8=BC=B8=E5=87=BA=20+=20=E5=AE=89=E8=A3=9D=E5=BE=8C?= =?UTF-8?q?=E8=87=AA=E6=88=91=E9=A9=97=E8=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Python script 對每個 PID install 後 capture traceback,不再 swallow - 裝完立刻 scan_devices() 驗證 SDK 能不能開 handle,印 is_connectable - 安裝結果完整寫到 server.stderr.log(而不是只回錯誤摘要) - 判斷 DONE 標記 + 至少一個 OK 才算成功 這讓我們能清楚看到 libwdi 到底是「沒權限 / DLL 載入失敗 / 綁定成功但 裝置 is_connectable 還是 False(代表 driver 層級 OK 但裝置需要重插)」 哪一種情境,而不是只看到籠統的「error 28」。 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../api/handlers/system_driver_windows.go | 77 ++++++++++++++++--- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/local-tool/server/internal/api/handlers/system_driver_windows.go b/local-tool/server/internal/api/handlers/system_driver_windows.go index faf6e2d..db40ba7 100644 --- a/local-tool/server/internal/api/handlers/system_driver_windows.go +++ b/local-tool/server/internal/api/handlers/system_driver_windows.go @@ -28,30 +28,60 @@ func installKneronWinUSBDriverHandler(pythonBin string) error { // venv 根目錄 venvRoot := filepath.Dir(filepath.Dir(pythonBin)) + // Python script: + // 1. 對三種 PID 呼叫 kp.core.install_driver_for_windows + // 2. 安裝完立刻 scan_devices() 驗證,看 SDK 能不能開 handle + // 3. 不 swallow exception — 把 traceback 完整寫到 result 檔 pyScript := fmt.Sprintf(` -import sys, os +import sys, os, traceback result_path = r'%s' +lines = [] +def out(msg): + lines.append(str(msg)) + try: + sys.stdout.write(str(msg) + '\n'); sys.stdout.flush() + except Exception: + pass try: kp_lib = os.path.join(r'%s', 'Lib', 'site-packages', 'kp', 'lib') if os.path.isdir(kp_lib): os.environ['PATH'] = kp_lib + ';' + os.environ.get('PATH', '') - os.add_dll_directory(kp_lib) + try: + os.add_dll_directory(kp_lib) + except Exception as dle: + out(f"WARN: add_dll_directory failed: {dle}") import kp - results = [] + out(f"kp module imported: {kp.__file__}") for pid in [kp.ProductId.KP_DEVICE_KL520, kp.ProductId.KP_DEVICE_KL720, kp.ProductId.KP_DEVICE_KL720_LEGACY]: try: + out(f"Installing driver for {pid.name} (value={pid.value})...") kp.core.install_driver_for_windows(pid) - results.append(f"OK: {pid.name}") + out(f" OK: {pid.name}") except Exception as e: - results.append(f"SKIP: {pid.name}: {e}") - with open(result_path, 'w') as f: - f.write('\n'.join(results) + '\nDONE\n') + out(f" SKIP: {pid.name}: {type(e).__name__}: {e}") + # 驗證:裝完立刻 scan + 印 is_connectable + out("Verifying with scan_devices()...") + try: + descs = kp.core.scan_devices() + out(f" found {descs.device_descriptor_number} Kneron device(s)") + for i in range(descs.device_descriptor_number): + d = descs.device_descriptor_list[i] + out(f" [{i}] pid=0x{d.product_id:04X} usb_port={d.usb_port_id} connectable={d.is_connectable} fw={d.firmware}") + except Exception as se: + out(f" scan_devices failed: {type(se).__name__}: {se}") + out("DONE") except ImportError as e: - with open(result_path, 'w') as f: - f.write(f'ERROR: kp module not available: {e}\n') + out(f"ERROR: kp module not available: {e}") + out(traceback.format_exc()) except Exception as e: - with open(result_path, 'w') as f: - f.write(f'ERROR: {e}\n') + out(f"ERROR: {type(e).__name__}: {e}") + out(traceback.format_exc()) +finally: + try: + with open(result_path, 'w', encoding='utf-8') as f: + f.write('\n'.join(lines)) + except Exception: + pass `, resultPath, venvRoot) scriptPath := filepath.Join(os.TempDir(), "visiona-local-install-usb-driver.py") @@ -77,9 +107,32 @@ except Exception as e: } result := strings.TrimSpace(string(resultData)) + // 把完整結果印到 server stderr 方便使用者 / 我們 debug + fmt.Fprintln(os.Stderr, "=== Kneron driver install result ===") + fmt.Fprintln(os.Stderr, result) + fmt.Fprintln(os.Stderr, "=== end ===") + if strings.Contains(result, "ERROR:") { - return fmt.Errorf("driver 安裝失敗:%s", result) + return fmt.Errorf("driver 安裝失敗(完整訊息見 server.stderr.log):%s", firstLines(result, 6)) + } + + // 檢查 DONE 標記 + 至少一個 OK + if !strings.Contains(result, "DONE") { + return fmt.Errorf("driver 安裝未完成(沒有 DONE 標記,見 server.stderr.log):%s", firstLines(result, 6)) + } + if !strings.Contains(result, "OK:") { + return fmt.Errorf("driver 安裝全部失敗,沒有任何 PID 成功(見 server.stderr.log):%s", firstLines(result, 10)) } return nil } + +// firstLines 取結果的前 n 行,避免 error message 過長 +func firstLines(s string, n int) string { + lines := strings.Split(s, "\n") + if len(lines) > n { + lines = lines[:n] + lines = append(lines, "...") + } + return strings.Join(lines, " | ") +}