• 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,或者有更好的解决方案可以在评论区说出来。最后由衷感谢那位不知名的大佬,在网上提供了初版代码,让我可以跌跌撞撞地入门学习。

  • 相关阅读:
    SQL学习指南第三篇
    SQL学习指南第二篇
    Rebuilding Roads
    TOJ4244: Sum
    K-th Number
    【模板】后缀数组
    冰水挑战
    旅途
    Monkey and Banana
    Max Sum Plus Plus
  • 原文地址:https://www.cnblogs.com/flh1/p/12409266.html
Copyright © 2020-2023  润新知