简介:这是一份面向工业自动化领域开发者的 C# OPC 客户端测试工程,适合需要快速掌握 OPC 通信的上位机软件工程师。项目基于 OPC Net Api Chs 库,使用 Visual Studio 2010 开发,完整演示了连接 OPC 服务器、浏览组与数据项、订阅实时值、写入数据以及断开连接等关键操作,能帮助读者理解客户端与服务器之间的数据交换机制。压缩包共 71 个文件,大小 2.84MB,包含 C# 源码、工程方案、动态链接库、可执行程序及界面资源文件,结构与常规上位机项目一致,便于直接打开、编译和测试。该资源已有 253 人学习,特别适合学习工业数据采集、设备对接或希望基于 OPC 协议开发上位机功能的初学者。通过查看源码和运行测试窗体,可以获得一套可复用的 OPC 客户端框架,包括连接管理、项遍历、事件订阅、异常处理等模块,具有直接参考和二次开发价值。
1. 为什么通过 C# 写 OPC 客户端测试:一次拧紧机定位问题的复盘
做产线设备集成的工程师,大概率都遇到过这样的场景:上位机需要从拧紧轴控制器、PLC 或者数据采集器里读扭矩、角度、OK/NG 信号,而设备厂商给的接口清一色是 OPC。前段时间我调试某一型号的 Atlas Copco 拧紧控制器,就用 C# 写了一个 OPC 客户端测试工具,把功率头 6000 的实时扭矩值跑通了。整个过程给我的体会是:OPC 不复杂,但网络上现成的 C# 示例大多是半成品,要么没讲清 OPC DA 和 OPC UA 怎么选,要么忽略了 DCOM 权限、证书信任这些隐藏坑。这篇文章就是想把「理论选型 → 客户端实现 → 参数验证 → 排错」完整串一遍,纯干货,新手能照着敲,熟手也能直接对比自己的写法。
2. OPC DA 还是 OPC UA:客户端测试的协议选型
动手写代码之前必须先把协议选型搞清楚。OPC 经典实现基于 Windows COM/DCOM 技术,也就是常说的 OPC DA(Data Access);而新一代 OPC UA 则摆脱了 COM 依赖,跨平台、带加密和证书认证。做客户端测试时选错协议,后面全部白搭。
2.1 OPC DA:老产线的现实选择
OPC DA 是 1996 年就诞生的老协议,至今在制造业还有大量存量设备在用。它最大的特点是「不用装 SDK,系统自带 COM 组件」。C# 访问 OPC DA 通常通过 OPCDAAuto.dll 这个 COM 互操作程序集,ProgID 是 "OPC.Automation"。这个组件随 Windows 系统自动注册,不需要额外安装第三方库。
选择 OPC DA 的理由很直接:如果你的 OPC Server 是老的——比如产线原有的 SIMATIC NET、Kepware 旧版本——它们通常只暴露 DA 接口。更关键的是,DA 走 DCOM 远程调用,在同一台 Windows 机器上调试非常简单,Server 和 Client 都装在同一台工控机上,连网络配置都省了。
C# 引用 OPCDAAuto.dll 的方法是在项目里右键「添加引用」→「COM」选项卡,找到 "OPC Server Automation 2.02"(不同系统显示略有差异),系统会自动生成 Interop.OPCAutomation.dll。注意:引入后一定要在项目属性里把目标平台设为 x86,原因是绝大多数老 OPC Server 是 32 位 COM 组件,如果你在 64 位系统上编译成 AnyCPU,运行时调用 32 位 COM 会直接抛 "CLSID 未注册" 异常。
2.2 OPC UA:新项目和新设备的基础
OPC UA 和 DA 最大的差别是传输层。UA 不再依赖 DCOM,而是基于 TCP 或者 HTTPS,默认端口 4840,数据用二进制或 XML 编码,配合 X.509 证书做双向认证。这意味着它天然能跨网段部署,甚至能穿透到云平台——这也是很多新设备(比如较新固件的拧紧控制器、西门子 S7-1500 的 OPC UA Server)只提供 UA 接口的原因。
C# 访问 OPC UA 的成熟方案是 OPC Foundation 官方的 .NET 标准库,NuGet 包名是OPCFoundation.NetStandard.Opc.Ua。我一般只在需要完整 UA 功能(浏览地址空间、订阅数据变化、调用方法)时才用它,因为它相对 DA 的自动化接口要重一些,需要先构建 ApplicationConfiguration 和 Session。
从测试角度看,我建议做客户端时把 DA 和 UA 分成两个独立的测试项目,不要试图一个程序兼容两种协议。原因很简单:两种协议的连接逻辑、类型映射、错误返回机制完全不同,混在一起代码会很难排查。实际项目中我的习惯是:先确认设备端的 OPC Server 支持什么协议,用免费的客户端探一下再写代码。
2.3 选型清单与协议对比表
| 对比项 | OPC DA | OPC UA |
|---|---|---|
| 传输机制 | COM/DCOM(Windows 限定) | TCP/HTTPS(跨平台) |
| 默认端口 | 动态端口 + 135 固定 | 4840 |
| 安全性 | DCOM 权限控制 | 证书认证 + 加密 |
| C# 访问方式 | Interop.OPCAutomation.dll | OPCFoundation.NetStandard.Opc.Ua NuGet |
| 适合场景 | 存量产线设备、工控机本地 | 新设备、跨网段、云平台 |
| 免费测试工具 | Matrikon OPC Explorer | Unified Automation UaExpert |
这里有个容易被忽略的点:OPC DA 在远程访问时不仅需要 135 端口,还会动态申请 1024 以上的随机端口。要远程测试 DA Server,光放行 135 不够,还得配 DCOM 的动态端口范围,这个放到第 5 章的避坑部分详细说。UA 就没有这个烦恼,只要把 4840 放通即可。
3. 用 Interop.OPCAutomation 实现 OPC DA 客户端:连接、读取、断开
这一章给出 C# 访问 OPC DA 的完整代码套路。核心对象有三个:OPCServer、OPCGroups、OPCGroup,分别对应服务器连接、组集合和单个数据组。
3.1 创建 OPCServer 实例并连接
连接 DA Server 的 C# 代码非常简单,但要理解每一步背后的 COM 行为,不然出错时无从下手。
using OPCAutomation; // 创建 OPCServer 对象 OPCServer server = new OPCServer(); // 连接到本机 "Kepware.KEPServerEX.V6" 这个 ProgID 对应的 OPC Server string serverProgID = "Kepware.KEPServerEX.V6"; string hostName = "localhost"; try { server.Connect(serverProgID, hostName); Console.WriteLine($"已连接: {server.ServerName},状态: {server.ServerState}"); } catch (Exception ex) { Console.WriteLine($"连接失败: {ex.Message}"); }这段代码的逻辑是先创建 COM 对象,再调用 Connect 传入两个参数:第一个是 OPC Server 的 ProgID(可以在注册表 HKEY_CLASSES_ROOT 下查找,以 OPC 开头),第二个是主机名。连接成功后 ServerState 返回 1 表示运行中。
关于 ProgID 有个坑:你不能凭空猜这个字符串。最靠谱的办法是在目标机器上用注册表编辑器(regedit)搜索 "OPC.Server" 或直接看 HKEY_LOCAL_MACHINE\SOFTWARE\Classes 里 OPC 开头的项。另外,有些 Server 是 64 位的,它的 ProgID 在 WOW6432Node 下,32 位程序访问不到,反过来也是。后面避坑章会专门说这个。
3.2 添加组、添加项并同步读取数值
连接上 Server 之后,OPC 的读取模型是先建组(Group),再往组里添加项(Item)。每一项对应一个具体的 OPC 标签,比如扭矩值Channel1.Device1.Torque。
// 添加一个组 OPCGroups groups = server.OPCGroups; OPCGroup group = groups.Add("ReadGroup"); // 设置组属性 group.IsActive = true; group.IsSubscribed = false; // 关闭订阅,只做主动读 group.UpdateRate = 100; // 刷新率(毫秒) // 往组里添加两个标签项 string[] itemIDs = new string[] { "Channel1.Device1.Torque", "Channel1.Device1.Angle" }; OPCItems items = group.OPCItems; Array itemHandles = null; Array itemErrors = null; int[] clientHandles = new int[itemIDs.Length]; for (int i = 0; i < itemIDs.Length; i++) { clientHandles[i] = i + 1; } items.AddItems( itemIDs.Length, ref itemIDs, ref clientHandles, out itemHandles, out itemErrors); // 同步读取所有项 Array values; Array errors; Array qualities; Array timestamps; group.SyncRead( (short)OPCDataSource.OPCDevice, itemIDs.Length, out values, out errors, out qualities, out timestamps); for (int i = 0; i < itemIDs.Length; i++) { Console.WriteLine($"标签 {itemIDs[i]},值: {values.GetValue(i)},质量: {qualities.GetValue(i)}"); }这段代码的重点是 AddItems 的参数同步:itemIDs 是标签字符串数组,clientHandles 是客户端自定义句柄数组,两个数组长度必须一致,且 AddItems 次数要显式传入。返回的 itemErrors 数组长度和 itemIDs 一致,元素为 0 表示添加成功,非 0 则要看具体错误码。
SyncRead 是同步阻塞读取,OPCDataSource.OPCDevice 表示从设备读取;也可以传 OPCDataSource.OPCCache 表示只读缓存,区别是前者会真正访问硬件,速度慢但数据新鲜,后者快但可能是旧值。测试扭矩这种实时性要求高的数据,必须选 OPCDevice。values 和 qualities 是 Object 数组,需要按索引取值。质量码(quality)是 OPC DA 里最容易忽视的东西——值为 192 表示 Good,64 是 Uncertain,0 是 Bad。很多上位机只读数值没看质量码,设备故障时读到 -9999 就直接当正常值用了,这是血泪教训。
从 OPCDAAuto.dll 拆解下来这套流程,本质上就是把 Server→Group→Item 三层模型实例化一遍。你可以把它封装成一个类库,对外只暴露 Connect、ReadTag、Disconnect 三个方法,后续项目直接调用。
3.3 异步读取与事件回调
如果产线上需要周期性地采集一组扭矩数据,轮询会占 CPU 和网络带宽。更合理的方案是订阅模式:把组的 IsSubscribed 设为 true,然后通过 OPCGroup 的 DataChange 事件接收数据变化通知。
group.IsSubscribed = true; // 注册数据变化事件 group.DataChange += new IOPCGroupEvent_DataChangeEventHandler(Group_DataChange); Console.WriteLine("正在等待数据变化,按任意键停止..."); Console.ReadKey(); void Group_DataChange(int numItems, Array clientHandles, Array values, Array qualities, Array timestamps) { for (int i = 0; i < numItems; i++) { Console.WriteLine($"句柄 {clientHandles.GetValue(i)} 值: {values.GetValue(i)},质量: {qualities.GetValue(i)}"); } }事件回调里拿到的 clientHandles 对应前面 AddItems 时传入的手柄,通过它就能知道哪个标签发生了变化。注意:这个回调运行在 COM 的线程池线程上,不能在回调里直接更新 WinForm 控件,必须用 Invoke 切回 UI 线程,否则会抛跨线程异常。
需要讨论的是 UpdateRate 的设置,它是 Server 向 Client 推送数据的死区控制。比如拧紧机的扭矩值每秒变化几十次,你可以设 10~50ms;但如果是温度等缓变量,设 500ms 反而更合理,能减少事件风暴。对于测试工具,一般保持默认的 100ms 就行,太高会丢细节,太低会淹没主线程。
4. 用 UA-.NETStandard 实现 OPC UA 客户端:从配置到会话
新设备走 OPC UA 的情况越来越多,客户端测试和 DA 的套路完全不同。UA 的复杂度主要集中在初始配置和会话建立上,一旦连上,后面读标签其实更统一。
4.1 安装依赖与配置 ApplicationConfiguration
OPC UA 的 C# 库是官方开源实现,NuGet 包名是OPCFoundation.NetStandard.Opc.Ua。安装后首先要构建 ApplicationConfiguration,这是客户端启动时的必选项。
using Opc.Ua; using Opc.Ua.Configuration; // 构建客户端应用配置 ApplicationConfiguration config = new ApplicationConfiguration { ApplicationName = "CSharpOpcTestClient", ApplicationUri = "urn:CSharpOpcTestClient", ApplicationType = ApplicationType.Client, SecurityConfiguration = new SecurityConfiguration { ApplicationCertificate = new CertificateIdentifier { StoreType = "Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\pki\own", SubjectName = "CN=CSharpOpcTestClient, DC=localhost" }, TrustedPeerCertificates = new CertificateTrustList { StoreType = "Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\pki\trusted" }, RejectedCertificateStore = new CertificateTrustList { StoreType = "Directory", StorePath = @"%CommonApplicationData%\OPC Foundation\pki\rejected" } }, TransportConfigurations = new TransportConfigurationCollection(), TransportQuotas = new TransportQuotas { OperationTimeout = 10000 } }; await config.Validate(ApplicationType.Client);这段配置的重点在安全目录结构。OPC UA 客户端首次连接 Server 时,会收到 Server 的证书;如果你的证书不在信任列表里,连接会被拒。Directory存储类型在 Windows 上默认是指定的物理路径,也就是受保护的 pki 目录。ApplicationCertificate 是客户端自己的证书,首次启动会自动生成;TrustedPeerCertificates 是信任的服务端证书列表,UaExpert 这类工具第一次连 UA Server 时弹出 "Accept Certificate" 对话框,本质就是把对端证书加入这个目录。
4.2 创建会话并读取标签
配置完成之后就是创建 Session 再读取,这里代码比 DA 麻烦一些,但也更接近工业标准的 URI 寻址方式。
using Opc.Ua.Client; // 定义服务器地址 string endpointUrl = "opc.tcp://192.168.1.100:4840"; string endpointDiscoveryUrl = "opc.tcp://192.168.1.100:4840"; // 发现服务器端点,选择合适的 security policy EndpointDescription selectedEndpoint = null; using (var discovery = DiscoveryClient.Create(endpointUrl)) { var endpoints = discovery.GetEndpoints(null); selectedEndpoint = endpoints.FirstOrDefault(e => e.SecurityPolicyUri == SecurityPolicies.Basic256Sha256); selectedEndpoint ??= endpoints.First(e => e.SecurityPolicyUri == SecurityPolicies.None); } // 创建会话 using (var session = Session.Create( config, selectedEndpoint, false, "CSharpTestSession", 60000, new UserIdentity(new AnonymousIdentityToken()), null)) { // 节点 ID 是 UA 的标签寻址方式 NodeId torqueNode = new NodeId("ns=2;s=Channel1.Device1.Torque"); ReadValueIdCollection nodesToRead = new ReadValueIdCollection { new ReadValueId { NodeId = torqueNode, AttributeId = Attributes.Value } }; DataValueCollection results; IList<ServiceResult> errors; session.Read( null, 0, TimestampsToReturn.Both, nodesToRead, out results, out errors); Console.WriteLine($"扭矩值: {results[0].Value}, 质量: {results[0].StatusCode}"); }Session.Create 的参数里,第一个是前面构建的配置,第二个是选好的端点,第三个表示是否创建安全通道,第五个是会话超时毫秒数,第六个是用户身份标识。匿名身份适合调试,生产环境一般用 UserIdentity 的 UsernamePasswordToken。
关于节点的标识方式:UA 里标签是一个字符串 ID,最常见的是类如 "ns=2;s=Channel1.Device1.Torque" 的语法,ns 是命名空间索引,s 是字符串标识。不同设备的标签名完全不一样,最有效的办法是在 UaExpert 里浏览地址空间,把你要读的节点完整路径复制出来,再填到代码里,盲猜是不行的。
StatusCodes 是 UA 的状态码枚举,Good等价于 DA 里的 192 质量码。读取结果的 statusCode 也是同样的逻辑:先看状态码再看值,这两个步骤缺一不可。
4.3 真实场景:读 power focus 6000 拧紧控制器的扭矩值
以拧紧工具控制器为例,这类设备通常内置 UA Server,地址空间里有 ToolData、TighteningResult 等分支。我在实测时读扭矩值的路径形如ns=2;s=Channel1.Tool1.Torque,用上面的代码直接改 NodeId 即可。
要注意的是:有些设备把扭矩值作为浮点写到 Value 属性,但测量单位是 Nm 还是 Ncm,必须查设备的 OPC 地址空间文档。我就吃过亏,读出来 75 以为是 75Nm,实际是 750Nm——少了个量纲转换。
另外,UA 协议里数值还可以带工程单位属性,可以通过读 AccessLevel 或 EURange 属性来获取量纲信息。多数时候单位不会自动换算,客户端要把量纲转换逻辑写死在一个配置表里,而不是散落在代码各个地方。
5. 客户端测试实战:免费 OPC Server 选型与数据验证方法
既然做客户端测试,就一定需要一个稳定、可控、免费的 OPC Server 来当靶子。工业现场的 Server 受产线运行状态影响,不适合做开发期的验证。我的习惯是本地起一个模拟器,把标签配好,然后用自写的 C# 客户端去连它。
5.1 免费的 DA 与 UA Server 怎么选
常用的测试 Server 有两类:一类是 OPC DA 的 Matrikon OPC Simulation Server,装上就能用,自带一批模拟标签,比如随机数、正弦波;另一类是 Prosys OPC UA Simulation Server,它自带 UA 地址空间,而且可以在线增删标签节点。
| 模拟器 | 协议 | 免费策略 | 典型标签 |
|---|---|---|---|
| Matrikon Simulation Server | OPC DA 2.0 | 免费授权 | Random.Int1、Bucket Brigade.Int4 |
| Prosys OPC UA Simulation Server | OPC UA | 免费使用 | ns=0;i=2258(当前时间) |
| KEPServerEX | DA/UA 都支持 | 免费 2 小时评估 | 自建标签 |
测试时我的做法是固定使用 Matrikon 来验证 DA 代码路径,Prosys 验证 UA 路径,生产项目再连真实设备。这么做能保证代码是「调试过」的,而不是上现场再试错。
5.2 客户端自测清单:从连接参数到读写验证
写一个客户端测试工具,验证点不只是「连上了没有」,至少要覆盖这五个维度:
第一,Server 连通性。包括 ProgID/Endpoint URL、主机名、端口是否可达、DCOM 或者证书握手是否能通过。
第二,标签寻址是否正确。DA 的 Item ID 必须在 Server 的地址空间里存在;UA 的 NodeId 必须能在 UaExpert 里浏览到。
第三,读写一致性。如果测试 Server 支持写入,可以写一个测试值,再读回来比对,校验权限配置和数据类型转换是否正确。
第四,异常容忍度。人为把 Server 停掉再启动,观察客户端是否能在业务层给出提示,而不是直接崩溃或者死循环。
第五,时钟与质量评估。确定读到的值有时间戳,质量码为 Good,且时间戳不是 Server 启动时间——如果所有值的时间戳相同,说明读的是缓存,不是实时数据。
这里给一张标签验证表模板,我每测一个现场设备都会维护这个表:
| 标签名 | 类型 | 期望值域 | 实测值 | 质量码 | 时间戳 | 结论 |
|---|---|---|---|---|---|---|
| Channel1.Device1.Torque | Float | 0~1000 | 75.2 | Good | 2024-01-10 10:00:00.123 | 通过 |
| Channel1.Device1.Angle | Float | 0~360 | 90.5 | Good | 2024-01-10 10:00:00.123 | 通过 |
5.3 把测试参数配置化
对于测试工具,我强烈建议把 Server 地址、标签清单、刷新率拆成一个配置文件,用 JSON 或 XML 存储,而不是硬编码在代码里。C# 里用 System.Text.Json 就能轻松读入。
{ "Protocol": "UA", "ServerEndpoint": "opc.tcp://192.168.1.100:4840", "SecurityPolicy": "Basic256Sha256", "UpdateRateMs": 100, "Tags": [ { "Id": "ns=2;s=Channel1.Device1.Torque", "Name": "扭矩" }, { "Id": "ns=2;s=Channel1.Device1.Angle", "Name": "角度" } ] }配置化的意义在于:现场设备的标签表跟开发环境不可能一致,把标签抽出来之后,去新项目只需要改 JSON 里的节点 ID,一行代码都不用动。我在多个项目里复用同一套客户端测试代码,只换了配置,效率提升了不少。
6. 常见问题排查与避坑指南:OPC 客户端测试的四个深坑
写这一章的时候,我把这几年在 OPC 客户端上踩过的坑按「现象 → 原因 → 解决」整理出来。这些都是能直接拿去对号入座的实战记录,比看文档管用。
6.1 坑一:CLSID 未注册 / 无法创建 COM 对象
现象:C# 项目里写了new OPCServer(),运行时报错 "Retrieving the COM class factory for component with CLSID {F8582CF2-88FB-11D0-B850-00C0F0104305} failed"。
原因:OPC 自动化接口是 32 位组件,但项目编译成 AnyCPU 或 x64,进程是 64 位的,无法加载 32 位 COM 组件。
解决:项目属性 → 生成 → 平台目标选 x86。如果 Server 是 64 位 COM(少见),则反过来选 x64。带上实际排查思路,是用 regedit 确认 OPCDAAuto.dll 注册在哪个 Wow6432Node,就能确定位数方向。
6.2 坑二:DA 远程连接超时,报 0x80070005 拒绝访问
现象:本地连接 OPC DA Server 成功,但从另一台机器连接同一 Server,提示 "Access is denied" 或 RPC 服务器不可用。
原因:OPC DA 的 DCOM 安全配置没有放开。默认情况下,DCOM 拒绝匿名用户访问,而跨机器调用时客户端身份验证会失败。
解决:在 Server 机器上运行dcomcnfg命令,组件服务 → 计算机 → 我的电脑 → COM 安全限制,把所在域用户或 Everyone 添加到「启动和激活权限」及「访问权限」中。同时确认 135 端口和动态端口范围在防火墙里放行。这套配置我在每个工控机上都要做一遍,建议写成一个批处理脚本统一执行,不然每台机器手点一次很崩溃。
6.3 坑三:UA 连接报 BadCertificateUntrusted
现象:UA 客户端调用 Session.Create 时报错 "BadCertificateUntrusted: The certificate is not trusted"。
原因:这是 UA 的证书双向信任机制:客户端不信任服务端的证书,或者服务端不信任客户端的证书。
解决:到客户端配置的 TrustedPeerCertificates 目录下查看有没有服务端证书;没有就说明在首次握手时证书被丢进了 Rejected 目录。把 rejected 里的证书文件移动到 trusted 目录,并确保存储类型一致(Directory 还是 X509Store)。另外,如果 Server 要求客户端证书,需要把客户端证书也放到 Server 的信任目录下。
6.4 坑四:异步事件的回调线程导致 UI 卡死
现象:DA 订阅模式在 WinForm 应用中运行,数据变化时界面无响应,或者报操作无效,跨线程操作控件异常。
原因:DataChange 事件回调在 COM 线程池中执行,不可以在该线程直接访问 UI 控件。
解决:在回调里用控件的 BeginInvoke 方法切回 UI 线程,典型的模式是:
private void Group_DataChange(int numItems, Array clientHandles, Array values, Array qualities, Array timestamps) { if (textBox1.InvokeRequired) { textBox1.BeginInvoke(new Action(() => UpdateUI(values, timestamps))); return; } UpdateUI(values, timestamps); }这类跨线程问题在控制台程序中不会出现,所以测试时最好先写一个控制台程序确认逻辑没问题,再封装进 GUI 项目。直接写 GUI 项目的话,把问题混在一起排查会花掉三倍时间。
7. 进阶验证技巧:用 UA 订阅替代轮询,测试实时推送稳定性
当客户端测试工具要长时间挂在产线上观察数据时,轮询模式的 CPU 占用率高,而且 DA 时代为了防止事件风暴还会设置较大的 UpdateRate。OPC UA 的订阅(Subscription)机制则更加优雅,Server 端按设定周期检测变化并推送,客户端只需注册一个回调。
实现订阅其实就是在前面 Session.Create 之后,加上 Subscriptions 的操作:
// 创建订阅 var subscription = new Subscription { PublishingInterval = 100, // 推送周期 100ms LifetimeCount = 1000, KeepAliveCount = 10 }; session.AddSubscription(subscription); subscription.Create(); // 添加监控项 MonitoredItem item = new MonitoredItem { StartNodeId = torqueNode, SamplingInterval = 100, QueueSize = 1, DiscardOldest = true }; item.Notification += MonitoredItem_Notification; subscription.AddItem(item); // 事件处理 void MonitoredItem_Notification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { var notification = e.NotificationValue as MonitoredItemNotification; if (notification != null) { Console.WriteLine($"扭矩值: {notification.Value.Value:F2}"); } }PublishingInterval 是服务端推送数据包的时间间隔,SamplingInterval 是服务端从底层采集数据的时间间隔。这两个参数经常被搞混——Publishing 决定你的 UI 多久刷新一次,Sampling 决定硬件值多久被读取一次。如果 Sampling 大于 Publishing,那是不可能看到亚秒级变化的。对于扭矩值这种动态变化信号,我一般设 PublishingInterval = 100,SamplingInterval = 50,这样 UI 每 100ms 收到一次包含最新采样值的通知。
QueueSize 和 DiscardOldest 是应对差值的两个关键参数:如果数据变化太快,而客户端处理不过来,队列会满。QueueSize = 1 + DiscardOldest = true 表示只保留最新值,适合扭矩这种只关心最终结果的场景;如果做波形分析,QueueSize 要设大且 DiscardOldest = false,保证每个采样点都被处理到。
如果订阅后收到通知,但数分钟后又停止:优先检查服务端会话超时时间(SessionTimeout),默认一般是 1 分钟。长时间没有操作,Server 会回收会话。解决办法是让 Session 周期性调用 KeepAlive,或者在订阅里检查 KeepAliveCount 的设置——KeepAliveCount 乘上 PublishingInterval,就是服务端发送心跳包的时间间隔。以 100ms PublishInterval、KeepAliveCount = 10 为例,每 1 秒发一次心跳,正好可以维持会话活性。
从那以后,我每次做 OPC 客户端测试都会强制把订阅和轮询两种模式各测一遍:先用轮询验证数据链路,再切订阅模式跑 30 分钟观察 CPU 占用率和数据完整性。订阅模式跑通了,才敢把工具给产线用。希望这篇文章里写的选型思路和排错记录,也能帮你在 OPC 这条路上少走几个来回。
本文还有配套的精品资源,点击获取