简介面向半导体设备自动化与工厂系统集成的 Java 开发者secs4j-master 是一份可直接参考的 SECS/GEM 协议实现源码解决设备与主机系统之间的通信与远程监控问题。内容包括高速 SECS-I 与低速 SECS-II 的消息处理、GEM 的设备初始化、状态报告、命令响应及数据采集可帮助工程师用 Java 快速搭建设备通信桥梁。包体共 80 个文件以 72 个 Java 源码为主另有 4 个 txt 说明、3 个 XML 配置和 1 个 Markdown 文档压缩包约 109KB。Java 代码覆盖连接管理、消息构建解析、事件订阅发布、异常处理与测试用例XML 用于组件配置文本和 Markdown 辅助理解协议背景与项目结构。压缩包目录结构清晰源码与文档分层放置便于按模块查阅。目前已有 2502 人浏览学习适合希望集成 SECS/GEM 或深入研究协议实现细节的半导体行业开发者复用现成逻辑可明显缩短自动化系统的开发周期。1. SECS/GEM源码落地别指望开箱即用拿到的是能改的骨架做半导体设备上位机这几年圈内有个共识SECS/GEM源码看着标准公开、规范文档一大堆真正能拿来跑业务实现的却没几个。secs4j-master就是Java圈子里少见的、能直接打开源码改的SECS/GEM实现。它把SECS-I/SECS-II的消息编解码、HSMS连接、GEM里常用的设备初始化、事件上报、远程命令都封装成了普通Java类解决了从底层字节流到业务消息之间那段黑匣子。适合正在接设备、写主机端、或者要把老网关换掉的工程师。但别指望它像商业库一样插上就跑真正的坑全在连接配置和消息结构上这篇文章就按我拆完的路径写给你。2. 先看透secs4j的协议骨架消息从哪来、字节往哪去解压secs4j-master之后别急着起服务先花半小时搞清楚它在协议栈里站在哪一层。很多人会把SECS-I和SECS-II混成一件事调了半天串口波特率真正的问题却出在消息层的长度解析上。新产线设备联网基本都走HSMS串口SECS-I只存在于老旧设备的备件库里这个判断决定了你后面所有配置的侧重点。2.1 协议分层secs4j守的是哪座城门SECS/GEM并不是一个单层协议而是一整条通信栈。物理层SECS-I基于RS-232串口传输层HSMS基于TCP/IP消息层SECS-II负责结构化的二进制消息应用层GEM定义的是设备与主机之间的行为模型比如设备初始化、报警上报、状态转换。secs4j的核心工作量集中在HSMS传输层和SECS-II消息层GEM应用层只提供了接口雏形状态机部分需要你按现场设备行为去补。选型时的判断标准很简单如果新采购的设备基本只有HSMS选项TCP端口通常要跟设备工程师确认常见值是5000或5001如果老产线还在用串口那才需要考虑SECS-I。我一般建议新项目直接忽略串口路径哪怕secs4j里能找到串口相关类也别把主要精力放在这上面。现在的设备网关绝大多数提供了以太网口跑HSMS比折腾RS-232流控要省心一个量级。2.2 依赖与文档pom.xml和doc目录标出的真实边界解压后的pom.xml能看出这个库的依赖很少意味着裁剪和改造的空间比较大。doc目录里放着协议说明和示例建议先翻这份文档再碰代码它比README更贴近实现细节。依赖少的另一个好处是后续打包进产线监控程序时不容易跟日志、序列化框架冲突。先做一次本地构建确认源码在你这台机器上能完整编译mvn -f pom.xml clean package -DskipTests -Dmaven.javadoc.skiptrue这条命令跳过了测试和javadoc生成。跳过测试不是因为测试没用而是这个库的测试用例依赖本机空闲端口和可能的串口环境在你自己电脑上跑会有误报。构建完成后target目录下会生成jar包后续有两种引入方式一种是直接把这个jar安装到本地Maven仓库另一种是干脆把源码模块作为子模块挂进你的工程。改源码场景下我更推荐第二种因为排错时可以直接跳进库内部看报文解析过程。2.3 消息对象四块拼图连接、消息、数据项与字节流secs4j里最核心的四个对象是连接管理、消息对象、数据项和消息编解码器。连接管理负责HSMS的建立和心跳消息对象持有一个结构化的SECS-II消息数据项封装了布尔、整数、浮点、字符串和List这些SECS数据编解码器把对象和字节流互相转换。SECS-II消息本质上是嵌套的List数据项类型包含B、U1/U2/U4/U8、I1/I2/I4/I8、F4/F8、A和L。调试时最常犯的错就是数据项类型对不上设备端声明U4你发了I4对方直接按Incorrect Data丢弃。明白这四块拼图之后再去看业务层的初始化序列和事件上报思路就顺了。消息层与传输层分离的设计也让单元测试更好写不需要真连设备就能验证SML消息结构。3. 把secs4j接到设备从Maven依赖到S1F13/S6F11这一章是全文真正能动手的部分。先解决库的引入方式再建立第一条HSMS连接然后按顺序把设备初始化S1F13/S1F14和事件上报S6F11/S6F12跑通。代码里API名称以你解压后源码中的实际类名为准我按包内常见结构给你示意。3.1 三种引入方式与第一条HSMS连接如果你只是想在现有工程里快速验证协议能力第一种方式是直接把刚才构建出的jar装进本地仓库mvn install:install-file \ -Dfiletarget/secs4j-1.0.0.jar \ -DgroupIdlocal.secs4j \ -DartifactIdsecs4j-core \ -Dversion1.0.0 \ -Dpackagingjar这个命令把jar写入本地Maven仓库groupId和version可以自己定义只要你自己的pom里引用同一个坐标就行。它的问题是后续想改库内部代码每次都要重新install。如果你预感到会踩消息编解码的坑建议直接建一个多模块工程把secs4j源码目录作为一个module引进来改动即时生效。连接设备的代码常见做法是这样import secs4j.hsms.HsmsConnection; HsmsConnection conn new HsmsConnection(); conn.setActive(true); // true表示主动建连主机侧连设备 conn.setHostIp(192.168.1.20); // 设备网口IP conn.setPort(5000); // 与设备工程师确认HSMS端口 conn.connect();setActive这个参数很容易理解。主机上位机作为客户端主动去连接设备的被动服务端就设成true如果你做的是设备端软件需要等待主机连接就设成false。后一种场景在国产设备开发里很常见有些设备厂商要你帮他做设备端GEM程序此时整条会话由主机侧发起。连接失败时先确认IP和端口通不通再查两边速度和流控参数别一上来就怀疑协议栈。3.2 设备初始化S1F13/S1F14的握手顺序设备上电后主机上位机先要发S1F13建立通信。这个动作告诉设备“我要接管你了”设备收到后回S1F14表示接受。很多新手会把S1F13当成普通业务消息随便发结果设备不搭理。用secs4j构建S1F13的方式示意如下import secs4j.SecsMessage; import secs4j.SecsMessageBuilder; SecsMessage s1f13 new SecsMessageBuilder() .setStream(1) .setFunction(13) .addList() .addListItem().addAscii(SECS4J-DEMO) // MDLN设备型号 .addListItem().addAscii(1.0) // SOFTREV软件版本 .build(); SecsMessage s1f14 conn.send(s1f13, 10_000);setStream和setFunction决定了消息头里的Stream/Function字节S1F13就是Stream 1 Function 13。addList开始构建消息体MDLN和SOFTREV是两个最基础的字段。send方法的第二个参数是超时毫秒数这里给10秒调试期别用默认的45秒不然一条消息卡住要等很久才报错。设备返回的S1F14里最重要的是COMMACK字段值为0表示建立通信成功非0时要看设备厂商日志基本逃不出“设备型号不匹配”和“设备拒绝当前主机”两类。S1F14的SystemBytes必须回显S1F13里的值主机才能把两条消息匹配到同一个事务。如果你在这步看到的响应是“transaction not found”优先查系统字节。3.3 事件上报S6F11与S6F12的应答逻辑设备初始化成功之后状态变化、报警、生产数据一般都通过事件消息上报。最常用的是S6F11事件数据就装在消息体里设备侧收S6F12作为确认。代码示意SecsMessage event new SecsMessageBuilder() .setStream(6) .setFunction(11) .addList() .addListItem().addInt(2, 10) // DATA_ID按厂商约定 .addListItem().addInt(2, 1001) // CEID事件ID .addListItem().addAscii(STAGE-1) // DTLS事件描述 .build(); SecsMessage ack conn.send(event, 10_000); if (ack.getFunction() 12) { // S6F12中ACK为0表示主机已接收事件 }DATA_ID用来区分哪条数据流CEID标示是什么事件DTLS是事件附带的具体内容。现场经验是CEID必须在设备厂商文档里提前约定好两边不一致时设备的S6F12也会回正常ACK但主机侧入库逻辑会丢掉数据。S6F11可以走和普通消息同一条连接不必额外开端口。高频率事件上报时不要在收到S6F12之前就发下一条否则会撑爆接收队列对端会因为你违反“一次一答”的规则断开连接。4. 常见问题与避坑五个让产线中断的细节这一章是血泪经验。下面五条几乎每个新接SECS/GEM的工程师都会遇到至少两条按“现象、原因、解决”给你列出排障路径。4.1 现象S1F13发出去石沉大海直到超时才报错原因最常见的是S1F14的SystemBytes没有按请求回显主机侧的事务匹配机制找不到对应事务。另一种可能是设备侧还没进入允许建立通信的状态你发得早了一点。解决方式把超时调到5到10秒方便调试同时打开库的报文日志确认S1F13头的SystemBytes和S1F14响应里的值是否一致。很多场景下对端回的是S9F9消息而不是S1F14说明它已经不认得你的消息格式了。这时候先不要改业务代码直接抓包对比标准消息头结构。4.2 现象连接建立后大约30秒被对端断开设备端一直没收到业务消息时会按HSMS的Linktest机制探活默认空载时间比较短。如果应用层没有周期性的消息来往对端就认为这个连接已经死了主动断开。解决方式是在代码里加一个定时探活任务常见做法是每10到15秒发一次S1F1Are You There并等待S1F2回复。不要把探活消息和业务事件混在一起探活是独立于事务的轻量消息。打开T7参数把它调到10秒左右让库在链路空闲时主动维持会话能减少大半的断连投诉。4.3 现象SML文件解析对不上总报Incorrect DataSML里字符串长度和实际字节数对不上是最常见的雷区。SECS-II里的A类型字符串是定长的长度必须和声明一致末尾空格也算合法字节。很多工程师在文本编辑器里看到行尾空格被自动删掉就成了“看着对、发出去错”的情况。嵌套List也会导致结构错位L的层数多一层或少一层设备端解析时根本读不到预期字段。解决时别在代码层面反复猜直接把报文和抓包数据逐字节对照。用TcpDump抓下HSMS数据帧把十六进制和SML定义放一起过一遍。4.4 现象设备上报的ASCII字段乱码日志里全是问号原因是源码里没有显式指定字符集不同操作系统默认字符集不一致。某台Windows开发机上是默认ANSI到了Linux服务器上变成UTF-8设备端按ISO-8859-1发来的字符串就全乱了。解决方式所有字符串编解码统一指定字符集ISO-8859-1。SECS-II标准里ASCII就是这个编码别用平台的默认charset。如果现场有中文需求要跟设备厂商确认编码规则不要自己脑补成UTF-8。4.5 现象走串口SECS-I始终连不上换网关后又正常secs4j源码里的SECS-I支持基本只是协议层的类框架串口侧还需要你自己处理波特率、数据位、停止位和硬件流控任何一个参数不对都时通时断。加上现在现场基本找不到原生串口设备用的都是串口服务器转网络中间多了两层转换更难排查。我的建议是直接放弃SECS-I路线改用串口服务器做协议转换把SECS-I的字节流转成网络包再用HSMS连接去对接。这样既能保留老设备的协议特性又不用在串口参数上浪费大半天。5. 改造进产线的四个动作依赖裁剪、超时参数、S2F41与日志保底拉到源码只是第一步真正让它在产线上稳定跑起来还要做四件事。这四件事的顺序不要乱先改依赖再调参数再谈业务消息设计最后把日志和抓包手段准备好。5.1 裁剪依赖并统一字符集secs4j的pom依赖虽然少但日志框架还是需要关注的。如果你的上位机程序用的是其他日志实现最好在引入时排除掉冲突项否则运行期会出现ClassNotFound之类的问题。依赖裁剪用Maven的exclusion配置就行。字符集的统一我在4.4里说过这里再强调一次把编解码字符集锁死在配置项里不要让代码继承系统属性。5.2 调优连接级参数T3、T5、T6、T7怎么设HSMS协议有一套标准超时参数secs4j源码里未必直接叫这个名字但你要能对应上。调试期和生产期的取值建议如下表参数 | 含义 | 调试期建议值 | 生产期建议值 T3 | 回复超时 | 5至10秒 | 30至45秒 T5 | 连接分离延迟 | 5秒 | 10秒 T6 | 控制消息超时 | 5秒 | 5秒 T7 | 空载探活间隔 | 15秒 | 10秒调试期把T3调短是为了让出错消息快速暴露不至于一条坏消息占住整个调试时间。T7调短则是为了让链路保持活跃减少像4.2那样被对端断开的情况。生产期再把T3放宽到30秒以上避免设备忙时偶发延迟被误判为超时。这些参数改完一定要两边设备都确认主机侧和设备侧取值不一致时同一套连机会出现“这边认为超时、那边还在处理”的中间状态。5.3 设计主机侧的S2F41远程命令应答顺序S2F41是主机远程命令请求典型场景是主机侧要求设备切换配方、执行复位。处理顺序非常讲究先立即回S2F42表示已收到再去执行动作执行完之后再通过事件消息上报结果。千万不要把执行动作放在应答之前主机侧等不到S2F42会直接判定命令超时然后对你的设备侧程序失去信任。// 收到S2F41后的正确处理顺序先应答再执行 SecsMessage s2f41 onMessage; SecsMessage ack new SecsMessageBuilder() .setStream(2) .setFunction(42) .addListItem().addInt(1, 0) // HCACK0 表示已接收 .build(); conn.send(ack, 5_000); executeRcmd(s2f41);HCACK的值按设备厂商约定填0是通用“已接收”。send的5秒超时确保ACK快速出去。执行完远程命令后发一条S6F11事件把远程命令的标识和完成状态带上主机才能把整个命令周期闭环。这个顺序如果写反轻则命令超时重发重则设备状态和主机记录不一致后面整段产线数据都对不上。5.4 日志与现场保底抓包是最后一道后悔药不管是调试期还是上线初期务必把库的日志级别开到DEBUG。secs4j内部在收发消息时会打印消息头信息这比你在业务里加打印点要全得多。日志字段至少要包含方向、Stream/Function、SystemBytes和消息长度。有了这四个字段大部分问题都能定位到是发送端拼错还是接收端解析错。如果日志还不够直接在网卡上抓包tcpdump -i eth0 -nn -s0 -A port 5000 -w secs_trace.pcap抓下来的pcap文件用Wireshark看但Wireshark默认可能不识别HSMS端口需要手动设置端口为HSMS协议或者在Decode As里指定。抓包能让你看到字节层最真实的情况避免被库内部日志的“二次加工”误导。有一回我在现场查了三个小时的消息解析问题最后就是抓包发现设备端把Length字段算少了两位这是任何日志层面都看不出来的。6. 验证方法用本地模拟对端把S1F13跑通一次6.1 一个只打印hex的模拟设备在上产线之前我养成了先本地起一个模拟对端的习惯。用Python写一个极简的TCP服务端监听HSMS端口收到数据就打印成十六进制不做任何协议响应。这个模拟器不验证返回逻辑只验证一个东西你的secs4j客户端发送出去的字节结构对不对。import socket import struct srv socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) srv.bind((127.0.0.1, 4000)) srv.listen(1) conn, _ srv.accept() print(client connected:, conn.getpeername()) while True: hdr conn.recv(4) if len(hdr) 4: break session, length struct.unpack(HH, hdr) rest b while len(rest) length - 2: chunk conn.recv(length - 2 - len(rest)) if not chunk: break rest chunk frame hdr rest print(, frame.hex())这段代码先读4字节解析出SessionID和Length再把剩余字节收完。Length字段在各家实现里有细微差别有的算上了10字节消息头有的只算正文所以这个模拟器里用了length-2的收法你实际跑的时候要对照报文修正一版。6.2 三个必须核对的字节级细节跑通之后至少核对三点。第一消息头Stream/Function是不是01 0D这是S1F13的标准编码第二SystemBytes每次会话是否不同并且S1F14回显时保持一致第三Length字段值与实际字节数是否匹配。这三项全对才说明你的库发送链路是可信的后续调试业务逻辑才有意义。早年在现场我因为没做这一步直接上了产线结果把一套新设备的状态机搞乱了最后发现是最初把S1F13当成普通业务消息在发。从那以后每次接新设备或改消息结构我都强制先跑一遍这个最小验证。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?