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,或者有更好的解決方案可以在評論區說出來。最后由衷感謝那位不知名的大佬,在網上提供了初版代碼,讓我可以跌跌撞撞地入門學習。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM