C#开发OPC UA客户端

使用C#开发OPC UA客户端能帮助你在工业自动化和物联网项目中与各种设备进行可靠的数据交换。提供两种主流的开发方式:使用官方OPC UA .NET Standard SDK 和使用第三方库OpcUaHelper。这两种方式各有特点,适合不同的开发场景。先通过一个表格来快速了解它们的区别:

特性 OPC UA .NET Standard SDK (官方) OpcUaHelper (第三方)
来源 OPC基金会官方 开源社区(基于官方SDK封装)
复杂度 相对较低,提供更底层的控制和灵活性 更高,封装了底层细节,API更简洁
学习曲线 较陡峭,需理解OPC UA模型 平缓,上手快速
功能 全面,支持所有OPC UA特性 覆盖常用功能(读、写、订阅、浏览)
跨平台 支持(.NET Standard) 依赖底层SDK
适用场景 需要深度控制、自定义功能或学习底层机制 快速开发常规客户端应用

使用官方 OPC UA .NET Standard SDK 开发

这是OPC基金会提供的官方库,功能全面,支持最新的OPC UA标准,适合需要深度控制或学习底层机制的场景。

开发准备

  1. 安装NuGet包:在Visual Studio中,通过NuGet包管理器安装官方SDK:

    bash 复制代码
    Install-Package OPCFoundation.NetStandard.Opc.Ua
  2. 引入命名空间

    csharp 复制代码
    using Opc.Ua;
    using Opc.Ua.Client;
    using System;
    using System.Threading.Tasks;

基础代码示例

1. 配置应用程序并连接服务器

连接OPC UA服务器是第一步,需要配置应用程序实例和连接参数。

csharp 复制代码
class Program
{
    private static ApplicationConfiguration config;
    private static Session session;

    static async Task Main(string[] args)
    {
        // 创建应用程序配置
        config = new ApplicationConfiguration()
        {
            ApplicationName = "My OPC UA Client",
            ApplicationUri = "urn:localhost:MyClient",
            ProductUri = "urn:MyProduct",
            SecurityConfiguration = new SecurityConfiguration
            {
                ApplicationCertificate = new CertificateIdentifier
                {
                    StoreType = "Directory",
                    StorePath = @"C:\Certificates\Client", // 证书存储路径
                    SubjectName = "CN=MyClient"
                }
            },
            TransportConfigurations = new TransportConfigurationCollection(),
            ClientConfiguration = new ClientConfiguration()
        };
        config.Validate(ApplicationType.Client); // 验证配置

        // 连接到服务器
        try
        {
            await ConnectToServer("opc.tcp://localhost:4840");
            Console.WriteLine("成功连接到服务器!");

            // ... 在这里执行读写操作

        }
        catch (Exception ex)
        {
            Console.WriteLine($"连接失败: {ex.Message}");
        }
        finally
        {
            session?.Close();
        }
    }

    private static async Task ConnectToServer(string serverUrl)
    {
        // 创建端点描述
        EndpointDescription endpointDescription = EndpointDescription.Create(serverUrl);
        EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(config);
        ConfiguredEndpoint endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);

        // 创建会话
        session = await Session.Create(
            config,
            endpoint,
            false,
            "My Client",
            60000,
            new UserIdentity(new AnonymousIdentityToken()), // 匿名身份
            null
        );
    }
}

2. 读取节点数据

读取数据需要知道节点的唯一标识符 NodeId

csharp 复制代码
static async Task ReadData()
{
    // 假设要读取的节点ID
    NodeId nodeIdToRead = new NodeId("ns=2;s=DemoSensor.Temperature"); 

    try
    {
        // 读取节点值
        DataValue dataValue = session.ReadValue(nodeIdToRead);
        Console.WriteLine($"节点值: {dataValue.Value}, 时间戳: {dataValue.SourceTimestamp}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"读取失败: {ex.Message}");
    }
}

3. 写入节点数据

写入数据同样需要 NodeId 和要写入的值。

csharp 复制代码
static async Task WriteData()
{
    // 假设要写入的节点ID
    NodeId nodeIdToWrite = new NodeId("ns=2;s=DemoActuator.SetPoint"); 
    double newValue = 42.5;

    try
    {
        // 创建DataValue对象并写入
        DataValue dataToWrite = new DataValue(new Variant(newValue));
        session.WriteValue(nodeIdToWrite, dataToWrite);
        Console.WriteLine("写入成功!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"写入失败: {ex.Message}");
    }
}

4. 订阅与监控数据变化

订阅允许你在数据变化时自动接收通知,而不是不断轮询。

csharp 复制代码
static void SubscribeToDataChanges()
{
    // 创建订阅
    Subscription subscription = new Subscription(session.DefaultSubscription) {
        PublishingInterval = 1000 // 发布间隔1秒
    };

    // 创建监控项
    NodeId nodeToMonitor = new NodeId("ns=2;s=DemoSensor.Pressure");
    MonitoredItem monitoredItem = new MonitoredItem(subscription.DefaultItem)
    {
        StartNodeId = nodeToMonitor,
        AttributeId = Attributes.Value,
        SamplingInterval = 1000, // 采样间隔1秒
        NotificationQueueSize = 10,
        DiscardOldest = true
    };

    // 添加通知事件处理程序
    monitoredItem.Notification += (MonitoredItem item, MonitoredItemNotificationEventArgs e) =>
    {
        foreach (var value in item.DequeueValues())
        {
            Console.WriteLine($"数据变化: {value.Value} at {value.SourceTimestamp}");
        }
    };

    // 将监控项添加到订阅中,并将订阅添加到会话
    subscription.AddItem(monitoredItem);
    session.AddSubscription(subscription);
    subscription.Create();
}

参考代码 OPC UA 的客户端开发示例,采用C#编写 www.youwenfan.com/contentcse/112035.html

使用 OpcUaHelper 库开发

如果你希望快速上手,简化开发流程,OpcUaHelper是一个很好的选择。它对官方SDK进行了封装,提供了更简洁的API。

开发准备

  1. 安装NuGet包

    bash 复制代码
    Install-Package OpcUaHelper
  2. 引入命名空间

    csharp 复制代码
    using OpcUaHelper;
    using System;
    using System.Threading.Tasks;

基础代码示例

1. 连接服务器

csharp 复制代码
private OpcUaClient opcUaClient = new OpcUaClient();

private async Task ConnectToServerSimple()
{
    try
    {
        // 直接连接服务器
        await opcUaClient.ConnectServer("opc.tcp://localhost:4840");

        // 如果需要用户名密码认证
        // opcUaClient.UserIdentity = new Opc.Ua.UserIdentity("username", "password");
        // await opcUaClient.ConnectServer("opc.tcp://localhost:4840");

        Console.WriteLine("成功连接到服务器!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"连接失败: {ex.Message}");
    }
}

// 程序关闭时断开连接
private void FormClosingHandler(object sender, FormClosingEventArgs e)
{
    opcUaClient.Disconnect();
}

2. 读取节点数据

csharp 复制代码
private void ReadDataSimple()
{
    try
    {
        // 读取已知类型的节点值
        string stringValue = opcUaClient.ReadNode<string>("ns=2;s=MyNodeID.StringValue");
        double doubleValue = opcUaClient.ReadNode<double>("ns=2;s=MyNodeID.DoubleValue");
        int intValue = opcUaClient.ReadNode<int>("ns=2;s=MyNodeID.IntValue");

        Console.WriteLine($"字符串值: {stringValue}");
        Console.WriteLine($"双精度值: {doubleValue}");
        Console.WriteLine($"整型值: {intValue}");

        // 批量读取多个节点
        string[] nodeArray = new string[] {
            "ns=2;s=MyNodeID.StringValue",
            "ns=2;s=MyNodeID.DoubleValue",
            "ns=2;s=MyNodeID.IntValue"
        };
        object[] values = opcUaClient.ReadNodes(nodeArray);
        foreach (var value in values)
        {
            Console.WriteLine($"批量读取值: {value}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"读取失败: {ex.Message}");
    }
}

3. 写入节点数据

csharp 复制代码
private void WriteDataSimple()
{
    try
    {
        // 写入单个节点
        opcUaClient.WriteNode("ns=2;s=MyNodeID.DoubleValue", 42.5);
        Console.WriteLine("写入成功!");

        // 批量写入多个节点
        string[] nodeArray = new string[] {
            "ns=2;s=MyNodeID.StringValue",
            "ns=2;s=MyNodeID.DoubleValue",
            "ns=2;s=MyNodeID.IntValue"
        };
        object[] valuesToWrite = { "Hello", 42.5, 42 };
        opcUaClient.WriteNodes(nodeArray, valuesToWrite);
        Console.WriteLine("批量写入成功!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"写入失败: {ex.Message}");
    }
}

4. 订阅数据变化

csharp 复制代码
private void SubscribeToDataChangesSimple()
{
    try
    {
        // 添加订阅,指定节点和回调函数
        opcUaClient.AddSubscription("ns=2;s=DemoSensor.Temperature", OnDataChanged);
        Console.WriteLine("订阅成功!");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"订阅失败: {ex.Message}");
    }
}

// 数据变化时的回调函数
private void OnDataChanged(object value)
{
    Console.WriteLine($"数据发生变化: {value}");
}

开发注意事项

无论选择哪种方式,以下几点都需要注意:

  • 节点ID格式 :OPC UA节点的标识符(NodeId)格式多样(字符串、数字、GUID等),使用时需确保与服务器定义的格式完全一致。通常格式为 ns=<namespaceindex>;<identifiertype>=<value>(如 ns=2;s=MySensor.Temperature)。在开发前,最好使用OPC UA浏览器(如UaExpert)或服务器提供的管理工具来浏览服务器地址空间,准确获取节点的ID和数据类型。
  • 异常处理 :网络中断、权限不足、节点不存在等情况在工业环境中很常见。务必使用 try-catch 块封装你的OPC UA操作,并进行适当的错误处理和重试逻辑,以增强应用的稳定性。
  • 安全配置 :OPC UA支持多种安全策略(Security Policies)和消息安全模式(Message Security Modes),从无安全到高强度的签名加密。生产环境不应使用无安全模式。同时,正确处理证书(应用程序实例证书和信任的服务器证书)是建立安全连接的关键,可能会比较繁琐。
  • 资源管理 :OPC UA会话(Session)、订阅(Subscription)等对象可能占用服务器和网络资源。在应用程序关闭或不再需要时,记得调用 Close(), Disconnect()Dispose() 方法来及时释放这些资源,尤其是在长期运行的客户端应用中。
  • 异步操作 :许多OPC UA操作,如连接、读取、写入,都有异步方法(如 Session.CreateAsync)。在UI应用程序中,使用异步编程可以避免阻塞用户界面,提升用户体验。
相关推荐
西部风情9 分钟前
聊聊连续、递增
java·开发语言
蒙娜丽宁11 分钟前
Rust 并发编程进阶:线程模型、通道通信与异步任务对比分析
开发语言·网络·rust
又是忙碌的一天43 分钟前
java字符串
java·开发语言
Hi2024021743 分钟前
Qt+Qml客户端和Python服务端的网络通信原型
开发语言·python·qt·ui·网络通信·qml
chxii1 小时前
ISO 8601日期时间标准及其在JavaScript、SQLite与MySQL中的应用解析
开发语言·javascript·数据库
Teable任意门互动1 小时前
主流多维表格产品深度解析:飞书、Teable、简道云、明道云、WPS
开发语言·网络·开源·钉钉·飞书·开源软件·wps
程序员大雄学编程2 小时前
「用Python来学微积分」16. 导数问题举例
开发语言·python·数学·微积分
Dreams_l2 小时前
redis中的数据类型
java·开发语言
梵得儿SHI2 小时前
Java IO 流详解:字符流(Reader/Writer)与字符编码那些事
java·开发语言·字符编码·工作原理·字符流·处理文本
太过平凡的小蚂蚁2 小时前
Kotlin 协程中常见的异步返回与控制方式(速览)
开发语言·前端·kotlin