3. SECS-II消息结构精讲

SECS-II 消息是 EAP 与设备之间真正“说话”的载体。一条 SECS-II 消息由两大部分组成:固定 10 字节的消息头(Message Header)和紧随其后的消息体(Message Body)。消息头负责寻址、标识和路由,消息体则用数据项(Data Item)承载具体业务内容。理解这两部分如何拼接、每个字段如何取值,是排查通信异常、编写报文解析代码的基础。

一、10 字节消息头:字段布局与含义

消息头固定 10 字节,按顺序排列如下:

字节位置字段长度说明
0设备ID高字节1 字节Upper Device ID
1设备ID低字节1 字节Lower Device ID
2W-bit1 bit等待位,置 1 表示需要回复
2Stream7 bit流号,0~127
3Function1 字节功能码,0~255
4~5System Bytes2 字节事务标识,用于请求/回复配对
6~9消息体长度4 字节消息体字节数,不含消息头

注意第 2 字节是“W-bit + Stream”共用一个字节:最高位是 W-bit,低 7 位是 Stream。解析时通常写作 byte2 = (W_bit << 7) | Stream。例如 W-bit=1、Stream=1,则第 2 字节为 0x81;W-bit=0、Stream=6,则为 0x06。

二、系统字节(System Bytes):请求与回复的“身份证”

系统字节是 2 字节无符号整数,由发起方生成,用于把一次 Primary Message 和它对应的 Secondary Message 关联起来。设备收到请求后,回复消息必须原样带回相同的 System Bytes。EAP 侧通常维护一个递增计数器,每发一条新请求就加一,并记录“System Bytes → 等待中的事务”映射表;收到回复时用 System Bytes 查表,找到对应的业务上下文。

如果回复的 System Bytes 与请求不一致,EAP 会认为这是一条“孤儿回复”,通常直接丢弃并记录告警。这是现场最常见的通信异常之一。

三、W-bit:决定“要不要回”

W-bit(Wait-bit)只有 1 位,却决定了通信的时序行为:

  • W-bit = 1:接收方必须回复一条 Secondary Message,且回复消息的 W-bit 必须为 0。
  • W-bit = 0:接收方不需要回复,属于单向通知。

例如 EAP 下发 S1F13(Establish Communications Request)时 W-bit=1,设备回 S1F14 时 W-bit=0。若 EAP 发出 W-bit=1 的请求后长时间收不到回复,就会触发 T3 超时。

四、Stream 与 Function:消息的“姓”和“名”

Stream(流)表示消息大类,Function(功能码)表示该类下的具体动作。二者合称 SxFy。约定俗成:奇数 Function 为请求,偶数 Function 为回复,即 Fy 与 F(y+1) 配对。例如:

  • S1F1 / S1F2:Are You There / On Line Data
  • S1F13 / S1F14:Establish Communications Request / Acknowledge
  • S2F41 / S2F42:Host Command Send / Host Command Acknowledge
  • S6F11 / S6F12:Event Report Send / Event Report Acknowledge

Stream 0 为保留;Stream 1 用于通信建立与状态查询;Stream 2 用于设备控制与参数;Stream 5 用于告警;Stream 6 用于事件报告;Stream 7 用于工艺程序管理。实际项目中,EAP 需要按设备能力集(Capability Set)决定支持哪些 SxFy。

五、消息长度与数据项编码规则

消息头第 6~9 字节是 4 字节大端整数,表示消息体的字节数。若消息体为空(例如 S1F2 的回复),该字段为 0。解析时必须先读长度,再按长度读取消息体,否则会粘包或截断。

消息体由若干数据项(Data Item)顺序排列组成。每个数据项由“格式字节 + 长度字节 + 数据”构成。格式字节高 6 位是格式码,低 2 位是长度字节数(Length Bytes)。常见格式码:

格式码类型示例
0o20Boolean0x25 0x01 0x01
0o40Binary0x41 0x02 0xAB 0xCD
0o60ASCII0x61 0x05 "HELLO"
0o70JIS-8日文编码
0o90I8 / I1有符号整数
0o A0I16 / I22 字节整数
0o B0I32 / I44 字节整数
0o F0F32 / F44 字节浮点
0o 00List0x01 0x02 表示 2 元素列表

以 ASCII 为例,格式字节 0x61:高 6 位 0o60 表示 ASCII,低 2 位 01 表示长度字段占 1 字节。若字符串为 "HELLO",则完整编码为 0x61 0x05 0x48 0x45 0x4C 0x4C 0x4F。若长度超过 255,则低 2 位改为 10(2 字节长度)或 11(3 字节长度)。

List 是唯一“长度表示元素个数”而非字节数的类型。例如 0x01 0x02 表示一个包含 2 个元素的 List,随后紧跟这 2 个元素的数据项编码。List 可以嵌套,形成树形结构,这是 SECS-II 表达复杂数据的核心机制。

六、完整报文示例

假设 EAP 向设备 ID=0 发送 S1F13,W-bit=1,System Bytes=0x0001,消息体为空。完整 10 字节消息头为:

00 00 81 0D 00 01 00 00 00 00
│  │  │  │  │     │
│  │  │  │  │     └─ 消息体长度 = 0
│  │  │  │  └─────── System Bytes = 0x0001
│  │  │  └────────── Function = 0x0D (13)
│  │  └───────────── W-bit=1, Stream=1 → 0x81
│  └──────────────── Device ID 低字节 = 0x00
└─────────────────── Device ID 高字节 = 0x00

设备回复 S1F14,W-bit=0,System Bytes 仍为 0x0001,消息体包含一个 2 元素的 List(如 COMMACK 和 MDLN)。解析时先读 10 字节头,确认 System Bytes 匹配,再按长度字段读取消息体,逐层解析 List 和其中的 ASCII 数据项。

SECS-II 消息结构:10 字节消息头 + 消息体 消息头(固定 10 字节) Byte 0-1 Device ID Byte 2 W-bit + Stream Byte 3 Function Byte 4-5 System Bytes Byte 6-9 消息体长度 消息体(Data Items,长度由 Byte 6-9 指定) List (0x01 0x02) 2 个元素 ASCII "0" (0x61 0x01 0x30) COMMACK = 0 ASCII "EQP01" (0x61 0x05 ...) MDLN = "EQP01" 数据项编码规则 格式字节 = 格式码(6bit) + 长度字节数(2bit) ASCII: 0x61 = 0o60 + 01 List: 0x01 0x02 → 2 个元素 长度字段:1/2/3 字节可选 完整报文示例:S1F13 请求 00 00 81 0D 00 01 00 00 00 00 DeviceID=0 | W=1,Stream=1 | Func=13 | SysBytes=1 | BodyLen=0

掌握上述结构后,无论是用 Python 的 struct.unpack 手工解析,还是借助 secs4net、PySECS 等库,都能快速定位问题:先看 10 字节头是否合法,再核对 System Bytes 是否配对,最后按格式码逐层展开数据项。现场排障时,把原始十六进制报文按“头 10 字节 + 体”切开,往往一眼就能看出是长度字段写错、W-bit 漏置,还是 List 嵌套层级不对。