diff --git a/esp32_server.py b/esp32_server.py index f8d656a..50620df 100644 --- a/esp32_server.py +++ b/esp32_server.py @@ -157,7 +157,21 @@ g_ecat_port = None # 记住上次探到的端口, 避免每次请求都从 809 # 2) 拉起时**必须带 --parent-pid**: 让 native 在本服务退出后自我了断, # 不留孤儿进程占着 Npcap 适配器和端口。 # 3) 不再需要 venv/pythonw —— 单文件 exe, 无 Python 运行时依赖。 -ECAT_DIR = os.path.normpath(os.path.join(BASE_DIR, "..", "ecat")) +def _resolve_ecat_dir(base_dir): + """定位 tools/ecat(ecat_native.exe 的仓库,与本仓库平级)。 + + 从 git worktree(.../tools/ESP32/.worktrees/)启动时,BASE_DIR 的 + 上一级是 .worktrees 而不是 tools,直接 join("..","ecat") 会拼成 + .../ESP32/.worktrees/ecat,找不到 exe。先走回主检出再取兄弟目录。 + """ + d = os.path.abspath(base_dir) + parent = os.path.dirname(d) + if os.path.basename(parent) == ".worktrees": + d = os.path.dirname(parent) # → .../tools/ESP32 主检出 + return os.path.normpath(os.path.join(d, "..", "ecat")) + + +ECAT_DIR = _resolve_ecat_dir(BASE_DIR) ECAT_NATIVE_EXE = os.path.join(ECAT_DIR, "build_native", "ecat_native.exe") g_ecat_child = None # 由本服务拉起的 ecat 服务子进程(复用已有实例时为 None) g_ecat_log_fp = None # native stdout 落盘句柄(见 _ecat_autostart 内注释) diff --git a/scripts/test_ecat_dir_worktree.mjs b/scripts/test_ecat_dir_worktree.mjs new file mode 100644 index 0000000..a4b5e1f --- /dev/null +++ b/scripts/test_ecat_dir_worktree.mjs @@ -0,0 +1,57 @@ +/** + * 从 git worktree 启动 esp32_server 时,ECAT_DIR 必须指回主检出的 + * tools/ecat,而不是 .../ESP32/.worktrees/ecat。 + * 复刻 _resolve_ecat_dir 的纯路径逻辑(无 DOM/无网络)。 + */ +import assert from "node:assert/strict"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +/** 与 esp32_server._resolve_ecat_dir 同构(JS 侧只测路径形状)。 */ +function resolveEcatDir(baseDir) { + let d = path.resolve(baseDir); + const parent = path.dirname(d); + if (path.basename(parent) === ".worktrees") { + d = path.dirname(parent); + } + return path.normalize(path.join(d, "..", "ecat")); +} + +let failed = 0; +function check(name, fn) { + try { + fn(); + console.log(" ✓", name); + } catch (e) { + failed++; + console.log(" ✗", name, "—", e.message); + } +} + +console.log("[worktree ECAT_DIR]"); + +check("主检出 BASE_DIR → 兄弟 tools/ecat", () => { + const base = path.resolve("G:/Robot_v2/DCU/tools/ESP32"); + const got = resolveEcatDir(base); + assert.equal(got, path.resolve("G:/Robot_v2/DCU/tools/ecat"), got); +}); + +check("worktree BASE_DIR → 仍指主检出的 tools/ecat", () => { + const base = path.resolve("G:/Robot_v2/DCU/tools/ESP32/.worktrees/device-link"); + const got = resolveEcatDir(base); + assert.equal(got, path.resolve("G:/Robot_v2/DCU/tools/ecat"), got); +}); + +check("嵌套 slug 也走 .worktrees 父级", () => { + const base = path.resolve("G:/x/tools/ESP32/.worktrees/foo-bar"); + const got = resolveEcatDir(base); + assert.equal(got, path.resolve("G:/x/tools/ecat"), got); +}); + +/* BASE_DIR 恒为 esp32_server.py 所在目录;普通主检出已在第一项覆盖。 */ + +if (failed) { + console.log(`失败 ${failed} 项`); + process.exit(1); +} +console.log("通过 3 项,失败 0 项");