2. SECS-II消息结构深度剖析
在SECS/GEM通信中,一条SECS-II消息并不是“一串数据”那么简单。它由消息头(Message Header)和消息体(Message Body,即数据区)两大部分组成。消息头固定10字节,承载了设备与主机之间“谁发给谁、属于哪个事务、要不要回复、消息有多长”等关键控制信息;数据区则按照SECS-II数据项(Item)的规则编码,承载真正的业务内容。理解这两部分的字节级结构,是后续解析日志、构造指令、排查通信异常的基础。
一、消息头的10字节布局
消息头是定长的10字节结构,按顺序依次为:设备ID(2字节)、W-bit与流码(1字节)、功能码(1字节)、事务ID(4字节)、系统字节(1字节)、数据区长度(1字节)。其中“W-bit与流码”共用一个字节,高1位是W-bit,低7位是Stream;“系统字节”高1位是结束位(End-bit),低7位是系统字节值。下面这张结构图把每个字段的位置、位宽和含义一次看清。
二、设备ID与事务ID:谁在说话、说的是哪件事
设备ID(Device ID)占2字节,用于标识消息的发送方或接收方设备。在单设备直连场景中通常为0;在多设备、多通道或通过网关转发的场景中,它用于区分不同设备。解析时要注意:设备ID是“逻辑设备标识”,不一定等于物理站号,具体含义由设备厂商的GEM手册定义。
事务ID(Transaction ID)占4字节,是主机或设备为一次“请求—回复”配对而分配的编号。例如主机发送S1F13时使用事务ID=0x00000001,设备回复S1F14时必须携带同一个事务ID,这样主机才能把回复与请求对应起来。事务ID由发起方维护,通常循环递增,避免在同一时间窗口内重复。若事务ID不匹配,上层软件会认为收到了“孤儿回复”,从而丢弃或报错。
三、W-bit:需不需要回复
W-bit(Wait-bit)位于Stream字节的最高位。它回答一个关键问题:这条消息是否需要对方回复?
- W-bit = 1:发送方等待回复,接收方必须用同一Stream、Function+1的消息回复。例如S1F13的W-bit=1,设备必须回S1F14。
- W-bit = 0:发送方不要求回复,接收方处理完即可,不产生配对消息。例如S6F11事件报告通常W-bit=0。
实际解析中,W-bit与Function码共同决定通信方向。以S1F13/S1F14为例:S1F13是主机发起的“建立通信请求”,W-bit=1;S1F14是设备回复的“建立通信确认”,其W-bit通常为0。若在日志中看到S1F13的W-bit=0,就说明发送方不等待回复,这通常不符合GEM规范,需要排查发送端实现。
四、系统字节:多块消息的拼接控制
系统字节(System Bytes)占1字节,其中最高位是结束位(End-bit),低7位是系统字节值。它的核心作用是支持多块消息(Multi-block Message)的传输。
当一条消息的数据区超过单条消息允许的最大长度时,发送方会把它拆成多个块。每个块都有自己的消息头,End-bit=0表示“后面还有块”,End-bit=1表示“这是最后一块”。接收方按系统字节值把同一事务的多个块重新组装成完整消息。对于大多数常规SECS-II消息,数据区较短,End-bit通常为1,系统字节值为0。
五、流与功能码:消息的“姓”和“名”
Stream(流)和Function(功能码)共同定义消息的语义。Stream占1字节的低7位,取值通常为1~127;Function占1字节,取值通常为0~255。二者组合成“SxFy”的记法,例如S1F1、S2F41、S6F11。
| 消息 | 方向 | 含义 | W-bit |
|---|---|---|---|
| S1F1 | 主机 → 设备 | Are You There 请求 | 1 |
| S1F2 | 设备 → 主机 | Are You There 确认 | 0 |
| S1F13 | 主机 → 设备 | 建立通信请求 | 1 |
| S1F14 | 设备 → 主机 | 建立通信确认 | 0 |
| S6F11 | 设备 → 主机 | 事件报告 | 0 |
| S2F41 | 主机 → 设备 | 远程命令 | 1 |
规律是:偶数Function通常是奇数Function的回复,且回复消息的W-bit一般为0。但并非绝对,具体以GEM手册和实际设备定义为准。解析时先看Stream和Function确定消息类型,再看W-bit判断是否需要等待回复,最后看事务ID做配对。
六、消息长度与数据区:真正承载业务的部分
消息头最后一个字节是数据区长度(Length),它只表示数据区的字节数,不包含10字节消息头。例如Length=0x0C表示后面还有12字节数据区。接收方先读10字节头,再根据Length读取对应字节数的数据区,从而完成一条消息的切分。
数据区由若干SECS-II Item组成。每个Item的结构是:1字节Item头(高6位格式码,低2位长度字节数)+ 长度字段 + 数据内容。例如一个ASCII字符串Item,格式码为0x10,若长度用1字节表示,则Item头为0x41,后跟长度值,再跟字符串内容。多个Item可以嵌套,形成列表(List)结构,用于表达复杂数据。
示例:S1F13 数据区包含一个 List,内含 MDLN 和 SOFTREV 两个 ASCII Item
数据区字节(十六进制):
01 02 00 02 4D 44 00 06 31 2E 30 2E 30
解析:
01 → List,长度1字节
02 → List 长度 = 2 个 Item
00 02 → ASCII,长度2字节
4D 44 → "MD"
00 06 → ASCII,长度6字节
31 2E 30 2E 30 → "1.0.0"
实际解析时,建议按“先头后体、先长度后内容”的顺序推进:读10字节头 → 取Length → 读Length字节数据区 → 按Item头逐层展开。遇到List时递归解析,遇到非List时按格式码解释数据。这样即使面对嵌套多层的复杂消息,也能稳定还原出完整的SECS-II结构。
把消息头10字节的每个字段、W-bit与Stream的位分配、事务ID的配对作用、系统字节的多块控制、Length与数据区的边界关系串起来,就形成了SECS-II消息解析的完整骨架。后续无论是抓包分析、构造指令,还是排查“回复对不上”“数据区解析乱码”等问题,都可以回到这张结构图上逐字段核对。