3、HSMS 消息结构:消息头格式与消息体编码规则
HSMS(High-Speed SECS Message Services)消息是 SECS/GEM 通信中设备与主机之间交换的基本数据单元。理解 HSMS 消息结构,特别是消息头(Header)的每个字段含义以及消息体(Body)的编码规则,是进行链路诊断和故障排查的基础。本节将详细拆解 HSMS 消息的二进制格式。
3.1 HSMS 消息头格式
每个 HSMS 消息都以一个固定长度为 10 字节(80 位)的消息头开始。消息头包含了控制消息类型、会话标识、数据项标识以及消息完整性校验所需的关键信息。其结构如下表所示:
| 字节偏移 | 字段名称 | 长度(字节) | 描述 |
|---|---|---|---|
| 0-1 | Length | 2 | 消息体长度(不包括消息头),大端序(Big-Endian) |
| 2 | Session ID | 1 | 设备 ID(Device ID),用于标识通信会话 |
| 3 | Header Byte 3 | 1 | 包含 Stream, Function, P/B, W bit 等子字段 |
| 4-5 | Stream & Function | 2 | Stream 号(高字节)和 Function 号(低字节) |
| 6-7 | P/B & W bit | 2 | 包含 P/B 类型和 W bit 标志 |
| 8-9 | System Bytes | 2 | 系统字节,用于消息匹配和超时控制 |
关键点: 消息头的前 2 字节(Length)不包含消息头自身的 10 字节,仅表示其后跟随的消息体(Data Message)的字节数。对于控制消息(Select, Deselect, Linktest, Reject),Length 字段始终为 0。
3.1.1 核心字段详解
- Session ID(设备 ID): 用于区分连接到同一主机的不同设备。在 HSMS 中,Session ID 通常与 SECS-I 中的 Device ID 对应。对于控制消息,此字段通常为 0xFFFF。
- Stream & Function: 标识 SECS 消息的类型。Stream 号(S)定义消息的类别(如 S1 为设备状态,S2 为设备控制),Function 号(F)定义具体操作(如 F0 为 Abort,F13 为 Establish Communication Request)。
- P/B(Primary/Block): 指示消息是主消息(Primary)还是从消息(Secondary)。P/B = 0 表示主消息,P/B = 1 表示从消息。
- W bit(Wait bit): 指示发送方是否期望收到回复。W bit = 1 表示需要回复,W bit = 0 表示不需要回复(Fire-and-Forget)。
- System Bytes: 由发送方生成的一个唯一标识符,用于将主消息与其对应的从消息(回复)进行匹配。接收方在回复时必须将 System Bytes 原样返回。
诊断提示: 在排查通信故障时,检查 System Bytes 是否匹配是判断消息是否被正确回复的关键。如果主机发送了一个 W bit = 1 的消息,但设备回复的 System Bytes 与主机发送的不一致,则说明消息匹配逻辑出现错误。
3.1.2 消息头结构 SVG 图
下图展示了 HSMS 消息头的完整字节布局,以及各字段在 10 字节中的具体位置:
3.2 消息体编码规则
消息体(Body)紧跟在 10 字节的消息头之后,其长度由消息头中的 Length 字段指定。消息体采用 SECS-II 数据格式进行编码,这是一种基于类型-长度-值(TLV)的嵌套结构。SECS-II 定义了多种数据类型,用于表示整数、浮点数、字符串、列表等。
3.2.1 基本数据类型
| 格式代码 | 数据类型 | 描述 | 示例 |
|---|---|---|---|
| 00 | List | 包含多个子项的列表 | <L [3]> |
| 10 | Binary | 二进制数据 | <B [2]> 0x01 0x02 |
| 20 | Boolean | 布尔值(TRUE/FALSE) | <BOOLEAN [1]> TRUE |
| 30 | ASCII | ASCII 字符串 | <A [5]> "HELLO" |
| 40 | JIS-8 | 8 位日文字符 | <J [3]> 0x82 0xA0 0x82 |
| 50 | 2-byte Unsigned Integer | 无符号 16 位整数 | <U2 [1]> 100 |
| 60 | 1-byte Signed Integer | 有符号 8 位整数 | <I1 [1]> -10 |
| 70 | 4-byte Signed Integer | 有符号 32 位整数 | <I4 [1]> 123456 |
| 80 | 8-byte Signed Integer | 有符号 64 位整数 | <I8 [1]> 9876543210 |
| 90 | 4-byte Float | 单精度浮点数 | <F4 [1]> 3.14 |
| A0 | 8-byte Float | 双精度浮点数 | <F8 [1]> 2.71828 |
3.2.2 编码规则:格式字节与长度字节
每个 SECS-II 数据项都以一个 格式字节(Format Byte) 开头,随后是 长度字节(Length Bytes),最后是 数据字节(Data Bytes)。
- 格式字节(1 字节): 高 4 位表示数据类型(如 0x5 表示 U2),低 4 位表示长度字节的个数(通常为 0 或 1)。如果低 4 位为 0,则数据项为空;如果为 1,则后面跟 1 个长度字节;如果为 2,则后面跟 2 个长度字节(用于长度大于 255 的数据)。
- 长度字节(1 或 2 字节): 表示数据字节的实际个数。对于 List 类型,长度表示子项的数量。
- 数据字节: 实际的数据内容,按照大端序排列。
常见编码错误: 在诊断过程中,经常发现格式字节的低 4 位与后续长度字节的个数不匹配。例如,格式字节为 0x51(表示 U2 类型,且低 4 位为 1),但后面却跟了 2 个长度字节,这会导致解析器读取错误的数据长度,进而引发消息解析失败。
3.2.3 消息体编码示例
以下是一个 S1F13(Establish Communication Request)消息体的编码示例,它包含一个 List,其中包含一个 ASCII 字符串(MDLN)和一个 ASCII 字符串(SOFTREV):
// S1F13 消息体编码(十六进制)
// 格式: <L [2]> <A [4]> "ABCD" <A [6]> "123456"
// 编码分解:
// 0x01 0x02 -- 消息头 Length = 2 字节(消息体长度)
// 0x01 0x13 -- 消息头 Stream = 1, Function = 13
// 0x00 0x01 -- 消息头 P/B = 0, W bit = 1
// 0x00 0x01 -- 消息头 System Bytes = 1
// 消息体开始:
// 0x01 0x02 -- 格式字节: 0x01 (List, 低4位=1, 表示1个长度字节), 长度字节: 0x02 (2个子项)
// 0x41 0x04 -- 格式字节: 0x41 (ASCII, 低4位=1), 长度字节: 0x04 (4个字符)
// 0x41 0x42 0x43 0x44 -- 数据: "ABCD"
// 0x41 0x06 -- 格式字节: 0x41 (ASCII), 长度字节: 0x06 (6个字符)
// 0x31 0x32 0x33 0x34 0x35 0x36 -- 数据: "123456"
3.2.4 消息体编码规则 SVG 图
下图展示了 SECS-II 消息体的 TLV 编码结构,以及格式字节和长度字节的详细位布局:
3.3 消息头与消息体组合示例
以下是一个完整的 HSMS 消息(S1F13 W)的十六进制转储示例,展示了消息头与消息体如何组合在一起:
// 完整的 S1F13 W 消息(十六进制)
// 消息头 (10 字节):
00 0E // Length = 14 (消息体有 14 字节)
00 // Session ID = 0 (设备 ID)
01 // Header Byte 3 (R=0, S=0, P/B=0, W=1)
01 0D // Stream = 1, Function = 13
40 01 // P/B = 0, W bit = 1
00 01 // System Bytes = 1
// 消息体 (14 字节):
01 02 // List, 2 个子项
41 04 // ASCII, 4 个字符
41 42 43 44 // "ABCD"
41 06 // ASCII, 6 个字符
31 32 33 34 35 36 // "123456"
诊断技巧: 使用网络抓包工具(如 Wireshark)捕获 HSMS 流量时,可以按照上述结构逐字节解析。首先找到 10 字节的消息头,确认 Length 字段的值,然后根据 Length 读取后续的消息体。如果 Length 字段的值与实际消息体长度不符,则说明消息帧存在错误,可能是由于 TCP 粘包或分包导致的。