4. SECS-II消息结构实战

SECS-II 是 SEMI E5 标准定义的消息内容层,它规定了设备与主机之间交换的消息“长什么样”。很多初学者在调试 EAP 时,能看懂日志里出现了 S1F1,却说不清这条消息的字节到底怎么排、系统字节有什么用、为什么有的数据项前面要带长度字节。本节把 SECS-II 消息拆成可手工构造的层次,并用一条 S1F1 完整走一遍编码与解析。

一、Stream 与 Function:消息的“家族”和“序号”

SECS-II 用 Stream(流) 和 Function(功能) 两个维度给消息分类,写作 SxFy。Stream 表示消息所属的功能大类,Function 表示该大类下的具体动作。例如 S1 是设备状态与通信类,S1F1 是主机询问设备“你在线吗”,S1F2 是设备回复“我在线”。

常见 Stream 的语义边界需要记牢:S1 负责通信与状态,S2 负责设备控制与诊断,S5 负责报警,S6 负责事件报告,S7 负责配方,S9 负责错误消息,S10 负责终端消息。Function 的奇偶也有约定:通常奇数 Function 由主机发起,偶数 Function 由设备回复,因此 S1F1 的应答是 S1F2,S1F13 的应答是 S1F14。这个约定不是强制的,但绝大多数设备都遵循,理解它能快速判断一条消息的方向。

二、消息头:10 个字节的“信封”

一条完整的 SECS-II 消息由消息头(Message Header)和消息体(Message Body)组成。消息头固定 10 字节,是解析的起点。它的字段顺序如下:

字节位置字段长度说明
0Session ID 高字节1会话标识,通常为 0
1Session ID 低字节1与高字节组成 16 位
2Header Byte 21含 W-bit 与 Stream
3Header Byte 31含 Function
4PType1协议类型,SECS-II 为 0
5SType1会话类型,SECS-II 为 0
6–9System Bytes4事务标识,用于请求应答配对

其中 Header Byte 2 的最高位是 W-bit(Wait-bit),表示发送方是否需要对方回复。W-bit 为 1 表示需要应答,为 0 表示不需要。低 7 位是 Stream 号。Header Byte 3 的低 8 位是 Function 号。PType 和 SType 在标准 SECS-II 中均为 0,若出现非 0 值通常意味着使用了非标准扩展或解析错位。

三、系统字节:请求与应答的“配对钥匙”

System Bytes 是 4 字节无符号整数,由发起方生成,应答方必须原样回填。它的作用是让主机在并发多条消息时,能把设备的回复准确对应到某一次请求。例如主机发出 S1F1 时 System Bytes 为 0x00000001,设备回复 S1F2 时也必须带 0x00000001,主机据此确认“这是刚才那条 S1F1 的应答”。如果 System Bytes 不匹配,EAP 通常会把该消息判为孤儿应答并丢弃。

W-bit 与 System Bytes 配合使用:W-bit=1 且 System Bytes 相同,才构成一次完整的请求—应答事务。调试时先看这两个字段,能排除大部分“消息对不上”的问题。

四、数据项格式:L/A/B/BOOLEAN/U/I/F

消息体由若干数据项(Data Item)组成,每个数据项以“格式字节 + 长度字节 + 数据”的形式出现。格式字节的高 6 位是格式码,低 2 位是长度字节数(1、2 或 3 字节)。常见格式码如下:

  • L(List):列表,格式码 0x00,长度表示元素个数,元素本身又是数据项。
  • A(ASCII):字符串,格式码 0x10,长度表示字符数。
  • B(Binary):二进制,格式码 0x20,长度表示字节数。
  • BOOLEAN:布尔,格式码 0x30,长度表示布尔个数,每字节 1 个。
  • U1/U2/U4:无符号整数,格式码 0x40/0x50/0x60,长度表示元素个数,每元素 1/2/4 字节。
  • I1/I2/I4:有符号整数,格式码 0x70/0x80/0x90,长度表示元素个数,每元素 1/2/4 字节。
  • F4/F8:浮点数,格式码 0xA0/0xB0,长度表示元素个数,每元素 4/8 字节,遵循 IEEE 754。

长度字节数由格式字节低 2 位决定:00 表示 1 字节长度,01 表示 2 字节,10 表示 3 字节。例如 0x41 表示 U1 且长度占 1 字节,0x21 表示 B 且长度占 1 字节。长度字段本身是“元素个数”还是“字节数”,取决于格式:A 和 B 按字节数,U/I/F 按元素个数,L 按元素个数,BOOLEAN 按布尔个数。

S1F1 消息结构与手工构造解析 消息头(10 字节) Session ID 0x00 0x00 Header Byte2 W=1, S=1 Header Byte3 F=1 PType 0x00 SType 0x00 System Bytes(4 字节) 0x00 0x00 0x00 0x01 消息体(S1F1 无数据项,消息体为空) S1F1 仅用于询问设备是否在线,标准定义其消息体为空,因此消息总长度为 10 字节。 消息体:无数据项(0 字节) 若设备回复 S1F2,则消息体通常包含 L,2 与 MDLN、SOFTREV 两个 A 数据项 数据项格式字节速查(格式码 + 长度字节数) L 列表 0x00 A 字符串 0x10 B 二进制 0x20 BOOLEAN 0x30 U1/U2/U4 0x40/50/60 I1/I2/I4/F4/F8 0x70/80/90/A0/B0 长度字节数:格式字节低 2 位 00=1 字节,01=2 字节,10=3 字节;长度含义随格式不同(元素个数或字节数)。

五、手工构造一条 S1F1

假设主机要向设备发送 S1F1,Session ID 为 0,System Bytes 取 0x00000001,W-bit 置 1。按字节顺序构造如下:

字节 0-1 : 00 00          // Session ID = 0
字节 2   : 81             // W-bit=1, Stream=1  -> 1000 0001
字节 3   : 01             // Function=1
字节 4   : 00             // PType=0
字节 5   : 00             // SType=0
字节 6-9 : 00 00 00 01    // System Bytes = 1
消息体   : (空)

完整 10 字节:00 00 81 01 00 00 00 00 00 01

这里 0x81 的二进制是 1000 0001,最高位 1 即 W-bit=1,低 7 位 0000001 即 Stream=1。若把 W-bit 改为 0,则字节 2 变为 0x01,表示“不需要回复”。

六、解析设备回复的 S1F2

设备收到 S1F1 后回复 S1F2,System Bytes 必须回填 0x00000001。S1F2 的消息体通常包含设备型号和软件版本,结构为 L,2 { A MDLN, A SOFTREV }。假设 MDLN 为 "EQP-01",SOFTREV 为 "1.0.3",则消息体编码为:

01 02                // L,2:格式码 0x00,长度 2 个元素
41 06 45 51 50 2D 30 31   // A,6:"EQP-01"
41 05 31 2E 30 2E 33      // A,5:"1.0.3"

逐段看:01 是 L 的格式字节,低 2 位为 01 表示长度占 1 字节;02 是长度,表示列表有 2 个元素。41 是 A 的格式字节,低 2 位为 01 表示长度占 1 字节;06 表示字符串 6 个字符,随后 6 个 ASCII 字节即 "EQP-01"。第二个元素同理,41 05 后跟 5 个字符 "1.0.3"。

解析时的关键判断顺序:先读 10 字节消息头,确认 PType=0、SType=0;再取 Header Byte2 的 W-bit 和 Stream,取 Header Byte3 的 Function;然后读 System Bytes 用于配对;最后从第 10 字节开始按“格式字节→长度→数据”循环解析消息体,直到消息体结束。

七、常见错误与排查要点

  • 长度字节数判断错:把 0x41 当成“长度 0x41”而不是“A 格式、长度占 1 字节”,会导致后续全部错位。先看格式字节低 2 位,再决定读几个长度字节。
  • System Bytes 不回填:设备回复时若用了新的 System Bytes,主机会认为应答不匹配。构造应答时必须复制请求的 4 字节。
  • W-bit 与应答混淆:W-bit=0 的消息不应期待应答;若主机对 W-bit=0 的消息等待回复,会超时。
  • U/I 长度按元素个数而非字节数:例如 U4 长度写 2,表示 2 个 4 字节整数,共 8 字节数据,而不是 2 字节。
  • F4/F8 字节序:SECS-II 使用大端序,浮点数需按 IEEE 754 大端编码,不能直接用小端内存布局。

把 S1F1 这条最简单的消息手工构造和解析一遍,消息头、系统字节、数据项格式三条主线就串起来了。后续遇到 S1F13、S2F41、S6F11 等复杂消息,只是消息体数据项更多、嵌套更深,解析方法完全一致:先定头,再配对,后逐项拆体。