解析SECS/GEM与HSMS协议栈:从概念到JngHightSpeedSecs实战

📅 2026/8/26 8:08:13
解析SECS/GEM与HSMS协议栈:从概念到JngHightSpeedSecs实战
简介在半导体制造和高端装备自动化领域设备与主机系统MES之间的稳定通信是产线运行的关键而SECS/GEM正是这一领域广泛采用的标准协议族。它涵盖从底层传输到上层设备行为模型的完整规范HSMS基于TCP/IP实现消息传输SECS-II定义消息结构与编码GEM则规定设备的状态、事件和远程命令等标准行为。理解这套协议栈对从事设备联机、数据采集和自动化改造的工程师至关重要。JngHightSpeedSecs作为一套开源C#实现完整封装了HSMS链路、消息编解码和GEM事务管理结合SECS Simulator进行联调验证可以高效打通设备与主机的通信并规避超时、字节序和状态机等常见坑点。本文从协议核心概念讲起结合源码库落地实践帮助工程师快速掌握从建联握手到事件上报的完整开发路径。 干半导体设备联机的朋友应该都有共鸣设备本身跑得再稳只要主机系统MES那边通过SECS/GEM来管你通信链路不通整条线就卡在那儿。我第一次接触SECS/GEM是在一套刻蚀设备的系统改造里甲方要求很直接——设备端必须支持HSMS通信能上报事件、接收远程指令。那时候我才发现以前常写的Modbus、自定义TCP协议在这一行几乎派不上用场SECS/GEM是一套完整且相当有年代感的协议栈入门资料又碎光是搞懂Stream、Function、W位就花了不少时间。后来拿到JngHightSpeedSecs这套开源的SECS/GEM源代码边读边改边调才真正把协议吃透。这篇文章就把这条路线完整走一遍从SECS/GEM协议栈的核心分层讲起到最容易卡住新手的几个概念再到JngHightSpeedSecs源码库的架构和实际用法最后给出用SECS Simulator联调验证的完整清单以及我在实测中踩过的坑。适合刚接手设备联机、需要从零实现或改造SECS/GEM接口的工程师也适合想通过读源码彻底搞懂这套协议的人。1. 设备联机绕不开的SECS/GEM先搞清它到底管到哪一层很多人一上来就找代码、找Demo结果连消息里的Stream和Function都没弄明白调起来四处碰壁。我建议先花半天把协议栈的分层模型理清楚后面看源码会轻松非常多。1.1 从SECS-I到HSMS传输层的两代方案SECS/GEM不是单个协议而是一组由国际半导体设备与材料协会SEMI制定的标准。其中最底层的是传输方式早期是SEMI E4标准定义的SECS-I走RS-232串口点对点连接速度慢一个字节一个字节地传适合老式单机设备。后来网络化之后SEMI E37标准定义了HSMSHigh-Speed SECS Message Services走TCP/IP把SECS消息封装成TCP报文传输速度、可靠性都上了一个台阶。这里要注意一个容易混淆的点HSMS虽然叫高速SECS消息服务但它只负责把消息从一个节点搬到另一个节点属于传输层。它并不定义消息内容本身。你常听到的SecsGem这个词实际是把传输HSMS和上层消息规范SECS-II以及设备行为模型GEM打包在一起的统称。JngHightSpeedSecs这个库的名字里也有HighSpeed它的核心价值就是用C#实现了完整的HSMS链路并且把SECS-II消息编解码、GEM事务管理都封装好了。1.2 SECS-II与GEM消息长什么样、设备该有什么行为传输层之上是SEMI E5标准定义的SECS-II它规定了消息的结构和编码方式。简单来说任何一条SECS消息都由两部分组成10字节的消息头Header和可变长的消息体Body。消息头里最关键的信息是Stream流1-255、Function功能1-255、W位Wait bit是否等待回复和System Bytes系统字节用于消息配对。打个比方如果把SECS消息比作一封信Stream和Function就是部门业务编号比如S1F13代表建立通信请求S2F41代表主机下发远程命令W位相当于是否需要收件人回执System Bytes则是这封信的流水号回信时必须带上相同的流水号收件人才能把应答和请求对上。再往上是SEMI E30标准定义的GEMGeneric Equipment Model它规定设备必须具备哪些标准行为比如在线/离线状态管理、告警上报、事件上报、数据采集、配方管理、远程命令等。GEM不关心你用什么语言实现它关心的是设备对外暴露的能力和行为是否一致。这也是为什么设备联机调试时主机方通常拿着GEM标准逐项验收。1.3 一张表理清SECS/GEM协议栈层级对应标准作用典型实现传输层SEMI E4SECS-I/ SEMI E37HSMS消息的物理传输串口或TCP/IPJngHightSpeedSecs中的HsmsConnection消息层SEMI E5SECS-II定义消息结构、数据类型、编码规则JngHightSpeedSecs中的SecsMessage、DataItem行为层SEMI E30GEM定义设备状态、事件、告警、远程命令等标准行为JngHightSpeedSecs中的SecsGem封装业务层设备自身的应用逻辑根据工艺和设备特性实现具体业务你写的业务代码这四个层次是递进关系传输层把消息搬过去消息层让双方能看懂消息内容行为层保证设备行为符合行业规范业务层才是你真正要写的设备逻辑。读源码时如果觉得某个类看不懂先退到它所在的层级问一句这层解决的是传输、编解码、还是设备行为问题思路马上清楚。2. 最容易卡住新手的几个核心概念消息结构、事务与数据编码2.1 Stream/Function/W位消息的门牌号与回执要求SECS消息的分类规则是SxFy其中x是Stream号y是Function号。Stream 1到Stream 7是比较常用的基础功能例如S1F13/S1F14建立通信请求/应答这是设备联机握手的第一步S2F41/S2F42主机下发远程命令/设备应答S5F1/S5F2设备上报告警/主机应答S6F11/S6F12设备上报事件/主机应答S7F3/S7F4配方Process Program下发/应答W位Wait bit决定了这条消息是不是一问一答的事务起点。W1表示这是Primary消息发送方期待对方回复一条对应的Secondary消息W0表示这是Secondary消息或不需要回复的单向消息。调通信最常犯的错误就是收到一条W1的消息后忘了回或者回复时System Bytes没有填成和请求一致导致对方一直在超时等待。2.2 System Bytes与事务机制谁先发、谁应答System Bytes是消息头里4字节的数字用来标识一次事务。主机和设备各自维护自己的System Bytes计数器每发一条Primary消息就加1。对方回复Secondary消息时必须把请求里的System Bytes原样带回这样请求方才知道这条回复对应的是哪一次请求。我调试时最喜欢用这个字段排查问题如果设备迟迟收不到主机的S1F14应答先抓包看主机回的数据包里System Bytes是否和S1F13请求一致不一致基本就是库的回复逻辑或你的代码里手动组装消息时漏填了。JngHightSpeedSecs里通常有SendReply一类的接口它会自动沿用请求的System Bytes但如果你自己new了一条消息去回就必须手动把它设置对。2.3 数据项与ListSML里层层嵌套的消息体SECS-II的消息体用了统一的数据项Data Item编码每个数据项由一个格式字节、长度字段和具体数据组成。常见的格式码有LIST0x20集合相当于一个容器嵌套其他数据项ASCII0x40字符串这是SML里最常用的类型BINARY0x00二进制字节INT4/UINT40x90/0x91等32位整数U4、U2、U8等无符号整数格式字节的高4位是类型低4位是长度字段占用的字节数。例如0x41表示ASCII类型、长度字段占1字节0x42表示ASCII类型、长度字段占2字节。SMLSECS Message Language则是一种文本化的消息描述方式例如S1F13的消息体可以写成S1F13 W L MDLN EQUIP-01 SOFTREV 1.0.0 读源码时建议先看懂SML再对照看库里DataItem的构造方式你会发现代码里的AddItem/ChildItem等操作就是在手工构建这棵数据树。GEM源代码里大量使用了这种嵌套List结构比如S6F11事件上报外层是个L里面又套了DATAID、CEID和报告数据列表。2.4 设备状态模型Online/OFFLINE/RemoteGEM标准定义了设备三种核心状态OFFLINE离线、ONLINE-LOCAL在线本地也叫Equipment On-Line但不受主机远程控制和ONLINE-REMOTE在线远程主机可以下发命令。联机时最典型的过程是设备上电 → 主机通过S1F13建立通信 → 设备处于ONLINE-LOCAL → 主机发送S1F17请求设备上线或设备主动请求上线 → 设备进入ONLINE-REMOTE。理解这个状态机对调试特别重要。很多设备看起来网络通了、握手也成功了但主机下发S2F41远程命令时设备不执行原因往往是设备还停留在ONLINE-LOCAL状态远程命令通道没有被正确使能。所以联机验证清单里我会把S1F13建联和S1F15/S1F17上线分成两步来测别混在一起。3. JngHightSpeedSecs源码库拿到手先看架构别急着改代码3.1 库的分层与命名空间JngHightSpeedSecs是一套用C#实现的SECS/GEM库源码结构比较清晰大体可以分成下面几块HSMS通信层负责TCP连接、会话选择Select、链路测试Linktest、断线重连对应HSMS的握手控制和数据收发。SECS-II消息层负责SecsMessage的构建、解析、序列化/反序列化包括Header、DataItem、SML解析等。GEM辅助层封装了常见的GEM消息处理逻辑比如建立通信、事件上报、远程命令的分发。扩展与Demo通常包含示例代码、模拟器或简单的配置界面。拿到源码我建议按通信层 → 消息层 → GEM层的顺序读不要跳着看。通信层让你明白连接是怎么建立的消息层让你明白字节是怎么变成对象的最后再看GEM层怎么拼接业务。3.2 连接对象与会话生命周期用这个库的时候你首先遇到的是一个连接配置对象和一个连接实例。以设备端被主机连接的一方为例典型的配置包括IP、端口、设备IDDevice ID、连接模式Active主动连接还是Passive被动监听以及HSMS的各类超时参数。设备端一般用Passive模式监听某个端口等主机来连主机端一般用Active模式主动发起TCP连接。TCP连接建立后HSMS还有一个会话选择步骤主机发送Select.req控制消息设备回复Select.rsp双方确认这个会话可用之后才能传SECS数据消息。这个机制很容易被忽略——如果你只是用TCP工具测通了端口不代表SECS/GEM会话已经建立。JngHightSpeedSecs里连接状态事件会明确告诉你当前是已连接未选择还是已选择可通信调试时务必先确认状态到了哪一步。3.3 消息收发与回调机制库通常提供两种消息处理方式事件回调和注册处理器。事件回调适合小项目订阅一个MessageReceived事件就能收到所有SECS消息然后在回调里用switch判断Stream/Function。注册处理器则适合正规一点的项目可以把S1F13、S2F41这类消息分别注册到对应的处理方法里代码更清晰也不会在回调里堆成一大坨switch。我在项目里倾向于混合使用关键的状态消息建联、上线、心跳类用事件回调统一处理业务消息远程命令、配方、事件上报用注册处理器分开管理。这样出了问题好定位也方便后续扩展新的Stream/Function而不影响已有逻辑。4. 从零跑通一个HSMS通信Demo设备端与服务端代码拆解4.1 环境准备与最小引用先用Visual Studio或.NET CLI创建一个控制台项目把JngHightSpeedSecs的源码或NuGet包引用进来。建议先用源码编译因为你要读源码调试时能直接跳进库里看每一步的字节处理。目标框架选.NET 6/8都行库本身对框架版本要求不高。最小引用其实只有一个核心命名空间一般就是包含HsmsConnection和SecsMessage的那个。如果你还要SML解析或GEM辅助功能再引用对应的命名空间。初次跑通不需要额外装任何第三方库。4.2 设备端Passive初始化代码我以设备端为例写一个最小可运行的初始化片段using var config new HsmsConfig { IpAddress 0.0.0.0, Port 5000, DeviceId 0, Active false, // Passive模式监听端口等主机连接 T3 45, // 等待回复超时秒 T5 10, // 会话选择超时秒 T6 5, // 控制消息超时秒 T7 10, // 连接空闲超时秒 T8 5, // 网络字节间超时秒 LinkTestInterval 30 // 链路测试间隔秒 }; var secsGem new SecsGem(config); secsGem.ConnectionChanged (s, e) { Console.WriteLine($[{DateTime.Now:HH:mm:ss}] 连接状态: {e.State}); }; secsGem.MessageReceived OnMessageReceived; secsGem.Start(); Console.WriteLine(设备端SECS/GEM已启动等待主机连接...); Console.ReadLine();这段代码里最重要的就是Active false它决定了你是被动等连接还是主动去连别人。半导体设备通常是被动侧主机那边配好IP和端口来连你。如果你在自己的电脑上测试设备端监听5000端口主机端用127.0.0.1:5000连过来即可。4.3 主机端Active连接代码主机端代码要简单一些把Active改成trueIpAddress填设备IP其余参数基本一样using var config new HsmsConfig { IpAddress 127.0.0.1, Port 5000, DeviceId 0, Active true, T3 45, T5 10, T6 5, T7 10, T8 5, LinkTestInterval 30 }; var secsGem new SecsGem(config); secsGem.Start();主机端启动后一般会自动发起TCP连接然后自动完成HSMS的Select握手。如果你想手动控制有些版本的库提供了Select()/Deselect()这类方法可以按需调用。联调时主机端的日志要打得足够详细至少能看到TCP连接成功、Select成功、收到设备消息这几类事件。4.4 处理S1F13/S1F14建联握手设备联机后的第一条真正的SECS数据消息通常是主机发来的S1F13建立通信请求W位1。设备收到后必须回S1F14否则主机一直等回复直到T3超时。下面是设备端收到S1F13并回复的典型代码private void OnMessageReceived(object sender, SecsMessageEventArgs e) { var msg e.Message; if (msg.Stream 1 msg.Function 13 msg.WaitBit) { Console.WriteLine(收到S1F13建立通信请求准备回复S1F14); var reply secsGem.CreateReply(msg); // 自动携带相同的System Bytes reply.Function 14; reply.AddItem(DataItem.Binary(0)); // COMMACK 0 表示接受 var detail new DataItem(FormatCode.List); detail.AddItem(DataItem.Ascii(EQUIP-01)); detail.AddItem(DataItem.Ascii(1.0.0)); reply.AddItem(detail); secsGem.SendMessage(reply); } }这段代码的关键是CreateReply或者库里叫SendReply/ReplyMessage之类的接口。它做的事本质上就是把请求消息的System Bytes拿过来新建一条Direction相反的回复消息并把Stream/Function设置成对应的应答号。如果你自己手动构造回复消息别忘了把System Bytes设成和请求一致这是新手最容易踩的坑。4.5 解析一条S6F11事件上报事件上报是设备联机里最常用的功能设备发生某个事件比如一个Lot加工完成就把事件编号和关联的数据打包成S6F11发给主机主机回S6F12确认。解析S6F11的代码会用到嵌套List的遍历if (msg.Stream 6 msg.Function 11) { var list msg.Root; // 根节点是LIST var dataId list.GetUInt32(0); // DATAID var ceId list.GetUInt32(1); // CEID事件编号 Console.WriteLine($事件上报: DATAID{dataId}, CEID{ceId}); // 第三个元素通常是报告数据的LIST var reportList list.GetListItem(2); if (reportList ! null) { foreach (var item in reportList.Items) { Console.WriteLine($ 数据项格式: {item.FormatCode}, 值: {item.Value}); } } var ack secsGem.CreateReply(msg); ack.Function 12; ack.AddItem(DataItem.UInt32(dataId)); ack.AddItem(DataItem.Binary(0)); // ACK 0 表示收到 secsGem.SendMessage(ack); }我见过不少人在解析S6F11时直接用字符串截取来找CEID这非常不可靠因为不同设备上报的嵌套结构可能不同。正确做法永远是用库里封装好的GetXXX方法按索引取子项或者先打印出整棵数据树的结构确认第几层是什么类型再写解析逻辑。5. 用SECS Simulator做联调与验证调通联机不是靠运气5.1 为什么调试SECS/GEM必须配模拟器SECS/GEM调试最烦人的地方是你没有一套真实的主机系统可以随时联。主机侧的MES往往要排队申请测试窗口而且你也不想在真机上把告警、事件、远程命令全测一遍万一哪条消息没回对影响的是生产。SECS Simulator就是为了解决这个问题出现的它在本机模拟一个主机或设备让你能在开发环境里把通信流程完整跑一遍。我强烈建议在写任何设备业务逻辑之前先把模拟器和设备端或主机端的通信调通。这样你后面写S6F11、S2F41的处理逻辑时有了一个稳定的对端环境改完代码马上能验证不用干等真实主机。5.2 常见模拟器选型市面上的SECS Simulator有不少我常用的几类商业模拟器像Cimetrix现属PDF Solutions提供的Demo/Toolkit功能全支持SML编辑、消息日志、状态模拟但价格不便宜。开源/免费模拟器部分SECS/GEM库自带简单的Simulator示例或者GitHub上有一些单文件的小工具支持基本的S1F13建联和自定义消息发送。自己用JngHightSpeedSecs写一个如果你主机端和设备端都是自己用这个库开发直接把主机端代码跑起来就是一个现成的模拟器再包一层简单的命令行或界面就能输入Stream/Function/DataItem手动发消息。实际项目中我用的最多的是库自带Demo 自己写的命令行模拟器组合。库自带Demo帮你验证基础链路自己写的模拟器能灵活构造各种异常消息比如故意不回S6F12、故意发非法的S2F41参数来测试设备端的容错逻辑。5.3 从零到建联的验证步骤用模拟器验证联机我一般按下面的顺序走每一步都确认了再进下一步网络连通性用TCP工具比如TcpClient测试脚本确认设备端口能连上TCP握手成功。HSMS Select握手查看日志确认Select.req发来后设备回了Select.rsp连接状态变成已选择。S1F13/S1F14建联用模拟器发S1F13确认设备回复S1F14且COMMACK为0。S1F15/S1F17上线确认设备能正确响应上线请求状态切到ONLINE-REMOTE。事件上报让设备端触发一个测试事件确认S6F11发出后模拟器能回S6F12。远程命令用模拟器发S2F41确认设备能解析参数并回S2F42。链路测试等一个LinkTest间隔确认双方能正常回应HSMS Linktest控制消息。每一步都要打开双方的日志。设备端看自己发了什么、收到了什么模拟器端也看同样的信息。两边日志一对照问题出在发送方还是接收方、是消息头问题还是消息体问题基本一眼就能定位。5.4 常用验证消息清单下面是我在联调中会反复用到的消息清单建议做成一个速查表贴在工位上消息方向用途关键返回S1F13/S1F14主机→设备建立通信COMMACK0S1F15/S1F16主机→设备请求设备离线OFLACK0S1F17/S1F18主机→设备请求设备上线ONLACK0S2F41/S2F42主机→设备远程命令HCACKS5F1/S5F2设备→主机告警上报ACKS6F11/S6F12设备→主机事件上报ACKS7F3/S7F4主机→设备配方下发ACK用这份清单测试时别只测正常路径还要测异常路径主机发S1F13时设备故意回COMMACK非0看看主机怎么处理设备收到不认识的Stream/Function时应回S9F1Unrecognized Device ID或S9F3Unrecognized Stream之类的报错消息这也是GEM验收的一个考察点。6. 调试中最容易踩的坑超时、字节序与状态机6.1 HSMS超时参数T3/T5/T6/T7/T8每个都有它的脾气HSMS标准定义了一组超时参数刚接触的人经常全用默认值但实际联调中这些参数很可能需要按现场情况调整。我列一下它们的作用和常用范围参数含义典型值踩坑表现T3等待SECS消息回复的超时45s设备处理业务慢T3太短会导致主机误判超时重发T5会话选择Select等待超时10s网络延迟高时T5太短Select直接失败T6控制消息Select/Linktest回复超时5s设备CPU忙时来不及回控制消息被断开T7连接空闲超时10s长时间没有数据消息T7太短会误断连接T8网络字节间超时5s跨公网调试时T8太短TCP粘包/拆包稍慢就断开最常见的坑是T3。设备端收到S2F41远程命令后如果业务处理要十几秒而主机T3只配了10秒主机就会超时并重发命令设备端可能会收到重复命令。这种情况下要么把T3调大到业务能接受的合理值要么在设备端做命令去重避免重复执行。6.2 字节序、消息长度与最大长度限制SECS-II的整数编码是大端Big-Endian字节序这个在协议里是写死的。如果你把设备端的整数解析写成小端收到的U4值就会变成完全离谱的数字。JngHightSpeedSecs这类库内部已经处理好了字节序但如果你自己写编解码或者从裸字节里截取数据一定要记得是大端。另一个需要留意的是消息长度限制。HSMS在消息头里用4字节表示消息长度但很多实现会限制单条消息的最大长度比如16MB或64MB。如果你要传输配方这类较大的数据块建议先查一下库的最大消息长度配置必要时把数据拆成多个S7F3消息分块传输避免一条消息把缓冲区撑爆。粘包和拆包也是HSMS调试里的常客。TCP是流式协议一条HSMS消息可能分多个TCP包到达多条消息也可能合并成一个TCP包。成熟的库会自己处理缓冲和消息边界但如果你看到消息长度异常解析失败这类日志先怀疑是不是自己手动接收数据时没有按消息长度精确切分。6.3 状态机与W位导致的假死现象我在设备联调中遇到过最头疼的问题是设备看起来一切正常但主机下发命令后设备毫无反应。最后排查发现设备在一个内部状态机里卡住了它认为当前还处于某个消息事务的处理中没有释放会话资源导致后续消息进了等待队列却一直没有被处理。这类假死通常和W位处理不当有关。比如设备收到一条W1的S2F41后业务代码抛了异常没有执行SendMessage发S2F42主机那边一直等不到回复等T3超时后主机重发设备端又因为上一次事务状态没清理仍然不处理。解决思路是无论业务处理成功还是失败都要在finally块里确保发送回复消息哪怕回复里带一个错误码同时给消息处理加超时保护防止单个消息把整个接收线程卡死。GEM状态机也要注意尤其是S1F13建联和S1F15/S1F17上线之间的耦合。有些设备在建立通信后默认进入ONLINE-LOCAL必须收到S1F17才切到ONLINE-REMOTE。如果主机只做了建联没做上线远程命令就会像石沉大海。调试时把设备当前状态OFFLINE/LOCAL/REMOTE打印到日志里省去很多猜测。7. 一点个人体会这几套标准加起来也不算特别复杂真正让人头大的往往是那些隐藏细节System Bytes要一致、W位要对应、状态机要正确、超时要合理、字节序不能错。我在实际项目里最大的体会是SECS/GEM联调一定要先通链路再通业务。链路通了Select成功S1F13/14能正常应答后面的事件上报、远程命令都是顺着这条链路一个个加业务而已链路不通后面全是空中楼阁。如果你用的是JngHightSpeedSecs这类开源库建议把源码下载下来随手注释掉几行再跑测试看看消息解析失败时的真实报错路径。我当初就是靠反复打断点一步一步跟踪S1F13从TCP字节流变成SecsMessage对象的全过程才真正把Header和DataItem的编码规则刻进脑子里。另外日志一定要从第一天就好好打最好把收发消息的原始字节都留一份很多莫名其妙的问题往往就是看数据包比猜代码更快。最后再分享一个小技巧调试时在设备端和主机端各放一个秒表遇到超时问题先看是谁在等、等了多久、日志时间戳差了多少基本就能判断是T3问题还是T8问题还是对方的回复根本没发出来。SECS/GEM联调怕的不是问题多而是没有方向方向对了剩下的只是时间问题。本文还有配套的精品资源点击获取