资讯动态

SECS/GEM协议详解:E5/E37/E30分层与二进制帧实践

发布时间:2026/10/27 10:38:26 来源:尧图企业网站定制
简介《SECS/GEM 中文详解》是一份面向半导体及工业设备自动化从业者的协议学习文档适合设备集成、上位机开发与产线运维人员梳理 SECS/GEM 的整体框架。针对网上资料零散、说明不细的问题文档用10个章节系统讲解 SECS 功能从协议介绍、消息日志、常用术语到 Streams and Functions 的基础定义、系统保留与自定义分类、指令含义及使用场景并展开设备集成成本、网络带宽利用、数据密度、无数据翻译、环路保证与安全等要点同时覆盖设备状态报告、控制命令与数据传输等典型应用。资源包共1个 PDF 文件约955KB目录层级清晰便于按章节查阅和对照实践。目前已有2272人学习下载适合需要系统理解设备通信协议、对照目录查漏补缺的工程技术人员可作为入门与速查材料。1. 32 字节的报告撑起一条无人化产线SECS/GEM 到底在省什么一份事件报告真实数据只有 32 字节两个 4 字节整数、两个 8 字节浮点、一个 4 字节事件 ID、一个 4 字节报告 ID。装进 SECS/GEM 的帧里算上 10 字节头和长度字段总量还不到 50 字节换成同样语义的 JSON光键名就把体积翻好几倍。这份 SECS/GEM 中文详解里反复回到的就是这条主线一条产线上每秒可能滚过上万条这样的消息字节数最后都会变成交换机的背板带宽和主机的解析开销。真正值得搞懂的是这套协议为什么能在九十年代定型、今天反而比通用格式更贴合产线——它只掌握了两个词抠字节、抠确定性。2. 三层拆分E5 管语义、E37 管传输、E30 管底线2.1 三条标准各锁一段谁也不侵入谁SECS/GEM 不是一份文档是三份标准捏在一起的合称各自负责协议栈的不同层面边界划得很硬标准名称负责的部分换掉它会发生什么SEMI E5SECS-II消息结构 标准消息库说什么语义全变所有主机软件重写SEMI E37HSMS在 TCP/IP 上承载 SECS-II 的二进制帧怎么送语义不变只换传输通道SEMI E30GEM最低要求、可选功能、用例与用户场景必须会什么合规性判定失效互操作没法谈这种切法的动机很实际。早期的 SECS-I 跑在串口上靠字节计数和握手保证完整后来产线换以太网只需要把传输层换成 HSMS上层那几百条 SxFy 消息一个字都不用改。反过来标准要新增一类业务语义也只动 E5 的消息库HSMS 的帧格式纹丝不动。我一般会把它理解成一次很早的「关注点分离」实践语义的演进速度和传输的演进速度本来就不一样绑在一起就是互相拖累。E30 的位置更微妙。它不定义任何一个字节怎么排只定义「一台自称符合 GEM 的设备至少得会哪些动作」。状态变量、设备常量、采集事件、报警、远程命令、时钟、控制状态Online/Offline、Local/Remote这些是底线配方管理、假脱机、终端服务、跟踪数据全部标成可选。产线上设备千差万别一台只有十几个采集事件的简单机台和一台能发五千个事件的复杂机台都得能用同一套技术描述这个分层就是把「最小合规」和「能力上限」解耦。2.2 一条消息的解剖10 字节头加长度字段HSMS 帧的结构非常克制。头固定 10 字节字段排布如下偏移字节数字段作用02Session ID / Device ID区分是哪台设备、哪条连接控制消息用全 121低 1 位 W-bit 高 7 位 StreamW-bit 标记「这条要不要回复」31Function与 Stream 组合成 SxFy41PType协议类型SECS-II 固定为 051SType会话类型数据消息为 0Select/Linktest 等控制消息非 064System Bytes事务号回复必须原样带回头后面跟长度字段再后面才是消息体。这里有个容易记岔的地方长度字段的长度是可变的1 到 4 字节取决于消息体多大HSMS 这一层通常固定成 3 字节能表达 16MB 量级够用且对齐简单。10 字节头加长度字段就是这条消息全部的固定开销——没有标签名没有分隔符没有转义。剩下每一个字节都在装真实数据。2.3 为什么 Session ID 和 System Bytes 是两个独立字段新手常把这两个当成冗余。它们解决的是完全不同的问题Session ID 回答「这条消息属于哪条连接」System Bytes 回答「这条消息属于哪次对话」。一台主机同时连着几十台设备每台设备的连接各有一个 Session ID而在同一条连接上主机可能并发发出好几个请求每个请求分配一个递增的 System Bytes设备回复时把这个 4 字节原样抄回来主机靠它把回复和请求对上山头。这套配对机制带来的直接好处就是环路保证任何一个请求要么收到匹配的回复要么在超时窗口里被判死不存在「发出去就石沉大海、程序永远卡在等回复」的中间态。产线软件最怕的就是这种悬空等待一条挂死的线程能让整条线的调度停摆。资源文档里单独拿一节讲环路保证原因就在这——它不是协议附赠的礼貌而是整个控制逻辑能写成同步代码的前提。2.4 GEM 的能力矩阵必备项少可选项多GEM 把功能拆成必备和可选落地时形成一张能力矩阵。必备的那部分大致是状态变量上报、设备常量读写、采集事件与事件报告、报警管理、远程命令、时钟、控制状态机。可选的那部分包括配方管理、假脱机、终端服务、跟踪数据采集、错误消息、加工程序管理等。这个设计有个隐性代价后面第 6 章会展开两台都宣称符合 GEM 的设备实际能力可能差出一大截互操作必须靠额外的能力协商和对接文档兜底。但换来的是覆盖率——从只有十几个事件的小机台到五千个事件的大型设备都能落在同一套框架里。3. 把数据压进一个字节格式码与数据密度3.1 item 头一个字节里塞进类型和长度宽度SECS-II 的消息体是树。列表套列表叶子是具体数据项。每个数据项前面有一个 item 头第一个字节这样切高 6 位是格式码低 2 位记录后面跟几个字节来表示长度。低 2 位的取值是「长度字节数减一」所以 0 表示 1 字节长度1 表示 2 字节2 表示 3 字节。格式码本身是一张固定的表常见取值按八进制记格式码类型单元素字节数0o00List列表装子项无值只有元素个数0o10Binary10o11Boolean10o20ASCII10o30 / 0o31 / 0o32 / 0o34有符号整数 I8 / I1 / I2 / I42 / 1 / 2 / 40o40 / 0o44浮点 F8 / F48 / 40o50 / 0o51 / 0o52 / 0o54无符号整数 U8 / U1 / U2 / U48 / 1 / 2 / 4关键在于「类型」和「数据」是绑在一起的。一个 4 字节整数在网络上的完整表示是 1 字节类型 1 字节长度 4 字节值 6 字节。多出来的 2 字节就是给类型和边界的全部税负没有别的东西了。下面这段编码逻辑基本就是所有实现里那个 encode item 的核心import struct # 格式码到 (struct 格式串, 单元素字节数) 的映射 FMT { 0o30: (q, 8), 0o31: (b, 1), 0o32: (h, 2), 0o34: (i, 4), 0o40: (d, 8), 0o44: (f, 4), 0o50: (Q, 8), 0o51: (B, 1), 0o52: (H, 2), 0o54: (I, 4), } def encode_item(code, value): 把单个数据项编码成 SECS-II 字节串value 为标量或同类型序列 if code 0o00: # 列表长度字段表示元素个数后面拼子项 body b.join(value) count len(value) else: fmt, width FMT[code] seq value if isinstance(value, (list, tuple)) else [value] body b.join(struct.pack(fmt, v) for v in seq) # 大端网络序 count len(seq) # 长度字段是“元素个数”不是字节数 # 选最短的长度字段宽度1/2/3 字节低 2 位存 “宽度-1” if count 0xFF: len_bytes, nlb struct.pack(B, count), 0 elif count 0xFFFF: len_bytes, nlb struct.pack(H, count), 1 else: len_bytes, nlb struct.pack(I, count)[1:], 2 # 取低 3 字节 header bytes([(code 2) | nlb]) # 高 6 位格式码低 2 位宽度 return header len_bytes body这段代码里有两处容易被忽略的细节。第一长度字段写的是元素个数而不是字节数一个含 100 个 F8 的列表长度字段写 100实际负载 800 字节。接收方拿个数乘以类型宽度自己算总长省下了表达字节数的额外位宽。第二浮点和整数一律大端显式指定不依赖运行机器的字节序。这两点加起来就是后面「无数据翻译」能成立的技术基础。3.2 数据密度负载越大开销占比越接近零按上面的结构可以推一下开销占比。固定开销是 10 字节头加 1 到 3 字节长度字段约 12 字节每个叶子项再付约 2 字节的 item 头税。场景实际数据SECS/GEM 约总量开销占比同语义 JSON 约总量一条典型事件报告32 字节45 到 50 字节三成上下120 字节以上一条含 100 个浮点的报告800 字节810 到 820 字节不足两个点1000 字节以上键名重复上表是量级估算不是精确基准但趋势很清楚固定开销基本恒定负载一变大就被摊薄。资源文档里给的那组实测也指向同一个结论——把示例消息从几个标量扩到 100 个 8 字节浮点之后SECS/GEM 的数据密度爬到 78%JSON 和 XML 之间的相对关系几乎没动因为它们每一位数据都要拖着键名或标签名走。这个差异在单条消息上看是小事放到整线规模就是另一回事。一台设备可能配了几十个采集事件、上百个跟踪数据项主机侧还挂着统计工艺控制、预测性维护、前馈反馈控制一堆应用每秒的消息量很容易上万。每条省下几十字节乘上去就是网卡和解析器的吞吐差别。所谓「非常有效地使用网络带宽」落到底层就是这个。3.3 无数据翻译二进制直通换来的确定性「无数据翻译」这一条经常被当成宣传语忽略过去其实它是个硬约束。SECS-II 里的 F8 就是 IEEE754 双精度在设备侧内存里怎么存到主机侧内存里就怎么读中间没有字符串化没有本地化数字格式没有千位分隔符也没有「12.5 序列化成 12.5000001」这类经典翻车。整数同理大端固定不存在字节序协商。代价是调试不友好。抓包看到的是二进制得靠工具反解。但产线软件要的不是好读是不出错。设备端算出来的腔室温度是 87.35 度主机拿到的必须还是 87.35中间多一层格式转换就多一层踩坑机会。资源里把「无数据翻译」和「数据密度小」并列本质上说的是一件事少一次转换少一次出错的可能。顺带一提二进制并不意味着不可读。SECS-II 的结构是完全自描述的类型和长度都在流里写个几十行的解析器就能还原成树状结构再导出成 JSON 或 XML 做人工排查这就是消息日志工具在做的事。4. 事务与状态机SxFy、W-bit、超时和连接状态4.1 SxFy 命名法流是话题功能是动词Stream 可以理解成业务话题的分类Function 是话题下的具体动作。整个编号体系有个极省心的规律奇数 Function 是请求偶数 Function 是成对的回复请求号加一就是回复号。Stream话题常用消息S1设备状态与管理状态变量、设备常量、控制状态切换S2设备控制与诊断定义报告、链接事件、远程命令S5报警报警上报、报警使能、报警列表S6数据收集事件报告、跟踪数据、多块数据S7配方管理配方上传下载、配方目录S9错误消息无法识别的流/功能/数据、事务超时S10终端服务操作员消息显示与确认S6F11 是设备主动上报事件报告S6F12 就是主机确认收到。S2F41 是主机下发远程命令S2F42 回执行结果。记住这个奇偶规律看日志时的反应速度会快很多。4.2 系统保留与自定义给私有扩展留一段编号标准 Stream 之外还有一段预留编号专门给厂商自定义消息用S64 就是常用的自定义承载流。这个设计的动机很朴素设备厂商总会遇到标准消息表达不了的需求如果每次都去推动标准组织改文档周期按年算。留一段私有编号厂商自己定义结构和语义双方对文档即可标准本身不用动。代价是私有段容易变成各家方言。同一个 S64F1在不同厂商的机台上可能完全是两回事。血泪经验是任何涉及 S64 的对接必须先把编号和结构对齐成一份书面约定别指望「大概一样」。4.3 W-bit 与事务配对不回 Reply 会怎样W-bit 是头上那一位置位。置 1 表示发送方期待回复置 0 表示这是单向通知不需要回。设备主动上报的报警、跟踪数据很多是 W-bit 为 0 的单向消息主机下发的命令、参数读写基本都是 W-bit 为 1。置 1 之后System Bytes 就成了这次事务的身份证。发送方在本地事务表里登记等待回复时按事务号匹配收到就把这一条摘掉超时窗口到了还没等到就判失败并发 S9F9 事务超时。事务表的最小实现大致是这样import itertools, time class TransactionTable: def __init__(self, t345.0): self.t3 t3 # T3等待回复的超时默认 45 秒 self._seq itertools.count(1) # System Bytes 递增分配 self._pending {} # system_bytes - (发送时刻, 原始请求) def open(self, request_bytes): sbid next(self._seq) 0xFFFFFFFF # 4 字节回绕别用 64 位 self._pending[sbid] (time.monotonic(), request_bytes) return sbid def claim(self, reply_bytes): sbid int.from_bytes(reply_bytes[6:10], big) # 回复必须带回同一个事务号 return self._pending.pop(sbid, None) # 匹配成功即摘除防止重复消费 def sweep(self): now time.monotonic() for sbid, (t0, req) in list(self._pending.items()): if now - t0 self.t3: # 超时清账 上报 S9F9 del self._pending[sbid] yield sbid, reqopen里的 4 字节回绕是个真实坑事务号用满会绕回去如果表里塞了大量长期未回的事务新号可能撞上旧号claim就会摘错条目。工程上的做法是控制未决事务数量并在回绕时跳过仍在表里的号。sweep是兜底定期扫一遍超时项保证「发出去等不到回复」这件事一定会被显式处理而不是静默堆在内存里。4.4 HSMS 的连接状态Selected 之前别发数据TCP 连上不等于能发业务消息。HSMS 在 TCP 之上又叠了一层会话状态从 Not Connected 建立 TCP 之后进入 Connected但此时是 Not Selected只能发控制消息。设备或主机任一方发 Select.req对方回 Select.rsp双方进入 Selected数据消息才允许通行。中间任何一方发 Deselect.req 就退回 Not Selected。Linktest 用来在空闲期探活不改变状态。这段设计解决的是「连接可用性」和「通信可用性」的区分。TCP 连接还在但对端上位机可能正在重启、正在切换配方、正在做自己的初始化这时候塞数据过去只会得到一堆错误回复。用一层显式的 Selected 状态把「你准备好接客了」这件事变成协议里可以确认的事实。4.5 超时不是玄学T3 到 T8 各守一段超时参数是这套协议里最像玄学、实际最讲道理的部分。每个定时器守的是一段特定边界互不重叠定时器常见默认值守的是哪一段T345 秒发出 W-bit 为 1 的请求后等回复的时长T510 秒连接已断等重新建立完成的时长T65 秒控制事务Select、Deselect、Linktest的回复等待T710 秒进入 Not Selected 后等再次被 Select 的时长T85 秒单个消息传输过程中字符之间的最大间隔T3 给得最宽因为对端可能在执行一条耗时命令T6 给得紧因为控制事务本该是毫秒级。T7 是最容易被忽略的一个如果一条连接在 Not Selected 状态下待满 T7实现应该主动断开重连否则会留下一条「TCP 活着但双方都不说话」的僵尸连接占着资源。T8 则防的是半包——发了半个消息就卡住的场景靠字符间隔判断对端是不是已经废了。5. 避坑联调现场最常见的五条翻车记录5.1 事务层消息发出去了程序卡在等回复现象主机下发 S2F41 之后主线程不返回日志里能看到请求已发出没有任何回复也没报错就这么挂着。原因这条请求的 W-bit 被置成了 0。设备端收到后发现「对方没要求回复」按标准就不回复主机侧却在事务表里死等。另一种同源情况是设备端回了但 System Bytes 被写成了固定值 0主机按事务号匹配不上回复被当成野包丢弃。解决抓包先看第二字节的最低位确认 W-bit 与业务意图一致再看回复消息的第 6 到 9 字节是否与请求完全一致。这两项对上了问题基本就解决了。排查时把事务表内容打出来未决条目一目了然。5.2 数据层报告收到了但值是空的现象S6F11 事件报告能收到结构也对就是里面的变量值全是默认值或空列表。原因主机只在启动时发过一次 S2F33定义报告和 S2F35把报告链接到事件但之后设备重启过报告定义丢了或者设备端的采集事件没有被 S2F37 使能上报的还是空壳。GEM 里「定义」「链接」「使能」是三个独立动作少一步都不会有数据。解决把这三步做成启动握手的固定序列每次连接进入 Selected 后重新走一遍而不是只在第一次连上时做。加上幂等处理重复定义不会出错。5.3 数据层编号对上了语义对不上现象设备上报的某个变量主机拿到的数值明显不对差了几个数量级。原因VID 或 CEID 的定义只对了编号没对单位。设备侧以毫秒为单位上报时间主机按秒解析。这类问题在字段名一致的情况下极难发现因为数据「看起来是合理的」。解决对接文档必须精确到每个数据项的格式码、单位、量纲和取值范围而不是只列编号。模拟量尤其要标注原始单位和换算方式。5.4 存储层断网重连之后事件丢了一大截现象网络闪断几十秒恢复后设备继续正常上报但断连期间发生的事件全部消失了主机侧报表出现空洞。原因设备没有启用假脱机功能。假脱机的作用是把发不出去的事件消息落到非易失性存储里重连后按原顺序补发。没启用的话发送失败就是丢弃而设备端通常只打一条日志不报警。解决先确认设备是否支持假脱机、存储配额多大、满了之后的策略是丢弃最旧还是拒绝新增。配额和策略比「支不支持」更重要——配一个小存储还不如不配因为它会给你一个「有兜底」的错觉。5.5 时间层日志能对上顺序对不上现象排查问题时把两侧日志按时间戳排序发现因果关系完全反了主机记录的「下发命令」居然早于设备记录的「上报状态」。原因两侧时钟不同步或者其中一侧用了本地时区而另一侧用了 UTC。SECS/GEM 有专门的时钟消息用于对时但很多设备只实现读取不支持被设置。解决不要拿两侧时间戳直接对比。以 System Bytes 和消息序号为因果依据时间戳只作参考。需要严格时序时先走一轮对时并明确约定时区。6. 设计取舍GEM 为什么把「可选」写进标准必备加可选这个分层短期看是妥协长期看是这套协议活得久的主要原因。半导体产线上的设备跨度极大一台几十年机龄的老设备只有几个状态点一台新机台能发五千个采集事件。如果标准要求全实现老设备根本进不来如果标准只要求最少的那点新设备的能力又没处表达。E30 的做法是把「最小合规」压到很低剩下的全部标成可选由设备厂商按需实现主机侧通过能力协商去发现对方到底会什么。这个取舍的代价也很实在合规不等于可对接。两台都贴了 GEM 标签的设备实际行为可能完全不同。所以工程上真正决定成败的不是「符不符合标准」而是三份东西——设备能力清单、数据项定义表、事件与报告映射表。这三份对不齐协议再规范也白搭。验证思路也由此而来。我会建议用一套固定的对拍流程连接进入 Selected 后先跑完整的握手序列清报告、定义报告、链接事件、使能事件然后触发一个已知事件比对上报的报告结构和数值再故意发一条非法格式的消息看对方是否按规范回 S9F7最后拔网线三十秒看假脱机的补发是否完整、顺序是否正确。这三步跑通这套接口的上线风险基本可控。初级对接常见的误区是把 GEM 当成一个「开箱即用的通用接口」。它不是接口它是一份约定模板具体长什么样得双方填。这也解释了为什么原始文档里那几节讲数据密度、无数据翻译、环路保证的内容放在最前面——它们描述的是一种确定性风格而整份文档后面所有章节都是在这个风格下展开的具体条目。从那以后我每次接手一个新的设备对接都强制走一遍那三步对拍不管对方文档写得多漂亮、不管对方工程师说「我们完全符合 GEM」。文档会骗人抓包不会。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑