ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C# WinForm SECS/HSMS 协议源码:从零搭建半导体设备通信与调试工具

C# WinForm SECS/HSMS 协议源码:从零搭建半导体设备通信与调试工具 简介这份资源面向半导体设备通信方向的C#开发者与工控软件工程师提供一套基于HSMS通信的SECS协议WinForm窗体程序及配套类库源码可用于快速理解SECSII/HSMS协议在.NET平台上的落地方式并作为设备端或主机端通信模块的开发起点。压缩包为zip格式整体约3.3MB内含窗体程序与类库工程源码并附有连接使用文档代码中带有中文注释便于对照阅读与二次开发。目前已有2724人学习下载说明该方向存在稳定的学习需求。读者可从中获取SECS消息收发、HSMS连接建立与状态管理、窗体交互与类库分层设计等完整实现思路结合文档快速跑通通信流程并在此基础上按自身设备协议扩展消息项与业务逻辑减少从零搭建通信框架的时间成本。1. 从一台半导体设备联调说起这套 C# WinForm SECS/HSMS 源码到底能干什么如果你在半导体、光伏、面板或者 SMT 行业做过上位机大概率遇到过这样的场景设备端的 PLC 和工控机都调通了唯独跟 MES/EAP 那台服务器连不上对方工程师甩过来一句“你们 SECS 没通先把 HSMS 建起来再说”。这时候你打开 Visual Studio发现手头只有一份设备厂商给的 PDF 协议文档连个能跑的 Demo 都没有。这套C# WinForm 基于 HSMS 通信的 SECS 协议窗体程序及类库源码解决的就是这个从零到一的问题——它把 SECS-II 消息编解码、HSMS 会话管理、WinForm 界面调试工具打包在一起让你能直接看到报文收发过程而不是对着日志猜。它适合三类人一是刚接触半导体设备通信、需要快速理解 SECS/GEM 报文结构的 C# 开发者二是要给现有设备加 SECS 接口、但不想从字节流开始手搓的工控上位机工程师三是拿它当教学案例讲 TCP 长连接、状态机、二进制协议解析的培训场景。源码本身是 WinForm 窗体加类库的结构类库负责协议逻辑窗体负责调试展示这个分层对后续集成到自己的项目里比较友好。2. 拆开类库看结构SECS-II 消息体与 HSMS 会话是怎么分层的2.1 先分清 SECS-II 和 HSMS 各管什么很多人第一次接触会把这俩混在一起说其实它们是两层。SECS-II 定义的是“消息长什么样”——一个消息由 Stream 和 Function 组成比如 S1F1 是 Are You There 请求S1F2 是回复。消息体里是一串 Item每个 Item 有格式码List、Binary、Boolean、ASCII、I4、F8 等和长度。HSMS 定义的是“消息怎么在 TCP 上跑”——包括连接建立、Select/Deselect 状态、心跳Linktest、以及把 SECS-II 消息包上 10 字节头再发出去。这套源码的类库通常会把这两层分开一个SecsMessage类管 Stream/Function/Item 的组装和解析一个HsmsConnection类管 Socket 连接和会话状态。你拿到源码后先看类库的命名空间划分一般能看出作者的分层意图。如果所有逻辑都塞在一个SecsHelper里那集成起来会痛苦一些如果分得清楚你就能只取 SECS-II 编解码部分把 HSMS 换成自己写的连接管理。2.2 消息头的 10 个字节每个都有用HSMS 消息头固定 10 字节这是整个通信的基石。源码里一般会有一个HsmsHeader结构体或者类来映射这 10 个字节。字段含义如下字节偏移字段说明0-1SessionID会话 ID设备端通常为 0xFFFF 表示不指定2Stream高 7 位第 8 位是 W-bit是否需要回复3Function功能码4PType协议类型SECS-II 固定为 05SType会话类型0 表示数据消息1 是 Select.req2 是 Select.rsp9 是 Linktest6-9SystemBytes系统字节用于匹配请求和回复你在调试时如果发现对方不回复先抓包看 SystemBytes 有没有原样带回。很多“玄学”问题其实是 SystemBytes 没对上或者 W-bit 设了但对方没回。2.3 用代码块看一个 S1F1 的组装过程下面这段代码演示如何用类库组装一条 S1F1 消息并发送。注意不同源码的 API 命名可能不同这里按常见设计写// 创建 SECS-II 消息S1F1W-bit 置 1 表示需要回复 SecsMessage msg new SecsMessage(1, 1, true); // S1F1 通常不带 Item但有些场景会带一个 ASCII 的 MDLN // msg.AddItem(new SecsItem(SecsFormat.ASCII, EQP001)); // 通过 HSMS 连接发送内部会封装 10 字节头 HsmsConnection conn new HsmsConnection(192.168.1.100, 5000); conn.Connect(); conn.Send(msg); // 接收回复通常需要异步或轮询 SecsMessage reply conn.Receive(); Console.WriteLine($收到 S{reply.Stream}F{reply.Function});逻辑说明SecsMessage构造函数接收 Stream、Function 和 W-bit。AddItem用于往消息体里追加 ItemS1F1 一般不带但如果你要传设备型号可以加。HsmsConnection.Send内部会做三件事把 SecsMessage 序列化成字节数组、拼上 10 字节头、通过 Socket 发出去。参数方面IP 和端口是设备端或 Host 端监听的地址SECS 常用端口是 5000但实际项目里可能改。提示如果你拿到的源码里Send是同步阻塞的在高频消息场景下要改成异步否则界面会卡死。2.4 类库的依赖和编译环境这套源码一般基于 .NET Framework 4.0 到 4.8 之间用 VS2015 或更高版本打开。类库项目通常不依赖第三方 NuGet 包纯 Socket 和字节操作这对现场部署是好事——不用考虑客户机器上能不能装包。但要注意如果源码里用了System.Threading.Tasks或者async/await那目标框架至少是 4.5。打开解决方案后先看每个项目的目标框架不一致的话统一一下否则编译报错。3. 把窗体程序跑起来连接、发消息、看日志的三步操作3.1 第一步配置连接参数并建立 HSMS 会话窗体程序一般会有一个区域让你填 IP、端口选择主动连接还是被动监听。半导体设备端通常是 Passive 模式等 Host 来连Host 端是 Active 模式。你调试时如果只有一台电脑可以开两个实例一个 Active 一个 Passive自己连自己。操作步骤打开解决方案找到 WinForm 项目设为启动项目。编译运行在界面上填入本机 IP 和端口 5000。如果作为 Passive 端点“监听”按钮如果作为 Active 端点“连接”按钮。观察状态栏是否显示“Selected”或“Connected”。HSMS 连接建立后还有一个 Select 过程只有 Select 成功才能发数据消息。// 被动监听模式示例 HsmsConnection conn new HsmsConnection(); conn.Port 5000; conn.Mode HsmsMode.Passive; conn.OnSelected () { // Select 完成后才能发 S1F1 this.Invoke(new Action(() { txtLog.AppendText(HSMS Selected, 可以发消息了\r\n); })); }; conn.Start();参数说明Mode决定主动还是被动OnSelected是 Select 成功后的回调。如果你发现连接上了但发消息没反应先看有没有触发 Selected。3.2 第二步手动构造消息并观察收发日志窗体上通常有 Stream、Function 输入框以及一个 Item 编辑区。你可以手动填 S1F1点发送然后在日志区看到十六进制报文。这一步是理解协议最快的方式。常见做法是先发 S1F1对方回 S1F2再发 S1F13Establish Communications Request对方回 S1F14。S1F13 是 GEM 里建立通信的标准消息很多 Host 要求先发这个。// 发送 S1F13带两个 ItemMDLN 和 SOFTREV SecsMessage s1f13 new SecsMessage(1, 13, true); s1f13.AddItem(new SecsItem(SecsFormat.ASCII, EQUIP01)); s1f13.AddItem(new SecsItem(SecsFormat.ASCII, 1.0.0)); conn.Send(s1f13);逻辑说明S1F13 的回复 S1F14 里第一个 Item 是 COMMACK0 表示成功。如果你收到非 0说明对方不接受通信建立要查对方的 GEM 配置。3.3 第三步用日志区排查字节级问题窗体程序的日志区一般会显示发送和接收的原始字节。你要学会看几个关键点前 4 个字节是不是FF FF 00 00SessionID 和 Stream/Function 的常见值、SType 是不是 0、SystemBytes 是不是递增。如果日志里只有发送没有接收先确认对方 IP 和端口对不对再用telnet测一下端口通不通。注意有些源码的日志区只显示解析后的消息不显示原始字节。这种情况下你需要自己在Send和Receive里加BitConverter.ToString打印否则排查字节对齐问题会很痛苦。3.4 把类库集成到自己项目的注意事项如果你不想用它的窗体只想用类库那步骤是把类库项目加到你的解决方案引用它然后自己写连接管理。注意几点一是类库里的HsmsConnection可能直接依赖System.Windows.Forms做日志回调集成到控制台或服务里会报错需要把日志回调改成事件或接口二是线程模型如果类库在后台线程收消息你更新 UI 要Invoke三是异常处理Socket 断线重连逻辑如果源码里没有你得自己补。4. 避坑与排查HSMS 连不上、消息不回、界面卡死的常见原因4.1 现象TCP 连上了但一直不 Selected原因HSMS 的 Select 过程需要双方交换 Select.req 和 Select.rsp。如果一方发了 Select.req 但另一方没回或者回了但 SystemBytes 不对就会卡在 Connecting 状态。有些源码把 Select 做成自动的有些需要手动触发。解决先确认双方模式匹配——Active 端发 Select.reqPassive 端回 Select.rsp。抓包看 SType 是不是 1 和 2。如果源码里 Select 逻辑有问题可以手动构造 SType1 的消息发出去。4.2 现象S1F1 发出去了对方不回 S1F2原因最常见的是 W-bit 没设。S1F1 的 W-bit 必须是 1对方才会回复。其次是 SystemBytes 冲突如果你连续发多条消息但 SystemBytes 没递增对方可能丢弃。还有可能是对方要求先建立通信S1F13才能响应其他消息。解决检查SecsMessage构造时第三个参数是不是true。检查 SystemBytes 生成逻辑确保每条消息唯一。先发 S1F13 再发 S1F1。4.3 现象窗体界面发完消息就卡死原因Send或Receive在 UI 线程上同步阻塞。SECS 通信是长连接Receive如果写成同步等待UI 线程就被占住了。解决把通信逻辑放到后台线程用Task.Run或Thread。UI 更新通过Invoke回到主线程。如果源码里已经是异步的检查是不是在async方法里用了.Result或.Wait()这也会死锁。4.4 现象收到的消息解析出来是乱码或 Item 错位原因SECS-II 的 Item 长度字段有 1 字节、2 字节、3 字节三种格式取决于长度值。如果解析时固定按 2 字节读遇到短消息就会错位。另外 ASCII 和 Binary 的长度单位不同ASCII 是字符数Binary 是字节数。解决对照 SEMI E5 标准检查 Item 长度解析逻辑。常见做法是先读第一个字节如果最高位是 0长度就是 1 字节如果是 1再看后续字节。源码里如果这部分写死了需要改。4.5 现象长时间运行后连接断开重连失败原因HSMS 有 Linktest 机制如果一段时间没数据一方会发 Linktest.req另一方要回 Linktest.rsp。如果源码没实现心跳中间的网络设备可能把长连接掐掉。重连失败往往是 Socket 没正确释放端口被占用。解决在类库里加定时器每隔 30 秒发一次 Linktest。重连时先Close再Dispose确保 Socket 资源释放。如果源码里没有心跳这是你必须补的第一个功能。5. 进阶用法把 SECS 类库改造成可复用的通信组件5.1 用接口隔离日志和 UI 依赖原始源码的类库如果直接调TextBox.AppendText那它只能用在 WinForm 里。改造成可复用组件的第一步是定义日志接口public interface ISecsLogger { void Log(string direction, byte[] data); void Log(string message); } // 在 HsmsConnection 里注入 public class HsmsConnection { public ISecsLogger Logger { get; set; } private void OnReceive(byte[] data) { Logger?.Log(RX, data); // 后续解析... } }这样你在 WinForm 里传一个把日志写到 TextBox 的实现在控制台里传一个写到 Console 的实现类库本身不依赖任何 UI 框架。参数说明direction用 TX 和 RX 区分收发data是原始字节方便你做十六进制展示或存文件。5.2 用状态机管理 HSMS 会话生命周期HSMS 会话有明确的状态NotConnected、Connected、Selected。用状态机管理比一堆 bool 标志清晰得多。常见做法是定义一个枚举和转移表当前状态事件下一状态动作NotConnectedTCP 连接成功Connected启动接收线程Connected收到 Select.reqSelected回 Select.rspConnected发出 Select.req 并收到 rspSelected触发 OnSelectedSelected收到 Linktest.reqSelected回 Linktest.rspSelectedTCP 断开NotConnected触发 OnDisconnected你可以在类库里加一个HsmsStateMachine类把状态转移和动作分开。这样排查问题时日志里打印状态变化一眼就能看出卡在哪一步。5.3 消息编解码的单元测试怎么写SECS-II 的编解码是纯逻辑最适合写单元测试。你可以构造已知字节数组解析成 SecsMessage再序列化回去看是否一致。比如 S1F1 的字节是00 00 00 01 00 00 00 00 00 0010 字节头无 Item你可以断言解析出的 Stream1、Function1、W-bittrue。[TestMethod] public void ParseS1F1_ShouldReturnStream1Function1() { byte[] raw new byte[] { 0xFF, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; SecsMessage msg SecsMessage.Parse(raw); Assert.AreEqual(1, msg.Stream); Assert.AreEqual(1, msg.Function); Assert.IsTrue(msg.WBit); }注意 SystemBytes 在测试里可以固定实际运行时必须递增。单元测试覆盖边界情况空 Item、超长 Item、多字节长度、嵌套 List。这些测过了现场调试会少很多血泪经验。5.4 我踩过的一个坑SystemBytes 回绕SystemBytes 是 4 字节无符号整数从 0 到 4294967295。我见过一个项目连续运行几个月后SystemBytes 回绕到 0结果和之前某条消息的 SystemBytes 撞了对方直接丢弃。后来我在生成逻辑里加了一个判断如果当前值等于 0就跳到 1并且记录日志。从那以后我每次集成 SECS 类库都强制走一遍 SystemBytes 递增和回绕测试确认不会重复。希望帮到你。本文还有配套的精品资源点击获取
返回列表