目录

一、内容准备

1.1、搭建OPC UA服务器环境

1.2、项目安装OPCUaHelper开源库

1.3、OpcUaHelper开源库

二、实现操作

2.1、连接OPC UA服务器,浏览所有节点信息

2.2、OpcUaHelper帮助类的常用方法

2.2.1、连接OPC UA服务器方法

2.2.2、读取OPC UA服务器的节点数据

2.2.3、读取OPC UA服务器中节点信息

三、运行测试

3.1、自己写的核心类

3.2、运行项目测试


一、内容准备

1.1、搭建OPC UA服务器环境

搭建OPC UA服务器环境图文教程

②注意:OPC UA服务器环境可以使用虚拟机搭建,如果从本机访问OPC UA服务器连接不上或超时,请先关闭OPC UA服务器的防火墙,或者在防火墙打开对应的端口即可。

1.2、项目安装OPCUaHelper开源库

1.3、OpcUaHelper开源库

OpcUaHelper开源库项目

二、实现操作

2.1、连接OPC UA服务器,浏览所有节点信息

①打开OPC UA服务器连接面板代码如下:

  using (FormBrowseServer form = new FormBrowseServer())
  {
       form.ShowDialog();
  }

②执行该代码后即可弹窗输入OPC UA服务器URL,连接服务器

③查看服务器的节点

2.2、OpcUaHelper帮助类的常用方法

2.2.1、连接OPC UA服务器方法

//实例化操作

OpcUaClient m_OpcUaClient = new OpcUaClient();

//设置匿名连接

m_OpcUaClient.UserIdentity = new UserIdentity( new AnonymousIdentityToken( ) );

//设置用户名连接

m_OpcUaClient.UserIdentity = new UserIdentity( "user", "password" );

//使用证书连接

X509Certificate2 certificate = new X509Certificate2( "[证书的路径]", "[密钥]", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable );
m_OpcUaClient.UserIdentity = new UserIdentity( certificate );
//设置完连接的权限之后,就可以真正的启动连接操作了,连接的操作必须要放到try...catch...之前,必须使用async标记方法

private async void button1_Click( object sender, EventArgs e )
{
    // 这是一个连接服务器的示例
    try
    {
        await m_OpcUaClient.ConnectServer( "opc.tcp://192.168.146.137:49321/Kepware.KEPServerEX.V6" );
    }
    catch (Exception ex)
    {
        ClientUtils.HandleException( "连接失败!!!", ex );
    }
}

2.2.2、读取OPC UA服务器的节点数据

①如果我们想要读取上图节点浏览器的温度数据,节点字符串为:

ns=2;s=数据类型示例.16 位设备.R 寄存器.Long4

②同步方式读取节点信息【1-单节点数据读取 ;2-多节点批量读取】

//1-同步单节点数据读取,类型为Int32, 所以我们使用下面的方法读取
try
{
   Int32 value = m_OpcUaClient.ReadNode<Int32>( "ns=2;s=数据类型示例.16 位设备.R 寄存器.Long4" );
}
catch(Exception ex)
{
    ClientUtils.HandleException( “读取失败!!!”, ex );
}



//2-同步批量节点数据读取的操作,分为类型不一致和类型一致两种操作,下面都做个示例
try
{
    // 添加所有的读取的节点,此处的示例是类型不一致的情况
    List<NodeId> nodeIds = new List<NodeId>( );
    nodeIds.Add( new NodeId( "ns=2;s=数据类型示例.16 位设备.R 寄存器.DWord1" ) );
    nodeIds.Add( new NodeId( "ns=2;s=数据类型示例.16 位设备.R 寄存器.Float1" ) );
    nodeIds.Add( new NodeId( "ns=2;s=数据类型示例.16 位设备.R 寄存器.LLong4" ) );
   );
  
    // dataValues按顺序定义的值,每个值里面需要重新判断类型
    List<DataValue> dataValues = m_OpcUaClient.ReadNodes( nodeIds.ToArray() );


    // 如果你批量读取的值的类型都是一样的,比如float,那么有简便的方式
    List<string> tags = new List<string>( );
    tags.Add( "ns=2;s=数据类型示例.16 位设备.R 寄存器.Float1" );
    tags.Add( "ns=2;s=数据类型示例.16 位设备.R 寄存器.Float2" );
    tags.Add( "ns=2;s=数据类型示例.16 位设备.R 寄存器.Float3" );

    // 按照顺序定义的值
    List<float> values = m_OpcUaClient.ReadNodes<float>( tags.ToArray() );

}
catch (Exception ex)
{
    ClientUtils.HandleException( this.Text, ex );
}

③异步方式读取节点信息【1-单节点数据读取 ;2-多节点批量读取】

//1-异步单节点数据读取,类型为Int32, 所以我们使用下面的方法读取
try
{
   Int32 value =await m_OpcUaClient.ReadNodeAsync<Int32>( "ns=2;s=数据类型示例.16 位设备.R 寄存器.Long4" );
}
catch(Exception ex)
{
    ClientUtils.HandleException( “读取失败!!!”, ex );
}



//2-异步批量节点数据读取的操作,分为类型不一致和类型一致两种操作,下面都做个示例
try
{
    // 添加所有的读取的节点,此处的示例是类型不一致的情况
    List<NodeId> nodeIds = new List<NodeId>( );
    nodeIds.Add( new NodeId( "ns=2;s=数据类型示例.16 位设备.R 寄存器.DWord1" ) );
    nodeIds.Add( new NodeId( "ns=2;s=数据类型示例.16 位设备.R 寄存器.Float1" ) );
    nodeIds.Add( new NodeId( "ns=2;s=数据类型示例.16 位设备.R 寄存器.LLong4" ) );
   );
  
    // dataValues按顺序定义的值,每个值里面需要重新判断类型
    List<DataValue> dataValues =await m_OpcUaClient.ReadNodesAsync( nodeIds.ToArray() );


}
catch (Exception ex)
{
    ClientUtils.HandleException( this.Text, ex );
}

④订阅方式读取节点信息【1、单节点数据读取;2-多节点批量读取】

//1-单节点数据订阅
m_OpcUaClient.AddSubscription( "A", "ns=2;s=数据类型示例.16 位设备.R 寄存器.Long4", SubCallback );

//1-单节点数据订阅的回调函数
private void SubCallback(string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args )
{
    if (InvokeRequired)
    {
        Invoke( new Action<string, MonitoredItem, MonitoredItemNotificationEventArgs>( SubCallback ), key, monitoredItem, args );
        return;
    }

    if (key == "A")
    {
        // 如果有多个的订阅值都关联了当前的方法,可以通过key和monitoredItem来区分
        MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
        if (notification != null)
        {
            textBox3.Text = notification.Value.WrappedValue.Value.ToString( );
        }
    }
}

//1-取消单节点数据订阅
m_OpcUaClient.RemoveSubscription( "A" );



//2-多节点批量数据订阅

private string[] MonitorNodeTags = null;

private void button5_Click( object sender, EventArgs e )
{
    // 多个节点的订阅
    MonitorNodeTags = new string[]
    {
       "ns=2;s=数据类型示例.16 位设备.R 寄存器.DWord1",
       "ns=2;s=数据类型示例.16 位设备.R 寄存器.LLong4",
       "ns=2;s=数据类型示例.16 位设备.R 寄存器.Long4",
    };
    m_OpcUaClient.AddSubscription( "B", MonitorNodeTags, SubCallback );
}

//2-多节点批量数据订阅回调函数
private void SubCallback(string key, MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs args )
{
    if (InvokeRequired)
    {
        Invoke( new Action<string, MonitoredItem, MonitoredItemNotificationEventArgs>( SubCallback ), key, monitoredItem, args );
        return;
    }

    if (key == "A")
    {
        // 如果有多个的订阅值都关联了当前的方法,可以通过key和monitoredItem来区分
        MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
        if (notification != null)
        {
            textBox3.Text = notification.Value.WrappedValue.Value.ToString( );
        }
    }
    else if(key == "B")
    {
        // 需要区分出来每个不同的节点信息
        MonitoredItemNotification notification = args.NotificationValue as MonitoredItemNotification;
        if (monitoredItem.StartNodeId.ToString( ) == MonitorNodeTags[0])
        {
           "ns=2;s=数据类型示例.16 位设备.R 寄存器.DWord1" = notification.Value.WrappedValue.Value.ToString( );
        }
        else if (monitoredItem.StartNodeId.ToString( ) == MonitorNodeTags[1])
        {
            "ns=2;s=数据类型示例.16 位设备.R 寄存器.LLong4" = notification.Value.WrappedValue.Value.ToString( );
        }
        else if (monitoredItem.StartNodeId.ToString( ) == MonitorNodeTags[2])
        {
            "ns=2;s=数据类型示例.16 位设备.R 寄存器.Long4" = notification.Value.WrappedValue.Value.ToString( );
        }
    }
}

//2-取消所有节点订阅
m_OpcUaClient.RemoveAllSubscription();

⑤读取单节点的历史数据

try
{
    // 此处演示读取历史数据的操作,读取8月18日12点到13点的数据,如果想要读取成功,该节点是支持历史记录的
    List<float> values = m_OpcUaClient.ReadHistoryRawDataValues<float>( "ns=2;s=数据类型示例.16 位设备.R 寄存器.Long4",
        new DateTime( 2021, 5, 1, 12, 0, 0 ), new DateTime( 2021, 2, 25, 13, 0, 0 ) ).ToList( );
    // 列表数据可用于显示曲线之类的操作

}
catch (Exception ex)
{
    ClientUtils.HandleException( this.Text, ex );
}

 

2.2.3、读取OPC UA服务器中节点信息

①读取一个节点的关联节点,包含了几个简单的基本信息

try
{
    ReferenceDescription[] references = m_OpcUaClient.BrowseNodeReference( "ns=2;s=数据类型示例.16 位设备.R 寄存器" );
    foreach (var item in references)
    {
       str = string.Format("节点:{0},节点类型:{1},节点名称:{2},节点显示名称:{3}",
                        item.NodeId, item.NodeClass, item.BrowseName, item.DisplayName);
    }

    ;

}
catch (Exception ex)
{
    ClientUtils.HandleException( this.Text, ex );
}


//执行结果
节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Boolean1,节点类型:Variable,节点名称:2:Boolean1,节点显示名称:Boolean1节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Boolean2,节点类型:Variable,节点名称:2:Boolean2,节点显示名称:Boolean2节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Boolean3,节点类型:Variable,节点名称:2:Boolean3,节点显示名称:Boolean3节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Boolean4,节点类型:Variable,节点名称:2:Boolean4,节点显示名称:Boolean4节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Double1,节点类型:Variable,节点名称:2:Double1,节点显示名称:Double1节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Double2,节点类型:Variable,节点名称:2:Double2,节点显示名称:Double2节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Double3,节点类型:Variable,节点名称:2:Double3,节点显示名称:Double3节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Double4,节点类型:Variable,节点名称:2:Double4,节点显示名称:Double4节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.DoubleArray,节点类型:Variable,节点名称:2:DoubleArray,节点显示名称:DoubleArray节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.DWord1,节点类型:Variable,节点名称:2:DWord1,节点显示名称:DWord1节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.DWord2,节点类型:Variable,节点名称:2:DWord2,节点显示名称:DWord2节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.DWord3,节点类型:Variable,节点名称:2:DWord3,节点显示名称:DWord3节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.DWord4,节点类型:Variable,节点名称:2:DWord4,节点显示名称:DWord4节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.DWordArray,节点类型:Variable,节点名称:2:DWordArray,节点显示名称:DWordArray节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Float1,节点类型:Variable,节点名称:2:Float1,节点显示名称:Float1节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Float2,节点类型:Variable,节点名称:2:Float2,节点显示名称:Float2节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Float3,节点类型:Variable,节点名称:2:Float3,节点显示名称:Float3节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Float4,节点类型:Variable,节点名称:2:Float4,节点显示名称:Float4节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.FloatArray,节点类型:Variable,节点名称:2:FloatArray,节点显示名称:FloatArray节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.LLong1,节点类型:Variable,节点名称:2:LLong1,节点显示名称:LLong1节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.LLong2,节点类型:Variable,节点名称:2:LLong2,节点显示名称:LLong2节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.LLong3,节点类型:Variable,节点名称:2:LLong3,节点显示名称:LLong3节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.LLong4,节点类型:Variable,节点名称:2:LLong4,节点显示名称:LLong4节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.LLongArray,节点类型:Variable,节点名称:2:LLongArray,节点显示名称:LLongArray节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Long1,节点类型:Variable,节点名称:2:Long1,节点显示名称:Long1节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Long2,节点类型:Variable,节点名称:2:Long2,节点显示名称:Long2节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Long3,节点类型:Variable,节点名称:2:Long3,节点显示名称:Long3节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Long4,节点类型:Variable,节点名称:2:Long4,节点显示名称:Long4节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.LongArray,节点类型:Variable,节点名称:2:LongArray,节点显示名称:LongArray节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.QWord1,节点类型:Variable,节点名称:2:QWord1,节点显示名称:QWord1节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.QWord2,节点类型:Variable,节点名称:2:QWord2,节点显示名称:QWord2节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.QWord3,节点类型:Variable,节点名称:2:QWord3,节点显示名称:QWord3节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.QWord4,节点类型:Variable,节点名称:2:QWord4,节点显示名称:QWord4节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.QWordArray,节点类型:Variable,节点名称:2:QWordArray,节点显示名称:QWordArray节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Short1,节点类型:Variable,节点名称:2:Short1,节点显示名称:Short1节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Short2,节点类型:Variable,节点名称:2:Short2,节点显示名称:Short2节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Short3,节点类型:Variable,节点名称:2:Short3,节点显示名称:Short3节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Short4,节点类型:Variable,节点名称:2:Short4,节点显示名称:Short4节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.ShortArray,节点类型:Variable,节点名称:2:ShortArray,节点显示名称:ShortArray节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Word1,节点类型:Variable,节点名称:2:Word1,节点显示名称:Word1节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Word2,节点类型:Variable,节点名称:2:Word2,节点显示名称:Word2节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Word3,节点类型:Variable,节点名称:2:Word3,节点显示名称:Word3节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.Word4,节点类型:Variable,节点名称:2:Word4,节点显示名称:Word4节点:ns=2;s=数据类型示例.16 位设备.R 寄存器.WordArray,节点类型:Variable,节点名称:2:WordArray,节点显示名称:WordArray

②读取一个节点的相关的所有的属性,主要包含了值,描述,名称,权限等级,等等操作

string str=null;
try
{
    OpcNodeAttribute[] nodeAttributes = m_OpcUaClient.ReadNoteAttributes( "ns=2;s=数据类型示例.16 位设备.R 寄存器.Long4" );
    foreach (var item in nodeAttributes)
    {
          str += string.Format("属性名称:{0},属性类型:{1},属性状态:{2},属性值:{3}",
                   item.Name, item.Type, item.StatusCode, item.Value);
    }
                
  
}
catch (Exception ex)
{
    ClientUtils.HandleException( this.Text, ex );
}


//执行结果
属性名称:NodeClass,属性类型:Int32,属性状态:Good,属性值:2属性名称:BrowseName,属性类型:QualifiedName,属性状态:Good,属性值:2:Long4属性名称:DisplayName,属性类型:LocalizedText,属性状态:Good,属性值:Long4属性名称:Description,属性类型:LocalizedText,属性状态:Good,属性值:属性名称:WriteMask,属性类型:UInt32,属性状态:Good,属性值:0属性名称:UserWriteMask,属性类型:UInt32,属性状态:Good,属性值:0属性名称:Value,属性类型:Int32,属性状态:Good,属性值:85747属性名称:DataType,属性类型:NodeId,属性状态:Good,属性值:i=6属性名称:ValueRank,属性类型:Int32,属性状态:Good,属性值:-1属性名称:AccessLevel,属性类型:Byte,属性状态:Good,属性值:3属性名称:UserAccessLevel,属性类型:Byte,属性状态:Good,属性值:3属性名称:MinimumSamplingInterval,属性类型:UInt32,属性状态:Good,属性值:10属性名称:Historizing,属性类型:Boolean,属性状态:Good,属性值:False

三、运行测试

3.1、自己写的核心类

/***
*	Title:"数据采集" 项目
*		主题:OPCUA与kepserver通讯帮助类
*	Description:
*		功能:
*		    1、打开连接【匿名方式】、【账号方式】、【证书方式】
*		    2、关闭连接
*		    3、获取到当前节点的值【同步读取】
*		    4、获取到当前节点数据【同步读取】
*		    5、获取到批量节点数据【同步读取】
*		    6、获取到当前节点的值【异步读取】
*		    7、获取到批量节点数据【异步读取】
*		    8、获取到当前节点的关联节点
*		    9、获取到当前节点的所有属性
*		    10、写入单个节点【同步方式】
*		    11、批量写入节点【同步方式】
*		    12、写入单个节点【异步方式】
*		    13、读取单个节点的历史数据记录
*		    14、读取单个节点的历史数据记录
*		    15、单节点数据订阅
*		    16、取消单节点数据订阅
*		    17、批量节点数据订阅
*		    18、取消所有节点的数据订阅
*		    
*	Date:2021
*	Version:0.1版本
*	Author:Coffee
*	Modify Recoder:
*/

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

namespace Utils
{
    public class OPCUAHelper
    {
        #region   基础参数
        //OPCUA客户端
        private OpcUaClient opcUaClient;


        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        public OPCUAHelper()
        {
            opcUaClient = new OpcUaClient();
        }

        /// <summary>
        /// 连接状态
        /// </summary>
        public bool ConnectStatus
        {
            get { return opcUaClient.Connected; }
        }
        


        #region   公有方法


        /// <summary>
        /// 打开连接【匿名方式】
        /// </summary>
        /// <param name="serverUrl">服务器URL【格式:opc.tcp://服务器IP地址/服务名称】</param>
        public async void OpenConnectOfAnonymous(string serverUrl)
        {
            if (!string.IsNullOrEmpty(serverUrl))
            {
                try
                {
                    opcUaClient.UserIdentity = new UserIdentity(new AnonymousIdentityToken());

                    await opcUaClient.ConnectServer(serverUrl);

                }
                catch (Exception ex)
                {
                    ClientUtils.HandleException("连接失败!!!", ex);
                }
               
            }
        }

        /// <summary>
        /// 打开连接【账号方式】
        /// </summary>
        /// <param name="serverUrl">服务器URL【格式:opc.tcp://服务器IP地址/服务名称】</param>
        /// <param name="userName">用户名称</param>
        /// <param name="userPwd">用户密码</param>
        public async void OpenConnectOfAccount(string serverUrl,string userName,string userPwd)
        {
            if (!string.IsNullOrEmpty(serverUrl) &&
                !string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(userPwd))
            {
                try
                {
                    opcUaClient.UserIdentity = new UserIdentity(userName,userPwd);

                    await opcUaClient.ConnectServer(serverUrl);
                }
                catch (Exception ex)
                {
                    ClientUtils.HandleException("连接失败!!!", ex);
                }
            }

        }

        /// <summary>
        /// 打开连接【证书方式】
        /// </summary>
        /// <param name="serverUrl">服务器URL【格式:opc.tcp://服务器IP地址/服务名称】</param>
        /// <param name="certificatePath">证书路径</param>
        /// <param name="secreKey">密钥</param>
        public async void  OpenConnectOfCertificate(string serverUrl,string certificatePath,string secreKey)
        {
            if (!string.IsNullOrEmpty(serverUrl) && 
                !string.IsNullOrEmpty(certificatePath) && !string.IsNullOrEmpty(secreKey))
            {
                try
                {
                    X509Certificate2 certificate = new X509Certificate2(certificatePath, secreKey, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
                    opcUaClient.UserIdentity = new UserIdentity(certificate);

                    await opcUaClient.ConnectServer(serverUrl);
                }
                catch (Exception ex)
                {
                    ClientUtils.HandleException("连接失败!!!", ex);
                }
            }
        }


        /// <summary>
        /// 关闭连接
        /// </summary>
        public void CloseConnect()
        {
            if (opcUaClient!=null)
            {
                try
                {
                    opcUaClient.Disconnect();   
                }
                catch (Exception ex)
                {
                    ClientUtils.HandleException("关闭连接失败!!!", ex);
                }
               
            }
        }


        /// <summary>
        /// 获取到当前节点的值【同步读取】
        /// </summary>
        /// <typeparam name="T">节点对应的数据类型</typeparam>
        /// <param name="nodeId">节点</param>
        /// <returns>返回当前节点的值</returns>
        public T GetCurrentNodeValue<T>(string nodeId)
        {
            T value = default(T);
            if (!string.IsNullOrEmpty(nodeId) && ConnectStatus)
            {
                try
                {
                    value = opcUaClient.ReadNode<T>(nodeId);
                }
                catch (Exception ex)
                {
                    ClientUtils.HandleException("读取失败!!!",ex);
                }
            }

            return value;
        }

        /// <summary>
        /// 获取到当前节点数据【同步读取】
        /// </summary>
        /// <typeparam name="T">节点对应的数据类型</typeparam>
        /// <param name="nodeId">节点</param>
        /// <returns>返回当前节点的值</returns>
        public DataValue GetCurrentNodeValue(string nodeId)
        {
            DataValue dataValue = null;
            if (!string.IsNullOrEmpty(nodeId) && ConnectStatus)
            {
                try
                {
                    dataValue = opcUaClient.ReadNode(nodeId);
                }
                catch (Exception ex)
                {
                    ClientUtils.HandleException("读取失败!!!", ex);
                }
            }

            return dataValue;
        }

        /// <summary>
        /// 获取到批量节点数据【同步读取】
        /// </summary>
        /// <param name="nodeIds">节点列表</param>
        /// <returns>返回节点数据字典</returns>
        public Dictionary<string,DataValue> GetBatchNodeDatasOfSync(List<NodeId> nodeIdList)
        {
            Dictionary<string, DataValue> dicNodeInfo = new Dictionary<string, DataValue>();
            if (nodeIdList != null && nodeIdList.Count>0 && ConnectStatus)
            {
                try
                {
                    List<DataValue> dataValues = opcUaClient.ReadNodes(nodeIdList.ToArray());

                    int count = nodeIdList.Count;
                    for (int i = 0; i < count; i++)
                    {
                        AddInfoToDic(dicNodeInfo, nodeIdList[i].ToString(),dataValues[i]);
                    }
                }
                catch (Exception ex)
                {
                    ClientUtils.HandleException("读取失败!!!", ex);
                }
            }

            return dicNodeInfo;
        }

       
        /// <summary>
        /// 获取到当前节点的值【异步读取】
        /// </summary>
        /// <typeparam name="T">节点对应的数据类型</typeparam>
        /// <param name="nodeId">节点</param>
        /// <returns>返回当前节点的值</returns>
        public async Task<T> GetCurrentNodeValueOfAsync<T>(string nodeId)
        {
            T value = default(T);
            if (!string.IsNullOrEmpty(nodeId) && ConnectStatus)
            {
                try
                {
                    value =await opcUaClient.ReadNodeAsync<T>(nodeId);
                }
                catch (Exception ex)
                {
                    ClientUtils.HandleException("读取失败!!!", ex);
                }
            }

            return value;
        }

        /// <summary>
        /// 获取到批量节点数据【异步读取】
        /// </summary>
        /// <param name="nodeIds">节点列表</param>
        /// <returns>返回节点数据字典</returns>
        public async Task<Dictionary<string, DataValue>> GetBatchNodeDatasOfAsync(List<NodeId> nodeIdList)
        {
            Dictionary<string, DataValue> dicNodeInfo = new Dictionary<string, DataValue>();
            if (nodeIdList != null && nodeIdList.Count > 0 && ConnectStatus)
            {
                try
                {
                    List<DataValue> dataValues = await opcUaClient.ReadNodesAsync(nodeIdList.ToArray());

                    int count = nodeIdList.Count;
                    for (int i = 0; i < count; i++)
                    {
                        AddInfoToDic(dicNodeInfo, nodeIdList[i].ToString(), dataValues[i]);
                    }
                }
                catch (Exception ex)
                {
                    ClientUtils.HandleException("读取失败!!!", ex);
                }
            }

            return dicNodeInfo;
        }




        /// <summary>
        /// 获取到当前节点的关联节点
        /// </summary>
        /// <param name="nodeId">当前节点</param>
        /// <returns>返回当前节点的关联节点</returns>
        public ReferenceDescription[] GetAllRelationNodeOfNodeId(string nodeId)
        {
            ReferenceDescription[] referenceDescriptions = null;

            if (!string.IsNullOrEmpty(nodeId) && ConnectStatus)
            {
                try
                {
                    referenceDescriptions = opcUaClient.BrowseNodeReference(nodeId);
                }
                catch (Exception ex)
                {
                    string str = "获取当前: "+nodeId+"  节点的相关节点失败!!!";
                    ClientUtils.HandleException(str, ex);
                }
            }

            return referenceDescriptions;
        }


        /// <summary>
        /// 获取到当前节点的所有属性
        /// </summary>
        /// <param name="nodeId">当前节点</param>
        /// <returns>返回当前节点对应的所有属性</returns>
        public OpcNodeAttribute[] GetCurrentNodeAttributes(string nodeId)
        {
            OpcNodeAttribute[] opcNodeAttributes=null;
            if (!string.IsNullOrEmpty(nodeId) && ConnectStatus)
            {
                try
                {
                    opcNodeAttributes = opcUaClient.ReadNoteAttributes(nodeId);
                }
                catch (Exception ex)
                {
                    string str = "读取节点;" + nodeId + "  的所有属性失败!!!";
                    ClientUtils.HandleException(str, ex);
                }
            }

            return opcNodeAttributes;
        }

        /// <summary>
        /// 写入单个节点【同步方式】
        /// </summary>
        /// <typeparam name="T">写入节点值得数据类型</typeparam>
        /// <param name="nodeId">节点</param>
        /// <param name="value">节点对应的数据值(比如:(short)123))</param>
        /// <returns>返回写入结果(true:表示写入成功)</returns>
        public bool WriteSingleNodeIdOfSync<T>(string nodeId,T value)
        {
            bool success = false;

            if (opcUaClient!=null && ConnectStatus)
            {
                if (!string.IsNullOrEmpty(nodeId))
                {
                    try
                    {
                        success = opcUaClient.WriteNode(nodeId, value);
                    }
                    catch (Exception ex)
                    {
                        string str = "当前节点:" + nodeId + "  写入失败";
                        ClientUtils.HandleException(str, ex);
                    }
                }
               
            }

            return success;
        }

        /// <summary>
        /// 批量写入节点
        /// </summary>
        /// <param name="nodeIdArray">节点数组</param>
        /// <param name="nodeIdValueArray">节点对应数据数组</param>
        /// <returns>返回写入结果(true:表示写入成功)</returns>
        public bool BatchWriteNodeIds(string[] nodeIdArray, object[] nodeIdValueArray)
        {
            bool success = false;
            if (nodeIdArray != null && nodeIdArray.Length > 0 &&
                nodeIdValueArray != null && nodeIdValueArray.Length > 0)

            {
                try
                {
                    success = opcUaClient.WriteNodes(nodeIdArray, nodeIdValueArray);
                }
                catch (Exception ex)
                {
                    ClientUtils.HandleException("批量写入节点失败!!!", ex);
                }
            }
            return success;
        }

        /// <summary>
        /// 写入单个节点【异步方式】
        /// </summary>
        /// <typeparam name="T">写入节点值得数据类型</typeparam>
        /// <param name="nodeId">节点</param>
        /// <param name="value">节点对应的数据值</param>
        /// <returns>返回写入结果(true:表示写入成功)</returns>
        public async Task<bool> WriteSingleNodeIdOfAsync<T>(string nodeId, T value)
        {
            bool success = false;

            if (opcUaClient != null && ConnectStatus)
            {
                if (!string.IsNullOrEmpty(nodeId))
                {
                    try
                    {
                        success = await opcUaClient.WriteNodeAsync(nodeId, value);
                    }
                    catch (Exception ex)
                    {
                        string str = "当前节点:"+nodeId+"  写入失败";
                        ClientUtils.HandleException(str, ex);
                    }
                }

            }

            return success;
        }

       
        /// <summary>
        /// 读取单个节点的历史数据记录
        /// </summary>
        /// <typeparam name="T">节点的数据类型</typeparam>
        /// <param name="nodeId">节点</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <returns>返回该节点对应的历史数据记录</returns>
        public List<T> ReadSingleNodeIdHistoryDatas<T>(string nodeId, DateTime startTime, DateTime endTime)
        {
            List<T> nodeIdDatas = null;
            if (!string.IsNullOrEmpty(nodeId) && startTime!=null && endTime!=null && endTime>startTime)
            {
                try
                {
                    nodeIdDatas = opcUaClient.ReadHistoryRawDataValues<T>(nodeId, startTime, endTime).ToList();
                }
                catch (Exception ex)
                {
                    ClientUtils.HandleException("读取失败", ex);
                }
            }

            return nodeIdDatas;
        }

        /// <summary>
        /// 读取单个节点的历史数据记录
        /// </summary>
        /// <typeparam name="T">节点的数据类型</typeparam>
        /// <param name="nodeId">节点</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <returns>返回该节点对应的历史数据记录</returns>
        public List<DataValue> ReadSingleNodeIdHistoryDatas(string nodeId, DateTime startTime, DateTime endTime)
        {
            List<DataValue> nodeIdDatas = null;
            if (!string.IsNullOrEmpty(nodeId) && startTime != null && endTime != null && endTime > startTime)
            {
                if (ConnectStatus)
                {
                    try
                    {
                        nodeIdDatas = opcUaClient.ReadHistoryRawDataValues(nodeId, startTime, endTime).ToList();
                    }
                    catch (Exception ex)
                    {
                        ClientUtils.HandleException("读取失败", ex);
                    }
                }
              
            }

            return nodeIdDatas;
        }


        /// <summary>
        /// 单节点数据订阅
        /// </summary>
        /// <param name="key">订阅的关键字(必须唯一)</param>
        /// <param name="nodeId">节点</param>
        /// <param name="callback">数据订阅的回调方法</param>
        public void SingleNodeIdDatasSubscription(string key, string nodeId, Action<string, MonitoredItem, MonitoredItemNotificationEventArgs> callback)
        {
            if (ConnectStatus)
            {
                try
                {
                    opcUaClient.AddSubscription(key,nodeId,callback);
                }
                catch (Exception ex)
                {
                    string str = "订阅节点:" + nodeId + " 数据失败!!!";
                    ClientUtils.HandleException(str, ex);
                }
            }
        }

        /// <summary>
        /// 取消单节点数据订阅
        /// </summary>
        /// <param name="key">订阅的关键字</param>
        public bool CancelSingleNodeIdDatasSubscription(string key)
        {
            bool success = false;
            if (!string.IsNullOrEmpty(key))
            {
                if (ConnectStatus)
                {
                    try
                    {
                        opcUaClient.RemoveSubscription(key);
                        success = true;
                    }
                    catch (Exception ex)
                    {
                        string str = "取消 " +key+ " 的订阅失败";
                        ClientUtils.HandleException(str, ex);
                    }
                   
                }
            }

            return success;
        }


        /// <summary>
        /// 批量节点数据订阅
        /// </summary>
        /// <param name="key">订阅的关键字(必须唯一)</param>
        /// <param name="nodeIds">节点数组</param>
        /// <param name="callback">数据订阅的回调方法</param>
        public void BatchNodeIdDatasSubscription(string key, string[] nodeIds, Action<string, MonitoredItem, MonitoredItemNotificationEventArgs> callback)
        {
            if (!string.IsNullOrEmpty(key) && nodeIds!=null && nodeIds.Length>0)
            {
                if (ConnectStatus)
                {
                    try
                    {
                        opcUaClient.AddSubscription(key, nodeIds, callback);
                    }
                    catch (Exception ex)
                    {
                        string str = "批量订阅节点数据失败!!!";
                        ClientUtils.HandleException(str, ex);
                    }
                }
            }
           
        }

        /// <summary>
        /// 取消所有节点的数据订阅
        /// </summary>
        /// <returns></returns>
        public bool CancelAllNodeIdDatasSubscription()
        {
            bool success = false;

            if (ConnectStatus)
            {
                try
                {
                    opcUaClient.RemoveAllSubscription();
                    success = true;
                }
                catch (Exception ex)
                {
                    ClientUtils.HandleException("取消所有的节点数据订阅失败!!!", ex);
                }

            }

            return success;
        }

        #endregion



        #region   私有方法

        /// <summary>
        /// 添加数据到字典中(相同键的则采用最后一个键对应的值)
        /// </summary>
        /// <param name="dic">字典</param>
        /// <param name="key">键</param>
        /// <param name="dataValue">值</param>
        private void AddInfoToDic(Dictionary<string,DataValue> dic,string key,DataValue dataValue)
        {
            if (dic!=null)
            {
                if (!dic.ContainsKey(key))
                {

                    dic.Add(key, dataValue);
                }
                else
                {
                    dic[key] = dataValue;
                }
            }
            
        }


       

        #endregion

    }//Class_end

}

3.2、运行项目测试

C#使用OpcUaHelper开源库开发的客户端实现读取、订阅OPC UA服务器节点项目工程下载

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐