feat(ppclock): batch JSONL 批量执行(单连接),104 测试全绿
This commit is contained in:
1 parent
f291c61676
commit
5ff399673a
6 files changed
+906
-71
No files matched your search
+102
-71
@@ -161,83 +161,114 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
sp.add_argument("hex")
|
||||
sp.add_argument("--channel", default="rxtx", choices=["rxtx", "epd"])
|
||||
sp.add_argument("--no-response", action="store_true")
|
||||
|
||||
sp = sub.add_parser("batch", help="JSONL 批量执行(单次连接,agent 编排)")
|
||||
sp.add_argument("file", help="JSONL 文件,每行 {\"argv\": [...]};'-' 读 stdin")
|
||||
sp.add_argument("--stop-on-error", action="store_true")
|
||||
return p
|
||||
|
||||
|
||||
async def _dispatch(args) -> dict | None:
|
||||
async def _run_command(args, t):
|
||||
"""在已有连接上执行单条命令(body 供 batch 复用)。"""
|
||||
c = args.cmd
|
||||
if c == "time":
|
||||
dt = (datetime.datetime.fromisoformat(args.datetime)
|
||||
if args.datetime else None)
|
||||
await C.set_time(t, dt)
|
||||
return {"set": (dt or datetime.datetime.now()).isoformat()}
|
||||
if c == "mode":
|
||||
await C.set_mode(t, args.name)
|
||||
return {"mode": args.name}
|
||||
if c == "toggle":
|
||||
await C.toggle(t, args.name)
|
||||
return {"toggle": args.name}
|
||||
if c == "clear":
|
||||
await C.clear(t)
|
||||
return None
|
||||
if c == "image":
|
||||
return await _cmd_image(args, t)
|
||||
if c == "template":
|
||||
return await _cmd_template(args, t)
|
||||
if c == "countdown":
|
||||
if args.off:
|
||||
await C.countdown_off(t)
|
||||
return {"countdown": "off"}
|
||||
if args.show_calendar:
|
||||
if not args.prefix:
|
||||
raise ValueError("--show-calendar 需要 --prefix 提供日历文字")
|
||||
await C.countdown_set(t, None, args.mode, prefix=args.prefix,
|
||||
show_calendar=True)
|
||||
return {"calendar_text": args.prefix}
|
||||
if not args.date:
|
||||
raise ValueError("缺少日期(YYYY-MM-DD)或 --off")
|
||||
d = datetime.date.fromisoformat(args.date)
|
||||
await C.countdown_set(t, d, args.mode, prefix=args.prefix)
|
||||
return {"date": d.isoformat(), "mode": args.mode,
|
||||
"prefix": args.prefix}
|
||||
if c == "sleep":
|
||||
on = args.on or not args.off
|
||||
await C.sleep(t, on, args.start, args.end)
|
||||
return {"on": on, "start": args.start, "end": args.end}
|
||||
if c == "parking":
|
||||
await C.parking(t, args.number)
|
||||
return {"number": args.number}
|
||||
if c == "rotation":
|
||||
await C.rotation(t, count=args.count, interval=args.interval, unit=args.unit)
|
||||
return {"count": args.count, "interval": args.interval, "unit": args.unit}
|
||||
if c == "lut":
|
||||
await C.lut(t, args.value)
|
||||
return {"lut": hex(args.value)}
|
||||
if c == "activate":
|
||||
if args.show_id:
|
||||
return {"device_id": await C.get_device_id(t)}
|
||||
if not args.code:
|
||||
raise ValueError("缺少激活码或 --show-id")
|
||||
await C.activate(t, args.code)
|
||||
return None
|
||||
if c == "wifi":
|
||||
await C.wifi(t, args.ssid, args.password, args.city)
|
||||
return None
|
||||
if c == "raw":
|
||||
data = bytes.fromhex(args.hex)
|
||||
if args.channel == "epd":
|
||||
await t.write_epd(data, response=not args.no_response)
|
||||
else:
|
||||
await t.write_rxtx(data, response=not args.no_response)
|
||||
return {"sent": len(data)}
|
||||
raise ValueError(f"未知命令 {c}")
|
||||
|
||||
|
||||
async def _run_batch(args) -> list:
|
||||
lines = (sys.stdin.read() if args.file == "-"
|
||||
else open(args.file, encoding="utf-8").read())
|
||||
results = []
|
||||
parser = build_parser()
|
||||
async with _make_transport(args) as t:
|
||||
for lineno, line in enumerate(lines.splitlines(), 1):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
item = json.loads(line)
|
||||
argv = item["argv"] if isinstance(item, dict) else item
|
||||
sub_args = parser.parse_args(argv)
|
||||
data = await _run_command(sub_args, t)
|
||||
results.append({"line": lineno, "ok": True, "cmd": sub_args.cmd,
|
||||
"data": data})
|
||||
except Exception as e:
|
||||
results.append({"line": lineno, "ok": False, "error": str(e)})
|
||||
if args.stop_on_error:
|
||||
break
|
||||
return results
|
||||
|
||||
|
||||
async def _dispatch(args) -> dict | list | None:
|
||||
if args.cmd == "scan":
|
||||
from .transport import scan
|
||||
return {"devices": await scan(timeout=args.timeout)}
|
||||
|
||||
async def body(t):
|
||||
c = args.cmd
|
||||
if c == "time":
|
||||
dt = (datetime.datetime.fromisoformat(args.datetime)
|
||||
if args.datetime else None)
|
||||
await C.set_time(t, dt)
|
||||
return {"set": (dt or datetime.datetime.now()).isoformat()}
|
||||
if c == "mode":
|
||||
await C.set_mode(t, args.name)
|
||||
return {"mode": args.name}
|
||||
if c == "toggle":
|
||||
await C.toggle(t, args.name)
|
||||
return {"toggle": args.name}
|
||||
if c == "clear":
|
||||
await C.clear(t)
|
||||
return None
|
||||
if c == "image":
|
||||
return await _cmd_image(args, t)
|
||||
if c == "template":
|
||||
return await _cmd_template(args, t)
|
||||
if c == "countdown":
|
||||
if args.off:
|
||||
await C.countdown_off(t)
|
||||
return {"countdown": "off"}
|
||||
if args.show_calendar:
|
||||
if not args.prefix:
|
||||
raise ValueError("--show-calendar 需要 --prefix 提供日历文字")
|
||||
await C.countdown_set(t, None, args.mode, prefix=args.prefix,
|
||||
show_calendar=True)
|
||||
return {"calendar_text": args.prefix}
|
||||
if not args.date:
|
||||
raise ValueError("缺少日期(YYYY-MM-DD)或 --off")
|
||||
d = datetime.date.fromisoformat(args.date)
|
||||
await C.countdown_set(t, d, args.mode, prefix=args.prefix)
|
||||
return {"date": d.isoformat(), "mode": args.mode,
|
||||
"prefix": args.prefix}
|
||||
if c == "sleep":
|
||||
on = args.on or not args.off
|
||||
await C.sleep(t, on, args.start, args.end)
|
||||
return {"on": on, "start": args.start, "end": args.end}
|
||||
if c == "parking":
|
||||
await C.parking(t, args.number)
|
||||
return {"number": args.number}
|
||||
if c == "rotation":
|
||||
await C.rotation(t, count=args.count, interval=args.interval, unit=args.unit)
|
||||
return {"count": args.count, "interval": args.interval, "unit": args.unit}
|
||||
if c == "lut":
|
||||
await C.lut(t, args.value)
|
||||
return {"lut": hex(args.value)}
|
||||
if c == "activate":
|
||||
if args.show_id:
|
||||
return {"device_id": await C.get_device_id(t)}
|
||||
if not args.code:
|
||||
raise ValueError("缺少激活码或 --show-id")
|
||||
await C.activate(t, args.code)
|
||||
return None
|
||||
if c == "wifi":
|
||||
await C.wifi(t, args.ssid, args.password, args.city)
|
||||
return None
|
||||
if c == "raw":
|
||||
data = bytes.fromhex(args.hex)
|
||||
if args.channel == "epd":
|
||||
await t.write_epd(data, response=not args.no_response)
|
||||
else:
|
||||
await t.write_rxtx(data, response=not args.no_response)
|
||||
return {"sent": len(data)}
|
||||
raise ValueError(f"未知命令 {c}")
|
||||
|
||||
return await _run_with_transport(args, body)
|
||||
if args.cmd == "batch":
|
||||
return {"results": await _run_batch(args)}
|
||||
return await _run_with_transport(args, lambda t: _run_command(args, t))
|
||||
|
||||
|
||||
def main(argv=None) -> int:
|
||||
|
||||
Reference in new issue
Block a user