2. SECS-II消息结构精解
SECS-II 是 SEMI E5 标准定义的设备通信消息层,它规定了主机(Host)与设备(Equipment)之间交换的消息长什么样。一条 SECS-II 消息由两部分组成:消息头(Message Header)和消息体(Message Body)。消息头固定 10 字节,负责“路由与语义标识”;消息体是可变长度的数据项序列,负责“承载业务数据”。理解这两部分的结构,是读懂任何一条 SxFy 消息、排查通信问题的起点。
一、消息头:10 字节的固定结构
消息头是定长的 10 个字节,按顺序排列如下:
| 字节位置 | 字段 | 长度 | 说明 |
|---|---|---|---|
| Byte 0 | Session ID / Device ID | 1 字节 | HSMS 中通常为 0xFFFF(未使用),SECS-I 中为设备号 |
| Byte 1 | Header Byte 2 | 1 字节 | W-Bit(bit7)+ Stream(bit0~6) |
| Byte 2 | Header Byte 3 | 1 字节 | Function(0~255) |
| Byte 3 | PType | 1 字节 | Presentation Type,SECS-II 固定为 0 |
| Byte 4 | SType | 1 字节 | Session Type,数据消息为 0,控制消息非 0 |
| Byte 5~8 | System Bytes | 4 字节 | 事务标识,用于请求/应答配对 |
| Byte 9 | Header Byte 10 | 1 字节 | 保留或用于特定扩展 |
其中最关键的是 Stream、Function、W-Bit 和 System Bytes 四个字段。
Stream(S)占 7 位,取值 0~127,表示消息的“大类”。例如 S1 是设备状态类,S2 是设备控制与诊断类,S6 是数据采集类。Stream 决定了这条消息属于哪个业务域。
Function(F)占 8 位,取值 0~255,表示该 Stream 下的“具体动作”。例如 S1F1 是“Are You There”,S1F2 是它的应答;S1F13 是“Establish Communications Request”,S1F14 是应答。Stream 与 Function 组合成 SxFy,就是一条消息的唯一语义标识。
W-Bit(Wait Bit)位于 Header Byte 2 的最高位。W-Bit = 1 表示发送方期待对方回复(Primary Message,主消息);W-Bit = 0 表示这是一条回复(Secondary Message,应答消息)。例如 S1F1 的 W-Bit 为 1,S1F2 的 W-Bit 为 0。W-Bit 是判断“谁该回谁”的第一依据。
System Bytes 是 4 字节的无符号整数,由发起方生成,应答方必须原样回填。它的作用是让请求与应答能够配对。例如主机发送 S1F1 时 System Bytes = 0x00000001,设备回复 S1F2 时也必须带 0x00000001,主机才能确认这是对刚才那条请求的应答。如果 System Bytes 不匹配,通信层就会认为应答丢失或错乱。
二、消息体:数据项与列表结构
消息体由若干数据项(Data Item)组成,每个数据项采用“格式字节 + 长度字节 + 数据”的结构。格式字节的高 6 位表示数据格式(Format Code),低 2 位表示长度字节的个数(Length Bytes)。常见格式码如下:
- L(List):格式码 0b000000,表示一个列表,其“数据”是若干子数据项,长度表示子项个数。
- B(Binary):格式码 0b001000,二进制字节串。
- BOOLEAN:格式码 0b001001,布尔值。
- A(ASCII):格式码 0b010000,ASCII 字符串。
- I1/I2/I4/I8:有符号整数,格式码 0b011001/0b011010/0b011100/0b011000。
- U1/U2/U4/U8:无符号整数,格式码 0b101001/0b101010/0b101100/0b101000。
- F4/F8:浮点数,格式码 0b100100/0b100000。
长度字节的个数由格式字节低 2 位决定:00 表示 1 个长度字节,01 表示 2 个,10 表示 3 个。长度值表示后续数据的字节数(对 L 类型则表示子项个数)。例如格式字节 0x01 表示“L 类型,1 个长度字节”,后跟 0x02 表示列表内有 2 个子项。
列表可以嵌套。例如一条 S6F11 事件报告消息,其消息体通常是:L,3 { U4 事件ID, L,2 { U4 报告ID, L,n { 变量数据项... } }, L,0 }。这种嵌套结构让 SECS-II 能表达复杂的树形数据。
三、常见 SxFy 消息示例
下面用几条工程中最常见的消息,把消息头与消息体串起来看。
S1F1 / S1F2(Are You There):主机发送 S1F1,W-Bit=1,System Bytes=0x00000001,消息体为空(L,0)。设备回复 S1F2,W-Bit=0,System Bytes 仍为 0x00000001,消息体通常为 L,2 { A 设备型号, A 软件版本 }。这是最基础的“握手”消息,用于确认设备在线。
S1F13 / S1F14(Establish Communications Request):主机发送 S1F13,W-Bit=1,消息体为 L,2 { A MDLN, A SOFTREV },其中 MDLN 是设备型号,SOFTREV 是软件版本。设备回复 S1F14,W-Bit=0,消息体为 L,2 { B 通信确认码, L,2 { A MDLN, A SOFTREV } }。通信确认码为 0 表示成功,非 0 表示失败。这条消息用于建立通信链路后的能力协商。
S6F11 / S6F12(Event Report Send):设备主动上报事件时发送 S6F11,W-Bit=1,消息体为 L,3 { U4 事件ID, L,2 { U4 报告ID, L,n { 变量数据项 } }, L,0 }。主机收到后回复 S6F12,W-Bit=0,消息体为空。这是数据采集场景中最核心的消息,设备通过它把状态变化、工艺参数、报警等信息推送给主机。
S2F41 / S2F42(Host Command Send):主机向设备下发命令时发送 S2F41,W-Bit=1,消息体为 L,2 { A 命令名, L,n { L,2 { A 参数名, A 参数值 } } }。设备执行后回复 S2F42,W-Bit=0,消息体为 L,2 { B 执行结果码, L,n { L,2 { A 参数名, A 参数值 } } }。结果码 0 表示成功,非 0 表示失败或拒绝。
四、实际解析时的观察顺序
拿到一串 SECS-II 报文时,建议按以下顺序解析:先看 Byte 1 的 bit7 判断 W-Bit,再看 bit0~6 得到 Stream;接着看 Byte 2 得到 Function;然后看 Byte 5~8 得到 System Bytes;最后从 Byte 10 开始解析消息体。消息体的解析要递归处理:遇到 L 类型就按长度字段确定子项个数,逐个解析子项;遇到基本类型就按格式码和长度读出数据。这样就能把一条二进制报文还原成可读的 SxFy 消息树。