
简介OpcUaClient_VB.zip 是一份面向工业自动化与上位机开发者的 OPC UA 客户端学习示例适合具备一定 VB 基础、希望理解 OPC UA 通信原理与客户端实现的中级开发者借鉴。包内共 181 个文件以 122 个 dll 类库、6 个 vb 源码、4 个 exe 可执行程序及 xml、config、resx 等配置资源为主整体约 15.26MB并附带 OPC Helper 2.02 工具可直接连接 OPC 服务器进行数据读写验证。资源围绕连接与认证、信息模型浏览、数据订阅与变化通知、读写操作、错误处理及多线程异步通信等关键知识点展开TestOpcUa 测试工程展示了会话创建、订阅发布与服务器交互的完整流程。目前已有 481 人学习借鉴读者可据此掌握 OPC UA SDK 的 API 调用方式理解节点、属性、方法与事件构成的信息模型并借助配套工具完成实际调试快速提升工业自动化编程技能。1. 从一份 VB 版 OPC UA 客户端源码说起它到底能解决什么问题车间里一台老设备跑着 OPC UA 服务端上位机却还在用 VB.NET 维护想接进去读几个点位翻遍官方 SDK 文档发现示例全是 C#硬翻成 VB 又踩了一堆异步和类型转换的坑。这种场景下一份能直接跑起来的 VB 版 OPC UA 客户端源码价值不在于代码多优雅而在于它把「连接、会话、订阅、读写」这条链路用 VB 的语法完整走通了让你能对照着改而不是从零猜。OpcUaClient_VB.zip就是这样一个东西一个用 VB.NET 实现的 OPC UA 客户端工程定位是学习借鉴。它适合两类人——一类是维护 VB 技术栈、需要快速接入 OPC UA 设备的工控开发者另一类是想搞懂 OPC UA 客户端底层交互流程、但被 C# 示例绕晕的初学者。它不解决高并发采集或复杂信息模型建模但能把最核心的客户端通信骨架摊开给你看。下面按「先搞懂它在做什么、再动手跑通、最后避开几个必踩的坑」的顺序拆。2. OPC UA 客户端通信骨架从 Endpoint 到 Subscription 的四个阶段2.1 为什么 VB 版客户端值得单独拆一遍OPC UA 的协议栈本身是语言无关的但各家 SDK 的封装风格差异很大。C# 生态里有官方栈和一堆开源封装示例丰富VB.NET 虽然同属 .NET 平台能直接引用同一套程序集但语法上的Async/Await、Handles子句、ByRef参数传递以及事件处理器的写法都和 C# 有肉眼可见的区别。很多 VB 开发者拿到 C# 示例后第一步就卡在「这段async Task怎么改成 VB 的Async Function」。这份源码的价值就在于它把 SDK 的调用全部用 VB 语法落地了。你不需要做语言翻译直接看它怎么声明会话对象、怎么挂事件、怎么在Try...Catch里处理ServiceResultException。常见做法是先用它跑通一个最小连接再把自己的点位表替换进去。我一般会建议先只改 IP 和端口确认能连上再去动节点 ID 和订阅参数这样出问题时排查范围小。2.2 四个阶段连接、会话、订阅、读写一个 OPC UA 客户端的生命周期拆开就是四步。第一步是发现 Endpoint客户端向服务端的发现地址发请求拿回它支持的传输协议和安全策略列表。第二步是创建 Session用选定的 Endpoint 建立会话这一步会涉及用户身份认证。第三步是创建 Subscription告诉服务端「我要订阅哪些节点、多久推一次」。第四步才是 Read/Write 和订阅回调里的数据处理。这四步在源码里通常对应四个方法或四个代码块。看的时候重点盯三处Endpoint 的 SecurityPolicy 选了哪个、Session 的 UserIdentity 用的什么类型、Subscription 的 PublishingInterval 设了多少毫秒。这三个参数决定了你能不能连上、以什么身份连、数据刷新快不快。下面用一段 VB 代码示意连接和会话的建立过程实际源码里的类名和变量名可能不同但结构一致。 创建应用配置ApplicationName 和 ApplicationUri 需唯一 Dim appConfig As New ApplicationConfiguration() With { .ApplicationName VbOpcClient, .ApplicationUri urn:localhost:VbOpcClient, .ApplicationType ApplicationType.Client } 加载端点描述发现服务端支持的 Endpoint Dim endpointDescription As EndpointDescription Nothing Dim endpointConfiguration As EndpointConfiguration EndpointConfiguration.Create(appConfig) Dim discoveryClient As DiscoveryClient DiscoveryClient.Create(endpointUrl, endpointConfiguration) Dim endpoints As EndpointDescriptionCollection discoveryClient.GetEndpoints(Nothing) 按安全策略筛选这里选 None 用于内网调试 For Each ep As EndpointDescription In endpoints If ep.SecurityPolicyUri.EndsWith(None) Then endpointDescription ep Exit For End If Next 用选定的 Endpoint 创建会话 Dim endpoint As New ConfiguredEndpoint(Nothing, endpointDescription) Dim session As Session Session.Create( appConfig, endpoint, False, VbOpcSession, 60000, New UserIdentity(), Nothing ).Result这段代码的逻辑是先构造应用配置再用发现客户端拉取端点列表然后按安全策略筛选出一个可用端点最后用这个端点创建会话。参数上ApplicationUri必须唯一重复了服务端可能拒绝Session.Create的第五个参数是超时时间单位毫秒60000 表示 60 秒UserIdentity传空对象表示匿名登录如果服务端要求用户名密码这里要换成New UserIdentity(user, pwd)。注意.Result是同步等待异步任务在 UI 线程里用会卡界面正式代码里应该用Await。2.3 订阅与数据变化通知的落地写法连接和会话只是通了真正让客户端「活」起来的是订阅。OPC UA 的订阅模型是客户端创建一个 Subscription在它下面挂若干 MonitoredItem每个 MonitoredItem 绑定一个节点和一个回调。服务端按 PublishingInterval 周期性地把变化推给客户端客户端在回调里处理数据。源码里这部分通常是事件驱动的。VB 里挂事件用AddHandler回调方法签名要和委托匹配。下面示意订阅创建和回调挂载。 创建订阅PublishingInterval 单位毫秒 Dim subscription As Subscription New Subscription(session.DefaultSubscription) With { .PublishingInterval 1000, .KeepAliveCount 10, .LifetimeCount 20, .MaxNotificationsPerPublish 100, .PublishingEnabled True } session.AddSubscription(subscription) subscription.Create() 创建监控项绑定节点 ID Dim item As New MonitoredItem(subscription.DefaultItem) With { .DisplayName Tag001, .StartNodeId New NodeId(ns2;sDevice1.Tag001), .AttributeId Attributes.Value, .SamplingInterval 500, .QueueSize 10 } 挂数据变化回调 AddHandler item.Notification, AddressOf OnDataChange subscription.AddItem(item) subscription.ApplyChanges()逻辑说明PublishingInterval是服务端推送周期1000 毫秒表示每秒推一次SamplingInterval是服务端采样周期500 毫秒表示每半秒采一次它应该小于等于推送周期否则没意义。QueueSize是队列长度网络抖动时能缓几个值。StartNodeId里的ns2;sDevice1.Tag001是节点标识ns是命名空间索引s表示字符串型标识符实际值要从服务端的地址空间里查。回调OnDataChange里拿到的MonitoredItemNotification对象包含值和状态码状态码不是Good时要判断是通信断了还是节点质量差。2.4 读写操作的同步与异步选择订阅适合周期性数据但有些场景需要主动读一次或写一次比如点击按钮读当前值、下发一个设定值。OPC UA 的 Read/Write 有同步和异步两套 API。VB 里同步调用写起来简单但在网络慢的时候会阻塞线程异步调用要用Await代码结构复杂一点但不卡界面。源码里如果两种都有建议先看同步版本理解流程再切到异步版本用于实际项目。读操作的关键是构造ReadValueId集合指定节点和属性写操作要构造WriteValue里面包含值和数据类型。写的时候数据类型必须和服务端节点定义的一致比如服务端定义的是Int16你传Integer就可能报类型不匹配。这是新手最容易翻车的地方之一。3. 把源码跑起来环境、依赖与最小连接验证3.1 开发环境与依赖项确认这份源码是 VB.NET 工程跑起来需要 .NET 环境和 OPC UA 库。常见做法是用 Visual Studio 打开.vbproj或.sln文件确认目标框架版本。如果源码里引用了Opc.Ua.Client、Opc.Ua.Core这类程序集需要用 NuGet 还原。VB 工程对 NuGet 的支持没问题但有时候packages.config和PackageReference两种管理方式混用会出问题还原失败时先看引用路径是不是指向了不存在的目录。环境确认清单.NET Framework 4.6 以上或 .NET Core 3.1 以上、Visual Studio 2019 及以上、NuGet 能正常访问包源。如果目标机器是内网离线环境需要提前把 nupkg 包下载好用本地源还原。这一步没有捷径依赖装不上后面全白搭。3.2 配置服务端地址与安全策略源码里通常有一个配置文件或一个常量区写着服务端地址。OPC UA 的地址格式是opc.tcp://主机:端口默认端口 4840。改地址的时候注意如果服务端和客户端不在同一台机器主机名要能被解析防火墙要放行对应端口。安全策略是第二个要改的地方。内网调试阶段很多人先用SecurityPolicy.None把链路跑通确认数据能读到了再回头配证书和加密。这个顺序是对的但要注意None策略下UserIdentity通常也只能匿名如果服务端强制要求加密和认证你就必须先生成客户端证书、导入服务端信任列表再改策略。证书这块的坑后面单独说。3.3 最小连接验证先读一个节点环境好了、地址对了下一步是验证连接。不要一上来就订阅一堆点位先读一个节点。找一个服务端上确定存在的节点比如ServerStatus下的CurrentTime用同步读的方式读一次看返回值是不是Good。 构造读请求只读一个节点的 Value 属性 Dim nodesToRead As New ReadValueIdCollection() nodesToRead.Add(New ReadValueId() With { .NodeId New NodeId(ns0;i2258), ServerStatus.CurrentTime .AttributeId Attributes.Value }) Dim results As DataValueCollection Nothing Dim errors As DiagnosticInfoCollection Nothing session.Read(Nothing, 0, TimestampsToReturn.Both, nodesToRead, results, errors) 检查结果 If results(0).StatusCode StatusCodes.Good Then Console.WriteLine(当前时间: results(0).Value.ToString()) Else Console.WriteLine(读取失败: results(0).StatusCode.ToString()) End If这段代码读的是命名空间 0 下的标准节点CurrentTime节点 ID 是数字型i2258。TimestampsToReturn.Both表示同时返回源时间戳和服务端时间戳。读成功说明连接、会话、权限都没问题可以进入下一步。如果返回BadNodeIdUnknown说明节点 ID 写错了返回BadNotConnected说明会话断了返回BadUserAccessDenied说明权限不够。按状态码排查比瞎猜快得多。3.4 替换成自己的点位并启动订阅最小连接通了之后把源码里的示例节点 ID 替换成你自己的点位。点位信息一般从服务端的地址空间浏览器里拿或者从设备手册里查。替换时注意命名空间索引ns2和ns3是完全不同的命名空间索引号不能想当然。替换完点位把订阅的PublishingInterval和SamplingInterval按实际需求调一下。数据变化快的点位采样周期调小变化慢的调大一点省资源。启动订阅后观察回调有没有正常触发值的变化是否符合预期。如果回调一直不触发先确认PublishingEnabled是不是True再确认ApplyChanges有没有调用。4. 避坑与排查VB 版 OPC UA 客户端最常见的五类问题4.1 连接被拒现象是BadConnectionRejected原因是安全策略不匹配现象客户端发起连接后立刻返回BadConnectionRejected或BadSecurityChecksFailed。原因通常是客户端选的 Endpoint 安全策略和服务端要求的不一致比如服务端只允许Basic256Sha256客户端却用了None。解决方法是先用发现客户端把服务端所有 Endpoint 打印出来看清楚它支持哪些策略再选一个匹配的。如果服务端要求加密客户端必须有自己的证书并且证书要被服务端信任。4.2 证书报错现象是BadCertificateUntrusted原因是信任列表没配现象连接时提示证书不受信任或者BadCertificateUriInvalid。原因是客户端证书没有被服务端信任或者服务端证书没有被客户端信任。OPC UA 是双向信任的两边都要把对方的证书加入信任列表。解决方法是找到客户端证书的存放目录通常在应用配置的SecurityConfiguration里指定把服务端证书复制进去同时把客户端证书导出给服务端管理员导入。证书的ApplicationUri必须和实际一致不一致也会报错。4.3 订阅不触发现象是回调一直不执行原因是参数或节点问题现象订阅创建成功但回调方法从来不执行。原因可能有三个PublishingEnabled没设成TrueApplyChanges没调用节点 ID 写错了导致监控项创建失败但没检查返回值。解决方法是先检查subscription.Create()和subscription.ApplyChanges()的返回状态再逐个检查MonitoredItem的Status.Error。如果监控项状态是BadNodeIdUnknown就是节点 ID 的问题。4.4 类型不匹配现象是写入失败BadTypeMismatch原因是数据类型对不上现象写操作返回BadTypeMismatch或BadTypeMismatch。原因是客户端写入的值类型和服务端节点定义的类型不一致比如服务端是UInt16客户端传了Integer。解决方法是先读一次该节点的值看返回的Value是什么类型写入时用Convert.ToUInt16之类的转换对齐。VB 里CType和DirectCast的行为不同类型转换要用Convert系列方法更稳妥。4.5 会话超时断开现象是运行一段时间后BadSessionClosed原因是超时或网络现象客户端跑了一段时间后突然报BadSessionClosed或BadTimeout。原因是会话超时时间设得太短或者网络中间有断开。OPC UA 的会话有SessionTimeout默认可能只有几万毫秒长时间无交互会被服务端回收。解决方法是在Session.Create时把超时时间设大一点同时加一个保活机制定期读一个节点维持会话。网络不稳定的话还要加重连逻辑捕获异常后重新创建会话和订阅。5. 进阶用法把这份源码改造成可复用的采集组件5.1 封装连接管理类处理断线重连源码跑通之后下一步是把它变成能长期运行的东西。最直接的做法是封装一个连接管理类把 Session、Subscription 的创建和销毁包起来对外只暴露「连接」「断开」「读」「写」几个方法。断线重连的逻辑放在类内部捕获ServiceResultException判断状态码是不是会话相关是的话销毁旧会话、重新创建、重新挂订阅。Public Class OpcClientManager Private _session As Session Private _subscription As Subscription Private _endpointUrl As String Public Sub Connect(endpointUrl As String) _endpointUrl endpointUrl Try 创建会话和订阅的代码 Catch ex As ServiceResultException If ex.StatusCode StatusCodes.BadSessionClosed OrElse ex.StatusCode StatusCodes.BadConnectionClosed Then 等待后重连 Threading.Thread.Sleep(5000) Connect(_endpointUrl) Else Throw End If End Try End Sub End Class这个类的关键是重连时的资源清理。旧会话要先Close再Dispose订阅要先Delete再释放不然会泄漏。重连不要无限递归加一个重试次数上限超过就报警。实际项目里我一般会把重试间隔做成递增的第一次 5 秒第二次 10 秒避免服务端还没恢复就被频繁冲击。5.2 用配置文件管理点位避免硬编码点位硬编码在代码里改一个点位就要重新编译这在现场很不方便。常见做法是把点位表放到配置文件里比如 XML 或 JSON程序启动时读取。每个点位配三个字段显示名、节点 ID、采样周期。订阅创建时遍历这个列表动态生成MonitoredItem。配置项示例值说明DisplayNameTag001显示名称用于日志和界面NodeIdns2;sDevice1.Tag001节点标识必须与服务端一致SamplingInterval500采样周期单位毫秒PublishingInterval1000推送周期单位毫秒配置文件的好处是现场调试时不用动代码改完重启即可。注意节点 ID 里的命名空间索引在不同服务端上可能不同部署到新环境时要重新核对。5.3 验证方法用服务端模拟器做回归测试改完代码怎么验证不要直接上生产设备先用 OPC UA 服务端模拟器。模拟器能造出各种类型的节点和异常状态用来测试你的重连逻辑、类型转换、订阅回调。测试时重点看三个场景正常读写、服务端主动断开后客户端能否自动重连、写入错误类型时是否返回明确错误。这三个场景过了基本就能上现场了。从那以后我每次拿到一份客户端源码都强制先跑通「读一个标准节点」这一步再动其他代码。这一步过了后面都是参数问题这一步没过后面全是玄学。希望帮到你。本文还有配套的精品资源点击获取