3. SECS消息结构解析:Message Header 与 Message Body
SECS(SEMI Equipment Communications Standard)消息是设备与主机之间通信的基本数据单元。一条完整的SECS消息由消息头(Message Header)和消息体(Message Body)两部分组成。理解消息结构是解析通信日志、排查协议故障、编写驱动代码的基础。
3.1 消息头(Message Header)结构
SECS-I(RS-232)和HSMS(TCP/IP)的消息头长度均为10字节,但字节偏移定义略有差异。以下以HSMS消息头为例进行解析:
| 字节偏移 | 字段名称 | 长度(字节) | 说明 |
|---|---|---|---|
| 0-1 | Length | 2 | 消息体长度(不含消息头),大端序 |
| 2 | Device ID(高字节) | 1 | 设备ID高8位,bit15为0表示主机→设备,1表示设备→主机 |
| 3 | Device ID(低字节) | 1 | 设备ID低8位 |
| 4 | Stream | 1 | 流号,范围0-127 |
| 5 | Function | 1 | 功能号,范围0-255,bit7为1表示是否需要回复(W-bit) |
| 6-7 | Message ID | 2 | 消息ID,用于请求-应答匹配 |
| 8-9 | System Bytes | 2 | 系统字节,用于消息超时和重传检测 |
3.1.1 Stream/Function 详解
Stream和Function共同构成SECS消息的“指令码”。Stream定义消息类别,Function定义具体操作。W-bit(Wait-bit)位于Function字节的bit7,表示发送方是否期望接收方回复。
- Stream 1:设备状态与控制(如S1F13建立通信请求)
- Stream 2:设备控制与诊断(如S2F41主机命令)
- Stream 5:报警管理(如S5F1报警上报)
- Stream 6:数据收集与事件通知(如S6F11事件报告)
- Stream 10:工艺程序管理(如S10F3工艺配方下载)
Function编号规则:奇数Function为Primary Message(主消息),偶数Function为Secondary Message(回复消息)。例如S1F13(主消息)对应S1F14(回复消息)。
3.1.2 Device ID 与 System Bytes
Device ID:用于标识消息的目标设备。在CVD集群设备中,每个反应腔室可能对应不同的Device ID。Device ID的bit15(最高位)表示消息方向:0=主机→设备,1=设备→主机。
System Bytes:2字节的循环计数器,每次发送新消息时递增。接收方通过System Bytes判断消息是否为重复发送(如因超时重传)。在HSMS中,System Bytes与Message ID共同保证消息的可靠性。
3.2 消息体(Message Body)结构
消息体由Item(数据项)嵌套组成,采用TLV(Type-Length-Value)编码格式。每个Item包含:
- Type(类型字节):1字节,高2位表示格式类别,低6位表示子类型
- Length(长度字节):1-3字节,表示Value部分的字节数
- Value(数据值):可变长度,具体格式由Type决定
3.2.1 Item 格式分类
| 格式码 | 类型名称 | 描述 | 长度编码 | 示例 |
|---|---|---|---|---|
| 0x00 | L(List) | 列表,包含多个子Item | 子Item数量 | L[3] 表示包含3个元素 |
| 0x10 | A(ASCII) | ASCII字符串 | 字符数 | A[5] "HELLO" |
| 0x20 | B(Binary) | 二进制字节数组 | 字节数 | B[4] 0x01 0x02 0x03 0x04 |
| 0x30 | BOOLEAN | 布尔值(1字节) | 固定1 | TRUE / FALSE |
| 0x40 | U1(Unsigned 1-byte) | 无符号1字节整数 | 1 | U1 0x64 = 100 |
| 0x50 | U2(Unsigned 2-byte) | 无符号2字节整数 | 2 | U2 0x03E8 = 1000 |
| 0x60 | U4(Unsigned 4-byte) | 无符号4字节整数 | 4 | U4 0x000F4240 = 1000000 |
| 0x70 | U8(Unsigned 8-byte) | 无符号8字节整数 | 8 | 大整数 |
| 0x80 | I1(Signed 1-byte) | 有符号1字节整数 | 1 | I1 0xFC = -4 |
| 0x90 | I2(Signed 2-byte) | 有符号2字节整数 | 2 | I2 0xFF38 = -200 |
| 0xA0 | I4(Signed 4-byte) | 有符号4字节整数 | 4 | I4 0xFFFF7960 = -34464 |
| 0xB0 | I8(Signed 8-byte) | 有符号8字节整数 | 8 | 大整数 |
| 0xC0 | F4(Float 4-byte) | 单精度浮点数 | 4 | IEEE 754格式 |
| 0xD0 | F8(Float 8-byte) | 双精度浮点数 | 8 | IEEE 754格式 |
3.2.2 长度编码规则
SECS Item的长度字段采用可变长度编码,以节省空间:
- 长度 ≤ 252:使用1字节,直接表示长度值
- 长度 253-65535:使用3字节,第1字节为0xFE或0xFF,后2字节为实际长度(大端序)
- 长度 100 → 0x64(1字节)
- 长度 1000 → 0xFE 0x03 0xE8(3字节,0xFE表示后续2字节为长度)
3.3 消息结构可视化
以下SVG图展示了SECS消息的完整结构,包括消息头各字段和消息体的嵌套Item层次:
3.4 实际消息解析示例
以下是一个CVD设备上报工艺结束事件的SECS消息(S6F11)的完整十六进制数据及解析过程:
// 原始十六进制数据(HSMS格式)
00 00 00 2E // Length = 46字节(消息体长度)
00 00 // Device ID = 0,bit15=0(主机→设备)
06 0B // Stream=6, Function=11(S6F11事件报告)
00 01 // Message ID = 1
12 34 // System Bytes = 0x1234
// 消息体开始
01 03 4C 49 44 // L[3]:列表包含3个元素
// 第1个元素:A类型,长度5
41 05 45 56 45 4E 54 // "EVENT"
// 第2个元素:U2类型,长度2
51 02 00 0A // U2值=10(事件ID)
// 第3个元素:L[2]子列表
01 02
// 子元素1:A类型,长度7
41 07 43 48 41 4D 42 45 52 // "CHAMBER"
// 子元素2:U4类型,长度4
61 04 00 00 00 64 // U4值=100(腔室编号)
1. 消息头中的Stream=6, Function=11表示这是一个“事件报告”消息(S6F11)。
2. 消息体根节点为L[3],包含3个元素:事件名称(A)、事件ID(U2)、数据列表(L[2])。
3. 数据列表内包含腔室名称(A)和腔室编号(U4),这是CVD设备常见的报告结构。
3.5 常见调试问题与避坑指南
| 问题现象 | 可能原因 | 排查方法 |
|---|---|---|
| 消息解析失败,长度不匹配 | Length字段计算错误,或消息体Item长度编码错误 | 逐字节核对Length值,确认大端序是否正确 |
| Device ID不匹配导致通信中断 | 主机与设备配置的Device ID不一致 | 检查双方配置文件中的Device ID值 |
| System Bytes未递增导致重传风暴 | 设备端未正确维护System Bytes计数器 | 抓包分析System Bytes变化规律 |
| Item类型解析为错误格式 | Type字节高2位与低6位组合错误 | 对照SECS-II标准确认Type编码 |
| 嵌套List深度超过设备支持 | 消息体嵌套层级超过设备固件限制 | 查阅设备手册确认最大嵌套深度 |
3.6 消息结构调试工具建议
在CVD设备现场调试中,推荐使用以下工具辅助解析SECS消息结构:
- Wireshark + SECS插件:实时抓包并自动解析HSMS消息头
- SECS Message Viewer:离线导入十六进制日志,可视化展示Item嵌套树
- 自定义解析脚本:使用Python的
secsgem库快速验证消息结构
# Python示例:使用secsgem库解析S6F11消息
from secsgem import HsmsMessage, SecsMessage
# 假设raw_data为接收到的完整HSMS消息字节流
raw_data = bytes.fromhex('0000002E0000060B0001123401034C4944054556454E54025102000A010241074348414D424552610400000064')
# 解析HSMS消息头
hsms_msg = HsmsMessage.from_bytes(raw_data)
print(f"Stream: {hsms_msg.stream}, Function: {hsms_msg.function}")
print(f"Device ID: {hsms_msg.device_id}, System Bytes: {hsms_msg.system_bytes}")
# 解析消息体
secs_msg = SecsMessage.from_bytes(hsms_msg.body)
print(f"Root Item: {secs_msg.root}")
print(f"Event Name: {secs_msg.root[0]}")
print(f"Event ID: {secs_msg.root[1]}")