安装 OPC UA SDK
通过 NuGet 包管理器,在 Visual Studio 中右键单击项目名称,选择 "管理 NuGet 程序包",在搜索框中输入 "OPCFoundation.NetStandard.Opc.Ua",找到对应的 OPC UA SDK 包后点击 "安装",将其集成到 C# 项目中。
它和OPC安装流程一样。
配置 OPC UA 客户端应用程序
- 创建一个
ApplicationConfiguration
对象,用于配置 OPC UA 客户端的应用程序名称、应用程序 URI、产品 URI、安全配置等信息。例如:
cs
ApplicationConfiguration config = new ApplicationConfiguration()
{
ApplicationName = "OPC UA Client",
ApplicationUri = "urn:localhost:OPC-UA-Client",
ProductUri = "urn:OPC-UA-Client",
SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier
{
StoreType = "Directory",
StorePath = @"C:\OPC_UA_Client_Certificates",
SubjectName = "OPC-UA-Client"
}
},
TransportConfigurations = new TransportConfigurationCollection(),
ClientConfiguration = new ClientConfiguration()
};
config.Validate(ApplicationType.Client);
连接到 OPC UA 服务器
- 使用
EndpointDescription
类指定 OPC UA 服务器的端点地址,格式通常为 "opc.tcp:// 服务器 IP 地址:端口号"。例如:EndpointDescription endpointDescription = EndpointDescription.Create("opc.tcp://localhost:4840");
cs
EndpointDescription endpointDescription = EndpointDescription.Create("opc.tcp://localhost:4840");
- 创建
EndpointConfiguration
和ConfiguredEndpoint
对象,用于配置和定义连接到服务器的端点。
cs
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(config);
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
- 使用
Session.Create
方法创建一个 OPC UA 会话,用于与服务器进行通信。
cs
Session session = Session.Create(config, endpoint, false, "OPC-UA-Client", 60000, new UserIdentity(new AnonymousIdentityToken()), null).Result;
读取设备数据
- 确定要读取的设备数据对应的 OPC UA 节点 ID,可以通过设备的文档或在 OPC UA 服务器的地址空间中查找获得。例如,假设要读取一个名为 "ns=2;s=Demo.Static.Scalar.Double" 的节点的值:
cs
NodeId nodeId = new NodeId("ns=2;s=Demo.Static.Scalar.Double");
DataValue dataValue = session.ReadValue(nodeId);
Console.WriteLine($"Node Value: {dataValue.Value}");
写入设备数据
- 确定要写入的设备数据对应的 OPC UA 节点 ID 和要写入的值。例如,要向一个名为 "ns=2;s=Demo.Static.Scalar.Int" 的节点写入整数值 100:
cs
NodeId nodeId = new NodeId("ns=2;s=Demo.Static.Scalar.Int");
DataValue dataValue = new DataValue(100);
session.WriteValue(nodeId, dataValue);
订阅数据变更和处理事件
- 使用
MonitoredItem
类创建一个监控项,指定要监控的节点 ID 和监控模式等。例如:
cs
NodeId nodeId = new NodeId("ns=2;s=Demo.Static.Scalar.Double");
MonitoredItem monitoredItem = new MonitoredItem(session.DefaultSubscription,
1000,
nodeId,
MonitoringMode.Reporting,
null);
monitoredItem.Notification += OnDataChange;
session.DefaultSubscription.AddItem(monitoredItem);
session.DefaultSubscription.ApplyChanges();
- 在事件处理方法中处理数据变更通知。例如:
cs
private static void OnDataChange(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args)
{
DataValue dataValue = args.NotificationValue;
Console.WriteLine($"Data changed: {dataValue.Value}");
}
断开与 OPC UA 服务器的连接
当不再需要与 OPC UA 服务器通信时,调用session.Close
方法关闭会话,释放资源。例如:
cs
session.Close();
OPC-UA协议通信
cs
using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.Threading.Tasks;
class OpcUaClient
{
private static ApplicationConfiguration config;
private static Session session;
static async Task Main()
{
// 配置应用程序
config = new ApplicationConfiguration()
{
ApplicationName = "OPC UA Client",
ApplicationUri = "urn:localhost:OPC-UA-Client",
ProductUri = "urn:OPC-UA-Client",
SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier
{
StoreType = "Directory",
StorePath = @"C:\OPC_UA_Client_Certificates",
SubjectName = "OPC-UA-Client"
}
},
TransportConfigurations = new TransportConfigurationCollection(),
ClientConfiguration = new ClientConfiguration()
};
config.Validate(ApplicationType.Client);
// 尝试连接到服务器
try
{
await ConnectToServer("opc.tcp://localhost:4840");
Console.WriteLine("Connected to OPC UA Server.");
// 读取数据
await ReadData();
// 写入数据
await WriteData();
// 订阅数据
await SubscribeData();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
// 关闭会话
await DisconnectFromServer();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
// 连接到服务器
static async Task ConnectToServer(string endpointUrl)
{
EndpointDescription endpointDescription = EndpointDescription.Create(endpointUrl);
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(config);
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);
session = await Session.Create(config, endpoint, false, "OPC-UA-Client", 60000, new UserIdentity(new AnonymousIdentityToken()), null);
}
// 读取数据
static async Task ReadData()
{
NodeId nodeId = new NodeId("ns=2;s=Demo.Static.Scalar.Double");
DataValue dataValue = await session.ReadValueAsync(nodeId);
Console.WriteLine($"Read value from node {nodeId}: {dataValue.Value}");
}
// 写入数据
static async Task WriteData()
{
NodeId nodeId = new NodeId("ns=2;s=Demo.Static.Scalar.Int");
DataValue dataValue = new DataValue(100);
await session.WriteValueAsync(nodeId, dataValue);
Console.WriteLine($"Wrote value to node {nodeId}");
}
// 订阅数据
static async Task SubscribeData()
{
NodeId nodeId = new NodeId("ns=2;s=Demo.Static.Scalar.Double");
MonitoredItem monitoredItem = new MonitoredItem(session.DefaultSubscription,
1000,
nodeId,
MonitoringMode.Reporting,
null);
monitoredItem.Notification += OnDataChange;
session.DefaultSubscription.AddItem(monitoredItem);
await session.DefaultSubscription.ApplyChangesAsync();
Console.WriteLine($"Subscribed to node {nodeId}");
}
// 处理数据变更事件
private static void OnDataChange(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args)
{
DataValue dataValue = args.NotificationValue;
Console.WriteLine($"Data changed: {dataValue.Value}");
}
// 断开连接
static async Task DisconnectFromServer()
{
if (session!= null)
{
await session.CloseAsync();
Console.WriteLine("Disconnected from OPC UA Server.");
}
}
}
OPC-UA实例解释:
-
配置应用程序:
-
ApplicationConfiguration
对象用于配置客户端的各种信息,包括应用程序名称、URI、安全配置等。 -
SecurityConfiguration
部分指定了证书存储的类型、路径和主题名称,确保安全通信。 -
config.Validate(ApplicationType.Client)
用于验证客户端配置是否正确。
-
-
连接到服务器:
-
ConnectToServer
方法根据提供的端点 URL 创建端点描述和配置,然后使用Session.Create
创建会话。 -
端点 URL 格式为
opc.tcp://服务器IP地址:端口号
,使用AnonymousIdentityToken
作为用户身份令牌。
-
-
读取数据:
-
ReadData
方法使用NodeId
指定要读取的节点,然后使用session.ReadValueAsync
方法异步读取该节点的值。 -
NodeId
包含节点的命名空间索引和标识符,通过它可以唯一确定一个节点。
-
-
写入数据:
-
WriteData
方法使用NodeId
指定要写入的节点,并创建一个包含要写入值的DataValue
对象。 -
然后使用
session.WriteValueAsync
方法将该值写入节点。
-
-
订阅数据:
-
SubscribeData
方法使用MonitoredItem
类创建一个监控项,指定要监控的节点和监控模式。 -
monitoredItem.Notification += OnDataChange
注册一个事件处理程序,当数据变化时触发OnDataChange
方法。 -
session.DefaultSubscription.ApplyChangesAsync()
将订阅更改应用到会话。
-
-
数据变更事件处理:
OnDataChange
方法接收MonitoredItemNotificationEventArgs
,从中提取数据值并输出。
-
断开连接:
DisconnectFromServer
方法使用session.CloseAsync
关闭会话。
使用说明:
-
确保在运行代码前,已经在
C:\OPC_UA_Client_Certificates
目录下配置了相应的证书(或者根据实际情况修改证书存储路径)。 -
代码中假定 OPC UA 服务器的端点为
opc.tcp://localhost:4840
,请根据实际服务器信息修改。 -
代码中的节点
ns=2;s=Demo.Static.Scalar.Double
和ns=2;s=Demo.Static.Scalar.Int
仅为示例,需根据实际服务器中的节点进行修改。
这个示例展示了如何使用 C# 和 OPC UA 进行基本的连接、读取、写入和订阅操作。
OPC-UA协议通信注意事项
安全方面
-
证书管理:OPC UA 通常依赖于证书进行身份验证和加密。确保正确配置客户端和服务器的证书,包括证书的生成、存储和加载。如果证书配置错误或不匹配,可能导致连接失败或安全漏洞,如在客户端配置中指定正确的证书存储路径和证书主题名称。
-
用户身份验证:根据服务器的安全策略,可能需要提供有效的用户身份信息进行登录。除了匿名访问外,还可能涉及用户名 / 密码、X.509 证书等多种身份验证方式,要确保正确处理身份验证过程,以获取相应的访问权限。
-
网络安全:由于 OPC UA 通信通常在网络环境中进行,要确保网络的安全性,如使用防火墙限制对 OPC UA 端口的访问,只允许授权的 IP 地址连接到 OPC UA 服务器,防止网络攻击和未经授权的访问。
连接与配置
-
端点配置:正确配置 OPC UA 服务器的端点地址,包括协议(如 opc.tcp://)、服务器 IP 地址和端口号等。如果端点配置错误,将无法建立连接,可通过网络配置工具或咨询服务器管理员获取准确的端点信息。
-
会话管理:合理地创建、使用和关闭会话。在使用完会话后及时关闭,以释放资源并确保与服务器的连接状态正确维护,避免出现资源浪费或连接异常的情况。
-
性能调优:根据实际应用场景,可能需要调整 OPC UA 客户端的性能相关配置,如设置合适的会话超时时间、操作超时时间、订阅采样间隔等,以平衡通信效率和系统资源消耗。
数据处理
-
数据类型映射:OPC UA 中的数据类型与 C# 中的数据类型可能不完全一致,需要进行正确的映射。在读取和写入数据时,确保将 OPC UA 数据类型正确转换为 C# 中的相应数据类型,反之亦然,以避免数据错误或丢失。
-
数据一致性:在进行批量数据读写或订阅多个节点时,要注意数据的一致性和同步性,确保在处理数据时不会出现数据冲突或不一致的情况,特别是在多线程或异步操作的环境中。
-
错误处理:在数据读写和订阅过程中,可能会出现各种错误,如节点不存在、权限不足、网络故障等。要全面地捕获和处理这些错误,提供清晰的错误提示和相应的处理机制,以提高系统的稳定性和可靠性。
兼容性与版本
-
OPC UA 规范版本:确保客户端和服务器都遵循相同版本的 OPC UA 规范,不同版本之间可能存在功能和数据结构的差异,导致通信问题或功能不兼容,必要时进行协议版本的适配和升级。
-
库版本兼容性:使用的 OPC UA 客户端库要与项目的其他依赖库和目标运行环境兼容,避免出现版本冲突或不兼容的情况,及时更新库版本以获取最新的功能和修复已知的问题。
内存管理
-
资源释放:在使用 OPC UA 相关对象,如会话、订阅、监控项等时,要及时释放它们占用的内存和系统资源,特别是在长时间运行的应用程序中,防止内存泄漏导致系统性能下降甚至崩溃。
-
大型数据处理:当处理大量的 OPC UA 数据时,要注意内存的使用情况,避免一次性加载过多数据导致内存溢出,可以采用分页、缓存等策略优化内存使用。