EXPERIMENT / 配套代码
status_client.py
← 返回文章#!/usr/bin/env python3
"""只查询 PTZ 状态或配置,不发送运动、停止或写配置命令。"""
import argparse
import json
import socket
import struct
import time
import uuid
MAX_FRAME = 64 * 1024
def receive_exact(sock, size, deadline):
data = bytearray()
while len(data) < size:
remaining = deadline - time.monotonic()
if remaining <= 0:
raise TimeoutError("等待设备应答超时")
sock.settimeout(remaining)
chunk = sock.recv(size - len(data))
if not chunk:
raise ConnectionError("设备在一帧接收完成前关闭连接")
data.extend(chunk)
return bytes(data)
def query(host, port, command):
if command not in ("ptz_status", "ptz_get_config"):
raise ValueError("此工具仅允许状态和配置查询")
query_id = "tutorial_" + uuid.uuid4().hex
request = {"magic": "TL_PROD", "cmd": command,
"params": {"query_id": query_id}}
payload = json.dumps(request, separators=(",", ":")).encode("utf-8")
with socket.create_connection((host, port), timeout=5) as sock:
sock.sendall(struct.pack("!I", len(payload)) + payload)
deadline = time.monotonic() + 10
while True:
size = struct.unpack("!I", receive_exact(sock, 4, deadline))[0]
if not 0 < size <= MAX_FRAME:
raise ValueError(f"非法帧长度:{size}")
reply = json.loads(receive_exact(sock, size, deadline).decode("utf-8"))
if not isinstance(reply, dict):
raise ValueError("应答不是 JSON 对象")
if reply.get("magic") != "TL_PROD" or reply.get("cmd") != command + "_ack":
continue
if reply.get("query_id") not in (None, "", query_id):
continue
if reply.get("phase") == "accepted":
continue
return reply
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("host")
parser.add_argument("command", choices=("status", "get-config"))
parser.add_argument("--port", type=int, default=8899)
args = parser.parse_args()
command = "ptz_status" if args.command == "status" else "ptz_get_config"
try:
reply = query(args.host, args.port, command)
print(json.dumps(reply, ensure_ascii=False, indent=2))
return 0 if reply.get("result") == "ok" else 1
except (OSError, ValueError, ConnectionError) as exc:
parser.exit(1, f"查询失败:{exc}\n")
if __name__ == "__main__":
raise SystemExit(main())