# DBNMQTTool 开发日志 > DLD960 IoT MQTT 桌面工具 | Python 3.11+ | PySide6 | paho-mqtt v2 | 跨平台 (Windows/Linux/macOS) > > 定位: DLD960 车检器 MQTT 调试工具 — 设备管理、实时数据监控、模拟上报、主题订阅/发布 --- ## 2026-08-18 — 脱机日志翻页:上一页/下一页 + 自动填充起始序号 ### 变更 - 脱机日志区按钮行新增 **◀ 上一页 / 下一页 ▶**(初始禁用,拉取成功后启用) - **下一页**:起始序号 = 当前页最后一条 `seq` + 1(锚点法,不依赖请求条数,快照流 2 条/事件流 4 条均精确);空记录兜底按 `start + count` 步进 - **上一页**:起始序号 = `max(1, 当前起始 - 条数)`,边界保护 - 翻页后自动填充起始序号 SpinBox 并触发拉取;页锚点在 `_apply_log_query_data` 更新(`_log_page_last_seq`) ### 验证 - offscreen 实测 6 场景全过:初始禁用 / 锚点=103 / 下一页→104 / 上一页→100 / 边界→1 / 空记录步进→504 / 快照流 2 条→12 --- ## 2026-08-18 — 命令集对齐:禁用固件未实现的 MQTT 命令按钮 ### 背景 实测发现 MQTT 通道 `ssc_net_query` / `iot_net_query` 返回 `code=4 unsupported command`——固件 MQTT 分发只实现 6 条命令,工具按协议文档命令表做了全按钮。 ### 变更 - **固件同步补齐**(vd960DBN V4.2):`ssc_net_query` / `iot_net_query` / `iot_topic_query` 三条查询命令已实现,工具对应查询按钮保持可用 - **工具禁用**固件仍未实现的命令按钮:`ssc_net_set` / `iot_net_set` / `iot_topic_set` / `pwd_set` / `factory_reset` / `device_reset`(`setEnabled(False)` + tooltip 引导走 TCP JSON/BLE 通道) - 设备刷新列表移除 `CMD_LOOP_PARAM_QUERY`(固件未实现,刷新时不再报 error) ### 验证 - offscreen 实例化:6 个按钮禁用 + tooltip 正确,查询类/验证密码/report_config/log_* 可用 --- ## 2026-08-18 — 脱机日志支持快照流 (stream=snapshot, 协议 V1.07) ### 变更 - **protocol.py**:新增 `STREAM_EVENT` / `STREAM_SNAPSHOT` 常量;`data_log_query` 加 `stream` 参数(快照流显式传 `stream` 字段 + count≤2);新增 `data_log_clear(stream)`(事件流省略 data 兼容老固件);新增 `SNAP_MISC_TYPE_DESC`(time/cut_count/flow_count/relay_count 中文描述) - **main.py**:脱机日志区加"日志流"选择器(事件日志/传感快照);统计/拉取/清除三按钮按当前流发请求(log_clear 确认框提示阻塞时长 2.8s/45ms);`log_stat`/`log_query` 响应展示区分流——快照记录解析 `channels[]` 逐字段(freq_level/direction/freq_type/sens/cond/loop/car/freq/variation/misc) ### 验证 - protocol 数据构建器断言 6 例全过(事件流不带 stream / 快照流 count≤2 / log_clear 流参数) - `QT_QPA_PLATFORM=offscreen` 实例化 MainWindow 实测:`log_stat(snapshot)` capacity=48064 展示、`log_query(snapshot)` 2 条快照记录逐字段展示(含负 variation、misc_type 中文)、事件流展示回归无损、空记录边界 - 依赖:venv + PySide6 + paho-mqtt(`venv/` 已加 .gitignore) ### 配套 - 固件 vd960DBN `log_*` stream=snapshot 分支(同日实现,devlog V4.1) - 协议文档:TCP JSON V1.03 / IoT MQTT V1.07 --- ## 2026-07-06 (晚) — 模拟上报 + 协议Topic + 自定义Topic `c19e465` ### 1. 模拟上报 Tab - **loop_data / event_report / heartbeat** 三类上报携带独立可编辑 JSON 编辑区 - **范例填充**:首次切换类型时自动填入协议定义的标准载荷 - 单次发送 + **周期上报**(QSpinBox 可调间隔) - 周期开关由 `QPushButton` toggle 控制 ### 2. 协议Topic Tab - 预加载双主题:`dld960/{sn}/srv`(下发)、`dld960/{sn}/dev`(上报) - QListWidget 展示,双击自动填充到发布区 - 发布区可编辑 JSON 载荷,一键发布 - 主题列表受 SN 输入框驱动,实时更新 ### 3. 自定义Topic Tab - 自由输入 topic + JSON 载荷,QoS (0/1/2) 可调 - **订阅 / 取消订阅**,接收消息实时展示在 QPlainTextEdit - `#` / `+` 通配符支持,`paho.mqtt.topic_matches_sub` 路由匹配 - 自定义订阅的消息自动分发到接收区,附带 topic 前缀 ### 4. 新增依赖 - `paho.mqtt.client` 导入新增 `topic_matches_sub` - UI 控件: QComboBox, QSpinBox, QPlainTextEdit --- ## 2026-07-06 — paho-mqtt v2 回调修复 `213c0dd` ### 问题 断开连接时报错: ``` TypeError: MqttClient._on_disconnect() missing 1 required positional argument: 'reason_code' ``` ### 根因 paho-mqtt v2 默认 `callback_api_version=VERSION2`,`on_disconnect` 回调签名为 5 参数 `(client, userdata, flags, reason_code, properties)`,而 v1 为 3 参数 `(client, userdata, rc)`。 ### 修复 - `_on_disconnect` 改用 `*args` 兼容 v1/v2 - `Client` 构造显式指定 `callback_api_version=CallbackAPIVersion.VERSION2` - `_on_connect` 保持 v2 签名不变 --- ## 2026-07-06 — tkinter → PySide6 迁移 `e1bf3dc` ### 决策 放弃 tkinter,改用 PySide6 (Qt for Python),理由: - 跨平台原生外观 (Fusion 风格) - Qt 信号/槽机制替代 tkinter 回调,更易维护 - 组件丰富 (QGroupBox, QSplitter, QTabWidget 等) ### 改动 - `main.py` 全量重写为 PySide6 主窗口 - 布局: QGroupBox 分组 → QSplitter 分栏 → QTabWidget 功能分区 - `requirements.txt` 新增 `PySide6>=6.6.0` - tkinter → Qt 映射: `StringVar`→信号/槽, `Listbox`→`QListWidget`, `Text`→`QTextEdit` --- ## 2026-07-06 — 项目初始化 `a2cfe46` ### 功能 (初始版本, tkinter) - MQTT Broker 连接管理 - 设备自动发现 (`dld960/+/dev` 通配符订阅) - 设备信息查询 + 主题配置 - 实时线圈数据 / 事件上报 / 心跳 监控 - 控制命令: 密码验证/设置、出厂初始化、设备复位 ### 项目结构 ``` DBNMQTTool/ ├── main.py # 主窗口 (tkinter → 后迁移至 PySide6) ├── dbn_mqtt_tool/ │ ├── protocol.py # DLD960 IoT MQTT 协议定义 (主题/命令/载荷模板) │ ├── mqtt_client.py # MQTT 客户端封装 (paho-mqtt v2) │ └── device_manager.py # 设备发现与状态管理 └── requirements.txt # paho-mqtt>=2.0.0 + PySide6>=6.6.0 ``` ### 协议依据 `DLD960_IoT_MQTT协议.md` V1.00 --- ## 当前状态 | 组件 | 状态 | |------|------| | MQTT 连接/断开 | ✅ 已实现,v1/v2 兼容 | | 设备发现 | ✅ 通配符订阅 | | 实时数据监控 | ✅ loop_data/event_report/heartbeat | | 控制命令 | ✅ 15 条命令 | | 模拟上报 | ✅ c19e465 | | 协议Topic订阅/发布 | ✅ c19e465 | | 自定义Topic订阅/发布 | ✅ c19e465 | | 固件联调 | ✅ vd960DBN IoT 模式稳定上报 | --- ## 2026-07-07 — MQTT V1.01 适配 + 稳定性修复 ### 1. MQTT 协议 V1.01 适配 - Topic 从多主题(`/loop_data`, `/event_report`, `/heartbeat`, `/cmd`…)压缩为双主题 `dld960/{sn}/dev`(上报)、`dld960/{sn}/srv`(下发) - `manage_mqtt_recv_message()` 实现 V1.01 协议命令分发,支持 `cmd`/`Method` 双格式 - MQTT PUBLISH 接收修复:topic 过滤 + 通配符路由修正 ### 2. MqttClient 重构:tkinter 回调 → QObject 信号/槽 **问题**: `paho.mqtt` C 层回调直接操作 Qt UI 控件 → `RuntimeError: wrapped C/C++ object has been deleted` 崩溃。 **修复**: - `MqttClient` 继承 `QObject`,使用 Qt 信号/槽机制 - `on_message` → `message_received` 信号 → 主线程 slot 安全更新 UI - 所有 publish 路径增加通用异常捕获,防止未捕获异常导致静默崩溃 - 回退 Qt 信号排队方案,改用 `QMetaObject.invokeMethod` + 日志追踪定位阻塞点 ### 3. DeviceManager 死锁修复 `threading.Lock` 在单线程 Qt 事件循环中自锁 → `RLock`。`add_device()` 和 `remove_device()` 同线程互调时 `Lock` 阻塞自身。 ### 4. UI/UX 改进 - **日志增强**: 收发打印 topic + payload 详情,调试信息更直观 - **查询响应自动回填**: 设备信息查询后自动填充 SN 等输入框 - **主动上报配置**: `report_config` 面板支持完整 7 参数独立开关 - `right` 局部变量 → `self._notebook`,修复 tab 引用丢失 --- ## 2026-08-05 — 脱机事件日志命令支持 (MQTT V1.06) 配合 vd960DBN 固件 P1.3(commit 1a01316)+ 协议 V1.06,工具新增脱机事件日志面板(参数配置 Tab 底部): ### 新增命令 | 命令 | 按钮 | 说明 | |------|------|------| | `log_stat` | 统计 | 日志统计/分页定位: enabled/boot_seq/count/capacity/seq_first/seq_last | | `log_query` | 拉取日志 | 按全局序号分页拉取, 起始序号 + 条数(1~4) 可调 | | `log_clear` | 清除日志 | 二次确认弹窗, 审计留痕不可撤销 | ### 实现要点 - `protocol.py`: 新增 `CMD_LOG_*` 常量、`LOG_COMMANDS` 集合、`data_log_query()` 构建器(start_seq≥1 / count 1~4 边界钳制)、`LOG_EVENT_TYPE_DESC` 事件类型中文描述(10 类 + unknown) - `main.py`: 参数配置 Tab 新增 g6 组(起始序号/条数 spinbox + 三按钮 + 只读显示区 QPlainTextEdit) - `_on_message` code==0 分支新增 `log_stat`/`log_query`/`log_clear` 响应处理 - 显示优化: log_query 记录带事件类型中文描述; unix_ts 已同步显示真实时间, 未同步显示 `boot+ts_ms(未同步)` - 清除日志前 QMessageBox 二次确认(防误触, 与出厂初始化/设备复位同模式) ### 验证 - `py_compile` 四个模块通过 - protocol 逻辑单测: data_log_query 边界钳制 (0→1, 99→4, 0→1)、命令枚举一致性、build_request 带 data