# 故障录波 MQTT 协议详细描述 > 状态:待补全的协议描述,2026-10-12;2026-10-14 按 [FaultFilter_MQTT协议文档](FaultFilter_MQTT协议文档.md) 更正业务层字段;2026-10-16 补充 `607~610` 文件上传与下载地址(第 8A 节)。本文按现有信息逐字段描述 `377~380`,供补充和确认使用;不是已经完成设备验证的最终规范。 > > 业务层(`dataArea` 内的 `cmd/data`、设备回复的 `result/list`)以 FaultFilter 协议文档为准;外层(`cmdType`、`snno`、`crc`、topic 等)不在该文档范围内,仍按本文记录。原始证据与样本时间线见 [protocol-observations.md](protocol-observations.md)。功能目标见 [requirements.md](requirements.md),架构讨论见 [technical-design.md](technical-design.md)。本文中的字段类型不等于完整的必填性、值域或失败行为约束。 ## 1. 信息来源与阅读规则 | 标记 | 来源与适用范围 | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | 样本记录 | 观察文档整理的 CSV 通讯记录,仅覆盖`cmd=0/2`、`aGate` 和成功结果;2026-09-29 附件未入库,2026-10-14 附件为 `CloudLog_2026-10-14_104644.csv`(含 `607~610`) | | 已验证代码 | 用户此前确认可信的现有参数设置请求结构;代码入口为`service/repositories/mqtt/fwh_mqtt/fwh_mqtt_fault_record.go` | | FaultFilter 文档 | [FaultFilter_MQTT协议文档](FaultFilter_MQTT协议文档.md):设备侧故障录波服务的 `{cmd, data}` 业务协议;业务字段以其为准,经 `377~380` 外层转发后的表现仍需实测 | | 现有实现 | 仓库当前行为,不能单独证明设备协议要求 | | 用户确认 | 本轮讨论确认的协议行为或服务端约定 | | 旧描述 | 先前协议说明;与 FaultFilter 文档冲突时以后者为准,其余未被样本或用户确认覆盖的部分仍待验证 | | 待补充 | 缺少资料、语义不清或证据存在冲突 | 本文 JSON 均为**结构示意**,不是完整原始抓包。占位字符串没有协议值域含义,`hexData` 占位内容不能发送给设备;不为示意报文编造 `crc/len`。 **业务层映射**:FaultFilter 文档的请求为 `{cmd, data:{list}}`,与 `377.dataArea` 一致;其回复为 `{cmd, data:{result, list, filename}}`,而样本 `379.dataArea` 中 `result/list/filename` 直接位于 `dataArea` 顶层,没有 `data` 包裹。推断网关转发时展开了 `data`,`379` 承载的即 FaultFilter 回复;该映射在 `cmd=1/3` 和错误场景下尚未实测。FaultFilter 文档中的 topic(`aws/faultrecord` / `faultfilter/faultrecord`)是该服务自身的收发主题,与云端 `fwh/s2c|c2s/mng/{SN}` 的关系待确认,不能互相替代。 ## 2. 协议角色、方向与传输 存在两套方向不同的请求应答: | 请求 | 请求方 | 应答 | 应答方 | 用途 | | ------- | ------ | ------- | ------ | -------------------------------- | | `377` | 本工具 | `378` | 设备 | 发起故障录波操作并获得指令应答 | | `379` | 设备 | `380` | FWH 平台 | 上传数据或处理结果并获得上传应答;本工具只监听 | | `607` | FWH 平台 | `608` | 设备 | 平台通知设备上传录波文件;本工具只监听(见 8A) | | `609` | 设备 | `610` | FWH 平台 | 设备报告文件上传结果;本工具只监听 | `378` 与 `379` 不是同一种响应。现有样本在 `377/378` 后出现 `379/380`,但尚未确认每次请求是否必然产生上传、上传条数以及严格顺序。两套协议分别关联;`379/380` 的关联不能反向证明某次 `379` 属于某次 `377`。FaultFilter 文档规定请求与回复通过 `cmd` 关联、设备同一时刻只处理一条业务命令(见第 4.1 节),可作为 `377→379` 业务关联的依据,但外层是否保证一请求一上传仍待实测。 | 消息 | MQTT topic | 来源/状态 | | ------- | --------------------------- | ------------------------------------------ | | `377` | `fwh/s2c/mng/{gatewaySN}` | 现有实现;所有设备和失败场景是否适用待确认 | | `378` | `fwh/c2s/mng/{gatewaySN}` | 现有实现的等待 topic;原 CSV 未包含 topic | | `379` | 待补充 | 不能假定与`378` 相同 | | `380` | 待补充 | 不能假定与`377` 相同;样本中由 FWH 平台发出 | | `607`/`610` | `fwh/s2c/debug/{gatewaySN}` | FWH 平台发给设备,本工具只监听(用户确认,2026-10-19) | | `608`/`609` | `fwh/c2s/debug/{gatewaySN}` | 设备发给 FWH 平台(用户确认,2026-10-19) | 测试服与生产服使用不同的 broker、证书和平台域名,同一操作的 MQTT 与 HTTP 下载必须属于同一环境,见 [technical-design.md](technical-design.md#3-测试服与生产服)。 现有通用客户端订阅 QoS 为 `1`,故障录波发布使用 QoS `0`、`retained=false`。这些是实现配置,设备要求的 QoS、会话和离线投递规则待补充。FaultFilter 文档中的 QoS `2` 针对其自身 topic,不直接适用于云端外层。 ## 3. 公共报文外层 四种消息的样本记录均包含以下外层字段。`dataArea` 是 JSON 对象,不是 JSON 字符串。 | 字段 | JSON 类型 | 含义/现有行为 | 来源与待补充内容 | | ------------- | --------- | ---------------------------------------------------------------- | ------------------------------------------------------------- | | `cmdType` | 整数 | 消息类型,分别为`377/378/379/380` | 样本记录、已验证代码 | | `equipNo` | 字符串 | 现有`377` 填目标网关 SN | 样本中均为同一网关;真正子设备场景及全部应答取值待确认 | | `type` | 整数 | 外层设备类型;现有发送填`0`,代码注释为网关 IBG | 样本为`0`;其他值待补充。不同于业务层字符串 `list[].type` | | `timeStamp` | 整数 | 现有发送使用 Unix 秒`time.Now().Unix()` | 接收端单位、生成时点和校时规则待确认 | | `snno` | 整数 | 消息序号;关联规则见第 4 节 | 取值范围、回绕及设备重发是否保留序号待补充 | | `len` | 整数 | 现有发送为序列化后`dataArea` 的字节长度 | 接收规则与失败校验行为待确认 | | `crc` | 字符串 | 现有发送为`dataArea` 序列化字节的 CRC32 IEEE,8 位大写十六进制 | 接收规则与校验失败行为待确认 | | `dataArea` | 对象 | 命令业务内容,详见第 5~8 节 | 业务字段见 FaultFilter 文档;外层失败报文的形态待补充 | 现有发送计算方式:先以 Go `json.Marshal(dataArea)` 得到字节 `B`,再取 `len(B)` 和 `CRC32_IEEE(B)`。发送外层中的 `dataArea` 仍为对象。协议尚未明确 JSON 字段顺序、空白、Unicode 转义和数值格式对接收端 CRC 校验的影响,不能直接规定“反序列化后重新序列化”就是正确的校验方式。FaultFilter 文档说明业务 JSON 字段顺序不固定、接收方应按字段名解析。 外层字段完整必填规则、最大报文长度、未知字段处理及空值规则均待补充。 ## 4. 序号、关联和当前服务端约定 ### 4.1 `377/378` - **用户确认**:设备端没有严格按请求 `snno` 应答,请求方和应答方各自维护序号,不能用它关联 `377/378`。 - **用户确认**:服务端发送 `377` 可使用固定 `snno=123` 或随机数;现有代码使用 `123`。 - **用户确认**:当前采用串行发送并等待对应类型应答的方向;串行范围及释放时点尚待细化。 - **FaultFilter 文档**:设备同一时刻只处理一条业务命令。处理期间收到非「退出(`cmd=4`)」命令时直接回复错误 `busy`,不中断当前处理;`cmd=4` 用于结束当前处理。判定顺序为先校验 `cmd`、再判忙、再校验 `data`。该 `busy` 回复经外层体现为 `378` 还是 `379` 待实测。 - **服务端暂定**:响应等待超时为 **15 秒**。在当前讨论中针对 `377/378` 梳理;计时起点尚待确认,不是设备承诺的最长响应时间,也不作为 `379` 的超时。 - **待设计**:建议核对 topic、`equipNo`、`cmdType=378`、`dataArea.cmd`,而非只匹配 `cmdType`;此过滤方案尚未落地。 - **待补充**:迟到或重复 `378`、其他发送方请求产生的 `378` 如何处理;相同网关和 `cmd` 的消息没有唯一关联凭据。 ### 4.2 `379/380` - **用户确认**:`380` 由服务端实现,可以使用 `snno` 关联 `379/380`。 - **待细化**:是否通过 `380.snno=379.snno` 实现关联,还是另有规则;尚未确认,本文不将回显写为既定要求。 - **证据冲突**:旧样本记录的 `379/380` 序号不同(`14→7536`、`26→7559`)。保留其历史事实,不将它解释为新服务端必须继续采用的行为;新关联方式需补充设备接受条件和验证结果。 - **待补充**:应答 topic、应答期限、重发规则、应答成功的语义及重复上传处理。 ## 5. `377`:服务端操作请求 ### 5.1 公共业务结构 | 字段路径(相对`dataArea`) | 类型 | 含义 | 来源/约束 | | ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | `cmd` | 整数 | `0` 获取实时数据;`1` 设置参数;`2` 获取记录;`3` 查询参数;`4` 退出;`90/99` 连通性测试 | FaultFilter 文档;样本覆盖`0/2`,已验证代码覆盖 `1`;`3/4/90/99` 经外层转发待实测 | | `data` | 对象 | 请求内容 | `cmd=4/90/99` 为空对象`{}` | | `data.list` | 对象数组 | 按设备类型分组 | 最小/最大数量待补充 | | `data.list[].type` | 字符串 | 目标设备类型:`pe` 或 `aGate` | 字典见第 9 节 | | `data.list[].info` | 对象数组 | 该类型下的目标设备和操作项 | 批量限制待补充 | | `data.list[].info[].sn` | 字符串 | 目标设备 SN;`pe` 必填并用于寻址(最多 15 台),`aGate` 可填但不用于寻址 | FaultFilter 文档;`pe` 的 SN 无法匹配时整条命令回复 `pe sn not find` | ### 5.2 `cmd=0`:获取实时数据 附加字段 `data.list[].info[].codeList` 为整数数组,每个元素为数据项编号。设备对每个 `code` 串行读取子设备,汇总后在回复的 `hexList` 中返回。 | `code` | 含义 | `hexData` 结构(FaultFilter 文档) | | ------ | ------------------------ | ---------------------------------------- | | `1` | 故障总体信息 | 5.1.1,变长 | | `2` | 设备故障录波功能基本信息 | 5.1.1,固定 16 字节 | | `3` | 故障录波总目录 | 5.1.1,1 字节条数 + N × 14 字节目录项 | `cmd=0` 只支持 `code=1~3`(用户确认,2026-10-14)。单一故障信息(`4`)和单一故障波形数据(`5`)属于具体记录,必须用 `cmd=2` 按记录序号导出(见 5.4)。样本请求 `[3]` 即查询录波总目录,目录项中的位置索引 `index` 是 `cmd=2` 的请求取值。重复值及空数组的行为未定义。 `dataArea` 结构示意: ```json { "cmd": 0, "data": { "list": [ { "type": "aGate", "info": [{ "sn": "<目标设备SN>", "codeList": [3] }] } ] } } ``` ### 5.3 `cmd=1`:设置参数 | 附加字段路径(相对`dataArea`) | 类型 | 含义 | | ---------------------------------------- | -------- | ---------------------------------------------------------------------------------------- | | `data.list[].info[].paraList` | 对象数组 | 待执行的功能项列表 | | `data.list[].info[].paraList[].id` | 整数 | **命令控制符(功能码)**,不是数据参数编号;取值 `1~7`,见下表 | | `data.list[].info[].paraList[].setVal` | 字符串 | 该功能码的输入参数,十六进制字符串(每 2 字符 1 字节);无输入参数的功能码可为空字符串 | | `id` | 功能 | `setVal` 字节结构 | | ---- | ------------------------ | ------------------------------------------------------------------------------------------------- | | `1` | 清除设备故障波形 | 无 | | `2` | 采样通道配置 | 通道数(1B) + n × 通道含义(2B,大端) | | `3` | 手动触发故障录波 | 无 | | `4` | 设备对时 | 年(2B,大端) 月 日 时 分 秒,共 7 字节 | | `5` | 设置采样频率 | 2 字节,Hz,大端 | | `6` | 配置可配采样通道 | 索引号(1B) + 数据类型(1B,高半字节=设置方式,低半字节=地址类型) + 数据(4B) | | `7` | 故障录波功能状态控制 | 1 字节:`0`=关闭,`1`=使能 | 完整定义见 FaultFilter 文档 5.4.1。 `dataArea` 结构示意,值取自现有辅助函数,不代表通用默认配置: ```json { "cmd": 1, "data": { "list": [ { "type": "pe", "info": [ { "sn": "<目标子设备SN>", "paraList": [ { "id": 6, "setVal": "010100015984" }, { "id": 6, "setVal": "020100015981" } ] } ] } ] } } ``` 两项 `id=6` 均为「配置可配采样通道」,由 `setVal` 首字节的通道索引区分:`01 01 00015984` 表示通道 1、默认方式、Uint16、地址 `0x00015984`;`02 01 00015981` 表示通道 2、地址 `0x00015981`。因此 `id` 不能作为条目唯一键;回复 `setResultList` 是否按请求顺序逐项对应待实测。前端目前只校验十六进制字符,未按功能码校验字节长度。HTTP DTO 使用 `val`,MQTT 使用 `setVal`,两者不可混淆。 ### 5.4 `cmd=2`:获取记录 附加字段 `data.list[].info[].index` 为整数数组,字段名为单数 `index`,值为数组。每个元素是**记录序号**,即 `cmd=0, code=3` 目录中各记录的位置索引(从 1 开始,不超过 `code=1` 的「最大可存储故障记录总数」)。旧描述的范围 `1~16` 未在 FaultFilter 文档中出现,以目录实际返回为准。样本请求 `[9]`。 设备对每个记录序号分别读取「故障数据」(`code=4`) 与「波形数据」(`code=5`),并保存为设备本地文件(见 7.4)。每次 `cmd=2` 开始前,设备会删除保存根目录下的**全部**历史记录文件。 ```json { "cmd": 2, "data": { "list": [ { "type": "aGate", "info": [{ "sn": "<目标设备SN>", "index": [9] }] } ] } } ``` ### 5.5 `cmd=3`:查询参数 FaultFilter 文档定义,无样本,经 `377` 转发待实测。附加字段 `data.list[].info[].idList` 为整数数组,元素为第 5.3 节的功能码,不带输入参数。 ```json { "cmd": 3, "data": { "list": [ { "type": "pe", "info": [{ "sn": "<目标子设备SN>", "idList": [2, 5, 7] }] } ] } } ``` ### 5.6 `cmd=4`/`cmd=90`/`cmd=99` - `cmd=4` 退出:`data` 为 `{}`,结束设备当前处理,不产生业务数据回复。 - `cmd=90`/`cmd=99` 连通性测试:`data` 为 `{}`,设备分别回复 `result="ok"`/`"test fail"`。 以上均来自 FaultFilter 文档,经外层转发的表现待实测。 ## 6. `378`:设备操作应答 | 字段路径(相对`dataArea`) | 类型 | 含义/状态 | | ---------------------------- | ---- | -------------------------------------------------------------------- | | `cmd` | 整数 | 样本为`0/2`;现有代码按 `0/1/2` 定义。是否始终等于请求命令待确认 | | `result` | 整数 | 样本成功为`0`;旧描述失败为 `-1`;完整错误值域待补充 | ```json { "cmd": 0, "result": 0 } ``` `378` 属于外层应答,FaultFilter 文档未定义(其回复 `result` 为字符串,对应 `379`)。样本与现有代码没有显示这里包含逐设备、逐参数或文件字节。`result=0` 表示收到、接受、排队还是执行完成,尚未确认;不能仅凭它断言数据已取得、文件可下载或参数已生效。 **待补充的应答行为**:一条请求对应一条还是多条应答;格式错误、设备忙和不支持命令时 `378` 是否仍为 `0`;重复及迟到应答规则;字段缺失规则;收到应答后能否立即发送下一条请求(FaultFilter 层忙时会回复 `busy`)。 ## 7. `379`:设备数据/处理结果上传请求 ### 7.1 公共业务字段 | 字段路径(相对`dataArea`) | 类型 | 含义/状态 | | ---------------------------- | -------- | -------------------------------------------------------------------------------- | | `cmd` | 整数 | 回显请求命令码;样本为`0/2` | | `result` | 字符串 | 整体结果:`"ok"` 全部子项成功,`"error"` 至少一个子项失败;错误回复时为错误说明 | | `list` | 对象数组 | 上传设备列表,位于`dataArea.list`,不是 `dataArea.data.list` | | `list[].type` | 字符串 | 设备类型:`pe` 或 `aGate` | | `list[].info` | 对象数组 | 各设备结果 | | `list[].info[].sn` | 字符串 | 设备 SN;样本类型为字符串,不是数组 | **错误回复**:命令无法进入业务处理时,FaultFilter 回复只含 `result`,没有 `list`: | `result` | 原因 | | ----------------- | --------------------------------------- | | `json unpack err` | 消息体不是合法 JSON | | `cmd unpack err` | 缺少 `cmd`(此时回复 `cmd` 为 `0`) | | `data unpack err` | 缺少 `data` | | `busy` | 设备正忙,收到非退出命令 | | `pe sn not find` | `pe` 的 SN 无法匹配 | | `cmd no define` | 不支持的命令码 | **子项结果 `resultOne`**(所有 `hexList[]`/`indexInfo[]`/`idInfo[]`/`setResultList[]` 子项通用): | 取值 | 含义 | | ----------------------------- | ----------------------------------------------------------------- | | `ok` | 成功,对应数据有效 | | `cancomerr:suberrcode=` | 子设备负响应错误码(NRC),见 FaultFilter 文档 10.6 | | `cancomerr:time out` | 与子设备通信超时 | | `cancomerr:index=` | 设备内部通信错误判定码,见 FaultFilter 文档 10.5 | | `emserr:can p null` | 未取到目标子设备 | | `emserr:can data null` | 子设备返回数据为空 | | `emserr:set cmd err` | 下发命令失败 | | `emserr:other` | 其他错误 | `resultOne` 不为 `ok` 时,对应 `hexData`/`idData` 可能为空或无效。错误回复经外层后是否仍以 `379` 上传待实测。 ### 7.2 `cmd=0`:实时数据上传 | 附加字段路径 | 类型 | 含义 | | ------------------------------------- | -------- | --------------------------------------------------------------------- | | `list[].info[].hexList` | 对象数组 | 各数据项结果 | | `list[].info[].hexList[].code` | 整数 | 回显请求的`code`;样本`3` 为故障录波总目录 | | `list[].info[].hexList[].resultOne` | 字符串 | 子项结果,见 7.1 | | `list[].info[].hexList[].hexData` | 字符串 | 子设备原始字节的十六进制字符串;结构按 `code` 见第 5.2 节 | ```json { "cmd": 0, "result": "ok", "list": [ { "type": "aGate", "info": [{ "sn": "<目标设备SN>", "hexList": [{ "code": 3, "resultOne": "ok", "hexData": "<原始十六进制数据>" }] }] } ] } ``` `code=3` 目录项固定 14 字节:位置索引(1) + 发生时间(7,年份大端) + 参数列表版本(4) + 事件触发源(2)。2026-10-14 样本的 `hexData` 共 211 字节 = 1 + 15 × 14,15 条记录均按此结构解析出合理的时间与触发源(`0x1001`/`0x1002`),结构已验证;解析结果见 [protocol-observations.md](protocol-observations.md#2026-10-14-样本)。 ### 7.3 `cmd=1`:参数设置结果上传 无样本,结构来自 FaultFilter 文档。旧描述的 `resultList[{id, result}]` 有误,正确字段为 `setResultList`: | 附加字段路径 | 类型 | 含义 | | ------------------------------------------- | -------- | ------------------------------------- | | `list[].info[].setResultList` | 对象数组 | 各功能项执行结果 | | `list[].info[].setResultList[].id` | 整数 | 回显请求 `paraList[].id`(功能码) | | `list[].info[].setResultList[].resultOne` | 字符串 | 子项结果,见 7.1;为字符串,不是数组 | ```json { "cmd": 1, "result": "ok", "list": [ { "type": "pe", "info": [{ "sn": "<目标子设备SN>", "setResultList": [{ "id": 6, "resultOne": "ok" }, { "id": 6, "resultOne": "ok" }] }] } ] } ``` 重复功能码(如两项 `id=6`)的结果是否严格按请求顺序返回,待实测。 ### 7.4 `cmd=2`:记录处理结果上传 | 附加字段路径 | 类型 | 含义 | | --------------------------------------- | -------- | ---------------------------------------------------------------------------------------- | | `filename` | 字符串 | 本次导出批次的时间戳目录名,格式`YYYY-MM-DD_HH_MM_SS`;样本为 `2026-09-29_14_18_29` | | `list[].info[].indexInfo` | 对象数组 | 各记录项结果 | | `list[].info[].indexInfo[].index` | 整数 | **记录数据类型码**:`4`=故障数据,`5`=波形数据;**不是**请求的记录序号 | | `list[].info[].indexInfo[].resultOne` | 字符串 | 子项结果,见 7.1;不是数组 | ```json { "cmd": 2, "result": "ok", "filename": "2026-09-29_14_18_29", "list": [ { "type": "aGate", "info": [{ "sn": "<目标设备SN>", "indexInfo": [{ "index": 4, "resultOne": "ok" }, { "index": 5, "resultOne": "ok" }] }] } ] } ``` 请求的每个记录序号在回复中展开为 `4`、`5` 两项,回复不回传记录序号本身。因此样本请求 `[9]` 后上传 `4/5` 是正常对应,并非索引转换。请求多个记录序号时,回复中会出现多组 `4/5`,无法仅凭回复区分属于哪个记录序号,只能依赖请求顺序(待实测)。 上传不含文件字节或下载地址。文件保存在设备本地: ``` /home/root/ExportFile/faultFilterRecode///index_<记录序号>/failuredata /home/root/ExportFile/faultFilterRecode///index_<记录序号>/waveformdata ``` 文件内容为原始字节的大写十六进制 ASCII 文本,无文件头;仅成功项落盘。内部结构见 FaultFilter 文档 5.2.2/5.2.3。文件由 FWH 平台通过 `607~610` 触发上传到 hes-upload,见第 8A 节。 ### 7.5 `cmd=3`:参数查询结果上传 无样本,结构来自 FaultFilter 文档。 | 附加字段路径 | 类型 | 含义 | | ------------------------------------ | -------- | ------------------------------------------------------------ | | `list[].info[].idInfo` | 对象数组 | 各功能项查询结果 | | `list[].info[].idInfo[].id` | 整数 | 回显请求的功能码 | | `list[].info[].idInfo[].idData` | 字符串 | 当前配置/状态的十六进制字符串,结构参照第 5.3 节对应功能码 | | `list[].info[].idInfo[].resultOne` | 字符串 | 子项结果,见 7.1 | ## 8. `380`:服务端上传应答 | 字段路径(相对`dataArea`) | 类型 | 含义/状态 | | ---------------------------- | ---- | ------------------------------------------------ | | `cmd` | 整数 | 样本为`0/2`;是否必须与 `379.cmd` 一致待明确 | | `result` | 整数 | 样本为`0`;旧描述失败为 `-1` | ```json { "cmd": 0, "result": 0 } ``` `380` 属于外层,FaultFilter 文档未定义。关联方向已由用户确认可使用 `snno`,具体赋值规则见第 4.2 节。`result=0` 是接收成功、校验通过还是保存成功尚待确认。业务项失败时能否仍回复上传接收成功,以及失败应答是否触发设备重发,也待补充。 **设计决策(2026-10-16)**:样本中的 `380` 由 FWH 平台回复,本工具不回复 `380`,只监听,避免设备收到重复应答。`cmd=0/1/3` 的 `380` 是否同样由平台回复待实测。 ## 8A. `607~610`:录波文件上传 来源:2026-10-14 样本记录(两轮 `cmd=2`,结构一致)。这一组消息由 FWH 平台在收到 `379(cmd=2)` 后自动发起,本工具只监听(用户确认,2026-10-16)。 | `cmdType` | 日志名称 | 方向 | `dataArea` 样本 | | --------- | -------- | ---- | --------------- | | `607` | 故障录波召唤下行数据 | 平台 → 设备 | `{"faultRecordID":5226,"fileName":"2026-10-14_05_46_20","url":"http://test.franklinwh.com/hes-upload/v1/file/upload","logTime":"2026-10-14"}` | | `608` | 故障录波召唤应答上行数据 | 设备 → 平台 | `{"result":0,"filename":"2026-10-14_05_46_20","result":5226}` | | `609` | 故障录波文件上传结果 | 设备 → 平台 | `{"result":0,"fileName":"2026-10-14_05_46_20.tar.bz2"}` | | `610` | 故障录波文件上传结果应答 | 平台 → 设备 | `{"result":1}` | 字段说明: | 字段 | 消息 | 类型 | 含义/状态 | | ---- | ---- | ---- | ---------- | | `faultRecordID` | `607` | 整数 | FWH 平台的召唤记录 ID;可与 DataDownload 使用的平台记录互查 | | `fileName` | `607` | 字符串 | 等于 `379.filename`,无扩展名 | | `url` | `607` | 字符串 | 设备 HTTP 上传地址;样本为测试服 `http://` 地址,不用于推导下载地址 | | `logTime` | `607` | 字符串 | 日期 `YYYY-MM-DD`,用途待确认 | | `filename` | `608` | 字符串 | 回显 `607.fileName`;注意键名小写 | | `result` | `608` | 整数 | 应答结果,样本为 `0` | | `id` | `608` | 整数 | 平台召唤记录 ID,等于 `607.faultRecordID`。**旧版本设备把字段名误写为 `result`**,同一对象出现两个 `result`,第二个即 `id`(用户确认,2026-10-16);后续版本修复,旧版本仍需兼容:解析前将第二个 `result` 键改名为 `id`,新旧格式按同一结构解析。10-19 测试服的 608 为 `{"result":0,"filename":…}`,既无重复 `result` 也无 `id`,此时平台记录 ID 只取自 607;608 与 607 到达顺序不固定 | | `result` | `609` | 整数 | 样本 `0`,推测为上传成功;失败值待补充 | | `fileName` | `609` | 字符串 | 上传文件名。**不同固件不一致**:10-14 样本为 `<批次>.tar.bz2`,10-19 测试服 EamonAux 为 `<批次>`(不带后缀)。实际下载文件都带 `.tar.bz2`,下载前统一补齐后缀 | | `result` | `610` | 整数 | 样本 `1`;与 `378/380` 的 `0=成功` 约定不同,不能统一按 `0` 判定成功 | 时序(样本):`379` 与 `607/608` 在同一秒内,`380` 晚约 1 秒,`609/610` 在 `607` 后约 11~12 秒。 **下载地址**(用户确认,2026-10-16): ```text https://<环境域名>/hes-upload/v1/file/download/logger//<609.fileName> ``` 测试服 `<环境域名>` 为 `test.franklinwh.com`,生产服为 `FWH_ENERGY` 配置的域名。下载**不需要 token**(用户确认,2026-10-16)。文件保留期限待确认。 **压缩包**(2026-10-14 样本验证):扩展名为 `.tar.bz2`,实际是 **gzip** 压缩的 tar,解压须按文件头(`1f 8b` 为 gzip,`42 5a 68` 为 bzip2)判断。内部结构为: ```text //index_<记录序号>/failuredata //index_<记录序号>/waveformdata ``` **文件解析规则**(以 FaultFilter 文档 5.2.2/5.2.3 为基础,按样本补充,详见 [protocol-observations.md](protocol-observations.md#2026-10-14-录波文件解析)): | 项目 | 规则 | 依据 | | --- | --- | --- | | `failuredata` 长度字段 | 等于整个文件字节数(含填充),不能当有效数据长度用;按结构逐项解析 | 样本 512 字节,结构在第 308 字节结束 | | `failuredata` 末尾 | `0xFF` 填充,解析到可配采样通道组结束即停止 | 样本 | | 版本字符串 | 取第一个 NUL 之前的内容。aGate 的 INV/FPGA 两段为填充,填充方式不限定,**不解析**,只跳过 64 字节 | 用户确认(2026-10-16);样本填充为 `01 02 … 40` | | 波形通道数 n | = `failuredata` 的“采样通道总数”,已包含可配采样通道 | 样本 16 通道,文件长度恰好整除 | | 波形点数 m | = 基本信息“采样点总数”;故障时刻在第“故障前采样点数”点 | 样本 1536 点,第 1024 点故障字跳变 | | 波形排列 | 采样点优先(同一点的 n 个通道连续) | 样本验证 | | 采样值 | 每点每通道 2 字节、大端;按 **int16** 解释(文档写 Uint16) | 样本正弦通道按有符号解释才连续 | | float32 可配通道 | 在波形中同样只占 2 字节,换算方式待确认 | 样本 | ## 9. 设备类型与数据字典 | 位置/来源 | 已出现的值 | 说明 | | -------------------------------- | -------------------------------------- | -------------------------------------------------------------------------- | | FaultFilter 文档 | `pe`、`aGate` | 业务层仅定义这两种;`pe` 按 SN 寻址(最多 15 台),`aGate` 系统内唯一 | | 用户确认(2026-10-20) | `pe`、`aGate`、`mppt`、`apbox`、`dif` | 377 `type` 全集:`mppt`=aPower-MPPT(与 aPower 同 SN)、`apbox`=aHub(aPbox2.0)、`dif`=Digital Interface(前端暂未开放) | | `377/379.list[].type` 样本 | `aGate` | `cmd=0/2` | | `377.list[].type` 已验证代码 | `pe`、`aGate` | 参数设置 | | `377.list[].type` 旧描述 | `pe`、`agate`、`mppt`、`apbox` | FaultFilter 文档未定义`agate`(小写)、`mppt`、`apbox`,不应使用 | | `379.list[].type` 旧描述 | `aGate`、`aPower` | FaultFilter 文档未定义`aPower`,回复类型与请求一致为 `pe`/`aGate` | | HTTP/页面类型 | `pe`、`agate` | 现有设置代码将 HTTP`agate` 转成 MQTT `aGate`;MQTT 层必须使用 `aGate` | 已由 FaultFilter 文档补齐的字典:`code` 含义及 `hexData` 结构(5.2)、功能码及 `setVal` 结构(5.3)、`indexInfo[].index` 类型码(7.4)、`resultOne` 及错误字符串(7.1)、事件触发源与子设备错误码(见该文档第 10 节)。通道含义参数代码(F14)见产品级《参数代码表》,未随该文档提供。 仍缺失:外层 `378/380.result` 值域、支持的设备型号与固件版本。 ## 10. 待补充清单 以下编号用于逐项讨论;“待补充”不是已经确定的限制。优先补齐 `A` 类,再继续分析上传协议。 | 编号 | 待补充信息 | 当前已知/缺口 | 可补充的材料 | | ---- | -------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------- | | A01 | `378.result` 的精确定义 | 只知道样本为`0`,成功阶段未知 | 协议说明、设备处理流程或对照实测 | | A02 | `378` 应答条数和批量语义 | 未知是否一请求一应答;批量子项结果已知在`379` 中按 `resultOne` 表达 | 多子设备/多参数请求及完整应答 | | A03 | `378` 字段与错误值域 | 业务层错误字符串已知(7.1),其在外层的体现方式未知 | 非法输入、忙、不支持等场景的完整报文 | | A04 | 连续请求的设备行为 | FaultFilter 层串行处理、忙时回复`busy`;`busy` 经外层如何体现未知 | 处理中发送下一条请求的结果 | | A05 | 迟到、重复和重发应答 | 无唯一关联标识,15 秒不保证设备停止回复 | 延迟/断线场景的完整时间线和最大时限说明 | | A06 | topic 与发送方隔离 | 当前实现有`mng/{SN}`;FaultFilter 自身 topic 与云端 topic 的关系未知 | 双向 topic、其他平台发送时的消息可见性 | | A07 | 外层字段和 CRC 规则 | 发送代码有算法,接收及错误规则未知 | 原始 JSON 字节、校验规范、失败样本 | | A08 | `377` 输入约束 | `pe` 最多 15 台;其余批量上限未知 | 数量限制和输入校验说明 | | A09 | 15 秒超时的计时口径 | 服务端暂定值,起点与超时后策略未确定 | 服务端决策;设备延迟证据供参考 | | B01 | `cmd=3`/`cmd=4` 外层支持 | FaultFilter 文档定义了 `cmd=3`(查询参数)和 `cmd=4`(退出),旧描述、现有代码和实报只有 `0/1/2`。用户判断可能支持(2026-10-16),待实测。不支持时参数读取、复读和设备侧取消不可用 | `cmd=3`、`cmd=4` 实测报文 | | B02 | ~~`code/hexData` 解释~~ | 已明确(5.2);`code=3` 已用 2026-10-14 样本验证(7.2),`code=1/2` 待样本 | `code=1/2` 原始十六进制样本 | | B03 | ~~参数 ID/值/重复 ID~~ | `id` 为功能码,`setVal` 结构及重复 `id=6` 的区分方式已明确(5.3) | 重复功能码结果顺序的实测 | | B04 | ~~文件传输链路~~ | 已明确:平台经 `607~610` 触发设备 HTTP 上传 `tar.bz2`,按规律下载(8A);平台是否对本工具直连发出的 `377` 同样自动上传待实测 | 本工具直连 `cmd=2` 的完整时间线 | | B05 | `379` 产生条件和批次 | 是否依赖成功`378`、是否主动/拆分上传未知 | 操作到全部上传的时间线 | | B06 | ~~`379(cmd=1)` 结构~~ | 已明确为`setResultList[{id, resultOne}]`(7.3) | 成功、部分失败实测报文 | | B07 | `379/380` topic 与关联 | 服务端可用`snno`,具体规则和旧样本不一致 | 明确赋值规则及设备接受新应答的实测 | | B08 | 上传应答语义和可靠性 | 成功判据、应答时限、重发和断线恢复未知;本工具不回复 `380`,此项影响降低 | 设备上传处理规范及异常时间线 | | B09 | ~~`607~610` topic 与订阅权限~~ | 已明确:`607/610` 在 `fwh/s2c/debug/{SN}`,`608/609` 在 `fwh/c2s/debug/{SN}`;测试服实测 s2c 方向可订阅(收到 380) | 生产服订阅实测 | | B10 | `609/610` 值域 | `608` 第二个 `result` 已确认为误写的 `id`,已兼容(8A)。仍待确认:`609` 失败值;`610.result=1` 含义。**待用户统一找人确认** | 设备方或平台方说明、上传失败样本 | | B11 | ~~下载认证与压缩包结构~~ | 已明确:不需要 token;实为 gzip tar;结构见 8A。保留期限和生产服是否一致待确认 | 生产服下载一个真实文件 | | B12 | float32 可配通道的 16 位表示 | 样本第 13、14 通道类型为 float32,波形中每点仅 2 字节,值在 -248~249;换算方式未知。**待用户统一找设备方确认** | 设备方说明 | | B13 | ~~`failuredata` 中 aGate 的 INV/FPGA 版本字段~~ | 已明确(用户确认,2026-10-16):aGate 中这两段是填充,填充方式不限定,解析时跳过 | — | | B14 | 采样值有无符号 | 文档写 Uint16,样本正弦通道按 int16 解释才连续;暂按 int16 处理 | 设备方确认 | | B15 | F14 物理量与换算系数 | **决策(2026-10-16)**:解析器先只显示原始值和通道含义代码,是否按《参数代码表》换算后续再定 | 《参数代码表》 | 补充时建议记录:编号、确认结论、适用设备/固件、证据来源、原始请求/响应和 topic。先保留历史样本,再更新字段或规则,避免将实现选择写成设备协议事实。