diff --git a/README.md b/README.md index f4d40bf..6409aeb 100644 --- a/README.md +++ b/README.md @@ -20,33 +20,50 @@ 同款结构。pywebview 窗口没有地址栏与刷新按钮,因此服务器会向 HTML 响应注入 `_reload.html`(右下角 ⟳ 悬浮按钮 + F5/Ctrl+R 拦截)。 -`build_mem.bat` 是从 Git Bash 调用 ESP-IDF 构建 `DCU/ESP32_COMM` 的辅助脚本 +`build_mem.bat`(在 `scripts/`)是从 Git Bash 调用 ESP-IDF 构建 `DCU/ESP32_COMM` 的辅助脚本 (会先清掉 MSYS 环境变量绕过 IDF 的 MSys 检测)。 +## 目录结构 + +``` +tabs.html 标签外壳 = 工具入口(GET / 会 302 到这里) +pages/ 主功能页,各自引用 ../css 与 ../js +lab/ 自包含实验页,零外部依赖,删文件即可移除 +css/ js/ 共享样式与 ES module +scripts/ 构建辅助脚本 +esp32_server.py 本地页面服务 + 桌面窗口外壳 +esp32_app.vbs esp32_web.vbs serve.bat 三个启动入口 +``` + ## 页面 `tabs.html` 是单窗口多标签外壳,用 iframe 承载下列功能页,激活标签记忆在 -`localStorage` 的 `dcu_tab`: +`localStorage` 的 `dcu_tab`(按文件名匹配,与目录层级无关): | 页面 | 用途 | 主要脚本 | |------|------|----------| -| `index.html` | OTA 中继控制台(ESP32 WiFi OTA / GD32 relay / BLE OTA) | `js/app.js`、`js/ota/*`、`js/ble_ota.js`、`js/ble/*` | -| `can.html` | CAN 通信(DBC 解析、消息编辑与批量发送) | `js/can.js`、`js/can/*` | -| `can_monitor.html` | CAN 接收监控 | `js/can_monitor.js` | -| `motor.html` | 电机控制台(CAN FD) | `js/motor/main.js`、`js/motor/tick.worker.js` | -| `continuity_test.html` | LAN9252 通断测试(自包含,含 WebGL 液态玻璃效果) | 内联 | -| `spi_test.html` | 【临时功能 SPI-SPEEDTEST】SPI 链路测速,完全自包含,删文件即可移除 | 内联 | +| `pages/index.html` | OTA 中继控制台(ESP32 WiFi OTA / GD32 relay / BLE OTA) | `js/app.js`、`js/ota/*`、`js/ble_ota.js`、`js/ble/*` | +| `pages/can.html` | CAN 通信(DBC 解析、消息编辑与批量发送) | `js/can.js`、`js/can/*` | +| `pages/can_monitor.html` | CAN 接收监控(独立页;也被 `can.html` 内嵌复用) | `js/can_monitor.js` | +| `pages/motor.html` | 电机控制台(CAN FD) | `js/motor/main.js`、`js/motor/tick.worker.js` | +| `lab/continuity_test.html` | LAN9252 通断测试(自包含,含 WebGL 液态玻璃效果) | 内联 | +| `lab/spi_test.html` | 【临时功能 SPI-SPEEDTEST】SPI 链路测速,完全自包含,删文件即可移除 | 内联 | ## 目录约定 - `css/` —— `base`(变量/基础)、`layout`、`components`、`can`、`motor`、`responsive`。 - `js/common/` —— 跨页公用:`base-url`(设备 IP)、`connection`、`polling`、`log`、`toast`、`utils`、`heap-low-history`。 -- `js/ota/`、`js/ble/`、`js/can/`、`js/motor/` —— 按功能域拆分的 ES module。 +- `js/ota/`、`js/ble/`、`js/can/`、`js/motor/` —— 按功能域拆分的 ES module,跨域引用只走 `js/common/`。 - 子系统配色:ESP32 = 青绿 · GD32 = 琥珀 · BLE = 蓝 · CAN = 绿 · 内存/调试 = 紫。 ## 改动须知 - **改 CSS/JS 后必须 bump 引用处的 `?v=` 缓存版本**,否则浏览器会加载旧代码。 +- `pages/` 与 `lab/` 下的页面引用共享资源要用 `../css/`、`../js/` 前缀; + **JS 模块内部引用其他资源(如 Worker)须用 `import.meta.url` 解析**, + 不能写文档相对路径,否则页面换层级就会 404(见 `js/motor/main.js` 的 `TICK_WORKER_URL`)。 +- 新增功能页放 `pages/`,并在 `tabs.html` 的 `TABS` 数组登记; + 一次性/实验页放 `lab/` 并保持自包含。 - OTA 流程以设备端为真相来源:WiFi OTA 由设备回读分区算 SHA-256 后自动重启; BLE OTA 由前端发 `0xF8 + 32B SHA-256`、设备端比较并通过 FINISH 事件报结果 (前端不做 SHA 预比较);GD32 relay 状态以 `/system/status` 为准。 diff --git a/esp32_server.py b/esp32_server.py index 7c5af20..1575347 100644 --- a/esp32_server.py +++ b/esp32_server.py @@ -5,10 +5,12 @@ 而所有业务 API 由 ESP32 固件的 80 端口跨域提供(js/common/base-url.js 里的 设备 IP, 存 localStorage 的 dcu_esp_ip)。因此本脚本不碰任何硬件, 只做静态服务。 - GET / → index.html (OTA 控制台; 标签外壳为 tabs.html) - GET / → 本目录静态文件(html/css/js) + GET / → 302 到 /tabs.html (标签外壳, 工具主页) + GET / → 本目录静态文件(tabs.html / pages/ / lab/ / css/ / js/) POST /api/shutdown → 结束本服务进程(窗口模式父进程用它通知子进程退出) +目录约定: tabs.html 在根(工具入口), 功能页在 pages/, 自包含实验页在 lab/。 + 启动模式: --window pywebview 桌面窗口(esp32_app.vbs) —— GUI 进程 + 独立服务子进程 默认 浏览器标签页(esp32_web.vbs / serve.bat) @@ -78,7 +80,10 @@ user-select:none;opacity:.55;transition:opacity .15s} var home=document.getElementById('__home'); var p=location.pathname; var isShell=!!document.getElementById('view'); - if(!isShell&&!/\\/$/.test(p)&&!/\\/index\\.html$/.test(p)){ + // 顶层且非标签外壳(浏览器直开 pages/xxx.html) → 给个 ⌂ 回到外壳。 + // 判定基准是外壳自身(有 #view / 路径为 tabs.html 或 /), 不能再看 index.html: + // 重排后 pages/index.html 只是普通功能页, 直开它同样该显示 ⌂。 + if(!isShell&&!/\\/$/.test(p)&&!/\\/tabs\\.html$/.test(p)){ home.style.display='block'; home.addEventListener('click',function(){location.href='__HOME__';}); } @@ -152,7 +157,14 @@ class Handler(SimpleHTTPRequestHandler): def do_GET(self): path = self.path.split("?")[0].lower() - if path.endswith(".html") or path == "/" or not os.path.splitext(path)[1]: + # 根路径不再有 index.html(已移入 pages/), 直接送去标签外壳 + if path in ("/", "/index.html"): + self.send_response(302) + self.send_header("Location", "/tabs.html") + self.send_header("Content-Length", "0") + self.end_headers() + return + if path.endswith(".html") or not os.path.splitext(path)[1]: # HTML(含无扩展名的 SPA 路由): 注入刷新悬浮按钮/快捷键后返回 fp = self.translate_path(self.path) if os.path.isdir(fp): diff --git a/js/motor/main.js b/js/motor/main.js index 0e218ba..80e019c 100644 --- a/js/motor/main.js +++ b/js/motor/main.js @@ -967,7 +967,9 @@ function scheduleFeedbackPoll(gen, delayMs) { * 故把 motor 轮询的节拍源放到 tick.worker.js:Worker 按设定间隔定期发 * 'tick' 消息,主线程收到后才真正发起一次 fetchMotorState。 * 前台行为与原来一致;后台也能守住设定的刷新速度。 */ -const TICK_WORKER_URL = "js/motor/tick.worker.js?v=20260802g"; +/* 用 import.meta.url 解析: 本模块在 js/motor/ 下, 与引用它的页面在哪层目录无关 + * (页面已移入 pages/, 文档相对路径会解析成 /pages/js/... 而 404)。 */ +const TICK_WORKER_URL = new URL("./tick.worker.js?v=20260802g", import.meta.url); let tickWorker = null; /* Web Worker 节拍源(null = 未创建/失败) */ let tickUseFallback = false; /* Worker 创建/加载失败 → 回退到 setTimeout 兜底 */ diff --git a/continuity_test.html b/lab/continuity_test.html similarity index 100% rename from continuity_test.html rename to lab/continuity_test.html diff --git a/spi_test.html b/lab/spi_test.html similarity index 100% rename from spi_test.html rename to lab/spi_test.html diff --git a/can.html b/pages/can.html similarity index 96% rename from can.html rename to pages/can.html index 36ab440..da03303 100644 --- a/can.html +++ b/pages/can.html @@ -6,11 +6,11 @@ ESP32 CAN 通信控制台 - - - - - + + + + +
@@ -329,6 +329,6 @@ 操作成功
- + diff --git a/can_monitor.html b/pages/can_monitor.html similarity index 95% rename from can_monitor.html rename to pages/can_monitor.html index 7518b57..5e270ef 100644 --- a/can_monitor.html +++ b/pages/can_monitor.html @@ -6,11 +6,11 @@ ESP32 CAN 接收监控 - - - - - + + + + +
@@ -184,6 +184,6 @@ 操作成功
- + \ No newline at end of file diff --git a/index.html b/pages/index.html similarity index 96% rename from index.html rename to pages/index.html index 6ec1868..85e6b49 100644 --- a/index.html +++ b/pages/index.html @@ -12,11 +12,11 @@ ESP32 OTA 中继控制台 - - - - - + + + + +
@@ -499,9 +499,9 @@ 操作成功
- - - + + + diff --git a/motor.html b/pages/motor.html similarity index 99% rename from motor.html rename to pages/motor.html index c9526b0..1b81852 100644 --- a/motor.html +++ b/pages/motor.html @@ -6,7 +6,7 @@ 电机控制台 — Motor CAN FD Console - + @@ -388,6 +388,6 @@ - + \ No newline at end of file diff --git a/build_mem.bat b/scripts/build_mem.bat similarity index 100% rename from build_mem.bat rename to scripts/build_mem.bat diff --git a/serve.bat b/serve.bat index 26b9349..ae8a111 100644 --- a/serve.bat +++ b/serve.bat @@ -11,7 +11,8 @@ echo ESP32 Debug Console (console mode) echo PORT: %PORT% echo ======================================== echo. -echo OTA Console: http://localhost:%PORT%/index.html +echo Tab shell: http://localhost:%PORT%/tabs.html +echo OTA Console: http://localhost:%PORT%/pages/index.html echo. echo Switch WiFi / BLE mode in the "Device Connect" card. echo No-black-box: double-click esp32_app.vbs instead. diff --git a/tabs.html b/tabs.html index 18d65ca..d8ae09e 100644 --- a/tabs.html +++ b/tabs.html @@ -1,6 +1,6 @@