C# 使用NetStandard.Opc.Ua读写OPC UA数据

本文详细介绍了OPC Foundation的.NetStandard.Opc.Ua开源库在OPC UA客户端应用中的使用方法,包括订阅分组、连接管理、数据读写及异常处理。强调了订阅分组限制、连接断开的处理方式、不同类型的读写操作以及数据类型的匹配问题。此外,还提供了一个C# API控制器的示例代码,展示了如何封装接口以进行数据交互。

       OPCFoundation.NetStandard.Opc.Ua是OPC基金会发布的开源OPC UA包,提供Client读写库方便读写OPC 服务器数据。

     OPCFoundation.NetStandard.Opc.Ua开源项目地址:https://github.com/OPCFoundation/UA-.NETStandard

     使用时有几点需要注意。

1、订阅分组

        OPC UA的数据订阅分组,和OPC DA的有所不同,每个组数据项有限,这个值具体是多少与服务器的配置有关,一般几百到上千。每个组的订阅名称也不能相同,需要考虑订阅名称分类。服务器对与订阅资源是有限的,有的服务器必须显式的释放订阅资源,不然会造成订阅资源耗尽而订阅失败,必须重启服务端软件才能解决。 

2、连接断开

     OPC Client与服务器连接断开有两种情况:主动断开和被动断开

     客户端主动断开时,必须先注销订阅的数据分组项,再进行断开。目前这个库不能在连接关闭时自动释放所有服务器的连接资源,这个也是这个库不太友好的地方。
     如果是服务端主动断开,很可能是服务器关闭了,这时直接关闭即可。

3、数据读取

      数据读取有订阅、同步和异步几种方法。订阅较为复杂但性能高、占用的网络带宽也最少。同步与异步仅调用语法有差异,其他类似。

      同步或异步都数据时,读多少数据点,就传入多少,然后到返回值中拿数据,C#可以作比较好的类型转换。

4、数据写入

      OPC UA在数据写入时,对数据类型有严格要求,数据类型不会自动转换,会写入失败。如果仅在C#系统内部写入数据,且仅少数点写入这时读有次数据,记录返回的数据类型,固定编码写入。

     在数据量较多,且有数据订阅的情况下,也可以记录每个点的数据类型,在写入数据时索引数据类型,然后自动转换。

    在把数据接口封装为API的情况下,同一个数据可以解析为很多数据类型,比如数据123,可以是Byte,I2、U2、I4、U4、R4等很多类型,反序列化时一般会解析为Single,直接调用写入会因数据类型不正确而失败。这时除了使用索引,自己处理数据兼容性问题,也可以多写几个wrapper来处理数据类型转换问题。

   示例的Controller类代码如下:

    [ApiController]
    [Route("[controller]/[action]")]
    public class OpcUaControlServiceController : ControllerBase
    {
        private readonly ILogger<OpcUaControlServiceController> _logger;

                                                                                               
        public OpcUaControlServiceController(ILogger<OpcUaControlServiceController> logger)
        {
            _logger = logger;
        }
        /// <summary>
        /// 获取服务运行信息
        /// </summary>
        /// <returns>运行信息概览JSON</returns>
        [HttpPost,HttpGet]
        public string info()
        {
            return ApiContentResultMsg.Success(OpcUaControlService.Instance.Info());
        }
        /// <summary>
        /// 启动OpcUa 控制服务
        /// </summary>
        /// <returns>执行信息JSON</returns>
        [HttpPost]
        public string Start()
        {
            OpcUaControlService.Instance.Start();
            return ApiContentResultMsg.Success();
        }

        /// <summary>
        /// 停止OpcUa 控制服务
        /// </summary>
        /// <returns>执行信息JSON</returns>
        [HttpPost]
        public string ShutDown()
        {
            OpcUaControlService.Instance.ShutDown();
            return ApiContentResultMsg.Success();
        }

        /// <summary>
        /// 写入Int32 类型 OpcUa NodeId数据
        /// </summary>
        /// <returns>执行信息JSON</returns>
        [HttpPost]
        public string SyncWriteOpcUaInt32(String nodeId, Int32 value)
        {
            bool b = OpcUaControlService.Instance.SyncWriteOpcUaInt32(nodeId, value);
            return b?ApiContentResultMsg.Success(): ApiContentResultMsg.Error("write fail!");
       }

        /// <summary>
        /// 写入float 类型 OpcUa NodeId数据
        /// </summary>
        /// <returns>执行信息JSON</returns>
        [HttpPost]
        public string SyncWriteOpcUaFloat(String nodeId, float value)
        {
            bool b = OpcUaControlService.Instance.SyncWriteOpcUaFloat(nodeId, value);
            return b ? ApiContentResultMsg.Success() : ApiContentResultMsg.Error("write fail!");
        }

        /// <summary>
        /// 写入bool 类型 OpcUa NodeId数据
        /// </summary>
        /// <returns>执行信息JSON</returns>
        [HttpPost]
        public string SyncWriteOpcUaBool(String nodeId, bool value)
        {
            bool b = OpcUaControlService.Instance.SyncWriteOpcUaBool(nodeId, value);
            return b ? ApiContentResultMsg.Success() : ApiContentResultMsg.Error("write fail!");
        }

        /// <summary>
        /// 写入String 类型 OpcUa NodeId数据
        /// </summary>
        /// <returns>执行信息JSON</returns>
        [HttpPost]
        public string SyncWriteOpcUaString(String nodeId, String value)
        {
            bool b = OpcUaControlService.Instance.SyncWriteOpcUaString(nodeId, value);
            return b ? ApiContentResultMsg.Success() : ApiContentResultMsg.Error("write fail!");
        }

    }

5、参考资料

日本DeviceXPlorer OPC Server软件,在日系PLC和数控支持上比较友好,适用于1万点以内的采集服务,其自带了有个客户端的Demo,参考如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
using Opc.Ua;
using System.Security.Cryptography.X509Certificates;

namespace DxpUaSimpleAPI
{
    public class DxpUaSimpleClass
    {
        public struct ReadItemResults
        {
            public object m_readResultValue;
            public StatusCode m_resultCodes;
            public DateTime m_sourceTimeStamp;
            public DateTime m_serverTimeStamp;
        }
        public struct WriteItemResults
        {
            public StatusCode m_resultCodes;
        }

        public delegate void onAsyncReadCompleteDelegate(List<string> asyncReadNodeName, List<ReadItemResults> AsyncReadItemResults);
        public delegate void onAsyncWriteCompleteDelegate(List<string> asyncWriteNodeName, List<WriteItemResults> asyncResultStatus);
        public delegate void monitoredItemNotification(List<string> monitoredItems, List<object> monitoredItemValue);

        private Session m_clientSession = null;
        private Dictionary<string, Subscription> m_subscriptionMap = new Dictionary<string, Subscription>();
        private ApplicationInstance m_application = new ApplicationInstance();
        private List<Subscription> m_subscriptions = new List<Subscription>();
        public onAsyncReadCompleteDelegate m_ReadEventHandler = null;
        public onAsyncWriteCompleteDelegate m_WriteEventHandler = null;
        public monitoredItemNotification m_MonitoredEventHandler = null;

        public struct CertificateStore
        {
            public string m_applicationCertificateFileName;
            public string m_applicationCertificateStore;
            public string m_trustedIssuerCertificatesStore;
            public string m_TrustedPeerCertificatesStore;
            public string m_RejectedCertificateStore;
        }

        public struct BrowseNodeResults
        {
            public QualifiedName m_browseName;
            public ExpandedNodeId m_nodeIdList;
            public LocalizedText m_displayName;
            public StatusCode m_statusCodes;
        }

        public void Initialize()
        {
            m_application.ApplicationType = ApplicationType.Client;
            m_application.ConfigSectionName = "Opc.Ua.SampleClient";

            try
            {
                m_application.LoadApplicationConfiguration(false);
                m_application.CheckApplicationInstanceCertificate(false, 0);
            }
            catch (Exception ex)
            {
 
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值