4. SECS/GEM通信协议实战
在EAP系统开发中,SECS/GEM是设备与主机之间最核心的通信标准。理解协议分层、消息编码规则以及典型会话流程,是写出稳定通信代码的前提。本节从SECS-II消息结构入手,逐步展开HSMS连接建立、S1F1/S1F2与S1F13/S1F14交互,以及事件与报警上报的代码实现方式。
一、SECS-II消息结构:从数据项到消息体
SECS-II消息由消息头(Header)和消息体(Body)组成。消息头固定10字节,包含设备ID、消息ID(Stream/Function)、等待位(W-bit)、系统字节和状态字节。消息体则由若干数据项(Data Item)按顺序排列,每个数据项由格式字节和长度字节描述,再跟实际数据。
格式字节高6位表示格式码,低2位表示长度字节数。例如格式码0b101001表示ASCII字符串,长度字节数由低2位决定。常见格式码包括:
- 0o20(0x10):二进制(Binary)
- 0o21(0x11):布尔(Boolean)
- 0o24(0x14):有符号整型(I1/I2/I4/I8)
- 0o30(0x18):无符号整型(U1/U2/U4/U8)
- 0o44(0x24):ASCII字符串
- 0o00(0x00):列表(List),用于嵌套结构
以S1F1(Are You There)为例,其消息体通常为空列表,编码后为0x01 0x00(List,长度0)。而S1F2(On Line Data)的响应体可能包含设备型号、软件版本等ASCII字符串,编码时需先写List头,再依次写入各数据项。
示例:假设设备型号为“EAP-3000”,软件版本为“1.2.5”,S1F2消息体可编码为:
List (长度 = 2)
ASCII "EAP-3000" → 0x41 0x08 0x45 0x41 0x50 0x2D 0x33 0x30 0x30 0x30
ASCII "1.2.5" → 0x41 0x05 0x31 0x2E 0x32 0x2E 0x35
实际开发中,建议使用成熟的SECS-II编解码库(如Java的SECS4Net、Python的secsgem),避免手工拼接字节流。但理解编码规则有助于排查通信异常,例如长度字节数错误会导致接收方解析越界。
二、HSMS连接建立:从TCP到Select状态
HSMS(High-Speed SECS Message Services)基于TCP/IP,默认端口5000。连接建立分为三个阶段:TCP三次握手、HSMS Select请求/响应、进入Selected状态。只有进入Selected状态后,才能收发SECS-II消息。
HSMS控制消息使用10字节头,其中Session ID为0xFFFF,Stream/Function为0,PType=0,SType表示控制类型。关键SType包括:
- 0x01:Select.req
- 0x02:Select.rsp
- 0x03:Deselect.req
- 0x04:Deselect.rsp
- 0x09:Linktest.req
- 0x0A:Linktest.rsp
主动方(通常是EAP)发送Select.req,被动方(设备)回复Select.rsp,状态码0表示成功。若设备未就绪,可能返回状态码1(已激活)或2(未激活)。EAP需实现超时重试机制,典型超时时间为45秒,重试间隔5秒。
三、S1F1/S1F2与S1F13/S1F14的代码实现
S1F1用于确认设备在线,S1F2返回设备信息。S1F13用于建立通信,S1F14返回通信确认。两者都带W-bit,要求对方回复。以下以Python secsgem库为例,展示核心代码结构。
from secsgem.hsms import HsmsConnection
from secsgem.secs import SecsStreamFunction
# 建立HSMS连接
conn = HsmsConnection(host="192.168.1.100", port=5000, active=True)
conn.connect()
# 发送S1F1并等待S1F2
s1f1 = SecsStreamFunction(1, 1, wbit=True)
s1f2 = conn.send_and_wait(s1f1, timeout=10)
if s1f2:
print("设备在线,型号:", s1f2.body[0].value)
print("软件版本:", s1f2.body[1].value)
# 发送S1F13并等待S1F14
s1f13 = SecsStreamFunction(1, 13, wbit=True)
s1f14 = conn.send_and_wait(s1f13, timeout=10)
if s1f14 and s1f14.body[0].value == 0:
print("通信建立成功")
实际项目中,需将发送与等待封装为异步回调,避免阻塞主线程。同时要处理超时、重发和连接断开重连。建议为每个Stream/Function定义独立的处理函数,并维护会话状态机。
四、事件与报警上报
设备通过S6F11(Event Report)上报事件,通过S5F1(Alarm Report)上报报警。EAP需先通过S2F33(Define Report)和S2F35(Link Event Report)配置事件与报告ID的映射,再通过S2F37(Enable/Disable Event)使能事件。
典型流程:EAP发送S2F33定义报告,包含报告ID和VID列表;发送S2F35将报告ID与事件ID绑定;发送S2F37使能事件。设备触发事件后,主动发送S6F11,携带报告ID和对应数据。EAP收到后回复S6F12(Ack)。
提示:报警上报使用S5F1,包含报警代码、报警类型(如0x01表示个人安全,0x02表示设备安全)和报警文本。EAP需回复S5F2确认。报警通常需要与MES联动,触发工单或停机逻辑。
代码实现时,需注册事件回调。以下为S6F11处理示例:
def on_s6f11(handler, message):
report_id = message.body[0].value
event_id = message.body[1].value
data = message.body[2]
print(f"收到事件:报告ID={report_id}, 事件ID={event_id}")
# 解析数据并更新EAP状态
handler.send_response(message, SecsStreamFunction(6, 12))
conn.register_callback(6, 11, on_s6f11)
报警处理类似,注册S5F1回调并回复S5F2。注意:事件和报警的使能状态需在设备重启后重新配置,EAP应实现自动重连和重新使能逻辑。
五、实战要点与排障建议
- 消息编码校验:使用Wireshark抓包时,注意HSMS头10字节与SECS-II体的分界。常见错误是长度字节数计算错误,导致解析偏移。
- 连接状态管理:HSMS连接可能因网络抖动断开,需实现心跳(Linktest)和自动重连。Linktest.req/rep每30秒一次,超时3次则判定断线。
- 事件使能顺序:必须先S2F33定义报告,再S2F35绑定事件,最后S2F37使能。顺序错误会导致设备不发送S6F11。
- 报警去重:设备可能重复上报同一报警,EAP需根据报警代码和时间戳去重,避免频繁触发MES工单。
掌握以上内容后,即可完成EAP与设备之间基础的SECS/GEM通信链路搭建。实际开发中,建议先使用模拟器(如SECS Simulator)验证消息交互,再接入真实设备调试。