简介SECS半导体设备通信标准开源实现库面向半导体设备自动化领域的开发者与研究人员用于简化设备控制器与生产线工具之间的数据交换与通信开发。资源包共11个文件约39KB以test测试脚本、tm模块文件、tcl脚本及c源码为主另含readme说明与copying3许可文件覆盖SECS消息处理、接口驱动、示例教程、API文档、测试工具与社区支持等模块可帮助开发者快速搭建I型、II型及HSMS协议通信链路。已有1501人学习下载适合希望减少从零实现协议成本、专注应用逻辑的中高级开发者参考也可作为小型企业与研究机构参与半导体设备开发的入门素材。1. secs-开源把半导体设备通信协议从黑匣子里拽出来产线上那台刻蚀机又报通信超时了设备端日志只有一行S1F1 WHost 端显示T3 timeout。厂商工程师说“协议栈是黑盒得换板卡”报价六位数。这种场景在半导体、面板、光伏设备圈子里太常见了——SECS/GEM 这套协议被少数几家方案商把持设备厂想自己改一行超时参数都无从下手。secs-开源这个方向要解决的就是这件事把 SEMI E4/E5/E30/E37 这套设备通信标准用开源方式实现出来让设备工程师能自己读源码、改参数、抓报文。它适合三类人做设备端通信的嵌入式工程师、做 EAP/MES 对接的 Host 端开发者、以及被私有协议绑死想换方案的技术负责人。下面按“协议怎么立住 → 最小系统怎么跑 → 坑在哪 → 怎么验证”的顺序拆开讲。2. SECS/GEM 协议栈的选型与最小可跑架构2.1 为什么开源实现能替代商业协议栈商业 SECS/GEM 栈的收费模式通常是按设备台数授权单台几千到几万不等而且源码不开放遇到非标场景只能等厂商排期。开源实现的核心价值不在于“免费”而在于可审计和可裁剪。半导体设备通信的本质是三层底层是 HSMSSEMI E37/E37.1负责 TCP 连接管理和会话状态机中间层是 SECS-IISEMI E5负责消息编码解码上层是 GEMSEMI E30定义标准状态模型和事件报告机制。商业栈把这三层打包成动态库开源实现则把每一层拆成独立模块你可以只替换 HSMS 层来适配非标传输或者只改 GEM 层的状态机来匹配自家设备的工艺流程。选型时要盯住三个指标状态机完整性、消息覆盖度、日志可追溯性。状态机完整性看它是否实现了 HSMS 的 Not Connected / Connected / Selected 三态迁移以及 GEM 的 Control State 模型Equipment Offline / Host Offline / Online Local / Online Remote。消息覆盖度看它支持多少条标准 SECS-II 消息至少要有 S1F1-S1F14、S2F13-S2F42、S5F1-S5F17、S6F1-S6F19 这些核心集。日志可追溯性看它能不能把每条报文的原始字节流和解析后的结构体同时打出来——这是后面排错的命根子。2.2 最小可跑系统的四步搭建先明确目标让一台模拟设备通过 HSMS 连上 Host完成 S1F13/S1F14 建立通信再发一条 S6F11 事件报告。以下用 Python 生态里常见的开源 secs 库举例其他语言实现逻辑一致。第一步装依赖并确认版本。开源 secs 库通常依赖twisted或asyncio做异步 IO先建虚拟环境隔离。python3 -m venv secs_env source secs_env/bin/activate pip install secsgem twisted python -c import secsgem; print(secsgem.__version__)逻辑说明虚拟环境避免和系统 Python 冲突secsgem是常见开源实现之一twisted提供 reactor 事件循环。参数说明如果公司内网有 PyPI 镜像加-i指向内部源否则装包可能超时。第二步写设备端配置。HSMS 连接需要本端 IP、端口、对端 IP、端口、设备 ID、会话 ID。# equipment_config.py import secsgem.hsms import secsgem.secs settings secsgem.hsms.HsmsSettings( address0.0.0.0, # 本端监听所有网卡 port5000, # 本端 HSMS 端口 connect_to192.168.1.100, # Host 端 IP connect_port5000, # Host 端端口 device_id0, # 设备 IDHost 侧要对应 session_id0, # 会话 ID通常与 device_id 一致 activeTrue, # True 表示设备主动连 Host timeout45 # T3 回复超时单位秒 )逻辑说明activeTrue是设备主动发起 TCP 连接半导体产线里设备端主动连 Host 更常见因为 Host 通常固定 IP。参数说明timeout45对应 SEMI E37 的 T3 定时器默认 45 秒如果 Host 处理慢可以调到 60但别超过 120否则 Host 侧 T3 先超时。device_id和session_id必须和 Host 配置一致不一致会卡在 Select 阶段。第三步注册 GEM 状态模型和事件。GEM 要求设备维护一套标准状态变量至少要有 ControlState、EventsEnabled、AlarmsEnabled 这几个。# gem_model.py from secsgem.gem import GemEquipmentHandler class MyEquipment(GemEquipmentHandler): def __init__(self, settings): super().__init__(settings) # 注册标准状态变量 self.register_state_variable(ControlState, U1, 1) self.register_state_variable(EventsEnabled, BOOLEAN, True) # 注册自定义事件 self.register_event(PROCESS_START, 1001) self.register_event(PROCESS_END, 1002) def on_process_start(self): # 触发事件报告携带两个数据变量 self.trigger_event(PROCESS_START, { LOTID: LOT20250101, RECIPE: ETCH_01 })逻辑说明register_state_variable把设备状态暴露给 Host 查询register_event定义事件 IDtrigger_event在工艺开始时主动上报。参数说明事件 ID 1001/1002 是自定义区间标准事件 ID 在 1-999自定义从 1000 开始避免冲突。数据变量名要和 Host 侧 EAP 配置的别名一致否则 Host 解析不到。第四步启动并抓第一条报文。启动后看日志里有没有S1F13发出和S1F14收到。python -c from equipment_config import settings from gem_model import MyEquipment eq MyEquipment(settings) eq.enable() eq.start() 逻辑说明enable()激活 HSMS 状态机start()启动 reactor 事件循环。参数说明如果日志停在HSMS Connecting检查 Host 防火墙和端口如果停在HSMS Selected之前检查 session_id 是否匹配。2.3 消息编码解码的实操要点SECS-II 消息是二进制格式每个消息项有格式字节、长度字节、数据体。开源实现通常提供SecsStreamFunction基类你只需要定义消息号和数据结构。from secsgem.secs import SecsStreamFunction, SecsVarU4, SecsVarList class S6F11(SecsStreamFunction): _stream 6 _function 11 _data_format [ SecsVarU4(DATAID), SecsVarU4(CEID), SecsVarList(REPORT, [ SecsVarU4(RPTID), SecsVarList(V, [ SecsVarU4(VID), SecsVarU4(VALUE) ]) ]) ]逻辑说明_data_format定义了 S6F11 的嵌套结构SecsVarList表示列表项SecsVarU4表示 4 字节无符号整数。参数说明DATAID默认填 0CEID是触发的事件 IDRPTID是报告 IDVID是变量 ID。编码时库会自动算长度字节解码时按同样结构反序列化。如果 Host 报Invalid format八成是_data_format和 Host 侧定义对不上用 Wireshark 抓包对比字节流最快。3. 从零跑通 HSMS 连接状态机、定时器与日志排查3.1 HSMS 三态迁移的触发条件HSMS 连接不是简单的 TCP 连上就能发消息它有一套独立于 TCP 的状态机。Not Connected是初始态TCP 连接建立后进入Connected此时只能发 Select 请求。收到 Select 响应后进入Selected才能收发 SECS-II 消息。任何一方发 Deselect 或 TCP 断开都会退回上一态。设备端主动连接时流程是TCP SYN → TCP ACK → 发 Select.req → 收 Select.rsp → Selected。如果卡在 Connected 超过 T6默认 5 秒说明 Select 没被响应。常见原因是 Host 侧 session_id 和 device_id 不匹配或者 Host 的 HSMS 端口没监听。排查方法是在设备端日志里找Select Request和Select Response两行没有 Response 就是 Host 没回。3.2 T3/T5/T6/T7/T8 五个定时器的实际取值SEMI E37 定义了五个关键定时器开源实现里通常可配。T3 是等待回复超时默认 45 秒T5 是连接分离超时默认 10 秒T6 是 Select 超时默认 5 秒T7 是 Not Selected 超时默认 10 秒T8 是网络字符间超时默认 5 秒。产线环境里最常调的是 T3 和 T8。T3 调大场景Host 侧 EAP 处理 S2F41 远程命令时要做数据库查询超过 45 秒才回 S2F42。这时设备端 T3 要大于 Host 处理时间否则设备先超时重发Host 收到重复命令可能执行两次。建议 T3 设为 Host 最大处理时间的 1.5 倍。T8 调小场景网络抖动导致报文分片到达T8 是等待同一报文后续字节的超时。默认 5 秒太宽松如果网络稳定可以调到 2 秒让异常报文更快暴露。但别低于 1 秒否则正常分片也会被误判。3.3 用日志定位“连上了但发不出消息”现象日志显示HSMS Selected但发 S1F1 后没有 S1F2 回复也没有报错。这种“静默失败”最坑。排查顺序如下。先看设备端日志有没有S1F1 Sent。没有就是应用层没调用发送函数检查代码里send_s1f1()是否在 Selected 回调里执行。有 Sent 但没 Received去 Host 端日志看有没有S1F1 Received。Host 没收到就是网络层丢包用tcpdump -i eth0 port 5000 -w secs.pcap抓包Wireshark 打开看有没有对应 TCP 段。Host 收到了但没回检查 Host 侧 S1F1 处理函数是否抛异常被吞掉。Host 回了但设备没收到检查设备端 T3 是否已超时关闭了等待。提示抓包时同时抓设备端和 Host 端对比时间戳。如果设备端显示发出但 Host 端抓不到中间有交换机或防火墙拦截。4. 避坑开源 SECS 实现里最容易翻车的五个地方4.1 坑一session_id 和 device_id 混用导致 Select 失败现象TCP 连上了日志停在Connected反复发 Select.req 但收不到 Select.rspT6 超时后断开重连。原因HSMS 的 Select 请求里携带的是 SessionID而 SECS-II 消息头里携带的是 DeviceID。很多开源库把这两个参数合并成一个配置项但 Host 侧可能分开校验。如果 Host 期望 SessionID1、DeviceID0而设备端都填 0Select 阶段就会被拒。解决查 Host 侧配置文档确认两个 ID 的独立取值在设备端配置里分开设置。如果库只提供一个参数看源码里它把该参数写进了 Select 请求的哪个字段必要时改源码。4.2 坑二S6F11 事件报告里 VID 和 RPTID 对不上现象设备发 S6F11 后 Host 回 S6F12 但带错误码或者 Host 直接不回。原因GEM 要求事件报告里的每个变量都要有对应的报告定义RPTID和变量定义VIDHost 侧维护一张映射表。设备端如果自己编了一套 VIDHost 查不到就丢弃。解决先通过 S2F33 定义报告、S2F35 链接事件和报告让 Host 确认后再发 S6F11。开源库里通常有define_report和link_event方法按标准流程走别跳过。4.3 坑三T3 超时后重发导致 Host 重复执行现象Host 侧日志显示同一条 S2F41 收到两次设备执行了两次工艺切换。原因设备端 T3 设为 45 秒Host 处理耗时 50 秒设备超时重发Host 处理完第一条后又收到第二条。解决设备端 T3 调到 Host 最大处理时间的 1.5 倍以上同时 Host 侧对 S2F41 做幂等处理用 DATAID 去重。开源实现里如果支持消息 ID 跟踪开启它。4.4 坑四字符编码不一致导致中文 Recipe 名乱码现象设备端发 S7F19 查询 Recipe 列表Host 返回的 Recipe 名里中文变成问号或乱码。原因SECS-II 的 ASCII 项默认按 ISO-8859-1 编码中文需要 UTF-8 或 GBK。开源库如果不显式指定编码按默认走就乱。解决在消息定义里把字符串项标为SecsVarA并指定编码或者统一用英文 Recipe 名。如果必须用中文设备端和 Host 端约定同一编码并在日志里打印原始字节验证。4.5 坑五开源库版本升级后 API 不兼容现象昨天跑通的代码今天pip install -U后报AttributeError: HsmsSettings object has no attribute timeout。原因开源项目迭代快小版本可能改配置项命名。解决生产环境锁定版本用pip freeze requirements.txt固定依赖。升级前先在测试环境跑一遍 S1F13/S1F14 和 S6F11 全流程确认 API 没变再上产线。5. 验证开源 SECS 栈是否达标三个可量化的测试方法5.1 用模拟 Host 做回归测试开源生态里通常有配套的 Host 模拟器或者你可以用同一套库写一个 Host 端脚本。测试用例至少覆盖S1F13/S1F14 建立通信、S1F1/S1F2 心跳、S2F33/S2F35 报告定义、S6F11 事件上报、S2F41/S2F42 远程命令、S5F1 报警上报。每个用例断言回复消息号和关键字段值。# test_host.py from secsgem.hsms import HsmsSettings, HsmsClient from secsgem.secs import SecsStreamFunction def test_s1f13(): settings HsmsSettings( address0.0.0.0, port5000, connect_to192.168.1.101, connect_port5000, device_id0, session_id0, activeFalse ) client HsmsClient(settings) client.enable() # 等待设备连入后发 S1F13 response client.send_and_wait(S1F13(), timeout10) assert response.header.stream 1 assert response.header.function 14逻辑说明activeFalse让 Host 端被动监听send_and_wait发 S1F13 并等 S1F14。参数说明timeout10是测试超时比 T3 短快速失败。断言检查回复的流和功能号确保协议层正确。5.2 用 Wireshark 做字节级验证抓包过滤tcp.port 5000看 HSMS 报文头。HSMS 头是 4 字节长度 10 字节头部SessionID 2 字节 HeaderByte2 1 字节 HeaderByte3 1 字节 PType 1 字节 SType 1 字节 SystemBytes 4 字节。Select.req 的 SType1Select.rsp 的 SType2Deselect.req 的 SType3数据消息的 SType0。如果 SType 不对说明状态机实现有问题。SECS-II 消息体从第 14 字节开始第一个字节是格式字节高 6 位是格式码低 2 位是长度字节数。5.3 用产线真实设备做灰度验证模拟器跑通后找一台非关键设备做灰度。先只开 S1F1 心跳和 S5F1 报警跑 24 小时看有没有断连。稳定后再开 S6F11 事件上报对比 Host 侧数据库里的事件记录和设备端日志是否一致。最后开 S2F41 远程命令但先限制为只读命令确认无误再放开写命令。灰度期间保留商业栈作为备份一旦开源栈异常5 分钟内切回。注意灰度验证时设备端和 Host 端的 T3 要设成一致否则一边超时重发、一边还在处理日志会对不上。我自己的习惯是每次改完配置先跑一遍S1F13握手看到S1F14回来才继续下一步。这个动作花不到 10 秒但能省掉后面半小时的抓包排查。开源 SECS 栈不是银弹它把黑匣子打开了代价是你得自己盯状态机和定时器。但一旦跑通改一行超时参数不用再等厂商排期这种掌控感值得前期投入。希望帮到你。本文还有配套的精品资源点击获取