做设备自动化这些年,我接触过不少刚接手 EAP 项目的工程师,第一反应都是去翻 SECS/GEM 厚厚一摞标准文档,结果被一堆编号和格式给劝退了。其实换个角度,把协议当成人话来看,事情会简单很多。SECS/GEM/GEM300 这一整套东西,说白了就是设备和主机之间的“对话规则”,而事件(Collection Event)是这套对话里最不能绕开的一环——设备什么时候开工、什么时候完工、中途有没有异常,全要靠事件来主动通知主机。这个系列第一篇,我想把事件单独拎出来,从报文结构、订阅流程到工程落地,一路讲到底。如果你正在做晶圆厂、光伏厂或者面板厂的设备自动化,或者刚接手 EAP 相关需求,这篇读完基本能上手处理 S6F11 了。
1. 事件在 SECS/GEM 协议栈中的定位:设备凭什么“开口说话”
1.1 从“打电话”到“报业务”:一整套标准在解决什么问题
先帮大家把几个名词理顺。SECS/GEM 不是一个单一协议,而是一族标准配合起来用。SECS-I 和 HSMS 解决的是“怎么把消息从一个地方送到另一个地方”,前者走串口,后者走 TCP/IP,现在大多数产线都走 HSMS;SECS-II 解决的是“每一句话按照什么格式说”,定义了消息的流(Stream)和功能(Function);而 GEM(也就是 E30 标准)解决的是“在什么业务场景下说什么话”,它规定了设备与主机之间如何处理事件、报警、状态变量;到了 300mm 时代又扩展出一批 GEM300 相关的标准,把物料搬运、加工流程、设备之间的自动化集成也纳入了统一模型,EAP(Equipment Automation Program)就是在这个阶段成为产线自动化的核心载体。
打个比方,SECS-I/HSMS 相当于电话线路,SECS-II 相当于语法规则,GEM 相当于客服话术手册,GEM300 则相当于把整个公司业务流程都规范成了标准操作。事件在这套体系里属于“设备主动向主机汇报”的消息类型,用 S6 这条流来承载。所以说事件是协议的“上行主力”一点也不为过。
1.2 事件、报警、状态变量:它们根本不是一回事
很多新人上来就问“事件是不是就是报警”,这是最常见的误解。我把三者放在一起对比,大家就清楚了:
| 概念 | 触发方向 | 消息流 | 典型用途 |
|---|---|---|---|
| 状态变量(SVID) | 主机主动查询,设备被动回答 | S1F3/S1F4 | 获取设备当前状态、配方名、腔体压力等 |
| 报警(Alarm) | 设备主动上报异常 | S5F1 等 | 设备门未关、真空异常、硬件故障 |
| 事件(Collection Event) | 设备主动上报状态/流程变化 | S6F11/S6F13/S6F15 | 开始生产、完成生产、物料到位等 |
状态变量永远都是“主机问,设备答”,适合做低频的核对和巡检;报警是设备在喊“救命”,它说明出了异常;而事件是不论正常还是异常,只要业务节点发生变化,设备就会主动发一条通知。举个例子:腔体当前的真空度是状态变量,你要主动去读;真空泵抽气完成、工艺开始,这是事件;真空度掉了且触发保护,这才是报警。分清这三者,你在设计 EAP 逻辑时就不会把所有需求都压在轮询上。
1.3 为什么事件是 EAP 的地基
如果没有事件机制,EAP 想感知设备进度就只能不停轮询状态变量。设备台数少还好,一旦上百台设备同时运行,轮询周期根本没法保证实时性,而且设备响应也会被拖垮。事件机制让设备在关键节点主动上报,主机不用猜,不用问,事情发生了就知道。GEM300 下面的自动化流程,比如 Process Job 的启停、物料在搬运站点到位,都依赖事件来驱动。换句话说,事件是 EAP 感知产线动态的神经末梢,这一环断了,再好的调度逻辑都是空谈。
2. 事件消息拆解:S6F11、S6F13、S6F15 到底长什么样
2.1 SECS-II 消息的骨架
理解事件消息之前,先记住 SECS-II 消息的基本结构:一个消息由消息头加数据体组成。消息头固定 10 字节左右,包含长度、Stream 号、Function 号、消息 ID、系统字节等信息。Stream 和 Function 的组合,决定了这条消息是“干什么的请求”还是“干什么的应答”。
事件相关的消息集中在 Stream 6,也就是“数据传输和报告”这条流。其中最重要的三个:
- S6F11:Event Report Send,设备向主机发送单个事件报告;
- S6F13:Data Set Send,设备发送数据集;
- S6F15:Multi-Data Set Send,设备一次发送多个数据集。
数据体则是一串按 SECS-II 格式编码的数据项,最外层的结构是列表(List)。List 可以嵌套,里面可以放整数、浮点数、ASCII 字符串等各类数据类型。SECS-II 之所以难入门,很多人就是卡在这层嵌套格式上。
2.2 S6F11:最基础的“事件报到”消息
S6F11 的数据体标准结构可以写成下面这样:
S6F11, W Data: L U4 CEID # 事件编号 L U4 RPTID # 报告编号 L ...数据项... # 与该事件绑定的数据集我来拆一个实际例子。假设某台刻蚀设备在工艺完成后上报事件,CEID = 2002,RPTID = 5,报告里带了一条状态字符串和一组测量数据:
L U4 2002 # CEID:工艺完成 L U4 5 # RPTID L <A "Completed"> <F8 25.5>主机收到这条 S6F11 后,第一件事就是从数据体里读出 CEID,判断是哪个事件发生了;接着读 RPTID,找到对应的数据定义;最后把后面的数据项按顺序解析出来。EAP 就可以用这些信息更新设备状态、写数据库、触发下一步搬送逻辑。
这里有一个工程细节值得单独强调:S6F11 消息头里有个 W 位(Reply Expected 标志),如果设备置 1,就要求主机回一条 S6F12 作为确认。S6F12 的数据体是 HCACK,0 表示接受。主机即使业务处理失败,也应该先回 0,再异步处理异常,否则会让设备认为消息根本没送达,触发超时重发。
2.3 数据量撑不住时:S6F13 和 S6F15 的适用场景
有些事件要携带的数据量很大。比如一台量测设备完成一片晶圆后,要上报几百个测量点的图谱数据,如果用 S6F11 硬塞,一条消息可能会超过几 KB 甚至几十 KB,这在高速产线上容易把通信窗口占满,拖累其他消息的时效性。GEM300 时代引入了数据集(Data Set)的概念,用 DATAID 标识一组数据,通过 S6F13 一次性上传;更多场景下用 S6F15,一次携带多个数据集。
工程上很多设备厂商会把“量测完成”这类大数据事件默认走 S6F13 或 S6F15,而把“工艺开始”“工艺完成”这种小负载事件走 S6F11。你写 EAP 解析逻辑时,不能只兼容 S6F11,最好是三种消息的解析都做成独立模块,再统一转成内部事件模型。
3. 事件的订阅与生命周期管理:不是设备默认就会上报一切
3.1 先定义报告(S2F33):给事件配上要带的数据包
设备上虽然有很多事件编号,但它不会默认把所有事件都上报,也不会自动带上你想要的全部数据。主机需要先通过 S2F33(Define Report)把一组数据项打包成一个报告(Report),用 RPTID 标识。比如你想在工艺完成事件里带出“结束状态”和“实测温度”,就要先在设备端定义一个 RPTID = 5 的报告,把对应的 SVID、DVVAL、ECID 放进去。
S2F33 的数据体结构大致是:
S2F33, W Data: L U4 RPTID # 报告编号 L U4 VID1 # 第一个数据项编号 U4 VID2 # 第二个数据项编号 ...定义报告前,你一定要先拿到设备端的 SECS 手册,确认哪些 SVID 和 DVVAL 是设备支持的。不同厂商的设备,甚至同一家厂商不同型号,支持的参数编号都不同。我遇到过不少联调现场,因为漏看了手册,定义了一个不存在的 VID,结果事件数据一直解析异常。
3.2 再启用事件(S2F35):Link 是决定上报与否的关键
报告定义好之后,还要把 CEID 和 RPTID 绑定起来,这一步通过 S2F35(Link Event Report)完成。S2F35 的数据体大致是:
S2F35, W Data: L U4 数量 L U4 CEID U4 RPTID ...这个 Link 动作有两个含义:一是告诉设备“事件发生时要带上这份报告”,二是把该事件的状态从禁用(Disabled)变为启用(Enabled)。反过来,如果想停用某事件,发送一个空的报告列表即可,相当于 Unlink。
现场最典型的翻车现场就是:“我明明定义了 Report,也看到设备事件触发了,但主机就是收不到 S6F11。” 原因十有八九是漏了 S2F35,事件没被启用。请把这个流程刻在脑子里:先 Define Report,再 Link Event Report,最后才能在主机侧收到事件。
3.3 订阅关系的持久化与恢复
事件订阅关系并不一定是持久化的。有些设备断线重连后 Link 关系还能保留,有些设备重启后就恢复成出厂默认状态。你无法靠猜,必须在联调阶段做一次“断线重连测试”,确认行为的设备差异。
我的建议是,EAP 侧维护一张订阅表,把“设备 ID、CEID、事件含义、RPTID、使能状态、订阅时间”全部记录下来。每次 EAP 启动或者发生网络重连后,都主动重新执行订阅流程。很多现场故障就是重连之后订阅关系丢失,设备链路看着是通的,心跳也正常,但事件就是不上来。有了这张表,排查起来就一句话的事:对照表看看当前的 CEID 有没有处于 Enabled 状态。
4. 事件的工程化设计:从 CEID 规划到事件风暴防护
4.1 CEID 编号规划:没有跨厂商统一标准,但有自己的约定
CEID 是设备厂商自定义的,没有一套跨厂商的强制编号规范。不过我在实际项目里观察,很多设备厂商都遵循类似的分段习惯:
| 编号段 | 典型含义 |
|---|---|
| 1xxx | 设备状态类事件,如 Ready、Running、Idle |
| 2xxx | 工艺类事件,如 Recipe Start、Process Complete |
| 3xxx | 物料类事件,如 Slot 状态变化、FOUP 到达 |
| 5xxx | 报警相关的事件 |
| 7xxx | 维护保养类事件 |
这种分段的做法的好处很明显:看 CEID 就能大概判断事件属于哪个环节,方便 EAP 侧路由和日志过滤。但千万不要凭经验硬套到新设备上,每个型号都要以厂商的 CEID 清单为准。做设备验收时,我会把“CEID 清单文档”列为正式交付物,缺失的话联调阶段必然吃亏。
4.2 用事件驱动设备状态机:设计在前,编码在后
EAP 内部维护的设备状态机,本质上就是被事件推着走的。我常用的状态集合是:IDLE、RUNNING、COMPLETE、ALARM、DOWN。事件与状态迁移的对应关系可以提前规划成这样:
| 当前状态 | 触发事件 | 目标状态 |
|---|---|---|
| IDLE | Process Start(CEID 2xxx) | RUNNING |
| RUNNING | Process Complete(CEID 2xxx) | COMPLETE |
| RUNNING | Alarm(S5F1 伴随事件) | ALARM |
| COMPLETE | 物料搬离/Reset | IDLE |
设计事件类型的时候,最好从业务反推:哪些状态迁移必须被 EAP 感知?哪些环节即使漏报也不会造成大问题?把这些想清楚再决定订阅哪些 CEID,而不是把所有事件一次性全 Link。事件订阅得越多,解析和处理的复杂度越高,出问题的面也就越大。
风险在于事件丢失后状态机会卡住。所以状态机不能完全依赖事件,还要有兜底机制:定时用 S1F3 读几个关键 SVID 做对账,或者周期性地发 S1F1 探测设备在线状态。这样即使偶发丢事件,也能及时发现状态不一致。
4.3 事件风暴:一个容易被低估的问题
某次现场调试,一台设备因为门传感器抖动,一晚上向主机发了上千条同类事件,直接把主机的日志存储打满,还把消息队列堵了。这种事不是个例。处理事件风暴有三层手段:
第一层在设备侧,能去抖就去抖,从源头减少无效上报;第二层在配置侧,只订阅你真正需要的事件,不要一股脑全 Link;第三层在 EAP 侧,做限频处理,比如同一 CEID 在 1 秒内最多处理一次,或者对连续重复事件做合并。
在设计 EAP 时,最好给每个 CEID 预留“预期频率”这个属性。工艺类事件几十秒一次、报警类事件不定期、传感器类事件可能密集出现。有了预期频率,线上出现异常高频时就能快速识别出来。
5. EAP 收到事件之后:处理流程与业务逻辑设计
5.1 从解析到 Ack:动作顺序不能搞错
EAP 收到一条 S6F11 后,我建议按这个顺序走:
- 记录原始报文,至少保留消息 ID、CEID、时间戳;
- 解析数据体,把 CEID 和所有 RPTID 数据提取出来;
- 查询订阅表,找到该事件对应的业务处理模块;
- 立即回 S6F12,HCACK 置 0;
- 异步执行业务逻辑:更新状态机、写库、通知 MES 或调度系统。
这里最重要的一点是 Ack 必须先回,业务后做。很多初学者把业务逻辑写在 Ack 之前,结果处理一慢,设备就超时重发了。重发本身不可怕,可怕的是 EAP 把同一条事件处理了两遍,导致状态机错乱或数据库重复写入。
解决重复处理的通用手段是幂等。你可以利用 SECS-II 消息头里的系统字节(System Bytes)作为唯一键,也可以自己维护“设备 ID + CEID + 时间戳”的 hash 表,处理前先查重。加上幂等之后,即使网络偶发抖动导致重发,处理逻辑也安全很多。
5.2 一条 Process Complete 事件背后的完整业务流
我用一条最常见的“工艺完成”事件来串一遍全流程。设备进入 RUNNING 时,会发 CEID = 2001 的事件,携带 Process Job ID 和 Recipe ID;EAP 收到后更新状态机为 RUNNING,记录开始时间,回 Ack。设备工艺结束,发 CEID = 2002 的事件,携带结束状态码和一批工艺数据;EAP 收到后先校验这个 Process Job 是否在跟踪列表中,然后把数据整理成统一的事件模型写入数据库,再根据业务规则通知 MES 或调度系统,触发下一批物料搬送或者生成完成记录。
如果工艺本身失败,设备通常还会同时发一条报警(S5F1)。EAP 不能孤立处理报警,要把报警和前后事件关联起来,结合上下文判断现场应该进入“自动恢复流程”还是“停线等待人工介入”。这就是事件链的意义所在。
5.3 断线重连后最容易踩的坑
EAP 编写时,很多同学只在启动的时候做一次订阅就完事了。设备断线重连后,如果设备的 Link 关系是易失的,那么所有订阅都会失效。典型现象是:网络恢复了,心跳也正常,但不管发生什么操作,主机都收不到任何 S6F11。排查半天链路,最后发现只是订阅关系没了。
解决办法是在连接建立成功的回调里,重新执行完整的订阅流程:先 S2F33 定义必要的 Report,再 S2F35 重新 Link 需要的事件。重连后的日志里,我会专门输出一句“当前已订阅 CEID 列表”,方便确认状态。这个小习惯在多次现场抢救中帮我省了不少时间。
6. 常见问题与排查技巧实录:事件调试速查表
| 问题现象 | 排查方向 | 处理方式 |
|---|---|---|
| 主机永远收不到某个 CEID 事件 | 事件是否已 Link;CEID 是否处于 Enabled | 重发 S2F35,确认订阅表中的 Link 状态 |
| S6F12 回非 0 | HCACK 非 0 说明设备不认该 CEID/RPTID | 核对 CEID 和 RPTID 是否定义无误,重新 S2F33 |
| 事件偶发丢失 | 检查 Ack 延时、HSMS 窗口是否过小 | 增大通信窗口,确认 Ack 处理足够快 |
| 事件重复上报 | 大概率是 Ack 超时导致的设备重发 | 在 EAP 侧加幂等,不要试图完全消除重发 |
| 事件延迟大 | 消息量大、处理串行阻塞 | 将大块数据改用 S6F13/S6F15,Ack 与业务异步化 |
| 某个事件上报异常高频 | 传感器抖动、设备去抖不充分 | 设备侧去抖,EAP 侧限频合并 |
| 断线重连后事件全部消失 | 订阅关系是易失的,断线后失效 | 在重连回调里重新执行订阅流程 |
| 事件数据解析出来全乱码 | 数据类型或字节序不对 | 对照 SECS 手册确认 VID 类型,用模拟器比对原始帧 |
我自己调试事件相关问题时还有个习惯,先搭一个纯软件环境,用事件模拟器把设备的 S6F11 报文灌给 EAP,或者用抓包工具先把真实设备的事件报文完整采集下来,再慢慢解析。这个“离线赛道”能帮你把解析逻辑、幂等逻辑、状态机逻辑都调到稳定,再上真机联调,能省下大量现场时间。
上真机之后,我也不会一次性把设备的所有事件全订阅下来,而是挑三个核心事件先跑通全链路:设备 Ready、Process Start、Process Complete。这三个事件能跑通,说明订阅流程、解析流程、Ack 流程、状态机更新流程都没问题,再逐步放开其他事件。这样即使出了问题,排查范围也被控制得很小。事件这个主题还有不少可以深挖的地方,比如报警体系和状态变量的分工配合,以及大数据量数据集在 GEM300 产线上的落地技巧,我放到后面的篇幅里慢慢聊。