You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.1 KiB
6.1 KiB
实时监控 / 设备详情状态轮询
日期:2026-09-03
状态:已确认(待写实现计划)
范围:仅前端 laic-frontend
1. 背景
「实时监控」页当前只在 onMounted(以及指令成功后的 reload)调用一次 devices.load()。左侧设备列表、右侧详情面板、地图 marker 都读同一份 Pinia devicesStore;store 不刷新,UI 就不更新。
直播会话(useMonitorLive)与指令进度(useCommandProgress)已有各自定时器,但设备状态本身没有轮询,与「实时监控」语义不符。
现有 devices.load() 契约:
GET /v1/docks+GET /v1/drones(近全量 pageSize=100)- 再对每条
GET /v1/docks/:id/GET /v1/drones/:id取online+realtime - 映射 status / environment / alarms / 地图坐标后写入
docks/drones/assets
设备详情页 DeviceDetailView 同样只在挂载时 devices.load() 一次,无持续刷新。
2. 目标
- 实时监控页停在页面上时,设备列表 / 选中详情 / 地图状态约每 5 秒自动更新。
- 设备详情页只刷新当前路由打开的那一台设备详情,约每 5 秒一次。
- 离开页面或标签页隐藏时停止轮询;回到前台立即补刷一次再恢复。
- 不改变直播 heartbeat / phase / play-url,也不改变指令进度轮询。
3. 非目标
- 设备管理列表页(
DevicesView)不自动刷新(仍手动刷新按钮)。 - 不新增后端批量快照 / WebSocket / SSE。
- 不在本轮优化掉 Monitor 的 N+1 详情请求(已知成本,后续可换批量 API)。
- 不改
DevicesView分页本地tableRecords数据源。
4. 方案(页面各自定时器)
采用页面级 setInterval,不引入 store 全局轮询器。
4.1 MonitorView
onMounted:先做现有首次devices.load()(失败仍 toast 一次,与现状一致);无论首次成败,只要仍挂在本页就启动setInterval(() => void pollDevices(), 5000)。pollDevices:- 若上一轮
load仍 in-flight → 跳过本轮(防请求堆积)。 - 否则
await devices.load()。 - 单次失败:静默(
console.warn即可),不 toast,下一轮继续。 401仍走现有http拦截器(登出 / 跳转)。
- 若上一轮
onUnmounted/pagehide:clearInterval;用页面级disposed/generation忽略卸载后才返回的轮询结果(避免卸载后无意义的续跑逻辑;store 已被load()写入可保留,不必回滚)。document.visibilitychange:hidden→ 暂停(clearInterval)visible→ 立即pollDevices()一次,再重新setInterval
- 指令路径现有
reload()继续直接devices.load();与轮询共享同一 in-flight 闸门更佳(同一loading/inflight标志),避免指令 reload 与定时器重叠打双份。
4.2 DeviceDetailView
- 保留挂载时一次
devices.load()(以及非 admin 的 logs/alarms)。 - 另启 当前
route.params.id的详情轮询,间隔 5000ms:- dock →
GET /v1/docks/:id - drone →
GET /v1/drones/:id
- dock →
- 响应经 store 新方法合并进对应一条记录(见 4.3),驱动本页与若仍挂着的 Monitor 共享状态。
watch(() => route.params.id):停旧定时器,按新 id 立即拉一次并重启 interval。- 生命周期 / visibility / in-flight 跳过 / 失败静默:与 Monitor 同规则。
- 离开页:清除定时器。
4.3 devicesStore 增量回写
新增(命名可微调,语义固定):
applyDockDetail(id, detail)applyDroneDetail(id, detail)
行为:
- 找到
docks/drones中对应id;找不到则 no-op +console.warn(不触发全量load,避免详情页单独制造全表风暴)。 - 用与
load()相同的映射规则更新该条:online、realtime、status/statusClass、environment、alarms、mode、无人机battery/_lat_lon_alt等派生字段。 - 同步更新
assets[id]上用于地图的 status / 坐标 / realtime 相关字段,避免 Monitor 与详情不一致。 - 不重拉列表,不重建无关条目。
load() 本身可抽一小段「detail → record 字段」纯函数供全量与增量共用,避免两套映射分叉;若改动面过大,允许增量路径先复制必要映射,但字段语义必须与 load() 一致。
5. 错误与并发
| 场景 | 行为 |
|---|---|
| 轮询 HTTP 业务/网络失败 | 静默,保留旧数据,下轮再试 |
| 401 | 现有拦截器登出 |
| 上轮未完成 | 跳过本轮 |
| 页面 hidden | 暂停 timer |
| 页面 visible | 立即补一次 + 重启 timer |
| 详情 id 切换 | 取消旧轮询,新 id 立即请求 |
6. 验收
- 停留在实时监控:约 5s 内列表状态 / 右侧详情(电量、舱门、环境、在线等)/ 地图 marker 会随后端变化更新。
- 打开设备详情 A:网络面板仅见对 A 的详情轮询;切换到 B 后改为 B。
- 设备管理列表页打开时无定时
/docks/:id/drones/:id轮询(仅用户点击刷新或进入时的请求)。 - 从监控/详情路由离开,或切到其它浏览器标签:轮询停止;切回后先补刷再按 5s 继续。
- 开播、停播、指令进度浮层行为与改前一致。
7. 风险
- Monitor 每 5s 全量
load()仍是列表 + N 次详情;设备数量上来后 QPS 偏高。本轮按产品选择接受;后续应用批量状态接口替换pollDevices内部实现,页面契约可不变。 - 详情增量映射若与
load()分叉,会出现「详情页字段」与「监控全量刷新后字段」不一致;优先抽共享映射。
8. 实现落点(预告)
| 文件 | 变更 |
|---|---|
src/stores/modules/devicesStore.js |
抽共享映射;新增 applyDockDetail / applyDroneDetail |
src/views/MonitorView/MonitorView.vue |
5s 轮询 + visibility + 与 reload 共用 in-flight |
src/views/DeviceDetailView/DeviceDetailView.vue |
当前 id 详情轮询 + id watch + visibility |
完成后:实现计划 → 编码 → 构建部署 → 人工看监控与详情是否按 5s 变。