zoukankan      html  css  js  c++  java
  • C#利用自动化接口编写OPC客户端,OPC Client,源码直接放网盘

    C#利用自动化接口编写OPC客户端,OPC Client,源码直接放网盘

    大部分源码参考自某位乐于分享的大佬,但是他的源码和接口都年代久远了(2009年的代码),基本用不了,还存在些许BUG,我的VS版本是2019最新版的,采用的OPC自动化接口也是最新的,修复致命BUG之后已经可以正常使用了,对于初学者来说算是不错的教程(非常讨厌那些写教程但源码还要积分下载的伪大佬,我呸!),大家可以先去下个OPC服务器模拟器,不然做出来的客户端是测不到数据的,相关的服务器模拟器可以百度一下,这个比较好找。

    少罗嗦,先看东西,客户端界面如图所示↓

    客户端运行图如图所示↓

    东西看完了,接下来是引用

    在“解决方案资源管理器”里鼠标右键单击项目依次选择“添加”→“引用”→“COM”,然后在右上角搜索框搜索“OPC”

    选择下图这个自动化接口dll就可以了↓

    并在代码区的头部(命名空间处)利用using关键词添加引用,如下图最后一行↓

    准备工作做完了,接下来就是思路了:
    1.先扫描本地的OPC服务器,将所有的服务器名加入到下拉框控件里
    2.填上IP地址,本地测试一般为127.0.0.1,点击连接按钮触发事件,连接服务器
    3.连接上服务器之后创建一个OPCBrowser对象,主要用于展开服务器的“树枝”和“叶子”,如下图↓就是服务器那边所有的节点。

    4.创建一个组和设置组的属性

    5.添加一个节点(通过选择列表里的节点达成添加操作)
    6.编写订阅事件,当服务器端的数据有变化时,会把第5步添加的节点的对应值返回来,并显示在对应的文本控件上
    7编写异步写入事件,可以通过写入按钮将对应文本框的值写入到服务端

    耐心的同学可以在博客慢慢看,不习惯的同学直接拉到末尾下载源码,然后在软件里看会舒服很多

      1 using System;
      2 using System.Collections.Generic;
      3 using System.ComponentModel;
      4 using System.Data;
      5 using System.Drawing;
      6 using System.Linq;
      7 using System.Net;
      8 using System.Text;
      9 using System.Threading.Tasks;
     10 using System.Windows.Forms;
     11 
     12 using OPCAutomation;
     13 
     14 namespace OPCtest4
     15 {
     16     public partial class Form1 : Form
     17     {
     18         OPCServer KepServer;
     19         OPCGroups KepGroups;
     20         OPCGroup KepGroup;
     21         OPCItems KepItems;
     22         OPCItem KepItem;
     23         bool opc_connected = false;//连接状态
     24         int itmHandleClient = 0;//客户端的句柄,句柄即控件名称,如“张三”,用来识别是哪个具体的对象,此处可理解为每个节点的编号
     25         int itmHandleServer = 0;//服务器的句柄
     26         public Form1()
     27         {
     28             InitializeComponent();
     29         }
     30 
     31         private void Form1_Load(object sender, EventArgs e)
     32         {
     33             GetLocalServer();
     34         }
     35 
     36         /// <summary>
     37         /// 获取本地的OPC服务器名称
     38         /// </summary>
     39         public void GetLocalServer()
     40         {
     41             IPHostEntry host = Dns.GetHostEntry("127.0.0.1");
     42             var strHostName = host.HostName;
     43             try
     44             {
     45                 KepServer = new OPCServer();
     46                 object serverList = KepServer.GetOPCServers(strHostName);
     47 
     48                 foreach (string turn in (Array)serverList)
     49                 {
     50                     cmbServerName.Items.Add(turn);
     51                 }
     52 
     53                 cmbServerName.SelectedIndex = 0;
     54                 btnConnServer.Enabled = true;
     55             }
     56             catch (Exception err)
     57             {
     58                 MessageBox.Show("枚举本地OPC服务器出错:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
     59             }
     60         }
     61 
     62 
     63         /// <summary>
     64         /// "连接"按钮点击事件
     65         /// </summary>
     66         /// <param name="sender"></param>
     67         /// <param name="e"></param>
     68         private void BtnConnServer_Click(object sender, EventArgs e)
     69         {
     70             try
     71             {
     72                 if (!ConnectRemoteServer(txtRemoteServerIP.Text, cmbServerName.Text))
     73                 {
     74                     return;
     75                 }
     76 
     77                 btnSetGroupPro.Enabled = true;
     78 
     79                 opc_connected = true;
     80 
     81                 GetServerInfo();
     82 
     83                 RecurBrowse(KepServer.CreateBrowser());
     84 
     85                 if (!CreateGroup())
     86                 {
     87                     return;
     88                 }
     89             }
     90             catch (Exception err)
     91             {
     92                 MessageBox.Show("初始化出错:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
     93             }
     94         }
     95 
     96 
     97         /// <summary>
     98         /// 连接服务器
     99         /// </summary>
    100         /// <param name="remoteServerIP">服务器IP</param>
    101         /// <param name="remoteServerName">服务器名称</param>
    102         /// <returns></returns>
    103         public bool ConnectRemoteServer(string remoteServerIP, string remoteServerName)
    104         {
    105             try
    106             {
    107                 KepServer.Connect(remoteServerName, remoteServerIP);
    108 
    109                 if (KepServer.ServerState == (int)OPCServerState.OPCRunning)
    110                 {
    111                     tsslServerState.Text = "已连接到-" + KepServer.ServerName + "   ";
    112                 }
    113                 else
    114                 {
    115                     //这里你可以根据返回的状态来自定义显示信息,请查看自动化接口API文档
    116                     tsslServerState.Text = "状态:" + KepServer.ServerState.ToString() + "   ";
    117                 }
    118             }
    119             catch (Exception err)
    120             {
    121                 MessageBox.Show("连接远程服务器出现错误:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    122                 return false;
    123             }
    124             return true;
    125         }
    126 
    127         /// <summary>
    128         /// 获取服务器信息,并显示在窗体状态栏上
    129         /// </summary>
    130         public void GetServerInfo()
    131         {
    132             tsslServerStartTime.Text = "开始时间:" + KepServer.StartTime.ToString() + "    ";
    133             tsslversion.Text = "版本:" + KepServer.MajorVersion.ToString() + "." + KepServer.MinorVersion.ToString() + "." + KepServer.BuildNumber.ToString();
    134         }
    135 
    136         /// <summary>
    137         /// 展开树枝和叶子
    138         /// </summary>
    139         /// <param name="oPCBrowser">opc浏览器</param>
    140         public void RecurBrowse(OPCBrowser oPCBrowser)
    141         {
    142             //展开分支
    143             oPCBrowser.ShowBranches();
    144             //展开叶子
    145             oPCBrowser.ShowLeafs(true);
    146             foreach (object turn in oPCBrowser)
    147             {
    148                 listBox1.Items.Add(turn.ToString());
    149             }
    150         }
    151 
    152 
    153         /// <summary>
    154         /// 创建组,将本地组和服务器上的组对应
    155         /// </summary>
    156         /// <returns></returns>
    157         public bool CreateGroup()
    158         {
    159             try
    160             {
    161                 KepGroups = KepServer.OPCGroups;//将服务端的组集合复制到本地
    162                 KepGroup = KepGroups.Add("S");//添加一个组
    163                 SetGroupProperty();//设置组属性
    164                 
    165                 KepItems = KepGroup.OPCItems;//将组里的节点集合复制到本地节点集合
    166 
    167                 KepGroup.DataChange += KepGroup_DataChange;
    168                 KepGroup.AsyncWriteComplete += KepGroup_AsyncWriteComplete;
    169             }
    170             catch (Exception err)
    171             {
    172                 MessageBox.Show("创建组出现错误:" + err.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    173                 return false;
    174             }
    175             return true;
    176         }
    177 
    178       
    179         /// <summary>
    180         /// 设置组的属性,从对应的控件里获取
    181         /// </summary>
    182         public void SetGroupProperty()
    183         {
    184             KepServer.OPCGroups.DefaultGroupIsActive = Convert.ToBoolean(txtGroupIsActive.Text);//激活组
    185             KepServer.OPCGroups.DefaultGroupDeadband = Convert.ToInt32(txtGroupDeadband.Text);// 死区值,设为0时,服务器端该组内任何数据变化都通知组
    186             KepGroup.UpdateRate = Convert.ToInt32(txtUpdateRate.Text);//服务器向客户程序提交数据变化的刷新速率
    187             KepGroup.IsActive = Convert.ToBoolean(txtIsActive.Text);//组的激活状态标志
    188             KepGroup.IsSubscribed = Convert.ToBoolean(txtIsSubscribed.Text);//是否订阅数据
    189         }
    190 
    191 
    192         /// <summary>
    193         /// 异步写方法
    194         /// </summary>
    195         /// <param name="TransactionID">处理ID</param>
    196         /// <param name="NumItems">项个数</param>
    197         /// <param name="ClientHandles">OPC客户端的句柄</param>
    198         /// <param name="Errors">错误个数</param>
    199         private void KepGroup_AsyncWriteComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)
    200         {
    201             lblState.Text = "";
    202             for (int i = 1; i <= NumItems; i++)
    203             {
    204                 lblState.Text += "Tran:" + TransactionID.ToString() + "   CH:" + ClientHandles.GetValue(i).ToString() + "   Error:" + Errors.GetValue(i).ToString();
    205             }
    206 
    207         }
    208 
    209 
    210         /// <summary>
    211         /// 数据订阅方法
    212         /// </summary>
    213         /// <param name="TransactionID">处理ID</param>
    214         /// <param name="NumItems">项个数</param>
    215         /// <param name="ClientHandles">OPC客户端的句柄</param>
    216         /// <param name="ItemValues">节点的值</param>
    217         /// <param name="Qualities">节点的质量</param>
    218         /// <param name="TimeStamps">时间戳</param>
    219         private void KepGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
    220         {
    221             for (int i = 1; i <= NumItems; i++)//下标一定要从1开始,NumItems参数是每次事件触发时Group中实际发生数据变化的Item的数量,而不是整个Group里的Items
    222             {
    223                 this.txtTagValue.Text = ItemValues.GetValue(i).ToString();
    224                 this.txtQualities.Text = Qualities.GetValue(i).ToString();
    225                 this.txtTimeStamps.Text = TimeStamps.GetValue(i).ToString();
    226             }
    227         }
    228 
    229 
    230         /// <summary>
    231         /// 选择列表时触发的事件
    232         /// </summary>
    233         /// <param name="sender"></param>
    234         /// <param name="e"></param>
    235         private void ListBox1_SelectedIndexChanged(object sender, EventArgs e)
    236         {
    237             try
    238             {
    239                 if (itmHandleClient != 0)
    240                 {
    241                     this.txtTagValue.Text = "";
    242                     this.txtQualities.Text = "";
    243                     this.txtTimeStamps.Text = "";
    244 
    245                     Array Errors;
    246                     OPCItem bItem = KepItems.GetOPCItem(itmHandleServer);
    247                     //注:OPC中以1为数组的基数
    248                     int[] temp = new int[2] { 0, bItem.ServerHandle };
    249                     Array serverHandle = (Array)temp;
    250                     //移除上一次选择的项
    251                     KepItems.Remove(KepItems.Count, ref serverHandle, out Errors);
    252 
    253 
    254                     itmHandleClient = 1;//节点编号为1
    255                     KepItem = KepItems.AddItem(listBox1.SelectedItem.ToString(), itmHandleClient);//第一个参数为ItemID,第二个参数为节点编号,节点编号可自定义
    256                     itmHandleServer = KepItem.ServerHandle;//获取该节点的服务器句柄
    257                 }
    258                 else
    259                 {
    260                     itmHandleClient = 1;//节点编号为1
    261                     KepItem = KepItems.AddItem(listBox1.SelectedItem.ToString(), itmHandleClient);//第一个参数为ItemID,第二个参数为节点编号,节点编号可自定义
    262                     itmHandleServer = KepItem.ServerHandle;//获取该节点的服务器句柄
    263 
    264                 }
    265                
    266             }
    267             catch (Exception err)
    268             {
    269                 //没有任何权限的项,都是OPC服务器保留的系统项,此处可不做处理。
    270                 itmHandleClient = 0;
    271                 txtTagValue.Text = "Error ox";
    272                 txtQualities.Text = "Error ox";
    273                 txtTimeStamps.Text = "Error ox";
    274                 MessageBox.Show("此项为系统保留项:" + err.Message, "提示信息");
    275             }
    276         }
    277 
    278 
    279         /// <summary>
    280         /// 设置组属性的按钮点击事件
    281         /// </summary>
    282         /// <param name="sender"></param>
    283         /// <param name="e"></param>
    284         private void BtnSetGroupPro_Click(object sender, EventArgs e)
    285         {
    286             SetGroupProperty();
    287         }
    288 
    289 
    290         /// <summary>
    291         /// “写入”按钮点击事件
    292         /// </summary>
    293         /// <param name="sender"></param>
    294         /// <param name="e"></param>
    295         private void BtnWrite_Click(object sender, EventArgs e)
    296         {
    297             OPCItem bItem = KepItems.GetOPCItem(itmHandleServer);
    298             int[] temp = new int[2] { 0, bItem.ServerHandle };
    299             Array serverHandles = (Array)temp;
    300             object[] valueTemp = new object[2] { "", txtWriteTagValue.Text };
    301             Array values = (Array)valueTemp;
    302             Array Errors;
    303             int cancelID;
    304             KepGroup.AsyncWrite(1, ref serverHandles, ref values, out Errors, 2009, out cancelID);
    305             //KepItem.Write(txtWriteTagValue.Text);//这句也可以写入,但并不触发写入事件
    306             GC.Collect();
    307         }
    308 
    309 
    310         /// <summary>
    311         /// 关闭窗口事件
    312         /// </summary>
    313         /// <param name="sender"></param>
    314         /// <param name="e"></param>
    315         private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    316         {
    317             if (!opc_connected)
    318             {
    319                 return;
    320             }
    321 
    322             if (KepGroup != null)
    323             {
    324                 KepGroup.DataChange -= new DIOPCGroupEvent_DataChangeEventHandler(KepGroup_DataChange);
    325             }
    326 
    327             if (KepServer != null)
    328             {
    329                 KepServer.Disconnect();
    330                 KepServer = null;
    331             }
    332 
    333             opc_connected = false;
    334         }
    335     }
    336 }

    至此,一个可读可写的客户端就编写好了,大家要是做得更好(比如多个节点的数据同时显示在ListView控件上)可以分享出来大家一起学习。

    源码百度云下载↓

    链接:https://pan.baidu.com/s/1BmCa622CAdUQRbW8ahXtMg 提取码:yvdw

    该分享连接期限为永久,送给爱学习的人。如果大家发现该源码里的BUG,或者有更好的解决方案可以在评论区说出来。最后由衷感谢那位不知名的大佬,在网上提供了初版代码,让我可以跌跌撞撞地入门学习。

  • 相关阅读:
    Druid 使用 Kafka 将数据载入到 Kafka
    Druid 使用 Kafka 数据加载教程——下载和启动 Kafka
    Druid 集群方式部署 —— 启动服务
    Druid 集群方式部署 —— 端口调整
    Druid 集群方式部署 —— 配置调整
    Druid 集群方式部署 —— 配置 Zookeeper 连接
    Druid 集群方式部署 —— 元数据和深度存储
    Druid 集群方式部署 —— 从独立服务器部署上合并到集群的硬件配置
    Druid 集群方式部署 —— 选择硬件
    Druid 独立服务器方式部署文档
  • 原文地址:https://www.cnblogs.com/flh1/p/12409266.html
Copyright © 2011-2022 走看看