手机主板温度:代理 0.8.0 上报,rack 拓扑图显示

- 机架代理 0.8.0(Thermal.java):读 /sys/class/thermal 的 quiet_therm(主板热敏)和最热的 CPU 核,
  每次长轮询拼在 URL 后面(&tb=&tc=,m°C),不为它多醒一次。电池那一路不报(电池拆了)。
- rack 后端 prrack/fleet.py:每 5 秒读 AppTask 后端的 /api/fleet/phones,按机架位 B-n 对到第 n 路,
  放进快照 phones[].live(在线 / 在跑活 / 代理版本 / 温度);变了才推 phones_live。只显示,不参与自动恢复。
  读不到就清空,不留旧值。服务单元加 --apptask-env。
- 前端拓扑图每台手机多一行「主板 / CPU」温度(50 °C 橙、60 °C 红,只改颜色)。
- agent_upgrade_safe.sh:升级后 4 分钟没开机就 power_cycle 救一次并补收尾。

五台已升到 0.8.0(两路并行推);第 2、1、5 路 staged 重启后卡死 / 僵住,断电重启后版本都已是新的。
后端测试 58 项全过;已部署。

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
zhou yang
2026-10-06 07:06:15 +08:00
共同撰写人 Claude Opus 5.5
父节点 580468e48e
当前提交 3bf4548c62
共修改 7 个文件,包含 175 行新增和 4 行删除
+11 -1
查看文件
@@ -46,6 +46,7 @@ class Hub:
self._clients: Set[asyncio.Queue] = set()
self._tasks: List[asyncio.Task] = []
self.tele_observers: List[Callable[[str, int, dict], None]] = []
self.phone_live: Dict[tuple, dict] = {} # (board, rail) → 手机自己报的那一半(fleet.py),只显示
# ---- 生命周期 ------------------------------------------------------------
async def start(self):
@@ -222,7 +223,16 @@ class Hub:
return {"id": board, "name": meta[0]["name"] if meta else "", "location": meta[0]["location"] if meta else "",
"online": b["online"], "stale": stale, "fw": b["fw"], "ip": b["ip"],
"last_tele": b["last_tele"], "status": b["status"], "pd": self.pd(board),
"phones": [dict(p) for p in phones]}
"phones": [dict(p, live=self.phone_live.get((board, p["rail"]))) for p in phones]}
def set_phone_live(self, live: Dict[tuple, dict]):
"""fleet.py 每轮读完调一次。只把**变了的板子**推给网页(温度几秒才动一下,不值得每轮都推)。"""
old, self.phone_live = self.phone_live, live
for board in {k[0] for k in old} | {k[0] for k in live}:
a = {r: v for (b, r), v in old.items() if b == board}
b_ = {r: v for (b, r), v in live.items() if b == board}
if a != b_:
self.broadcast({"t": "phones_live", "board": board, "live": b_})
def boards(self) -> List[dict]:
return [self.snapshot(b) for b in sorted(self.latest)]