2. 消息格式基础:SECS-II 消息结构
SECS-II 是 SEMI 标准 E5 定义的消息层协议,它规定了设备与主机之间交换数据的结构化格式。理解 SECS-II 的消息结构是构建高性能解析引擎的基石。本节将深入剖析其核心组成:Stream/Function 标识、消息体结构、数据项类型以及长度编码规则。
2.1 Stream / Function / Message Body 三层结构
SECS-II 消息采用三层逻辑结构,每一层都有明确的职责:
- Stream (流):消息的主类别,用 0-127 的整数表示。例如,Stream 1 通常用于设备状态与控制,Stream 6 用于数据收集。
- Function (功能):消息的具体操作,用 0-255 的整数表示。例如,Function 1 通常表示“请求”,Function 2 表示“响应”。
- Message Body (消息体):承载实际数据的部分,由一系列数据项(Item)组成,遵循 SML (SECS Message Language) 格式。
关键约定: Stream 和 Function 的组合 (S, F) 唯一标识一条消息。例如,
S1F1 是“Are You There”请求,S1F2 是“Are You There”响应。W-bit (Wait-bit) 用于指示是否需要回复。
2.2 数据项类型 (Item Types)
SECS-II 定义了 7 种基本数据项类型,用于在消息体中编码不同种类的数据。每种类型都有固定的格式标识符和编码规则。
| 类型名称 | 格式码 (Format Code) | 描述 | 字节长度 |
|---|---|---|---|
| List (L) | 0x00 | 包含多个子项的无序列表 | 可变 |
| ASCII (A) | 0x40 | 可打印的 ASCII 字符串 | 1-256 字节 |
| Binary (B) | 0x20 | 原始字节数据 | 1-256 字节 |
| Unsigned (U) | 0xA0 | 无符号整数 (1/2/4/8 字节) | 1/2/4/8 |
| Signed (I) | 0x80 | 有符号整数 (1/2/4/8 字节) | 1/2/4/8 |
| Float (F) | 0x90 | 浮点数 (4/8 字节) | 4/8 |
| Boolean (BOOLEAN) | 0x10 | 布尔值 (True/False) | 1 |
性能要点: 在解析引擎中,List 类型是递归解析的核心。解析器需要根据 List 的长度字段,动态创建子解析任务。对于 Unsigned 和 Signed 类型,需要根据长度字节数(1/2/4/8)选择正确的字节序转换算法,避免使用通用但低效的循环移位。
2.3 长度编码规则
SECS-II 使用一种紧凑的变长编码规则来表示数据项的长度。长度字段紧跟在格式码之后,其编码方式如下:
- 短长度 (0-252 字节):长度字段占用 1 个字节,直接表示长度值。
- 长长度 (253-65535 字节):长度字段的第一个字节为 0xFD (253),后续 2 个字节表示实际长度(大端序)。
- 超长长度 (65536 字节以上):长度字段的第一个字节为 0xFE (254),后续 4 个字节表示实际长度(大端序)。
常见陷阱: 许多初学者会忽略 超长长度 (4 字节) 的情况。在半导体设备中,某些数据收集消息(如 S6F15)的消息体可能包含数千个数据点,长度超过 65535 字节。解析引擎必须支持 4 字节长度编码,否则会导致解析失败或内存溢出。
2.4 消息体编码示例 (SML 格式)
以下是一个典型的 SECS-II 消息体示例,使用 SML 格式表示:
S1F2 W
<L[3]
<B 0x01>
<A "MDLN-123">
<U2 100 200 300>
>
对应的二进制编码解析如下:
| 字节偏移 | 值 (Hex) | 说明 |
|---|---|---|
| 0 | 0x00 | 格式码: List (L) |
| 1 | 0x03 | 长度: 3 个子项 |
| 2 | 0x20 | 格式码: Binary (B) |
| 3 | 0x01 | 长度: 1 字节 |
| 4 | 0x01 | 数据: 0x01 |
| 5 | 0x40 | 格式码: ASCII (A) |
| 6 | 0x08 | 长度: 8 字节 |
| 7-14 | 4D 44 4C 4E 2D 31 32 33 | 数据: "MDLN-123" |
| 15 | 0xA2 | 格式码: Unsigned 2 字节 (U2) |
| 16 | 0x03 | 长度: 3 个元素 |
| 17-22 | 00 64 00 C8 01 2C | 数据: 100, 200, 300 |
解析优化技巧: 在 C++ 实现中,可以使用
memcpy 直接读取多字节整数,避免逐字节移位。对于 List 类型,采用迭代器模式而非递归函数,可以减少函数调用开销,提升解析吞吐量。