#!/usr/bin/env python3 """V1 工厂 TCP 命令客户端;发送任何命令都必须显式使用 --execute。""" from __future__ import annotations import argparse import json import socket import struct import sys MAX_DATA = 16 * 1024 * 1024 def receive_exact(sock: socket.socket, length: int) -> bytes: chunks = bytearray() while len(chunks) < length: part = sock.recv(length - len(chunks)) if not part: raise ConnectionError("peer closed before complete V1 frame arrived") chunks.extend(part) return bytes(chunks) def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("host", help="device IP selected from discovery results") parser.add_argument("command", help="V1 CMD, decimal or 0x-prefixed") parser.add_argument("--data", default="", help="UTF-8 JSON/text payload (omit for empty Data)") parser.add_argument("--timeout", type=float, default=8.0) parser.add_argument("--execute", action="store_true", help="actually send the command") args = parser.parse_args() try: command = int(args.command, 0) payload = args.data.encode("utf-8") if not 0 <= command <= 0xFFFFFFFF: raise ValueError("CMD must fit uint32") if len(payload) > MAX_DATA: raise ValueError("request Data exceeds 16 MiB limit") if args.data: json.loads(args.data) # V1 factory command payloads are documented JSON. if not args.execute: print(json.dumps({"preflight_only": True, "host": args.host, "port": 4941, "command": "0x%08X" % command, "data_bytes": len(payload), "message": "No network connection opened; add --execute to send."}, ensure_ascii=False)) return 0 request = struct.pack("!II", command, len(payload)) + payload with socket.create_connection((args.host, 4941), timeout=args.timeout) as sock: sock.settimeout(args.timeout) sock.sendall(request) header = receive_exact(sock, 8) response_command, response_length = struct.unpack("!II", header) if response_length > MAX_DATA: raise ValueError("response Data exceeds 16 MiB limit") response = receive_exact(sock, response_length) result = {"request_command": "0x%08X" % command, "response_command": "0x%08X" % response_command, "response_length": response_length, "response_data": response.decode("utf-8", errors="replace")} print(json.dumps(result, ensure_ascii=False)) if response_command != command: return 2 return 0 except (OSError, ValueError, ConnectionError, json.JSONDecodeError) as exc: print("V1 command error: %s" % exc, file=sys.stderr) return 1 if __name__ == "__main__": raise SystemExit(main())