OPC UA(统一架构)是一种适用于工业自动化的跨平台、可扩展且安全的通信协议。它提供了从工厂车间设备到企业管理层之间的可靠数据交换。随着.NET Standard的出现,开发人员可以使用C#构建跨平台的OPC UA客户端应用程序。
本文将详细介绍如何使用C#和OPC UA .NET Standard库创建OPC UA客户端应用程序,包括多个示例代码,帮助您深入理解客户端的实现方式。
在开始编写代码之前,请确保您的开发环境中已安装以下工具:
在您的项目中,使用NuGet包管理器安装OPC UA .NET Standard库:
BashInstall-Package OPCFoundation.NetStandard.Opc.Ua

或者,您可以在项目的.csproj文件中添加以下依赖项:
XML<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua" Version="1.4.365.48" />
下面将引导您创建一个基本的OPC UA客户端,连接到OPC UA服务器并读取变量的值。
在Visual Studio中创建一个新的C#控制台应用程序,命名为OpcUaBasicClient。
在Program.cs中,添加以下命名空间:
C#using System;
using System.Threading.Tasks;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
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>
在基本客户端的基础上,添加对变量写入和订阅的功能。
创建一个新的C#控制台应用程序,命名为OpcUaAdvancedClient。
与基本客户端相同。
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}");
}
}
}
}

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}");
}
}

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("会话已关闭。");
}
}
}

浏览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}");
}

通过本文的介绍和丰富的示例代码,您应该对如何使用C#和OPC UA .NET Standard库创建功能完备的OPC UA客户端应用程序有了深入的了解。OPC UA作为工业通信的关键协议,结合.NET的强大开发能力,能够帮助您构建高效、可靠的工业应用程序。
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!