编辑
2025-11-29
C#
00

目录

引言
开发环境配置
安装OPC UA .NET Standard库
创建基本的OPC UA客户端
1. 创建项目
2. 添加命名空间引用
3. 编写客户端代码
4. 代码说明
进阶:实现读写功能的客户端
1. 创建项目
2. 添加命名空间引用
3. 编写客户端代码
4. 代码说明
示例:批量读取和写入变量
1. 批量读取变量
2. 批量写入变量
示例:浏览服务器的地址空间
总结

引言

OPC UA(统一架构)是一种适用于工业自动化的跨平台、可扩展且安全的通信协议。它提供了从工厂车间设备到企业管理层之间的可靠数据交换。随着.NET Standard的出现,开发人员可以使用C#构建跨平台的OPC UA客户端应用程序。

本文将详细介绍如何使用C#和OPC UA .NET Standard库创建OPC UA客户端应用程序,包括多个示例代码,帮助您深入理解客户端的实现方式。

开发环境配置

在开始编写代码之前,请确保您的开发环境中已安装以下工具:

  • Visual Studio 2019 或更高版本(可选,您也可以使用Visual Studio Code)
  • .NET Core SDK(用于跨平台开发)
  • OPC Foundation的OPC UA .NET Standard库

安装OPC UA .NET Standard库

在您的项目中,使用NuGet包管理器安装OPC UA .NET Standard库:

Bash
Install-Package OPCFoundation.NetStandard.Opc.Ua

image.png

或者,您可以在项目的.csproj文件中添加以下依赖项:

XML
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua" Version="1.4.365.48" />

创建基本的OPC UA客户端

下面将引导您创建一个基本的OPC UA客户端,连接到OPC UA服务器并读取变量的值。

1. 创建项目

在Visual Studio中创建一个新的C#控制台应用程序,命名为OpcUaBasicClient

2. 添加命名空间引用

Program.cs中,添加以下命名空间:

C#
using System; using System.Threading.Tasks; using Opc.Ua; using Opc.Ua.Client; using Opc.Ua.Configuration;

3. 编写客户端代码

C#
using Opc.Ua; using Opc.Ua.Client; using Opc.Ua.Configuration; namespace AppIndustry { internal class Program { static async Task Main(string[] args) { // 创建并配置应用程序实例 var application = new ApplicationInstance { ApplicationName = "OPC UA Basic Client", ApplicationType = ApplicationType.Client, ConfigSectionName = "OpcUaBasicClient" }; // 加载应用程序配置文件 await application.LoadApplicationConfiguration(false); // 检查应用程序证书 await application.CheckApplicationInstanceCertificate(false, 0); // 选择端点,连接到服务器 var endpointURL = "opc.tcp://127.0.0.1:49320"; // 替换为您的服务器地址 var selectedEndpoint = CoreClientUtils.SelectEndpoint(endpointURL, useSecurity: false); var endpointConfiguration = EndpointConfiguration.Create(application.ApplicationConfiguration); var endpoint = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration); // 创建会话 var session = await Session.Create( application.ApplicationConfiguration, endpoint, false, "OpcUaBasicClientSession", 60000, null, null); Console.WriteLine("已成功连接到OPC UA服务器。"); // 读取变量值 var nodeId = new NodeId("ns=2;s=Channel.Device.L1"); // 替换为您的变量NodeId var value = session.ReadValue(nodeId); Console.WriteLine($"读取到的变量值:{value.Value}"); // 关闭会话 session.Close(); Console.WriteLine("会话已关闭。"); } } }

OpcUaBasicClient.Config.xml

XML
<?xml version="1.0" encoding="utf-8"?> <ApplicationConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ua="http://opcfoundation.org/UA/2008/02/Types.xsd" xmlns="http://opcfoundation.org/UA/SDK/Configuration.xsd"> <ApplicationName>OPC UA Basic Client</ApplicationName> <ApplicationUri>urn:OpcUaBasicClient</ApplicationUri> <ProductUri>uri:opcfoundation.org:OpcUaBasicClient</ProductUri> <ApplicationType>Client_1</ApplicationType> <SecurityConfiguration> <ApplicationCertificate> <StoreType>Directory</StoreType> <StorePath>%CommonApplicationData%\OPC Foundation\CertificateStores\MachineDefault</StorePath> <SubjectName>CN=OPC UA Basic Client, C=US</SubjectName> </ApplicationCertificate> <TrustedIssuerCertificates> <StoreType>Directory</StoreType> <StorePath>%CommonApplicationData%\OPC Foundation\CertificateStores\UA Certificate Authorities</StorePath> </TrustedIssuerCertificates> <TrustedPeerCertificates> <StoreType>Directory</StoreType> <StorePath>%CommonApplicationData%\OPC Foundation\CertificateStores\UA Applications</StorePath> </TrustedPeerCertificates> <RejectedCertificateStore> <StoreType>Directory</StoreType> <StorePath>%CommonApplicationData%\OPC Foundation\CertificateStores\RejectedCertificates</StorePath> </RejectedCertificateStore> <AutoAcceptUntrustedCertificates>true</AutoAcceptUntrustedCertificates> </SecurityConfiguration> <TransportConfigurations></TransportConfigurations> <TransportQuotas> <OperationTimeout>600000</OperationTimeout> <MaxStringLength>1048576</MaxStringLength> <MaxByteStringLength>4194304</MaxByteStringLength> <MaxArrayLength>65535</MaxArrayLength> <MaxMessageSize>4194304</MaxMessageSize> <MaxBufferSize>65535</MaxBufferSize> <ChannelLifetime>300000</ChannelLifetime> <SecurityTokenLifetime>3600000</SecurityTokenLifetime> </TransportQuotas> <ClientConfiguration> <DefaultSessionTimeout>60000</DefaultSessionTimeout> <WellKnownDiscoveryUrls> <ua:String>opc.tcp://{0}:4840</ua:String> <ua:String>http://{0}:52601/UADiscovery</ua:String> <ua:String>http://{0}/UADiscovery/Default.svc</ua:String> </WellKnownDiscoveryUrls> <MinSubscriptionLifetime>10000</MinSubscriptionLifetime> </ClientConfiguration> <TraceConfiguration> <OutputFilePath>%CommonApplicationData%\OPC Foundation\Logs\OpcUaBasicClient.log</OutputFilePath> <DeleteOnLoad>true</DeleteOnLoad> </TraceConfiguration> </ApplicationConfiguration>

4. 代码说明

  • ApplicationInstance:用于配置和管理OPC UA客户端应用程序的实例。
  • Session.Create:建立与OPC UA服务器的会话。
  • ReadValue:读取指定NodeId的变量值。

进阶:实现读写功能的客户端

在基本客户端的基础上,添加对变量写入和订阅的功能。

1. 创建项目

创建一个新的C#控制台应用程序,命名为OpcUaAdvancedClient

2. 添加命名空间引用

与基本客户端相同。

3. 编写客户端代码

C#
using Opc.Ua; using Opc.Ua.Client; using Opc.Ua.Configuration; namespace AppIndustry { internal class Program { static async Task Main(string[] args) { // 配置与连接过程同前述基本客户端 var application = new ApplicationInstance { ApplicationName = "OPC UA Basic Client", ApplicationType = ApplicationType.Client, ConfigSectionName = "OpcUaBasicClient" }; await application.LoadApplicationConfiguration(false); await application.CheckApplicationInstanceCertificate(false, 0); var endpointURL = "opc.tcp://127.0.0.1:49320"; var selectedEndpoint = CoreClientUtils.SelectEndpoint(endpointURL, useSecurity: false); var endpointConfiguration = EndpointConfiguration.Create(application.ApplicationConfiguration); var endpoint = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration); var session = await Session.Create( application.ApplicationConfiguration, endpoint, false, "OpcUaAdvancedClientSession", 60000, null, null); Console.WriteLine("已成功连接到OPC UA服务器。"); // 设置要操作的变量NodeId var nodeId = new NodeId("ns=2;s=Channel.Device.L1"); // 读取变量值 var readValue = session.ReadValue(nodeId); Console.WriteLine($"读取到的变量值:{readValue.Value}"); // 写入变量值 var nodeId1 = new NodeId("ns=2;s=Channel.Device.L2"); var writeValue = new WriteValue { NodeId = nodeId1, AttributeId = Attributes.Value, Value = new DataValue { Value = 42, // 要写入的新值 StatusCode = StatusCodes.Good, ServerTimestamp = DateTime.MinValue, SourceTimestamp = DateTime.MinValue } }; var writeValues = new WriteValueCollection { writeValue }; StatusCodeCollection statusCodes; DiagnosticInfoCollection diagnosticInfos; session.Write( null, writeValues, out statusCodes, out diagnosticInfos); if (StatusCode.IsGood(statusCodes[0])) { Console.WriteLine("成功写入变量值。"); } else { Console.WriteLine("写入变量值失败。"); } // 订阅变量变化 var subscription = new Subscription(session.DefaultSubscription) { PublishingInterval = 1000 }; session.AddSubscription(subscription); subscription.Create(); var monitoredItem = new MonitoredItem(subscription.DefaultItem) { DisplayName = "Variable1", StartNodeId = nodeId, AttributeId = Attributes.Value }; monitoredItem.Notification += MonitoredItem_Notification; subscription.AddItem(monitoredItem); subscription.ApplyChanges(); Console.WriteLine("已订阅变量变化。按任意键退出。"); Console.ReadKey(); // 清理资源 subscription.Delete(true); session.Close(); Console.WriteLine("会话已关闭。"); } private static void MonitoredItem_Notification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e) { foreach (var value in monitoredItem.DequeueValues()) { Console.WriteLine($"监控到的变量新值:{value.Value}"); } } } }

image.png

4. 代码说明

  • Write:向服务器中的变量写入新值。
  • SubscriptionMonitoredItem:订阅变量的变化,以接收数据更新。
  • MonitoredItem_Notification:当变量值变化时,触发该事件处理函数。

示例:批量读取和写入变量

1. 批量读取变量

C#
// 定义要读取的节点 var nodesToRead = new ReadValueIdCollection { new ReadValueId { NodeId = new NodeId("ns=2;s=Channel.Device.L1"), AttributeId = Attributes.Value }, new ReadValueId { NodeId = new NodeId("ns=2;s=Channel.Device.L2"), AttributeId = Attributes.Value } }; DataValueCollection results; DiagnosticInfoCollection diagnosticInfos; session.Read( null, 0, TimestampsToReturn.Both, nodesToRead, out results, out diagnosticInfos); for (int i = 0; i < results.Count; i++) { if (StatusCode.IsGood(results[i].StatusCode)) { Console.WriteLine($"变量{i + 1}的值:{results[i].Value}"); } else { Console.WriteLine($"读取变量{i + 1}失败,状态码:{results[i].StatusCode}"); } }

image.png

2. 批量写入变量

C#
using Opc.Ua; using Opc.Ua.Client; using Opc.Ua.Configuration; namespace AppIndustry { internal class Program { static async Task Main(string[] args) { // 配置与连接过程同前述基本客户端 var application = new ApplicationInstance { ApplicationName = "OPC UA Basic Client", ApplicationType = ApplicationType.Client, ConfigSectionName = "OpcUaBasicClient" }; await application.LoadApplicationConfiguration(false); await application.CheckApplicationInstanceCertificate(false, 0); var endpointURL = "opc.tcp://127.0.0.1:49320"; var selectedEndpoint = CoreClientUtils.SelectEndpoint(endpointURL, useSecurity: false); var endpointConfiguration = EndpointConfiguration.Create(application.ApplicationConfiguration); var endpoint = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration); var session = await Session.Create( application.ApplicationConfiguration, endpoint, false, "OpcUaAdvancedClientSession", 60000, null, null); Console.WriteLine("已成功连接到OPC UA服务器。"); // 定义要写入的节点和值 var nodesToWrite = new WriteValueCollection { new WriteValue { NodeId = new NodeId("ns=2;s=Channel.Device.S1"), AttributeId = Attributes.Value, Value = new DataValue(new Variant("Abc", TypeInfo.Scalars.String)) }, new WriteValue { NodeId = new NodeId("ns=2;s=Channel.Device.L2"), AttributeId = Attributes.Value, Value = new DataValue(new Variant(123, TypeInfo.Scalars.Int32)) } }; StatusCodeCollection writeResults; DiagnosticInfoCollection writeDiagnosticInfos; session.Write( null, nodesToWrite, out writeResults, out writeDiagnosticInfos); for (int i = 0; i < writeResults.Count; i++) { if (StatusCode.IsGood(writeResults[i])) { Console.WriteLine($"成功写入变量{i + 1}。"); } else { Console.WriteLine($"写入变量{i + 1}失败,状态码:{writeResults[i]}"); } } // 清理资源 session.Close(); Console.WriteLine("会话已关闭。"); } } }

image.png

示例:浏览服务器的地址空间

浏览OPC UA服务器的地址空间有助于动态发现可用的节点和变量。

C#
// 开始浏览 ReferenceDescriptionCollection references; byte[] continuationPoint; session.Browse( null, null, ObjectIds.ObjectsFolder, 0u, BrowseDirection.Forward, ReferenceTypeIds.HierarchicalReferences, true, (uint)NodeClass.Object | (uint)NodeClass.Variable, out continuationPoint, out references); Console.WriteLine("服务器的地址空间:"); foreach (var refDesc in references) { Console.WriteLine($"显示名称:{refDesc.DisplayName.Text}, NodeId: {refDesc.NodeId}, BrowseName: {refDesc.BrowseName.Name}"); }

image.png

总结

通过本文的介绍和丰富的示例代码,您应该对如何使用C#和OPC UA .NET Standard库创建功能完备的OPC UA客户端应用程序有了深入的了解。OPC UA作为工业通信的关键协议,结合.NET的强大开发能力,能够帮助您构建高效、可靠的工业应用程序。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!