当前位置: 首页 > news >正文

【OPC UA】C# 通过OpcUaHelper建立OPC客户端访问KEPServerEx6 OPC服务器数据

OpcUaHelper

一个通用的opc ua客户端类库,基于.net 4.6.1创建,基于官方opc ua基金会跨平台库创建,封装了节点读写,批量节点读写,引用读取,特性读取,历史数据读取,方法调用,节点订阅,批量订阅等操作。还提供了一个节点浏览器工具。

KEPServerEX

第三方的OPC服务器,各不同厂家多种设备下位PLC与上位机之间通讯。KEPServerEX是行业领先的通信平台,用于向您的所有应用程序提供单一来源的工业自动化数据。该平台的设计使用户能够通过一个直观的用户界面来连接、管理、监视和控制不同的自动化设备和软件应用程序。KEPServerEX 利用 OPC(自动化行业的互操作性标准)和以 IT 为中心的通信协议(如 SNMP、ODBC 和 Web 服务)为用户提供单一来源的工业数据。要开始使用,只需下载 KEPServerEX,然后从Kepware 的包含 150 多个设备驱动程序、客户端驱动程序和高级选项的库中进行选择,以满足您独特的工业控制系统需求。

以下基于开源的OpcUaHelper自带的demo,修改了界面,测试了部分功能。

504723c5c0931a4c082f9fe468940a0d.png

e256f3e0bd7bf1045f6089c6e744ef29.png主界面

C# 通过OPC UA 访问KEPServerEx6 演示视频

部分代码:

//同步读取
        private void button1_Click( object sender, EventArgs e )
        {
           
            if (cbo函数Nodeid.Text=="")
            {
                return;
            }
            DataValue dataValue = m_OpcUaClient.ReadNode( new NodeId(cbo函数Nodeid.Text ) );
            if (dataValue.WrappedValue.Value != null)
            {
                txtvalue.Text = dataValue.WrappedValue.Value.ToString();
            }
            //test();
        }
//单节点订阅
        private void button2_Click( object sender, EventArgs e )
        {
            // sub
            m_OpcUaClient.AddSubscription( "A", textBox4.Text, SubCallback );
        }
        //取消订阅 关键字A
        private void button3_Click( object sender, EventArgs e )
        {
            // remove sub
            m_OpcUaClient.RemoveSubscription( "A" );
        }
        //订阅回调
        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])
                {
                    textBox5.Text = notification.Value.WrappedValue.Value.ToString( );
                }
                else if (monitoredItem.StartNodeId.ToString( ) == MonitorNodeTags[1])
                {
                    textBox9.Text = notification.Value.WrappedValue.Value.ToString( );
                }
                else if (monitoredItem.StartNodeId.ToString( ) == MonitorNodeTags[2])
                {
                    textBox10.Text = notification.Value.WrappedValue.Value.ToString( );
                }
            }
        }
// 缓存的批量订阅的节点
        private string[] MonitorNodeTags = null;
        //多节点订阅
        private void button5_Click( object sender, EventArgs e )
        {
            // 多个节点的订阅
            MonitorNodeTags = new string[]
            {
                textBox6.Text,
                textBox7.Text,
                textBox8.Text,
            };
            m_OpcUaClient.AddSubscription( "B", MonitorNodeTags, SubCallback );
        }
        //取消订阅    
        private void button4_Click( object sender, EventArgs e )
        {
            // 取消多个节点的订阅
            m_OpcUaClient.RemoveSubscription( "B" );//关键字 B
        }
//异步读取节点
        private async void buttonReadNodeAsync_Click(object sender, EventArgs e)
        {
            Opc.Ua.DataValue value = m_OpcUaClient.ReadNode(cboNodeid.Text);//SelectedItem.ToString()
            if (value.Value == null)
            {
                return;
            }
            // 一个数据的类型是不是数组由 value.WrappedValue.TypeInfo.ValueRank 来决定的
            // -1 说明是一个数值
            // 1  说明是一维数组,如果类型BuiltInType是Int32,那么实际是int[]
            // 2  说明是二维数组,如果类型BuiltInType是Int32,那么实际是int[,]
            if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Int32)
            {
                if (value.WrappedValue.TypeInfo.ValueRank == -1)
                {
                    int temp = (int)value.WrappedValue.Value;               // 最终值
                    txtvalue.Text = temp.ToString();
                }
                else if (value.WrappedValue.TypeInfo.ValueRank == 1)
                {
                    int[] temp = (int[])value.WrappedValue.Value;           // 最终值
                    txtvalue.Text = temp.ToString();
                }
                else if (value.WrappedValue.TypeInfo.ValueRank == 2)
                {
                    int[,] temp = (int[,])value.WrappedValue.Value;         // 最终值
                    txtvalue.Text = temp.ToString();
                }
            }
            else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.UInt32)
            {
                uint temp = (uint)value.WrappedValue.Value;                 // 数组的情况参照上面的例子
                txtvalue.Text = temp.ToString();
            }
            else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Float)
            {
                float temp = (float)value.WrappedValue.Value;               // 数组的情况参照上面的例子
                txtvalue.Text = temp.ToString();
            }
            else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.String)
            {
                string temp = (string)value.WrappedValue.Value;             // 数组的情况参照上面的例子
                txtvalue.Text = temp.ToString();
            }
            else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.DateTime)
            {
                DateTime temp = (DateTime)value.WrappedValue.Value;         // 数组的情况参照上面的例子
                txtvalue.Text = temp.ToString();
            }
            else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Int16)
            {
                short temp = (short)value.WrappedValue.Value;         // 数组的情况参照上面的例子
                txtvalue.Text = temp.ToString();
            }
            else if (value.WrappedValue.TypeInfo.BuiltInType == Opc.Ua.BuiltInType.Double)
            {
                double temp = (double)value.WrappedValue.Value;         // 数组的情况参照上面的例子
                txtvalue.Text = temp.ToString();
            }
        }
//浏览节点引用
        private void buttonBrowseNodeReference_Click(object sender, EventArgs e)
        {
            richTextBox1.Clear();
            try
            {
                //ReferenceDescription[] references = m_OpcUaClient.BrowseNodeReference("ns=2;s=Devices.分厂一.车间二.ModbusTcp客户端");
                ReferenceDescription[] references = m_OpcUaClient.BrowseNodeReference(comboBox1.SelectedItem.ToString());
                richTextBox1.AppendText(string.Format("{0,-30}", "NodeClass")+ string.Format("{0,-30}","BrowseName")+ string.Format("{0,-40}","DisplayName")+ string.Format("{0,-40}","NodeId")+"\r\n");
                foreach (var item in references)
                {
                    Console.Write(string.Format("{0,-30}", item.NodeClass));
                    Console.Write(string.Format("{0,-30}", item.BrowseName));
                    Console.Write(string.Format("{0,-20}", item.DisplayName));
                    Console.WriteLine(string.Format("{0,-20}", item.NodeId.ToString()));
                    /*  string s1 = "中文";
                    string s2 = "语言学English";
                    Console.WriteLine("{0}{1}", padRightEx(s1, 20), "hello");
                    Console.WriteLine("{0}{1}", padRightEx(s2, 20), "hello");*/
                    //richTextBox1.AppendText(string.Format("{0,-30}", item.NodeClass));
                    //richTextBox1.AppendText(string.Format("{0,-30}", item.BrowseName));
                    //richTextBox1.AppendText(string.Format("{0,-20}", item.DisplayName));
                    //richTextBox1.AppendText(string.Format("{0,-20}", item.NodeId.ToString()) + "\r\n");
                    richTextBox1.AppendText(string.Format("{0}{1}{2}{3}\r\n", padRightEx(item.NodeClass.ToString().Trim(), 30), padRightEx(item.BrowseName.ToString().TrimStart(), 40), padRightEx(item.DisplayName.ToString().TrimStart(), 40), padRightEx(item.NodeId.ToString().TrimStart(), 100)));
                }


                ;
                // 输出如下
                //  NodeClass                     BrowseName                      DisplayName           NodeId


                //  Variable                      2:温度                          温度                  ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/温度
                //  Variable                      2:风俗                          风俗                  ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/风俗
                //  Variable                      2:转速                          转速                  ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/转速
                //  Variable                      2:机器人关节                    机器人关节            ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/机器人关节
                //  Variable                      2:cvsdf                         cvsdf                 ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/cvsdf
                //  Variable                      2:条码                          条码                  ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/条码
                //  Variable                      2:开关量                        开关量                ns=2;s=Devices/分厂一/车间二/ModbusTcp客户端/开关量
            }
            catch (Exception ex)
            {
                ClientUtils.HandleException(this.Text, ex);
            }
        }
//启用设备属性-操作模式-模拟  可读取节点属性
        private void buttonReadNoteAttributes_Click(object sender, EventArgs e)
        {
            richTextBox1.Clear();
            try
            {
                //OpcNodeAttribute[] nodeAttributes = m_OpcUaClient.ReadNoteAttributes("ns=2;s=Devices.分厂一.车间二.ModbusTcp客户端.温度");
                OpcNodeAttribute[] nodeAttributes = m_OpcUaClient.ReadNoteAttributes(cboNodeid.Text);
                richTextBox1.AppendText(string.Format("{0,-30}", "Name") + string.Format("{0,-40}", "Type") + string.Format("{0,-40}", "StatusCode") + string.Format("{0,-40}", "Value") + "\r\n");
                foreach (var item in nodeAttributes)
                {
                    //Console.Write(string.Format("{0,-30}", item.Name));
                    //Console.Write(string.Format("{0,-20}", item.Type));
                    //Console.Write(string.Format("{0,-20}", item.StatusCode));
                    //Console.WriteLine(string.Format("{0,20}", item.Value));
                    richTextBox1.AppendText(string.Format("{0}{1}{2}{3}\r\n", padRightEx(item.Name.ToString().Trim(), 30), padRightEx(item.Type.ToString().Trim(), 40), padRightEx(item.StatusCode.ToString().Trim(), 40), padRightEx(item.Value.ToString().Trim(), 100)));


                }
//写入操作
        private void buttonWrite_Click(object sender, EventArgs e)
        {
            try
            {
                // 此处演示写入一个short,2个float类型的数据批量写入操作。启用设备模拟后写入成功。但是很快就被模拟的0覆盖
                bool success = m_OpcUaClient.WriteNodes(new string[] {
                    "ns=2;s=Devices.分厂一.车间二.ModbusTcp客户端.温度",
                    "ns=2;s=Devices.分厂一.车间二.ModbusTcp客户端.风俗",
                    "ns=2;s=Devices.分厂一.车间二.ModbusTcp客户端.转速"},
                    new object[] {
                        (short)1234,
                        123.456f,
                        123f
                    });
                //bool success = m_OpcUaClient.WriteNode("ns=2;s=Devices.分厂一.车间二.ModbusTcp客户端.风俗", 123.456f);
                if (success)
                {
                    MessageBox.Show("写入成功!");
                }
                else
                {
                    // 写入失败,一个失败即为失败
                    MessageBox.Show("写入失败!");
                }
            }
            catch (Exception ex)
            {
                ClientUtils.HandleException(this.Text, ex);
            }
        }

参考: 

https://github.com/dathlin/OpcUaHelper

https://opcfoundation.cn/about/opc-technologies/opc-ua/

https://blog.csdn.net/xiaochenXIHUA/article/details/117070003

https://www.cnblogs.com/bgh408/p/10887580.html

https://blog.csdn.net/Vampire_1122/article/details/119025477

https://cloud.tencent.com/developer/article/1899065

https://blog.csdn.net/han_better/article/details/83034023

https://www.douban.com/note/837168906/?_i=61791832R3OOz3

The End

相关文章:

  • 大数据算法系列4:二叉树,红黑树和B树
  • 想知道图片转表格用什么软件?不妨试试这些软件
  • 作为一名测试人员,如何拾开发者牙慧,开启兼职赚钱之路
  • Web自动化测试(二)—— Selenium-API操作
  • 【面试分享】Java 面试题(Spring Boot / Spring Cloud)
  • 【计算机毕业设计选题】10套易过的精品毕设分享(源码+论文)
  • 推荐一个最好用的高性能、低内存、跨平台的图片处理库
  • Windows使用内存映射文件
  • 基于Matlab使用激光雷达检测分类跟踪车辆仿真(附源码)
  • 火狐浏览器 优化教程
  • 计算机的发展史,让你想到了什么?
  • 记一次SQL注入的收获
  • 大数据必学Java基础(八十):网络编程的深入了解
  • 建议收藏丨你想了解的动捕内容全在这儿!
  • 基于蚂蚁-遗传优化算法的路径规划问题(Matlab代码实现)
  • ES6指北【2】—— 箭头函数
  • Android优雅地处理按钮重复点击
  • Hexo+码云+git快速搭建免费的静态Blog
  • mysql常用命令汇总
  • node和express搭建代理服务器(源码)
  • PHP 小技巧
  • React系列之 Redux 架构模式
  • Redux系列x:源码分析
  • springMvc学习笔记(2)
  • 对JS继承的一点思考
  • 基于 Babel 的 npm 包最小化设置
  • 离散点最小(凸)包围边界查找
  • 你不可错过的前端面试题(一)
  • 数据结构java版之冒泡排序及优化
  • 无服务器化是企业 IT 架构的未来吗?
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • ​ 轻量应用服务器:亚马逊云科技打造全球领先的云计算解决方案
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • (1)(1.11) SiK Radio v2(一)
  • (21)起落架/可伸缩相机支架
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (第61天)多租户架构(CDB/PDB)
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (译) 函数式 JS #1:简介
  • (转)c++ std::pair 与 std::make
  • (转)创业家杂志:UCWEB天使第一步
  • ***原理与防范
  • .Net Core webapi RestFul 统一接口数据返回格式
  • .NET MVC之AOP
  • .NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件
  • .net6+aspose.words导出word并转pdf
  • .Net的DataSet直接与SQL2005交互
  • .NET的数据绑定
  • /etc/apt/sources.list 和 /etc/apt/sources.list.d
  • @EventListener注解使用说明
  • @Query中countQuery的介绍
  • [ vulhub漏洞复现篇 ] Apache Flink目录遍历(CVE-2020-17519)
  • [000-002-01].数据库调优相关学习