diff --git a/CHANGELOG.md b/CHANGELOG.md index 99d4713..ae7b706 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,8 @@ | vd960DBN 固件 (CH32V208, DLD960GA) | 1.0 | | DLD960Loop 串口协议(MCU 间 0x7F) | V1.05 | | DLD960 串口通信协议(TTL) | V1.01 | -| DLD960 TCP JSON 协议(:5960) | V1.01 | -| DLD960 IoT MQTT 协议 | V1.05 | +| DLD960 TCP JSON 协议(:5960) | V1.02 | +| DLD960 IoT MQTT 协议 | V1.06 | > ⚠ 自协议 V1.05 起,Loop 固件与 DBN 固件必须同版本配套刷写,禁止混跑(variation 字段宽度变更导致帧格式不兼容)。 @@ -37,11 +37,12 @@ ### vd960DBN(通信 MCU,CH32V208) -**TCP JSON 服务(:5960,协议 V1.01)** +**TCP JSON 服务(:5960,协议 V1.02)** +- 密码鉴权 + 18 条命令(新增脱机日志 log_stat/log_query/log_clear) - 鉴权状态机 + 15 条命令;服务异常自动重启(3 条件 + 3 次限额 + 10min 冷却) - event_report 客户端必答:5s 超时重发同 msg_id/原始 ts ×3 -**IoT MQTT(协议 V1.05)** +**IoT MQTT(协议 V1.06)** - 双主题 `dld960/{sn}/srv` + `dld960/{sn}/dev`,消息类型由 `cmd` 区分 - 上电 initialize 上线消息(dev_serial/model/hard_ver/soft_ver) - loop_data 三档调度:空闲按配置间隔 / 活动 300ms / **car_state 翻转沿立即上报** @@ -49,6 +50,7 @@ - 设备时钟同步方案B:平台经 report_config 下发 Unix ts,设备无 RTC 也能上报真实时间 - 稳定性修复:mqtt_publish 缓冲溢出发垃圾包致 broker RST 风暴(现场 P0)、packet_id=0 断连、PINGREQ 保活、分批发送(MSS=576,Publish≤500B) - loop_data 陈旧快照修复:0xC0 帧双消费路径统一摄取,事件与缓存同源 +- **脱机事件日志(W25Q32 环形 8064 条,V1.06 新增)**:log_stat / log_query / log_clear 三命令;BOOT 复位原因、MQTT 连接/断开、event ACK 超时、线圈事件、时钟锚点共 10 类;掉电不丢 **基础设施** - BLE 小程序配置 + OTA;Loop MCU ISP 串口透传升级 diff --git a/README.md b/README.md index bd3424d..0847f1d 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,8 @@ DLD960 是一款基于环形线圈检测原理的四通道车辆检测器,支 |------|------|--------|------| | [DLD960Loop_串口通信协议.md](docs/DLD960Loop_串口通信协议.md) | V1.05 | Loop MCU ↔ DBN MCU | 0x7F 帧、0xC0 传感主动上报、variation 3B 有符号 | | [DLD960_串口通信协议.md](docs/DLD960_串口通信协议.md) | V1.01 | 整机 TTL 串口 | 设备管理、参数配置、数据上报 | -| [DLD960_TCP_JSON协议.md](docs/DLD960_TCP_JSON协议.md) | V1.01 | 以太网 TCP :5960 | 鉴权 + 命令 + event_report 客户端必答 | -| [DLD960_IoT_MQTT协议.md](docs/DLD960_IoT_MQTT协议.md) | V1.05 | 云平台 MQTT | 双主题 `{sn}/srv`+`{sn}/dev`、initialize、event_report 平台必答、设备时钟同步 | +| [DLD960_TCP_JSON协议.md](docs/DLD960_TCP_JSON协议.md) | V1.02 | 以太网 TCP :5960 | 鉴权 + 命令 + event_report 客户端必答 + 脱机日志 | +| [DLD960_IoT_MQTT协议.md](docs/DLD960_IoT_MQTT协议.md) | V1.06 | 云平台 MQTT | 双主题 `{sn}/srv`+`{sn}/dev`、initialize、event_report 平台必答、设备时钟同步、脱机日志 | | [DLD960硬件资源.md](docs/DLD960硬件资源.md) | — | 硬件 | 双 MCU IO 分配、继电器、指示灯、拨码 | ## 开发文档 diff --git a/docs/DLD960_IoT_MQTT协议.md b/docs/DLD960_IoT_MQTT协议.md index e598b0a..e71813d 100644 --- a/docs/DLD960_IoT_MQTT协议.md +++ b/docs/DLD960_IoT_MQTT协议.md @@ -110,6 +110,12 @@ dld960/{dev_serial}/{direction} - 校准前(含首个 `initialize`),`ts` 为上电秒数;平台应能容忍并可按数量级区分。 - 设备重启后需重新校准(无掉电保持)。平台在每次设备 `initialize` 后都应下发一次带 `ts` 的 `report_config`。 +**脱机日志时间戳语义(V1.06 起)**: + +- 设备侧事件日志(`log_query` 拉取)每条记录携带双时间戳:`ts_ms`(boot 内相对时间,单位 ms,断电归零)+ `unix_ts`(已同步 Unix 秒,**0 = 未同步**)。 +- 设备经本协议同步成功后,其后记录的 `unix_ts` 均为真实 Unix 时间;同步前(含每次上电的 `boot` 事件)`unix_ts = 0`,仅 `ts_ms` 相对时间。 +- 回算规则:`绝对时间 = 锚点.unix_ts + (记录.ts_ms - 锚点.ts_ms)/1000`,锚点取该 boot 段内第一条 `time_anchor` 事件(`unix_ts` 即平台下发值,严格一致)。从未同步过的 boot 段只有相对时间。 + --- # 3 命令详表 @@ -131,6 +137,9 @@ dld960/{dev_serial}/{direction} | `loop_param_set` | 设置车检器多路参数 | srv→dev | 0x63 | | `loop_param_query` | 读取车检器多路参数 | srv→dev | 0x64 | | `report_config` | 设置主动上报 | srv→dev | 0xC5 | +| `log_stat` | 查询脱机事件日志统计 | srv→dev | — | +| `log_query` | 分页拉取脱机事件日志 | srv→dev | — | +| `log_clear` | 清除脱机事件日志(审计留痕) | srv→dev | — | | `initialize` | 设备上电初始化登陆 | dev→srv | — | | `loop_data` | 线圈传感数据上报 | dev→srv | 0xC0 | | `event_report` | 事件上报(**平台须应答**,见 §5.3) | dev→srv | — | @@ -639,6 +648,142 @@ dld960/{dev_serial}/{direction} --- +## 4.16 查询脱机事件日志统计 `log_stat` + +> Topic: `dld960/{sn}/srv` +> 设备本地 W25Q32 环形事件日志(256KB / 8064 条,掉电不丢)。用于日志拉取前的分页定位。 + +**请求:** + +```json +{ + "msg_id": 16, + "cmd": "log_stat", + "ts": 1719000000, + "data": {} +} +``` + +**响应 data:** + +```json +{ + "stream": "event", + "enabled": true, + "boot_seq": 2, + "count": 1234, + "capacity": 8064, + "seq_first": 100, + "seq_last": 1333 +} +``` + +| data 字段 | 类型 | 说明 | +|-----------|------|------| +| `stream` | string | 日志流,当前仅 `event`(快照流预留) | +| `enabled` | bool | 日志功能是否启用(Flash 初始化成功) | +| `boot_seq` | uint16 | 当前启动序号(每次上电 +1,区分复位段) | +| `count` | uint16 | 有效记录条数(0~8064,环形覆盖后 < capacity) | +| `capacity` | uint16 | 容量上限(8064) | +| `seq_first` | uint32 | 逻辑首条记录全局序号(`seq_last - count + 1`) | +| `seq_last` | uint32 | 最新一条记录全局序号(跨 boot 单调递增) | + +--- + +## 4.17 分页拉取脱机事件日志 `log_query` + +> Topic: `dld960/{sn}/srv` +> **分页按全局序号,不按时间**(未同步段时间不可靠)。`count` 上限 **4**(受 MQTT 发布 ≤500B 限制,MSS=576 教训)。 + +**请求:** + +```json +{ + "msg_id": 17, + "cmd": "log_query", + "ts": 1719000000, + "data": { + "start_seq": 1330, + "count": 4 + } +} +``` + +| data 字段 | 类型 | 说明 | +|-----------|------|------| +| `start_seq` | uint32 | 起始全局序号(含);越界(< `seq_first` 或 > `seq_last`)返回空 `records` | +| `count` | uint8 | 拉取条数,**上限 4**,超限按 4 处理 | + +**响应 data:** + +```json +{ + "start_seq": 1330, + "records": [ + { + "seq": 1330, + "boot_seq": 2, + "ts_ms": 456789, + "unix_ts": 1784768575, + "type": "iot_ready", + "data": null + } + ] +} +``` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `seq` | uint32 | 全局序号 | +| `boot_seq` | uint16 | 所属启动段 | +| `ts_ms` | uint32 | boot 内相对时间(ms,断电归零) | +| `unix_ts` | uint32 | 已同步 Unix 秒;**0 = 未同步**(回算见 §2.3) | +| `type` | string | 事件类型名(下表) | +| `data` | object/null | 类型相关参数 | + +**事件类型表:** + +| type | 含义 | data 字段 | +|------|------|-----------| +| `boot` | 上电/复位 | `{"rst": 复位原因寄存器原始值}`,位解析:bit31=IWDG(看门狗) bit30=WWDG bit29=LPWR bit26=NRST引脚 bit25=POR(真断电) bit24=软件复位 | +| `iot_connect` | MQTT TCP 连接成功 | null | +| `iot_ready` | MQTT 订阅完成 → 发 initialize | null | +| `iot_disconnect` | MQTT 断连 | `{"reason": N}`,1=断开 2=超时 3=CONNACK拒绝 4=连接超时 | +| `iot_reconn` | 重连退避 | `{"backoff_ms": 5000}` | +| `evt_retry` | event_report ACK 超时重发 | `{"msg_id":5,"retry":2}` | +| `evt_giveup` | event_report 重试耗尽挂起 | `{"msg_id":5}` | +| `coil` | 线圈事件 | `{"sub":"car_enter","ch":1,"value":97}`;sub: car_enter/car_leave/loop_cut/loop_restore,value 为 50ms 单位时间量 | +| `time_anchor` | 时钟同步锚点 | null(`unix_ts` 即平台下发值,严格一致) | +| `log_clear` | 日志清除(审计) | null | + +--- + +## 4.18 清除脱机事件日志 `log_clear` + +> Topic: `dld960/{sn}/srv` +> ⚠ **高风险操作**:清除动作本身写入事件流(`log_clear` 审计——谁在何时清了日志,留痕不可清除)。平台侧应做权限控制。 + +**请求:** + +```json +{ + "msg_id": 18, + "cmd": "log_clear", + "ts": 1719000000, + "data": { + "stream": "event" + } +} +``` + +| data 字段 | 类型 | 说明 | +|-----------|------|------| +| `stream` | string | 日志流,当前仅 `event`;缺省等同 `event` | + +**响应:** 标准成功/失败。成功后 `log_stat` 的 `count` 归 1(仅剩审计记录),`seq_last` 继续递增(序号不复位)。 + +--- + # 5 设备主动上报 ## 5.1 设备上电登陆信息 `initialize` @@ -893,4 +1038,5 @@ dld960/{dev_serial}/{direction} | V1.03 | 2026-07-09 | 增加设备上电初始化指令 | wangfq | | V1.04 | 2026-07-15 | `event_report` 增加**平台必答**机制:应答格式(回显 `msg_id`)、设备 5s 超时重发(同 `msg_id`/`ts`,最多 3 次)、待发队列合并上报、平台去重与先落库后应答要求 | wangfq | | V1.05 | 2026-07-15 | 增加**设备时钟同步**(§2.3,方案B):设备无 RTC,`initialize` 上线后平台经 `report_config` 命令下发 Unix `ts`,设备据此校准,之后上行 `ts` 为真实 Unix 时间;校准前为上电秒数 | wangfq | +| V1.06 | 2026-08-04 | 增加**脱机事件日志**命令:`log_stat`(统计/分页定位)、`log_query`(按全局序号分页,count≤4)、`log_clear`(清除+审计留痕);§2.3 补充日志双时间戳语义(`ts_ms` 相对 + `unix_ts` 已同步,0=未同步,锚点回算规则) | wangfq | diff --git a/docs/DLD960_TCP_JSON协议.md b/docs/DLD960_TCP_JSON协议.md index 8ffd504..8df0b87 100644 --- a/docs/DLD960_TCP_JSON协议.md +++ b/docs/DLD960_TCP_JSON协议.md @@ -119,6 +119,9 @@ Client Device (DLD960) | `loop_param_set` | 设置车检器多路参数 | 是 | 0x63 | | `loop_param_query` | 读取车检器多路参数 | 是 | 0x64 | | `report_config` | 设置主动上报 | 是 | 0xC5 | +| `log_stat` | 查询脱机事件日志统计 | 是 | — | +| `log_query` | 分页拉取脱机事件日志 | 是 | — | +| `log_clear` | 清除脱机事件日志(审计留痕) | 是 | — | | 主动推送 | | | | | `loop_data` | 线圈传感数据(设备→客户端) | — | 0xC0 | | `event_report` | 事件上报(设备→客户端,**客户端须应答**,见 §5.2) | — | — | @@ -342,6 +345,99 @@ Client Device (DLD960) --- +## 4.16 查询脱机事件日志统计 `log_stat` + +> 设备本地 W25Q32 环形事件日志(256KB / 8064 条,掉电不丢)。用于日志拉取前的分页定位。 + +**请求:** + +```json +{"msg_id":16,"cmd":"log_stat","ts":1719000000,"data":{}} +``` + +**响应 data:** + +```json +{"stream":"event","enabled":true,"boot_seq":2,"count":1234,"capacity":8064,"seq_first":100,"seq_last":1333} +``` + +| data 字段 | 类型 | 说明 | +|-----------|------|------| +| `stream` | string | 日志流,当前仅 `event`(快照流预留) | +| `enabled` | bool | 日志功能是否启用(Flash 初始化成功) | +| `boot_seq` | uint16 | 当前启动序号(每次上电 +1,区分复位段) | +| `count` | uint16 | 有效记录条数(0~8064) | +| `capacity` | uint16 | 容量上限(8064) | +| `seq_first` | uint32 | 逻辑首条记录全局序号(`seq_last - count + 1`) | +| `seq_last` | uint32 | 最新一条记录全局序号(跨 boot 单调递增) | + +--- + +## 4.17 分页拉取脱机事件日志 `log_query` + +> **分页按全局序号,不按时间**(未同步段时间不可靠)。`count` 上限 **4**(受响应帧 ≤800B 限制)。 + +**请求:** + +```json +{"msg_id":17,"cmd":"log_query","ts":1719000000,"data":{"start_seq":1330,"count":4}} +``` + +| data 字段 | 类型 | 说明 | +|-----------|------|------| +| `start_seq` | uint32 | 起始全局序号(含);越界(< `seq_first` 或 > `seq_last`)返回空 `records` | +| `count` | uint8 | 拉取条数,**上限 4**,超限按 4 处理 | + +**响应 data:** + +```json +{"start_seq":1330,"records":[{"seq":1330,"boot_seq":2,"ts_ms":456789,"unix_ts":1784768575,"type":"iot_ready","data":null}]} +``` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `seq` | uint32 | 全局序号 | +| `boot_seq` | uint16 | 所属启动段 | +| `ts_ms` | uint32 | boot 内相对时间(ms,断电归零) | +| `unix_ts` | uint32 | 已同步 Unix 秒;**0 = 未同步**(回算见 §2.3) | +| `type` | string | 事件类型名(下表) | +| `data` | object/null | 类型相关参数 | + +**事件类型表:** + +| type | 含义 | data 字段 | +|------|------|-----------| +| `boot` | 上电/复位 | `{"rst": 复位原因寄存器原始值}`,位解析:bit31=IWDG(看门狗) bit30=WWDG bit29=LPWR bit26=NRST引脚 bit25=POR(真断电) bit24=软件复位 | +| `iot_connect` | MQTT TCP 连接成功 | null | +| `iot_ready` | MQTT 订阅完成 → 发 initialize | null | +| `iot_disconnect` | MQTT 断连 | `{"reason": N}`,1=断开 2=超时 3=CONNACK拒绝 4=连接超时 | +| `iot_reconn` | 重连退避 | `{"backoff_ms": 5000}` | +| `evt_retry` | event_report ACK 超时重发 | `{"msg_id":5,"retry":2}` | +| `evt_giveup` | event_report 重试耗尽挂起 | `{"msg_id":5}` | +| `coil` | 线圈事件 | `{"sub":"car_enter","ch":1,"value":97}`;sub: car_enter/car_leave/loop_cut/loop_restore,value 为 50ms 单位时间量 | +| `time_anchor` | 时钟同步锚点 | null(`unix_ts` 即平台下发值,严格一致) | +| `log_clear` | 日志清除(审计) | null | + +--- + +## 4.18 清除脱机事件日志 `log_clear` + +> ⚠ **高风险操作**:清除动作本身写入事件流(`log_clear` 审计——谁在何时清了日志,留痕不可清除)。客户端应做权限控制。 + +**请求:** + +```json +{"msg_id":18,"cmd":"log_clear","ts":1719000000,"data":{"stream":"event"}} +``` + +| data 字段 | 类型 | 说明 | +|-----------|------|------| +| `stream` | string | 日志流,当前仅 `event`;缺省等同 `event` | + +**响应:** 标准成功/失败。成功后 `log_stat` 的 `count` 归 1(仅剩审计记录),`seq_last` 继续递增(序号不复位)。 + +--- + # 5 设备主动推送 主动推送帧与请求-响应共用同一条 TCP 连接,由设备在任意时刻发出。 @@ -457,3 +553,4 @@ Client Device (DLD960) |------|----------|----------|--------| | V1.00 | 2026-06-22 | 初始版本,基于串口协议 V1.01 | wangfq | | V1.01 | 2026-07-15 | `event_report` 增加**客户端必答**机制:应答格式(回显 `msg_id`)、设备 5s 超时重发(同 `msg_id`/`ts`,最多 3 次)、待发队列合并上报、客户端去重与先落库后应答要求(与 MQTT 协议 V1.04 对称) | wangfq | +| V1.02 | 2026-08-04 | 增加**脱机事件日志**命令:`log_stat`(统计/分页定位)、`log_query`(按全局序号分页,count≤4)、`log_clear`(清除+审计留痕);事件类型表与 MQTT 协议 V1.06 对齐 | wangfq | diff --git a/docs/DLD960_产品手册.md b/docs/DLD960_产品手册.md index e99c42f..3ba4338 100644 --- a/docs/DLD960_产品手册.md +++ b/docs/DLD960_产品手册.md @@ -149,8 +149,8 @@ DLD960 是一款四通道环形线圈车辆检测器,一台设备即可覆盖 |------|------| | 《DLD960 技术规格书》 | 完整技术参数 | | 《DLD960 串口通信协议》V1.01 | 串口 对接开发 | -| 《DLD960 TCP JSON 协议》V1.01 | 局域网对接开发 | -| 《DLD960 IoT MQTT 协议》V1.05 | 云平台对接开发 | +| 《DLD960 TCP JSON 协议》V1.02 | 局域网对接开发 | +| 《DLD960 IoT MQTT 协议》V1.06 | 云平台对接开发 | | 《环路车辆检测器验收标准》 | 采购/部署验收 | --- diff --git a/docs/DLD960_技术规格书.md b/docs/DLD960_技术规格书.md index 14eecc2..c378326 100644 --- a/docs/DLD960_技术规格书.md +++ b/docs/DLD960_技术规格书.md @@ -105,8 +105,8 @@ DLD960 是一款基于环形线圈(LC 振荡)检测原理的四通道车辆 | 协议 | 版本 | 通道 | 要点 | |------|------|------|------| | DLD960 串口通信协议 | V1.01 | TTL | 设备管理、参数配置、数据上报 | -| DLD960 TCP JSON 协议 | V1.01 | ETH :5960 | 密码鉴权 + 15 条命令;event_report 客户端必答(5s×3 重发) | -| DLD960 IoT MQTT 协议 | V1.05 | ETH → Broker | 双主题 `dld960/{sn}/srv`+`/dev`;initialize 上线、loop_data 三档调度、event_report 平台必答、设备时钟同步 | +| DLD960 TCP JSON 协议 | V1.02 | ETH :5960 | 密码鉴权 + 18 条命令;event_report 客户端必答(5s×3 重发);脱机日志 log_stat/log_query/log_clear | +| DLD960 IoT MQTT 协议 | V1.06 | ETH → Broker | 双主题 `dld960/{sn}/srv`+`/dev`;initialize 上线、loop_data 三档调度、event_report 平台必答、设备时钟同步、脱机日志 log_stat/log_query/log_clear | | DLD960Loop 串口协议 | V1.05 | MCU 间(内部) | 0x7F 帧、0xC0 传感上报(variation 3B 有符号) | ### 5.2 数据上报能力