简介:secs4j-master 是一套面向半导体设备自动化领域的 Java 版 SECS/GEM 协议实现库,适合从事设备通信、工厂自动化系统开发的工程师与学习者使用。它把 SECS-I、SECS-II 的物理层与应用层协议,以及 GEM 规范中的设备初始化、状态报告、命令控制、数据采集等交互流程,封装成可直接调用的类与方法,帮助开发者免去从零编写底层通信逻辑的繁琐。压缩包共 80 个文件,以 72 个 java 源码为主体,辅以 4 个 txt 说明、3 个 xml 配置和 1 个 md 文档,整体约 109KB,结构紧凑、便于阅读。内容涵盖 SECS 连接管理、消息构建与解析、GEM 接口、事件订阅发布、数据交换模型、异常处理及单元与集成测试用例,可帮助读者快速理解协议分层与消息结构,搭建设备与主机系统之间的通信桥梁。目前已有 2500 人学习下载,适合作为协议入门与工程集成的参考实现。
1. 从一台封测设备的联机现场说起:secs4j 到底能省掉哪段活
凌晨两点,封测车间一台新到的测试机要跟 EAP 对接,主机侧发 S1F13 请求建立通信,设备回了个 S1F14,然后卡在 S1F1/S1F2 的在线确认上不动了。现场最怕这种局面:协议栈是设备厂商封的,日志只有一行 "communication error",你连报文长什么样都看不到。这时候如果手里有一套能自己收发、自己解析的 SECS/GEM 实现,问题定位会快一个数量级——secs4j-master 就是干这个的。
它是 SECS/GEM 协议的 Java 实现,把半导体设备与主机之间那套通信逻辑用纯 Java 类库的方式摊开给你:连接管理、消息构建与解析、GEM 状态机、事件上报、异常处理都在里面。适合三类人:做 EAP/主机侧对接的后端、给设备写 SECS 驱动的嵌入式或上位机工程师、以及需要拿一套可读源码去理解 SECS-II 报文结构的学习者。它不替代设备厂商的协议栈,但能让你在联调、模拟、抓包分析时有完全可控的一端。
2. SECS-II 报文结构与 secs4j 的消息模型:先看懂再动手
2.1 为什么 SECS-II 的报文不能当普通字节流处理
SECS-II 消息不是「一段 JSON 加个长度头」那么简单。一条消息由 10 字节的 Message Header 加若干 Data Item 组成,Header 里塞了 Session ID、Stream、Function、W-Bit、System Bytes 这些字段,Data Item 则是带格式码和长度字节的嵌套结构,能套 List,也能放二进制、布尔、各种位宽的整数和浮点。你如果拿普通 socket 读字节的方式去解,长度字节的位数规则(1/2/3 字节长度域)和 List 的递归嵌套会立刻把你绕进去。
secs4j 的价值就在于它把这套结构抽象成了对象。常见做法是:Header 对应一个消息头对象,Data Item 对应可嵌套的数据项对象,Stream/Function 用常量或枚举表达。你构造消息时是拼对象,发送时由库负责序列化成字节;接收时反过来,字节流进库,出来的是可遍历的数据结构。理解这一层,后面所有调试才有抓手。
2.2 用 secs4j 构造并发送一条 S1F1 的完整步骤
下面这段是典型的「主动发一条 Are You There」的写法,也是联调时最常用的探活手段。代码按 secs4j 的常见 API 风格组织,具体类名以你拿到的源码为准,逻辑是通用的。
// 1. 建立到设备/主机的连接(SECS-I 走串口,HSMS 走 TCP) SecsConnection conn = new SecsConnection("192.168.1.50", 5000); conn.connect(); // 2. 构造 S1F1 消息体,W-Bit 置 1 表示需要对方回复 SecsMessage s1f1 = new SecsMessage(1, 1, true); // 消息体为空,S1F1 本身不带 Data Item // 3. 发送并等待 S1F2 回复,超时时间按现场网络情况设 SecsMessage reply = conn.send(s1f1, 5000); // 4. 校验回复的 Stream/Function 是否符合预期 if (reply.getStream() == 1 && reply.getFunction() == 2) { System.out.println("设备在线,通信正常"); } else { System.out.println("收到非预期回复: S" + reply.getStream() + "F" + reply.getFunction()); }逻辑说明:第一步的connect()在 HSMS 下是 TCP 三次握手加 HSMS 的 Select 流程,在 SECS-I 下是串口打开加握手,两种物理层在 secs4j 里通常由不同实现类承担,选错实现类是最常见的翻车点。第二步的 W-Bit 是关键参数,置 true 才会等回复,置 false 就是单向通知,很多「发了没反应」的问题其实是 W-Bit 设错了。第三步的超时值不要照抄 5000,产线网络抖动大时可以放到 10000 以上,但也不能无限等,否则线程会挂死。第四步的校验别省,SECS 协议里设备回错 Function 是常事,不校验就会把错误回复当成功。
2.3 Data Item 的构造:以 S1F3 请求状态变量为例
S1F3 是主机向设备要指定 SVID 的当前值,请求体里带一串 SVID,回复 S1F4 里带对应的值。构造请求时要用到 List 和整型数据项:
// 构造 S1F3 请求,Data Item 是一个 List,里面放要查询的 SVID SecsMessage s1f3 = new SecsMessage(1, 3, true); // 外层 List SecsDataItem list = SecsDataItem.list(); // 往 List 里塞三个 SVID,格式码用 U4(无符号 4 字节整数) list.add(SecsDataItem.u4(1001)); list.add(SecsDataItem.u4(1002)); list.add(SecsDataItem.u4(1003)); s1f3.setDataItem(list); SecsMessage s1f4 = conn.send(s1f3, 5000); // 遍历回复里的值,注意顺序与请求的 SVID 一一对应 SecsDataItem values = s1f4.getDataItem(); for (SecsDataItem v : values.getList()) { System.out.println("SVID 值: " + v.getNumber()); }参数说明:u4对应 SECS-II 的 U4 格式码,SVID 在多数设备上就是无符号整数,但少数设备用 U2 或 U1,格式码对不上会直接解析失败,这是血泪经验里排前三的坑。getList()拿到的顺序必须和请求顺序一致,GEM 规范要求如此,但个别设备实现会乱序,联调时如果发现值对不上号,先怀疑顺序而不是怀疑自己算错。
3. GEM 状态机与事件上报:把设备行为跑通
3.1 GEM 的通信状态与控制状态到底在管什么
GEM 在 SECS 之上加了一层状态机,最核心的是通信状态(Communication State)和控制状态(Control State)。通信状态管的是「设备跟主机有没有建立通信」,控制状态管的是「谁在控制设备」——是本地操作员还是远程主机。这两个状态不是摆设,设备初始化序列、在线确认、远程命令能不能执行,全看它们当前处在哪个状态。
secs4j 里通常会有对应的状态管理类或字段,你在实现设备侧逻辑时,必须让状态迁移跟着报文走:收到 S1F13 且同意建立通信,通信状态才进到 Communicating;收到 S1F17 请求在线,控制状态才进到 Online Remote。如果状态没跟着报文更新,后面主机发 S2F41 远程命令,设备会以「不在远程模式」为由拒绝,而你在日志里只看到一句拒绝,根本不知道是状态机没走对。
3.2 实现一个 Collection Event 上报的落地写法
GEM 里设备主动上报数据靠 Collection Event,也就是 CE。设备侧要定义 CEID,绑定报告(Report)和变量(VID),事件触发时发 S6F11 把报告推给主机。下面是一个简化的上报流程:
// 1. 定义报告:把若干 VID 绑到一个 RPTID 上 Report report = new Report(2001); // RPTID = 2001 report.addVid(3001); // 温度 report.addVid(3002); // 压力 report.addVid(3003); // 状态码 // 2. 定义事件:CEID 绑定报告 CollectionEvent ce = new CollectionEvent(5001); // CEID = 5001 ce.addReport(report); // 3. 事件触发时构造 S6F11 并发送 SecsMessage s6f11 = new SecsMessage(6, 11, true); SecsDataItem data = SecsDataItem.list(); data.add(SecsDataItem.u4(5001)); // CEID data.add(SecsDataItem.u4(1)); // 报告数量 data.add(SecsDataItem.u4(2001)); // RPTID data.add(SecsDataItem.list() // 报告值列表 .add(SecsDataItem.f8(25.3)) // 温度,F8 浮点 .add(SecsDataItem.f8(101.2)) // 压力 .add(SecsDataItem.u4(0))); // 状态码 s6f11.setDataItem(data); conn.send(s6f11, 5000);逻辑说明:S6F11 的结构是 CEID + 报告列表,每个报告又是 RPTID + 值列表,嵌套层级容易写错。第三步里f8对应 8 字节浮点,温度和压力用浮点是常见做法,但有些老设备用整数放大十倍传,格式码和量纲都要跟设备文档对齐。发送时 W-Bit 一般置 true,主机收到会回 S6F12 确认,如果主机没回,设备侧要有重发或告警机制,否则数据就静默丢了。
3.3 主机侧如何订阅和响应设备事件
主机侧的逻辑是反过来的:收到 S6F11 后解析 CEID 和报告值,做业务处理,然后回 S6F12。secs4j 通常提供消息监听或回调机制,你注册一个处理器,按 Stream/Function 分发。常见做法是维护一张 CEID 到业务处理函数的映射表,收到事件查表调用,查不到就记日志告警——因为设备可能上报了你没定义过的 CEID,这在设备固件升级后很常见。
响应侧要注意的是 S6F12 的 System Bytes 必须和收到的 S6F11 一致,这是 SECS 协议的基本要求,secs4j 一般会自动处理,但如果你自己拼回复消息,漏了这一步主机会认为回复不匹配而超时重发。
4. 避坑与排查:联调现场最容易翻车的五个点
4.1 现象:连接建立成功但收不到任何回复
原因:HSMS 的 Select 流程没走完,或者 Session ID 不匹配。HSMS 在 TCP 之上还有一层会话层,Select.req/Select.rsp 没成功,后面的数据报文设备直接丢弃。另外 Session ID 在 Header 里,设备侧如果配了固定 Session ID,你发 0 或别的值会被忽略。
解决:先确认 HSMS 状态机进到 Selected 状态,再发业务报文。Session ID 跟设备文档对齐,多数设备用 0 或 1,别自己拍脑袋。抓包看 Select 流程有没有走完,比看应用日志快得多。
4.2 现象:S1F3 发出去,S1F4 回来但值全是 0 或解析异常
原因:SVID 的格式码不匹配。请求里用 U4,设备内部可能按 U2 存,回复时格式码和长度对不上,解析就出乱码或 0。另一种是 SVID 根本不存在,设备回了空 List 或错误码。
解决:先拿设备文档核对每个 SVID 的格式码,别假设都是 U4。回复解析异常时把原始字节打出来看格式码字节,对照 SECS-II 格式码表确认。SVID 不存在的情况,GEM 规范里设备应该回 S1F4 带空值或特定错误,但实现质量参差,得实测。
4.3 现象:S6F11 上报后主机没反应
原因:W-Bit 没置,或者主机侧没注册对应 CEID 的处理器,或者报告结构跟主机预期不一致。主机侧如果按固定模板解析,你多塞一个 VID 就会解析错位。
解决:确认 S6F11 的 W-Bit 为 true,确认主机侧 CEID 映射表里有这个事件,报告里的 VID 顺序和数量跟主机约定的一致。联调阶段建议先用手工构造的最小报告跑通,再逐步加字段。
4.4 现象:远程命令 S2F41 被设备拒绝
原因:控制状态不在 Online Remote。设备可能还在 Local 或 Online Local 状态,这时候远程命令一律拒绝。也可能是命令的 RCMD 或参数格式跟设备定义的不一致。
解决:先发 S1F17 请求在线,等设备回 S1F18 确认后再发 S2F41。RCMD 和参数列表严格按设备文档来,参数名大小写、数据类型都要对。拒绝原因设备一般会在 S2F42 里带错误码,别只看「拒绝」两个字。
4.5 现象:长时间运行后连接静默断开
原因:HSMS 有 T3 超时和心跳机制,长时间没数据交互,设备侧可能主动断链。或者网络中间有设备回收了空闲 TCP 连接。
解决:实现定时的心跳或状态查询,比如每隔一段时间发 S1F1 探活。secs4j 里可以起一个定时任务做这件事。断链后要有重连逻辑,重连后状态机要重新走一遍初始化序列,不能直接接着发业务报文。
5. 从源码到产线:把 secs4j 用成自己的调试利器
把 secs4j 跑起来只是第一步,真正让它产生价值的是拿它当调试和验证工具。我一般的做法是:在联调前先用 secs4j 写一个最小主机端,能发 S1F1、S1F13、S1F3、S2F41 这几条,再写一个最小设备端,能回 S1F2、S1F14、S1F4、S2F42,两边对着跑一遍。这一步能把协议层的理解漏洞全暴露出来,比直接上产线试错成本低得多。
进阶用法上,可以基于 secs4j 做一个报文录制回放工具。把现场抓到的报文按 Stream/Function 和 System Bytes 存下来,回放时按时间戳重发,用来复现偶发问题。SECS 的偶发问题很多是时序相关的,比如设备在某个状态下才回某个错误码,靠人工复现很难,回放能稳定触发。
验证方法上,建议对照 SEMI 标准的 E 系列文档逐条核对。E5 管 SECS-II 消息结构,E30 管 GEM 行为,E37 管 HSMS。secs4j 的源码里如果某个行为的实现跟标准对不上,以标准为准,但也要考虑设备厂商的实际实现——产线上「符合标准」和「能跑通」经常是两回事,以能跑通为准,标准用来解释为什么跑不通。
一个具体技巧:调试时把 secs4j 的日志级别开到最细,把收发的原始字节和解析后的对象都打出来。SECS 的问题十有八九出在格式码、长度域、W-Bit、System Bytes 这四个地方,原始字节一摆出来,对照格式码表,问题基本无处遁形。我习惯在send和receive两个入口各加一行十六进制打印,联调时省下的时间远超这点日志开销。
从那以后我每次接手新的 SECS/GEM 对接,都强制先用 secs4j 搭一对最小收发端跑通再碰产线设备,这个习惯帮我避掉了至少一半的现场返工。希望帮到你。
本文还有配套的精品资源,点击获取