2026-09-20 04:31:42 +08:00
|
|
|
|
"""测量端到端真实延迟:发一条 auth_str,看应答多久到达(最长等 75 秒)。
|
|
|
|
|
|
|
|
|
|
|
|
退出码(全仓统一口径):0 = 三步都有应答;1 = 有步骤无应答/断言失败;
|
|
|
|
|
|
2 = 连接或环境问题(DB 连不上、TCP 建连失败)。
|
|
|
|
|
|
⛔ 以前它只打印 ✅/❌ 却永远 exit 0 —— 无应答也算"通过",属假绿。
|
|
|
|
|
|
"""
|
2026-09-19 23:40:46 +08:00
|
|
|
|
import hashlib
|
|
|
|
|
|
import json
|
2026-09-20 04:31:42 +08:00
|
|
|
|
import os
|
2026-09-19 23:40:46 +08:00
|
|
|
|
import socket
|
2026-09-20 04:31:42 +08:00
|
|
|
|
import sys
|
2026-09-19 23:40:46 +08:00
|
|
|
|
import time
|
|
|
|
|
|
import pymysql
|
|
|
|
|
|
|
2026-09-20 04:31:42 +08:00
|
|
|
|
# 3001 是 device-server 的 TCP 业务端口(设备经公网/ZeroTier 直连),与数据库通路无关,保持不变
|
2026-09-19 23:40:46 +08:00
|
|
|
|
HOST, PORT = "10.8.0.252", 3001
|
|
|
|
|
|
IMEI = "860000000000001"
|
|
|
|
|
|
|
|
|
|
|
|
# 复位夹具,保证能重新申领
|
2026-09-20 04:31:42 +08:00
|
|
|
|
# 默认指向 SSH 隧道(tools/dev-tunnel.ps1),不再使用已移除的 ZeroTier/nginx 转发
|
|
|
|
|
|
try:
|
|
|
|
|
|
conn = pymysql.connect(host=os.environ.get("PMS_MYSQL_HOST", "127.0.0.1"),
|
|
|
|
|
|
port=int(os.environ.get("PMS_MYSQL_PORT", "13306")),
|
|
|
|
|
|
user="root", password="Hbhyg731024@",
|
|
|
|
|
|
database="pms_dev", connect_timeout=8, autocommit=True)
|
|
|
|
|
|
except pymysql.MySQLError as e:
|
|
|
|
|
|
print(f"✗ MySQL 连接失败:{e}", file=sys.stderr)
|
|
|
|
|
|
print("连接被拒:请先运行 `pwsh tools/dev-tunnel.ps1` 建立 SSH 隧道", file=sys.stderr)
|
|
|
|
|
|
sys.exit(2)
|
2026-09-19 23:40:46 +08:00
|
|
|
|
cur = conn.cursor()
|
|
|
|
|
|
cur.execute("UPDATE cabinets SET auth_str=NULL, auth_str_issued=0 WHERE imei=%s", (IMEI,))
|
|
|
|
|
|
cur.execute("DELETE FROM charge_records WHERE cabinet_id=1")
|
|
|
|
|
|
cur.execute("DELETE FROM charge_orders WHERE cabinet_id=1")
|
|
|
|
|
|
cur.execute("DELETE FROM device_logs WHERE cabinet_id=1")
|
|
|
|
|
|
cur.execute("DELETE FROM batteries")
|
|
|
|
|
|
cur.execute("UPDATE compartments SET status=0")
|
|
|
|
|
|
conn.close()
|
|
|
|
|
|
print(" 夹具已复位")
|
|
|
|
|
|
|
2026-09-20 04:31:42 +08:00
|
|
|
|
try:
|
|
|
|
|
|
s = socket.create_connection((HOST, PORT), timeout=5)
|
|
|
|
|
|
except OSError as e:
|
|
|
|
|
|
print(f"✗ TCP 建连失败 {HOST}:{PORT}:{e}", file=sys.stderr)
|
|
|
|
|
|
print("(该端口是 device-server 业务端口;测试环境为 :3001)", file=sys.stderr)
|
|
|
|
|
|
sys.exit(2)
|
2026-09-19 23:40:46 +08:00
|
|
|
|
s.settimeout(1.0)
|
|
|
|
|
|
buf = b""
|
2026-09-20 04:31:42 +08:00
|
|
|
|
fails = [] # 收集失败项,决定退出码(非 0 = 失败)
|
2026-09-19 23:40:46 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def send(obj):
|
|
|
|
|
|
line = json.dumps(obj, separators=(",", ":")) + "\n"
|
|
|
|
|
|
s.sendall(line.encode())
|
|
|
|
|
|
return time.time()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def wait_reply(t0, timeout=75):
|
|
|
|
|
|
"""等到收到一行报文,返回 (耗时, 报文) 或 (None, None)。"""
|
|
|
|
|
|
global buf
|
|
|
|
|
|
while time.time() - t0 < timeout:
|
|
|
|
|
|
try:
|
|
|
|
|
|
chunk = s.recv(65536)
|
|
|
|
|
|
except socket.timeout:
|
|
|
|
|
|
continue
|
|
|
|
|
|
if not chunk:
|
|
|
|
|
|
return None, None
|
|
|
|
|
|
buf += chunk
|
|
|
|
|
|
if b"\n" in buf:
|
|
|
|
|
|
line, buf = buf.split(b"\n", 1)
|
|
|
|
|
|
return time.time() - t0, json.loads(line.decode())
|
|
|
|
|
|
return None, None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print("\n=== ① auth_str 延迟实测 ===")
|
|
|
|
|
|
t0 = send({"act": "auth_str", "msg_id": 1, "dev_id": IMEI})
|
|
|
|
|
|
dt, resp = wait_reply(t0)
|
|
|
|
|
|
if resp:
|
|
|
|
|
|
print(f" ✅ 应答耗时 {dt:.1f}s : {json.dumps(resp, ensure_ascii=False)}")
|
|
|
|
|
|
auth_str = resp.get("auth_str")
|
2026-09-20 04:31:42 +08:00
|
|
|
|
if not auth_str:
|
|
|
|
|
|
fails.append("① auth_str 有应答但未带 auth_str")
|
2026-09-19 23:40:46 +08:00
|
|
|
|
else:
|
|
|
|
|
|
print(f" ❌ {dt if dt else '>75'}s 内无应答")
|
|
|
|
|
|
auth_str = None
|
2026-09-20 04:31:42 +08:00
|
|
|
|
fails.append(f"① auth_str 无应答(等待 {dt if dt else '>75'}s)")
|
2026-09-19 23:40:46 +08:00
|
|
|
|
|
|
|
|
|
|
if auth_str:
|
|
|
|
|
|
print("\n=== ② login 延迟实测 ===")
|
|
|
|
|
|
ts = int(time.time())
|
|
|
|
|
|
sign = hashlib.sha256(f"{IMEI}{auth_str}{ts}".encode()).hexdigest()[56:64]
|
|
|
|
|
|
t0 = send({"act": "login", "msg_id": 2, "dev_id": IMEI, "sign": sign, "timestamp": ts})
|
|
|
|
|
|
dt, resp = wait_reply(t0)
|
|
|
|
|
|
print(f" {'✅' if resp else '❌'} 应答耗时 {dt:.1f}s : {json.dumps(resp, ensure_ascii=False) if resp else '无'}")
|
2026-09-20 04:31:42 +08:00
|
|
|
|
if not resp:
|
|
|
|
|
|
fails.append(f"② login 无应答(等待 {dt if dt else '>75'}s)")
|
2026-09-19 23:40:46 +08:00
|
|
|
|
|
|
|
|
|
|
print("\n=== ③ status_post 延迟实测 ===")
|
|
|
|
|
|
t0 = send({"act": "status_post", "msg_id": 3, "rssi": 85,
|
|
|
|
|
|
"cabinet": {"status1": "00", "status2": "00", "temp": 29800, "meter": 125500},
|
|
|
|
|
|
"boards": [{"board": 1, "channels": ["008200000000000000", "008200000000000000"]},
|
|
|
|
|
|
{"board": 2, "channels": ["008200000000000000", "008200000000000000"]}]})
|
|
|
|
|
|
dt, resp = wait_reply(t0)
|
|
|
|
|
|
print(f" {'✅' if resp else '❌'} 应答耗时 {dt:.1f}s : {json.dumps(resp, ensure_ascii=False) if resp else '无'}")
|
2026-09-20 04:31:42 +08:00
|
|
|
|
if not resp:
|
|
|
|
|
|
fails.append(f"③ status_post 无应答(等待 {dt if dt else '>75'}s)")
|
2026-09-19 23:40:46 +08:00
|
|
|
|
|
|
|
|
|
|
s.close()
|
2026-09-20 04:31:42 +08:00
|
|
|
|
|
|
|
|
|
|
# ⛔ 退出码必须反映结果(口径:0=通过,1=断言失败,2=环境/连接问题)
|
|
|
|
|
|
if fails:
|
|
|
|
|
|
print("\n未通过项(stderr):", file=sys.stderr)
|
|
|
|
|
|
for f in fails:
|
|
|
|
|
|
print(f" ❌ {f}", file=sys.stderr)
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
print("\n✅ 三步均有应答")
|
|
|
|
|
|
sys.exit(0)
|