106 lines
4.7 KiB
Markdown
106 lines
4.7 KiB
Markdown
---
|
||||
|
|
feature: device-link
|
|||
|
|
status: designed
|
|||
|
|
updated: 2026-09-16
|
|||
|
|
branch: feat/device-link
|
|||
|
|
commits:
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Device Link(全链路统一连接层)
|
|||
|
|
|
|||
|
|
## Report
|
|||
|
|
|
|||
|
|
## [S1] Problem
|
|||
|
|
|
|||
|
|
多 iframe 保活后,各页仍**各自** `fetch` 设备,只是共用 `localStorage.dcu_sp_ip`:
|
|||
|
|
|
|||
|
|
- OTA 页轮询 `/system/status` → 顶栏「WiFi 已连接」
|
|||
|
|
- LAN9252 页轮询 `/spi/send` → 同一 IP 却显示「连接失败 · signal timed out」
|
|||
|
|
- EtherCAT 负载/状态又是一条独立轮询
|
|||
|
|
- BLE 在页内 GATT,其它页完全不知
|
|||
|
|
|
|||
|
|
用户直觉是「同一条设备连接」,实现却是 N 条互不证明的 HTTP/蓝牙会话。
|
|||
|
|
结果:一页绿灯一页红灯,像系统坏了,其实是**健康态没有单一真相源**。
|
|||
|
|
|
|||
|
|
## [S2] Design
|
|||
|
|
|
|||
|
|
### 原则
|
|||
|
|
|
|||
|
|
1. **外壳持有链路健康轮询**(tabs.html 常驻),功能页不再各自判定「设备在线」。
|
|||
|
|
2. **BroadcastChannel**(`dcu-device-link`)广播快照;页内只订阅渲染。
|
|||
|
|
3. **命令仍页内发**(SPI 0xA2、CAN send、OTA upload 等),但**连接灯只认 DeviceLink**。
|
|||
|
|
4. SPI/业务命令失败显示为 **子系统故障**(如「SPI 超时」),不得覆盖成「设备离线」——除非 DeviceLink 已判 ESP32 离线。
|
|||
|
|
5. 页面可**独立打开**(无外壳):检测不到 owner 时回退本地轮询,行为与改前一致。
|
|||
|
|
|
|||
|
|
### 模块
|
|||
|
|
|
|||
|
|
`js/common/device-link.js` 同一文件双角色:
|
|||
|
|
|
|||
|
|
| 角色 | 谁加载 | 职责 |
|
|||
|
|
|------|--------|------|
|
|||
|
|
| Owner | `tabs.html`(`window.__dcuDeviceLinkOwner = true`) | 轮询 ESP32 `/system/status`、ECAT `/ecat/api/status`+`/api/app/load`;维护快照并广播 |
|
|||
|
|
| Client | 各功能页 | `subscribe()` 收快照;`setIp()`;`reportBle()` 上报 GATT 状态 |
|
|||
|
|
|
|||
|
|
### 快照契约
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
{
|
|||
|
|
ts: 1710000000000,
|
|||
|
|
ip: "192.168.31.127",
|
|||
|
|
links: {
|
|||
|
|
esp32: { online: boolean, ageMs: number, failStreak: number },
|
|||
|
|
ecat: { online: boolean, busOpen: boolean, op: boolean, loop: boolean,
|
|||
|
|
loadValid: boolean, loadPerm: number, ageMs: number },
|
|||
|
|
ble: { online: boolean, deviceName: string, owner: string|null }
|
|||
|
|
},
|
|||
|
|
esp32Status: object|null // 最近一次 /system/status 全文(可 null)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 消息
|
|||
|
|
|
|||
|
|
| type | 方向 | 语义 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `hello` | Client→Owner | 请求立刻回一份 snapshot |
|
|||
|
|
| `snapshot` | Owner→All | 全量快照(ESP32 状态变化或 1s 心跳时发) |
|
|||
|
|
| `set-ip` | Client→Owner | 改设备 IP;Owner 写 localStorage 并立刻重拉 |
|
|||
|
|
| `ble-state` | Client→Owner | `{online, deviceName}` GATT 连/断 |
|
|||
|
|
|
|||
|
|
Owner 用 `BroadcastChannel`;无 BC 的环境 client 降级为「仅 localStorage 读 IP + 本地轮询」。
|
|||
|
|
|
|||
|
|
### 接入点(本轮)
|
|||
|
|
|
|||
|
|
| 页面/模块 | 改动 |
|
|||
|
|
|-----------|------|
|
|||
|
|
| `tabs.html` | 置 owner 标志并 `import` device-link 启动 |
|
|||
|
|
| `pages/index.html` + `js/common/polling.js`/`connection.js` | 有 DeviceLink 时:连接胶囊/健康读快照;`pollStatus` 仍可保留作数据源或改为消费 `esp32Status`(见任务) |
|
|||
|
|
| `lab/lan9252_pm.html` | 连接灯改读 DeviceLink;`/spi/send` 失败文案改为「SPI 失败」 |
|
|||
|
|
| `js/ble/gatt.js` 或 session | connect/disconnect 时 `reportBle` |
|
|||
|
|
| `js/ota/ecat_load.js` | 可选:owner 已有 load 时页内 poller 可只读快照(本轮先并存,避免双写 UI) |
|
|||
|
|
|
|||
|
|
### 超时与判定
|
|||
|
|
|
|||
|
|
- ESP32:成功 → `online=true, failStreak=0`;连续失败 ≥3 → `online=false`
|
|||
|
|
- ECAT:`/api/status` ok 且 slave 存在 → 轨道 `busOpen`;`/app/load` `valid && age_ms≤3000` → load 新鲜
|
|||
|
|
- BLE:仅 GATT 主人上报;无上报时 `online=false`
|
|||
|
|
|
|||
|
|
### 与 keep-alive 的关系
|
|||
|
|
|
|||
|
|
DeviceLink 跑在**外壳**,与 iframe 是否可见无关;隐藏页仍能显示统一灯(打开即亮)。
|
|||
|
|
|
|||
|
|
## [S3] Out of Scope
|
|||
|
|
|
|||
|
|
- 不把 SPI/CAN/OTA 命令全部塞进共享队列(串行化另开)
|
|||
|
|
- 不迁 SharedWorker
|
|||
|
|
- 不改 ESP32/ECAT 固件协议
|
|||
|
|
- 不合并 BLE GATT 到外壳(本轮只广播状态)
|
|||
|
|
|
|||
|
|
## Tasks
|
|||
|
|
|
|||
|
|
- [ ] T1: `device-link.js` Owner+Client + BroadcastChannel 协议 — acceptance: 单测式手工:外壳起后 postMessage/snapshot 可被 mock client 收到 (covers: S2)
|
|||
|
|
- [ ] T2: `tabs.html` 启动 Owner — acceptance: 打开外壳后 BC 上出现 esp32/ecat 快照,且关页后不再刷 (covers: S2)
|
|||
|
|
- [ ] T3: OTA 页连接态改读 DeviceLink(独立打开回退) — acceptance: 外壳内 OTA 与 LAN9252 的「在线/离线」一致;LAN9252 SPI 失败不再显示设备离线 (covers: S2)
|
|||
|
|
- [ ] T4: LAN9252 页灯与文案接入 — acceptance: 同 (covers: S2)
|
|||
|
|
- [ ] T5: BLE GATT 状态上报 — acceptance: BLE 连上后快照 ble.online=true,其它页可观察 (covers: S2)
|
|||
|
|
- [ ] T6: 语法检查 + 评审 — acceptance: node --check / 人工路径说明写入 Report (covers: S2)
|