- 分区重排(16 MB,nvs 0x9000 不动):ota_0 / ota_1 各 3 MB、otadata 0x610000、evlog 挪到 0x612000;
flash 大小改正为 16 MB;开 CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE
- ota.{h,c}(宿主机可测):URL / sha256 校验、判定(下载中 busy、待验证拒、同镜像不重下)、健康判据
- ota_esp.{h,c}:t_ota 任务经 HTTP 下载、边下边算 sha256、核 project_name 后切启动分区;
新固件 120 s 内连不上 broker 就回滚(t_link 判 + esp_timer 兜底);重启前等回应发出、事件落盘
- 新命令 ota = 13(两条链路都收,reboot 默认 false:复位 = 五台手机断电一次);status 加 ota 一块;
新事件 LOG_EV_OTA = 18;PROJECT_VER 取 PHONERAIL_FW_VERSION
- test_ota.c 13 例,全工程 442 例全绿;版本 0.1.0-c2
- 服务器:nginx 7765 只放行 /fw/<16 hex>.bin(不过门户,路由器转发已加);tools/ota_push.py 推送;
rack protocol.py 加事件 18 / 命令 13(已部署)
- 板 B:推送到自证健康 19 s;错哈希不切分区;删掉 broker 账号后 120 s 回滚,bad 报出被回滚的版本
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PhoneRail 机架管理(服务器前后端)
需求文档 D7 的实现:板子经 WiFi → MQTT(TLS)连到 home 服务器,这里把遥测存下来、画出来, 让人在网页上操作每一台手机,并按电流判据做自动崩溃恢复(先软后硬)。
板子 ──mqtts 8883──▶ mosquitto(evse 共用)──127.0.0.1:1884──▶ 后端 prrack(127.0.0.1:7764)
│ SQLite(WAL)
浏览器 ──▶ rack.home.example:7755(统一门户,auth_request)──────────┘ 同进程托管前端 dist
| 目录 | 内容 |
|---|---|
backend/prrack/ |
protocol.py 板侧协议镜像(命令白名单 / 事件表 / seq)· bus.py MQTT 抽象(paho / 进程内假 broker)· hub.py 核心 · recovery.py 自动恢复 · db.py 存储 · api.py HTTP+WS · simboard.py 模拟板子 · main.py 入口 |
backend/tests/ |
25 例(pytest),本机与服务器(Python 3.8)都跑 |
frontend/ |
Vue3 + Vite + Element Plus + ECharts(按需引入) |
deploy/ |
deploy.sh 一键部署 · systemd unit · 每日备份 |
规矩(都来自板侧协议 §10.8,改代码前先看)
- 重传用同一个 seq(
Hub.call)。换 seq 重发 = 「按 10 秒电源键」可能执行两遍。测试test_retransmit_reuses_the_same_seq…钉着。 - 失联不动作:板子离线 / 遥测中断只标状态;自动恢复只在「开了 + 在线 + 在位 + 有电 + 执行器空闲」时判定。
- 时间用服务器收到的时刻(板子没有墙钟),事件表另存板子的
t_ms作参考。 - 只绑 127.0.0.1:
X-Auth-User(门户覆盖式注入)按 L2 信任,只用于审计「谁按的」;main.py拒绝非回环绑定。 - 命令白名单在
protocol.build_command:wifi/mqtt(只收 USB)与events(服务器自己同步)不给界面。板子的校验仍是唯一权威。
本机开发
cd backend && python -m prrack.main --sim 2 --db dev.db --static ../frontend/dist # 两块模拟板,第 1 块第 2 路「卡死」
cd frontend && pnpm dev # http://localhost:5173,/api 反代到 7764
cd backend && python -m pytest -q tests
部署 / 运维
bash rack/deploy/deploy.sh # 构建 → 传 → 服务器上 Python 3.8 跑测试(不过不换)→ 重启
| 项 | 值 |
|---|---|
| 服务 | systemd phonerail-rack,/root/phonerail-rack/{backend,frontend/dist,venv,data} |
| 日志 | /var/log/phonerail-rack.log |
| 库 | /root/phonerail-rack/data/rack.db;每日 03:40 sqlite3 .backup 到 backup/,留 14 份 |
| MQTT | phonerail-backend @ 127.0.0.1:1884,口令读 /root/phonerail/mqtt.env |
| 门户 | 应用 id 16,key rack、端口 7764(phonerail 已被需求文档站占用);授权 admin + staff,不给 viewer(没有只读模式,能断电重启) |
| 保留期 | 原始 1 s 遥测 2 天;分钟聚合 90 天;事件 / 审计 / 恢复记录永久 |
加一块新板:USB 配网(firmware/tools/prlink.py 发 wifi / mqtt)+ 服务器上给它开 MQTT 账号(见 memory 里的 broker 说明),上线后自动出现在总览。
踩过的坑
- uvicorn 没装
websockets时不报错:/api/ws被当成普通 GET 落到 SPA 兜底,回 200 HTML,页面能开但永不刷新。 TestClient 自带 WS 实现测不出来 ⇒main.py启动自检 +test_a_real_websocket_library_is_installed(部署时在服务器上跑)。 - 服务器 Python 3.8 + 老 pip:starlette 最高 0.44.0、websockets 13.1;新版本在清华镜像上直接「找不到」。
- 本机有 HTTP 代理(
HTTP_PROXY=127.0.0.1:7890),经它访问不存在的公网端口会回 502 而不是连不上;验「端口没暴露」要加--noproxy '*'。 - 门户的
/admin/grants是整张矩阵覆盖写:脚本化加授权要先读出现有全部授权再加新的。改门户前先备份portal.db。 - 门户进程把应用表缓存在内存,直接改库它看不见(没有重载信号,只能重启);走它自己的
/admin/apps接口会自动reloadAcl()。
已知限制 / 下一步
- 自动恢复阈值没有实测依据(平线 5 mA / 过低 30 mA / 窗口 5 分钟)。插上刀片和手机后先看几天曲线再定,默认逐台关闭。
- adb 探活:接口
recovery.Prober已留,服务器目前够不着手机(机架在 192.168.8.x 的 NAT 后面)。 - 前端 Element Plus 是整包引入(gzip ≈335 KB),手机首屏偏慢;改成按需引入可以再砍一半。
- 没有细分权限:门户授权了这个应用的人都能操作;审计记着是谁。