feat(vd960DBN)+fix(DBNMQTTool): log_query 改 hex 原始字节上报 (2026-08-18)

背景: MQTT 快照流实测 MQTTSerialize_publish failed — JSON 化快照记录 ~810B/条 超 800B 发送缓冲
方案(用户拍板): 对齐 BLE 通道, 原始字节 hex 上报

固件 (V4.3):
- offlog.c/h: 新增 offlog_evt_to_hex() (32B→64 hex)
- snapshot.c/h: 新增 snap_rec_to_hex() (64B→128 hex); 删 SNAP_MAX_QUERY_JSON, 恢复 count=2
- tcp_json_srv.c / iot_mqtt_srv.c: log_query 改 {"seq":N,"hex":"..."}; SEND_BUF 保持 800
- 2 条快照 hex 响应 406B < 800B

文档: TCP JSON V1.03 / MQTT V1.07 §4.17 records 改 hex + 解析表引用 BLE §6.4/§7

工具: parse_offlog_hex/parse_snap_hex/offlog_payload_desc + hex 展示; 验证: gcc 9 断言 + 工具解析全过 + offscreen UI
This commit is contained in:
wangfq
2026-08-18 14:04:26 +08:00
parent 5e9b188c6a
commit 5a1893cd1c
15 changed files with 331 additions and 180 deletions
+31 -25
View File
@@ -34,6 +34,7 @@ from dbn_mqtt_tool.protocol import (
CMD_LOG_STAT, CMD_LOG_QUERY, CMD_LOG_CLEAR,
STREAM_EVENT, STREAM_SNAPSHOT,
ERROR_MSGS, FREQ_LEVELS, OUTPUT_MODES, EVENT_TYPES, LOG_EVENT_TYPE_DESC, SNAP_MISC_TYPE_DESC,
OFFLOG_TYPE_NAMES, parse_offlog_hex, offlog_payload_desc, parse_snap_hex,
data_ssc_net_set, data_iot_net_set, data_iot_topic_set,
data_pwd_verify, data_pwd_set, data_report_config, data_log_query, data_log_clear,
build_request, next_msg_id,
@@ -1219,43 +1220,48 @@ class MainWindow(QMainWindow):
def _apply_log_query_data(self, data: dict):
records = data.get("records", [])
start_seq = data.get("start_seq", 0)
# 快照记录带 coil_count/channels 字段, 事件记录带 type 字段
is_snap = bool(records) and ("coil_count" in records[0] or "channels" in records[0])
# V1.07: 记录为 {"seq":N,"hex":"..."} 原始字节; hex 长度 128=快照(64B) / 64=事件(32B)
is_snap = bool(records) and len(records[0].get("hex", "")) == 128
stream_name = "传感快照" if is_snap else "事件日志"
lines = [f"log_query[{stream_name}] start_seq={start_seq} → 返回 {len(records)} 条:"]
for rec in records:
ts_ms = rec.get("ts_ms", 0)
hex_str = rec.get("hex", "")
if is_snap:
coil_count = rec.get("coil_count", 0)
s = parse_snap_hex(hex_str)
if "error" in s:
lines.append(f" seq={rec.get('seq', 0)} 解析失败: {s['error']}")
continue
lines.append(
f" seq={rec.get('seq', 0)} boot={rec.get('boot_seq', 0)} "
f"boot+{ts_ms}ms ({coil_count}ch)"
f" seq={s['seq']} boot={s['boot_seq']} "
f"boot+{s['ts_ms']}ms ({s['coil_count']}ch)"
)
for ch in rec.get("channels", []):
mt = ch.get("misc_type", "?")
for ch in s["channels"]:
mt = ch["misc_type"]
misc_desc = SNAP_MISC_TYPE_DESC.get(mt, mt)
lines.append(
f" ch{ch.get('ch', 0)}: {ch.get('freq_level', '?')} "
f"dir={ch.get('direction', 0)} ftype={ch.get('freq_type', 0)} "
f"sens={ch.get('sensitivity', 0)} cond={ch.get('condition', 0)} "
f"loop={'OK' if ch.get('loop_ok') else ''} "
f"car={'' if ch.get('has_car') else ''} "
f"freq={ch.get('freq', 0)}Hz Δ={ch.get('variation', 0)} "
f"[{misc_desc}] misc={ch.get('misc', 0)}"
f" ch{ch['ch']}: {ch['freq_level']} "
f"dir={ch['direction']} ftype={ch['freq_type']} "
f"sens={ch['sensitivity']} cond={ch['condition']} "
f"loop={'OK' if ch['loop_ok'] else ''} "
f"car={'' if ch['has_car'] else ''} "
f"freq={ch['freq']}Hz Δ={ch['variation']} "
f"[{misc_desc}] misc={ch['misc']}"
)
else:
rtype = rec.get("type", "?")
desc = LOG_EVENT_TYPE_DESC.get(rtype, rtype)
unix_ts = rec.get("unix_ts", 0)
if unix_ts:
ts_str = datetime.fromtimestamp(unix_ts).strftime("%Y-%m-%d %H:%M:%S")
e = parse_offlog_hex(hex_str)
if "error" in e:
lines.append(f" seq={rec.get('seq', 0)} 解析失败: {e['error']}")
continue
tname = OFFLOG_TYPE_NAMES.get(e["type"], f"0x{e['type']:02x}")
desc = LOG_EVENT_TYPE_DESC.get(tname, tname)
if e["unix_ts"]:
ts_str = datetime.fromtimestamp(e["unix_ts"]).strftime("%Y-%m-%d %H:%M:%S")
else:
ts_str = f"boot+{ts_ms}ms (未同步)"
rdata = rec.get("data")
data_str = json.dumps(rdata, ensure_ascii=False) if rdata else ""
ts_str = f"boot+{e['ts_ms']}ms (未同步)"
pdesc = offlog_payload_desc(e["type"], e["payload"])
lines.append(
f" seq={rec.get('seq', 0)} boot={rec.get('boot_seq', 0)} "
f"{ts_str} [{desc}] {data_str}"
f" seq={e['seq']} boot={e['boot_seq']} "
f"{ts_str} [{desc}] {pdesc}"
)
if not records:
lines.append(" (无记录 / 起始序号越界, 可先用 log_stat 确认范围)")