Robot Cloud:打通设备、云端与浏览器的实时链路¶
Robot Cloud 是一个面向机器人运维场景的前后端分离项目。它不是只有设备 CRUD 的管理页,而是完整跑通了设备上线、遥测上报、异常掉线、远程指令、执行回执和浏览器实时更新这条主链路。
技术栈包括 FastAPI、SQLAlchemy 2.0、MySQL、Vue 3、TypeScript/TSX、Pinia、Element Plus、EMQX、paho-mqtt 与 WebSocket。
项目定位
这是用于验证机器人云端关键技术的本地演示系统,尚未进行生产压测,也未实现 OTA、日志归档和多租户。页面中会把“当前实现”和“生产扩展”明确区分。
我希望解决的问题¶
机器人运维系统同时面对三种通信模式:管理操作适合 HTTP,请求之外的设备事件适合 MQTT,浏览器实时更新则需要 WebSocket。难点不是分别连接三个协议,而是定义跨协议后的同一套状态语义。
项目最终形成三条数据流:
flowchart LR
Device[机器人 / 模拟设备] -->|telemetry · status| EMQX[EMQX Broker]
EMQX -->|MQTT subscribe| Backend[FastAPI 后端]
Backend -->|WebSocket broadcast| Browser[Vue 管理台]
Browser -->|POST command| Backend
Backend -->|MQTT cmd · QoS 1| Device
Device -->|cmd/ack| Backend
Backend -->|ACK / timeout| Browser
核心设计一:业务状态与物理连接状态分离¶
设备表中保留两个独立字段:
status: str # online / offline / fault,业务侧状态
mqtt_status: str # connected / disconnected,Broker 观察到的连接状态
它们看起来相似,实际回答不同问题。管理员可以把设备停用,但客户端仍保持 MQTT 连接;设备也可能业务上处于“在线”,网络却刚刚中断。把二者合并会让告警、筛选和故障定位都产生歧义。
前端因此使用两类标签分别展示业务状态和物理连接状态,WebSocket 收到 device/{sn}/status 时只更新 mqtt_status,避免实时事件覆盖人工设置。
核心设计二:把同步 MQTT 回调安全送回异步事件循环¶
FastAPI 与异步数据库会话运行在 asyncio 主事件循环,而 paho-mqtt 的 on_message 运行在后台线程。直接在回调里 await 广播并不可行。
项目在启动阶段保存主事件循环,MQTT 线程收到消息后使用线程安全调度:
数据库也采用两套会话:HTTP 请求走 AsyncSession + aiomysql,MQTT 后台回调走同步 Session + pymysql。这是一项基于运行环境的务实取舍:既保留 FastAPI 主链路的异步能力,也不强行让同步 SDK 伪装成异步组件。
核心设计三:远程命令必须有生命周期¶
“MQTT publish 成功”只代表 Broker 接收了消息,不代表机器人已经执行。项目为每条命令生成短 ID,并维护明确的状态变化:
后端把命令发到 device/{sn}/cmd;设备执行后在 device/{sn}/cmd/ack 返回同一 ID。后台监视任务每 3 秒检查一次待处理命令,超过 15 秒没有 ACK 就标为 timeout,并通过 WebSocket 通知前端。
这一闭环解决了两个容易被忽略的问题:前端不会无限显示“等待中”,运维人员也能区分“下发失败”和“执行超时”。当前状态保存在进程内,生产环境应迁移到 Redis 或数据库,并增加审计日志。
前端工程¶
Vue 端不是把所有逻辑写在设备列表页中:
- Axios 拦截器负责 Token 注入和 401 统一处理;
- Pinia 管理设备列表、分页、过滤、排序和加载状态;
- Vue Router 守卫处理登录访问边界;
WsClient封装 15 秒心跳和 1—30 秒指数退避重连;- TSX
DynamicForm使用FieldSchema[]驱动输入、数字、选择和开关字段。
动态表单的价值不只是少写一份模板,而是把字段定义变成可复用数据。未来设备型号的配置字段来自后端 Schema 时,渲染层仍然可以复用同一套组件。
验证方式¶
项目提供了四类辅助脚本:初始化设备档案、模拟多台设备、监听 WebSocket、强杀模拟进程并观察 MQTT Will。一次完整的手工验收覆盖:
- 用户注册、登录和受保护路由;
- 设备 CRUD、过滤、排序与分页;
- 模拟设备上线后,页面连接标签实时变绿;
- 遥测温度和电量抵达浏览器;
- 下发命令后收到对应 ACK;
- 强杀设备进程,EMQX 代发 Will,页面状态变为断开;
- 关闭网络后,浏览器 WebSocket 自动退避重连。
当前边界与下一步¶
| 已实现 | 下一阶段 |
|---|---|
| 设备资产、JWT、分页筛选、状态聚合 | RBAC 与多租户 |
| MQTT 上线、遥测、Will、Retained | 设备证书、Topic ACL |
| 命令 ID、ACK、15 秒超时 | Redis / DB 持久化与审计 |
| 单进程 WebSocket 广播 | Redis Pub/Sub 支持多副本 |
| MySQL 基础模型与索引 | 遥测时序表、分区与归档策略 |
这个项目让我确认:物联网后台的价值不在“使用了 MQTT”,而在于是否为断线、重复、延迟和跨线程定义了可以追踪的结果。