资讯动态

SECS/GEM协议详解:从HSMS报文到设备事件联动排查

发布时间:2026/10/14 16:54:33 来源:尧图企业网站定制
简介SECS/GEM协议是工业设备互联的重要标准但国内系统性介绍资料较少。这份中文详解文档面向半导体、汽车制造、电子组装等领域从事设备集成与自动化工作的工程师针对协议资料分散、讲解不详的痛点系统整理核心功能与应用场景。文档共包含10个章节从第一章介绍协议如何降低集成成本、适用于所有制造设备、高效利用网络带宽到第二章消息日志、第三章常用术语解释再到第四章Streams and Functions基础介绍逐步覆盖系统保留与自定义、Stream分类、详细指令含义及使用场景并涉及无数据翻译、环路保证与安全机制层次清晰。资源为PDF格式共1个文件、约955KB方便离线查阅。目前已有2265人学习下载适合希望从概念到指令用法完整掌握SECS/GEM的入门与进阶读者也可作为日常集成维护的参考手册。1. 从“设备不说话”到“设备会报数”SECS/GEM 到底解决了什么在半导体产线上刻蚀机、清洗机、量测设备不是各自闷头干活的MES 和 EAP 系统需要知道设备的腔体状态、物料流转结果还要能远程下达加工命令。让这些不同厂家的设备统一“开口说话”的标准就是 SECS/GEM。我第一次接触它时也被绕得不轻SECS 管消息格式GEM 管设备行为中间还混着串口、TCP/IP、状态机和事件上报。这篇就当作一份接地气的 SECS/GEM 中文详解从传输层一路拆到抓包定位。它适合三类人做设备端软件开发的、写工厂 EAP/MES 集成的、还有负责设备选型验收的——读完你能回答一个实际问题对面这台设备到底能不能信它真的支持 GEM。2. 传输层与报文从 RS-232 到 TCP/IP一条 SECS 消息的完整解剖2.1 选传输层SECS-I 串口和 HSMS 以太网新项目为什么直接选 HSMSSECS/GEM 在传输层上有两套方案早期的 SECS-I 走 RS-232 串口后来绝大多数新设备改用 HSMS走以太网。SECS-I 是点对点串行通信常见波特率 9600 起跳一条线只能连一台设备还得处理串口电平、地线干扰这类物理层问题。HSMS 则基于 TCP/IP物理链路用网线就行远程调试、抓包分析都方便得多消息吞吐也完全不在一个量级。两套方案的核心区别在下表对比项SECS-I串口方案HSMS以太网方案物理介质RS-232 串口线TCP/IP 以太网拓扑点对点单主单从点对点但可远程、可多会话典型速率9600 / 19200 bps百兆 / 千兆网卡消息承载分块传输SECS-II 报文拆成块直接承载完整 SECS-II 报文定时器体系T1 ~ T4T5 ~ T8联调难度现场串口干扰不好查抓包方便问题定位更快实际选型我一般只看一条新项目一律 HSMS老设备改造先翻设备手册确认有没有 HSMS 选项。现在还在坚持纯串口的设备已经很少除非是十几年没换代的老产线。硬要说 SECS-I 还有什么存在价值就是极小规模的改造项目里设备侧实在没有网口才退回去用串口转接。业务上完全没有必要为了“协议一致性”刻意选 SECS-IHSMS 就是 GEM 规范当前默认的传输层。2.2 直接拆开 HSMS 消息Length、Session ID、Stream 与 Function 怎么排布一条完整的 HSMS 消息格式非常固定却是我见过被读错次数最多的地方。它由三部分组成4 字节的 Length 字段、10 字节的固定头部、以及可选的 SECS-II 消息正文。这里的 Length 统计的是“从 Session ID 开始到消息结束”的字节数不包含 Length 字段自身这是最容易踩的第一个坑。很多人按习惯把 Length 算成整条消息长度结果设备端解析直接错位。10 字节头部按顺序拆开是这样的字段长度说明Session ID2 字节高 15 位是 Device ID最低位是会话标志Stream1 字节SECS-II 消息类常见 1、2、5、6Function1 字节消息功能号和 Stream 组成 S1F13、S6F11PType1 字节0 表示 SECS-II 数据消息SType1 字节0 表示普通数据消息5/6 是 Linktest 心跳System Bytes4 字节事务标识响应消息必须原样带回发一条 S1F13 建连请求如果设备带了模块信息正文的 SML 表示法长下面这样L [3]声明这是一个含 3 个元素的 ListS1F13 W L [3] A MDLN A EQ001 A SOFTREV PType0、SType0 意味着这是一条承载 SECS-II 数据的普通消息对端拿到后要按 SECS-II 的 SML 规则解析正文。System Bytes 这个字段特别容易被忽略请求和响应靠它配对两个请求用了同一个 System Bytes对端就会把后一条当成重传丢进黑匣子表现为“设备莫名不回包”。2.3 用 Python 构造并解析一条 HSMS 帧最小可运行代码与其背结构图不如直接写几行代码把这些字节拼出来。下面这段 Python 只依赖标准库核心是struct.pack把整数按大端序塞进对应字段import struct def build_hsms_message(session_id, stream, function, system_bytes, bodyb): # header 不含 Length共 10 字节SessionID(2) Stream(1) Function(1) # PType(1) SType(1) SystemBytes(4) header struct.pack(HBBBB, session_id, stream, function, 0, 0) header struct.pack(I, system_bytes) # Length 统计 header body不统计 Length 自己 length 10 len(body) return struct.pack(I, length) header body def parse_hsms_message(frame): length struct.unpack(I, frame[:4])[0] session_id struct.unpack(H, frame[4:6])[0] stream, function, ptype, stype struct.unpack(BBBB, frame[6:10]) system_bytes struct.unpack(I, frame[10:14])[0] body frame[14:14 length - 10] return { length: length, session_id: session_id, stream: stream, function: function, ptype: ptype, stype: stype, system_bytes: system_bytes, body: body, } frame build_hsms_message(session_id100, stream1, function13, system_bytes1, bodyb) parsed parse_hsms_message(frame) print(parsed[stream], parsed[function], parsed[system_bytes])build_hsms_message里HBBBB把 Session ID 拼成 2 字节Stream、Function、PType、SType 各 1 字节I再拼 4 字节 System Bytes组合正好 10 字节。解析时先用I读 Length再按固定偏移读剩余字段。注意body frame[14:14 length - 10]这段Length 是“10 body 长度”所以从帧偏移 14 之后才是正文。这段代码跑通之后你已经亲手把 HSMS 层的字节排布验证了一遍后面看任何设备的抓包都不会发怵。3. GEM 行为模型状态机、事件、命令什么样的设备才算真支持 GEM3.1 GEM 三大状态模块通信、控制、处理分别在什么时候切换GEM 不只是消息格式约定它真正有价值的部分是定义了设备的“行为规矩”。一套符合 GEM 的设备在协议层面上需要维护三个并行的状态模块通信状态、控制状态、处理状态。状态模块典型取值触发源通信状态NOT COMMUNICATING / COMMUNICATING细分为 WAITING、RETRY、ACTIVETCP 连接建立/断开、Linktest 心跳结果控制状态OFF-LINE / ON-LINE细分为 LOCAL、REMOTE、ATTEMPT_ON_LINE主机下发离线/在线命令操作员面板切换处理状态厂商自定义常见 IDLE、READY、PROCESSING设备内部工艺事件三个状态里控制状态对集成工程师最要命。只有设备处于 ON-LINE:REMOTE主机才有资格下发远程命令如果设备在 OFF-LINE 或 ON-LINE:LOCAL主机发的 S2F41 会被拒绝或者干脆不理。所以联调时第一步不是急着发命令而是先把设备切到 REMOTE再确认通信状态是 ACTIVE。这个顺序反了后面全乱。通信状态里的 NOT COMMUNICATING 和 COMMUNICATING 之间靠 TCP 连接和 Linktest 心跳维护。设备起机后默认 NOT COMMUNICATINGTCP 建链后进入 WAITING 或 RETRY主机发 S1F13 拿到 S1F14 后才到 ACTIVE。这套状态模型相当于设备的“对外健康度报告”主机侧协议栈必须能读取并展示调试时一眼能看出问题出在哪一层。3.2 一套标准 GEM 流程建连、开 REMOTE、收 Event、发 Command 的先后顺序设备联网后主机和设备之间要走一套固定的“破冰流程”。我一般按下面这个顺序验证缺一步就补一步TCP 建链主机作为客户端连设备的服务端口。S1F13 / S1F14建立 SECS 通信双方交换能力信息。S1F1 / S1F2问设备是否在线拿到设备 ID 和软件版本。通过操作面板或 S1F15 / S1F17 把设备切到 ON-LINE:REMOTE。S2F37 / S2F38使能事件报告设备开始主动上报。主机发 S2F41 下发远程命令设备用 S2F42 应答。第 5 步是关键转折点。使能事件之前设备即便发生了工艺结束、报警也不会主动说话使能之后设备才“有资格”在事件发生时推 S6F11 给主机。所谓“支持 GEM”至少要把这六步完整走下来。很多号称支持 GEM 的设备实际上只实现了 S1F13 建连和简单查询事件上报和远程命令都是空壳。远程命令的消息正文长这样命令名START和参数列表以L嵌套形式组织S2F41 W L [2] L [2] A START L [1] L [2] A RECIPE A RECIPE_001 S2F41 的命令名一般要求全大写参数是“参数名 参数值”成对出现。有些设备还要求命令名带前缀或命名空间必须和设备数据字典核对不能想当然。命令是否执行成功也不能只看设备回了 S2F42 就算完还要看返回的 HCACK 值0 是正常非 0 各有各的失败原因需要对照厂商错误码表。3.3 数据字典与事件映射为什么设备上报的报文和文档对不上事件上报是 GEM 里最常用、也最容易对不上的功能。设备内部发生一个事件时上报的 CEID 是哪个关联哪些 RPTID每个 RPTID 又采集哪些 VID这些映射关系不是协议固化的而是每台设备出厂时由厂商配置好的。这份映射就是“数据字典”。CEID含义关联 RPTID关联 VID1000加工完成11 配方名2 加工时间3 报警计数1001加工异常21 异常码2 腔体压力1002设备空闲34 待机时长主机侧要做的是把设备数据字典里的 CEID、RPTID、VID 逐个录入 EAP 配置并保持两边一致。我踩过最典型的坑是厂商文档写 CEID1000 是加工完成设备固件升了一版之后实际发 1001主机按旧文档配置事件全被当成未知数据丢掉。所以每次联调不要只信纸版文档一定用厂商的 SECS 配置工具把设备当前生效的数据字典导出来逐条比对。如果事件上报“有 CEID 没数据”多半是 RPTID 到 VID 的映射没有下发成功。设备侧事件被触发但不知道该采集哪些数据点就会只发一个空壳事件。这种问题从消息层看完全正常因为 S6F11 结构没坏但对业务来说数据就丢了。排查时先把事件关联的 RPTID 和 VID 配置重新下发一遍再触发一次事件对比结果。4. 跑起来用模拟设备走通 SECS/GEM 最小闭环从 TCP 到事件上报4.1 最小环境怎么搭先选库还是先写底层我的建议在接触真实设备之前强烈建议先用模拟环境把协议链路跑通。最常见的做法是在一台机器上起两个进程一个当主机TCP 客户端一个当设备TCP 服务端。工程上我建议第一步不要直接套第三方 SECS 协议栈而是用 Python 标准库把 HSMS 的收发写一遍。原因很简单第三方库把消息封装得太好出了问题你完全不知道字节在哪一层丢的。先自己写底层还有一个好处对 Device ID、System Bytes、Stream/Function 这三个关键参数的印象会深得多。等到链路通了、你也看明白了再决定是继续用自研代码还是换成熟协议栈。如果你的项目时间紧直接上开源库也行但至少要看一眼它的抓包日志确认它发出去的报文和你要的一致。模拟环境的设备端要保持两个习惯一是维护一个递增的 System Bytes 计数器每个新事务分配一个二是收到请求后把请求的 System Bytes 原样带进响应。这两条是 HSMS 层不出幺蛾子的基本素养。4.2 让设备回一条 S1F14服务端代码与握手时序下面这段代码是完整的设备端模拟器核心逻辑。它监听 5000 端口收到 S1F13 后回 S1F14import socket, struct def recv_exact(sock, n): buf b while len(buf) n: chunk sock.recv(n - len(buf)) if not chunk: raise ConnectionError(对端断开) buf chunk return buf def parse_hsms(sock): # 先收 4 字节 Length再收 Length 个字节的 frame length struct.unpack(I, recv_exact(sock, 4))[0] full recv_exact(sock, length) session_id struct.unpack(H, full[:2])[0] stream, function, ptype, stype struct.unpack(BBBB, full[2:6]) system_bytes struct.unpack(I, full[6:10])[0] body full[10:] return session_id, stream, function, ptype, stype, system_bytes, body def make_hsms(session_id, stream, function, system_bytes, bodyb): header struct.pack(HBBBB, session_id, stream, function, 0, 0) header struct.pack(I, system_bytes) return struct.pack(I, 10 len(body)) header body srv socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.bind((0.0.0.0, 5000)) srv.listen(1) conn, addr srv.accept() while True: session_id, stream, function, ptype, stype, sys_bytes, body parse_hsms(conn) if stream 1 and function 13: # S1F14 正文通常是 L用空 List 的二进制编码表示 reply make_hsms(session_id, 1, 14, sys_bytes, b\x01\x00) conn.sendall(reply) print(已回 S1F14)recv_exact必须写因为 TCP 是流一次recv拿到的可能不是完整报文。先收 4 字节拿到 Length再按 Length 收完剩余部分这样才不会被粘包问题坑到。make_hsms里的参数和上一章的构造函数一致b\x01\x00是 SECS-II 里空 List 的二进制编码表示“正文是一个含 0 个元素的 List”用在这里表示一个标准的 S1F14 应答。我可以直接跑这段代码然后用一个只发 S1F13 的客户端脚本去连控制台会在收到请求后打印“已回 S1F14”。这里有一个参数要特别注意sys_bytes必须从请求里原样取出来放进响应如果自己新造一个值主机侧会匹配不上事务直接判定超时。4.3 主动上报 S6F11 事件全双工消息的第一次体验SECS/GEM 的消息分两类一类是一问一答的事务型比如 S1F13/S1F14另一类是设备主动推送的事件型典型就是 S6F11。设备在工艺结束时不需要等主机问直接往 TCP 连接里写一条 S6F11主机收到后回 S6F12 确认。在上一节的设备端代码里收到 S1F13 并回完 S1F14 后我顺手模拟一次加工完成事件上报def build_event_body(): # S6F11 最小正文CEID1000RPTID1VID1VALUE42 # 生产环境请用厂商配置导出这里演示手写二进制 return ( b\x01\x1e # L: 外层后续内容 30 字节 b\x05\x04\x00\x00\x03\xe8 # U4 CEID 1000 b\x01\x16 # L: report 列表 b\x05\x04\x00\x00\x00\x01 # U4 RPTID 1 b\x01\x0e # L: data list b\x01\x0c # L: 单个 data item b\x05\x04\x00\x00\x00\x01 # U4 VID 1 b\x05\x04\x00\x00\x00\x2a # U4 VALUE 42 ) # 在回完 S1F14 后主动推一条 S6F11System Bytes 必须用新值 event_msg make_hsms(session_id, 6, 11, sys_bytes 1, build_event_body()) conn.sendall(event_msg) print(已推送 S6F11 事件)build_event_body里每一段都是一个 SECS-II 数据项的编码\x05\x04是 U4 类型头后面跟 4 字节大端值。嵌套的 List 用\x01开头第二个字节是内容长度。这段正文的手工编码只是为了演示链路真正的项目里事件正文都是按设备数据字典由协议栈动态生成的不需要自己拼字节。注意事件消息的 System Bytes它是一条新事务必须用递增后的新值不能复用刚才 S1F13 的值。主机侧收到 S6F11 后要回一条 S6F12把这条 S6F11 的 System Bytes 原样带回设备才知道“主机收到我的事件了”。如果主机不回设备端会按策略决定重发还是放弃T6 超时就是这么来的。5. 避坑SECS/GEM 联调最常见的 5 个翻车点现象、原因、解决5.1 连接秒断TCP RST 背后的 T7/T8 与防火墙现象设备和主机 TCP 握手成功S1F13/S1F14 也通了但连接保持一两分钟就断开。协议栈日志提示 Linktest 超时或 T8 超时。原因HSMS 连接不是建好就完事空闲时双方要定期发 Linktest 心跳SType5/6确认对方还活着。T7 是空闲多久触发 LinktestT8 是等 Linktest 响应多久算超时。如果设备侧 T7 设得比主机侧短设备先发心跳防火墙又把空闲连接回收了TCP 层直接 RSTSECS 层就只能看到连接断了。解决先抓包确认 RST 是设备发的还是网络中间设备发的。如果是设备发的核对两边的 T7/T8 参数保持一致。如果 RST 来源是网络多半是防火墙空闲超时放行端口并调大连接保活时间。我当时在这问题上耗了半天最后发现是虚拟化平台的空闲连接回收策略把端口关了跟 SECS 协议一点关系没有。5.2 S1F13 无响应不是玄学是 Length 或 System Bytes 写错了现象主机发 S1F13设备完全不回也不报错。抓包看设备明明收到了 TCP 数据。原因设备按 HSMS 头解析后发现 Length 对不上、Session ID 不对或者 System Bytes 没有递增直接丢弃。最典型的错误是把 Length 算成了整条消息长度多算了 4 字节。另一个是我见过不少次的System Bytes 固定写 0两台设备用同一个值对端当成重复帧丢掉。解决逐字节核对抓包里的 Length。S1F13 正文为空时Length 应该是 10不是 14。System Bytes 从 1 开始每个新事务加 1响应用请求的值。Session ID 要和设备配置的 Device ID 一致很多设备在日志里会打invalid session去翻设备端日志比在主机侧猜快得多。5.3 CEID 对不上设备上报的数据主机全丢现象设备事件触发S6F11 也发出来了但主机侧显示“未知 CEID”或者事件收到了但没有对应数据。原因设备固件版本和厂商文档不一致CEID 或 RPTID 编号发生偏移。还有一种情况是事件上报链路没问题但 RPTID 到 VID 的映射没下发成功事件是个空壳。解决用厂商的 SECS 配置工具导出设备当前生效的 CEID、RPTID、VID 清单和主机配置文件逐条比对。比对通过后再触发一次真实事件看 S6F11 正文里是否带了数据点。不要因为厂商文档写“CEID1000 是加工完成”就信以为真以设备实际导出的为准。这条建议在设备换固件之后尤其重要一定要重新导一遍。5.4 S2F41 命令被拒“设备支持 GEM”不等于“支持你这个命令”现象主机下发 S2F41 远程命令设备回 S2F42但 HCACK 非 0或者干脆不回。原因GEM 标准只定义了命令的框架不规定具体命令名。设备支持START但不支持PAUSE或者命令名区分大小写主机发了Start设备认不出来。参数类型不匹配也是高频点设备要字符串主机传了数字。解决先确认设备支持的命令清单一般通过厂商配置工具或数据字典里的 CMD 列表查到。命令名统一用大写参数严格按数据字典定义的类型传。拿到 S2F42 的 HCACK 非零值后对照厂商错误码表不要凭感觉猜。我在一个项目里遇到设备回 error code 0x02查表才知道是“参数个数不对”。5.5 T6 定时器超时主机不是没收到是没空回现象主机发请求后设备秒回响应但主机协议栈报 T6 超时。抓包看响应确实到了主机网卡。原因T6 是事务超时从主机发请求开始计时到主机收到并处理完响应为止。很多时候响应到了但主机侧的处理线程被占住Event 上报和命令响应挤在同一个串行队列里应答没来得及发出。解决抓包分两段看请求是否到达设备响应是否发出。如果响应发出但主机还超时问题在主机侧处理速度不在网络。做法是把消息接收和业务处理拆成两个线程接收线程立刻回确认业务线程慢慢处理。调大 T6 只能缓解症状不解决根本问题真正治本是保证协议栈有独立的发送通道别让慢业务堵住响应。6. 收尾技巧用抓包把 SECS/GEM 从黑匣子变成透明管道6.1 照着这张表做 GEM 合规性自查验证一台设备是否真支持 GEM不用把标准从头到尾读一遍按这张表逐项过检查项消息对通过标准通信建立S1F13 / S1F1410 秒内完成握手设备身份S1F1 / S1F2返回 MDLN 和 SOFTREV事件上报S6F11 / S6F12CEID、RPTID、VID 完整值正确远程命令S2F41 / S2F42命令生效HCACK 为 0报警上报S5F1 / S5F2ALID 和文本可读离线/在线切换S1F15 / S1F17状态切换后行为符合预期6.2 只看 HSMS 头怎么定位八成问题抓包时不用急着解 SECS-II 正文先看每帧最前面的 14 个字节。用下面的命令把 5000 端口的流量存下来tcpdump -i any tcp port 5000 -w hsms.pcap打开抓包后先看每帧的 Length 和 Stream/Function 两个字段。Length 是 10 或稍大Stream/Function 在预期范围说明传输层正常如果 Length 等于 14 或更大却解析不出正常消息就能马上判断是哪一侧写错了长度。再看响应帧的 System Bytes 是否和请求一致一致才说明事务配对正确。T6 超时的定位也一样看请求帧发出到响应帧返回的时间间隔如果间隔小但主机报超时问题一定在主机的处理线程。6.3 最后一条习惯我现在的固定流程是手边永远放着一套“主机 模拟设备 抓包”三件套任何协议问题来了先抓包先看头再看正文最后才翻代码。这个习惯帮我把不少“玄学”问题变成了明确的字节比较。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑