diff --git a/rack/README.md b/rack/README.md index 5617781..eab1924 100644 --- a/rack/README.md +++ b/rack/README.md @@ -77,7 +77,8 @@ bash rack/deploy/deploy.sh # 构建 → 传 → 服务器上 Python 3.8 | 门户 | 应用 id 16,key `rack`、端口 7764(`phonerail` 已被需求文档站占用);授权 admin + staff,**不给 viewer**(没有只读模式,能断电重启) | | 保留期 | 原始 1 s 遥测 2 天;分钟聚合 90 天;事件 / 审计 / 恢复记录永久 | -加一块新板:USB 配网(`firmware/tools/prlink.py` 发 `wifi` / `mqtt`)+ 服务器上给它开 MQTT 账号(见 memory 里的 broker 说明),上线后自动出现在总览。 +加一块新板:配网(固件 ≥0.1.19:手机连板子自己开的热点 `PhoneRail-xxxxxx`,在 192.168.4.1 网页里填 WiFi 与 MQTT, +热点口令 `88888888`;或 USB:`firmware/tools/prlink.py` 发 `wifi` / `mqtt`)+ 服务器上给它开 MQTT 账号(见 memory 里的 broker 说明),上线后自动出现在总览。 ## 踩过的坑 diff --git a/rack/backend/prrack/protocol.py b/rack/backend/prrack/protocol.py index 9087e67..d369159 100644 --- a/rack/backend/prrack/protocol.py +++ b/rack/backend/prrack/protocol.py @@ -172,7 +172,15 @@ def build_command(req: dict) -> dict: duty = req.get("duty") if not isinstance(duty, int) or isinstance(duty, bool) or not 0 <= duty <= 1000: raise CmdError("风扇下限必须是 0~1000‰(0 = 自动)") - return {"cmd": cmd, "duty": duty} + out = {"cmd": cmd, "duty": duty} + # 固件 ≥0.1.21:`sec` = 这个下限保持多少秒,到期自动回落到自动(没给 = 一直保持)。 + sec = req.get("sec") + if sec is not None: + if not isinstance(sec, int) or isinstance(sec, bool) or not 0 <= sec <= 86400: + raise CmdError("风扇下限的保持时间必须是 0~86400 秒(0 / 不给 = 一直保持)") + if sec: + out["sec"] = sec + return out raise CmdError("不支持的命令:%r" % (cmd,)) diff --git a/rack/backend/prrack/simboard.py b/rack/backend/prrack/simboard.py index 247afcd..b18ae19 100644 --- a/rack/backend/prrack/simboard.py +++ b/rack/backend/prrack/simboard.py @@ -185,7 +185,10 @@ class SimBoard: return {"ok": True, "oldest": 0, "next": len(self.events), "ev": ev} if cmd == "fan": self.fan_floor = req["duty"] - return {"ok": True, "floor": self.fan_floor} + rsp = {"ok": True, "floor": self.fan_floor} + if "sec" in req: + rsp["sec"] = req["sec"] # 模拟板不倒计时:只把形状回对 + return rsp if req.get("seq") is None: return {"ok": False, "err": "no_seq"} if cmd == "hub": # 0.1.14:固件 hubctl.h 的命令形状;不模拟倒计时 diff --git a/rack/backend/tests/test_core.py b/rack/backend/tests/test_core.py index 28ff92a..61b3c8b 100644 --- a/rack/backend/tests/test_core.py +++ b/rack/backend/tests/test_core.py @@ -59,12 +59,24 @@ def test_build_command_whitelist_and_ranges(): {"cmd": "power", "rail": True, "on": True}, # bool 不是 int {"cmd": "vbus", "rail": 1, "on": 1}, {"cmd": "fan", "duty": 1001}, + {"cmd": "fan", "duty": 500, "sec": 86401}, + {"cmd": "fan", "duty": 500, "sec": -1}, + {"cmd": "fan", "duty": 500, "sec": "60"}, + {"cmd": "fan", "duty": 500, "sec": True}, ] for b in bad: with pytest.raises(protocol.CmdError): protocol.build_command(b) +def test_fan_floor_may_carry_a_hold_time(): + """固件 0.1.21:`sec` 原样带下去;0 / 不给 = 一直保持,命令里就不出现这个键(老固件不认它也没关系)。""" + assert protocol.build_command({"cmd": "fan", "duty": 600}) == {"cmd": "fan", "duty": 600} + assert protocol.build_command({"cmd": "fan", "duty": 600, "sec": 0}) == {"cmd": "fan", "duty": 600} + assert protocol.build_command({"cmd": "fan", "duty": 600, "sec": 90}) == {"cmd": "fan", "duty": 600, "sec": 90} + assert protocol.build_command({"cmd": "fan", "duty": 0, "sec": 86400}) == {"cmd": "fan", "duty": 0, "sec": 86400} + + def test_seq_is_strictly_increasing_and_in_range(): t = [1700000000.0] g = protocol.SeqGen(clock=lambda: t[0]) diff --git a/rack/frontend/src/pages/Board.vue b/rack/frontend/src/pages/Board.vue index b62d73a..cb605ae 100644 --- a/rack/frontend/src/pages/Board.vue +++ b/rack/frontend/src/pages/Board.vue @@ -46,9 +46,10 @@