天天看點

C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤

C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤

大部分源碼參考自某位樂于分享的大佬,但是他的源碼和接口都年代久遠了(2009年的代碼),基本用不了,還存在些許BUG,我的VS版本是2019最新版的,采用的OPC自動化接口也是最新的,修複緻命BUG之後已經可以正常使用了,對于初學者來說算是不錯的教程(非常讨厭那些寫教程但源碼還要積分下載下傳的僞大佬,我呸!),大家可以先去下個OPC伺服器模拟器,不然做出來的用戶端是測不到資料的,相關的伺服器模拟器可以百度一下,這個比較好找。

少羅嗦,先看東西

用戶端界面如圖所示↓

C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤

用戶端運作圖如圖所示↓

C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤

東西看完了,接下來是引用

在“解決方案資料總管”裡滑鼠右鍵單擊項目依次選擇“添加”→“引用”→“COM”,然後在右上角搜尋框搜尋“OPC”

選擇下圖這個自動化接口dll就可以了↓

C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤

并在代碼區的頭部(命名空間處)利用using關鍵詞添加引用,如下圖最後一行↓

C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤

準備工作做完了,接下來就是思路了:

1.先掃描本地的OPC伺服器,将所有的伺服器名加入到下拉框控件裡

2.填上IP位址,本地測試一般為127.0.0.1,點選連接配接按鈕觸發事件,連接配接伺服器

3.連接配接上伺服器之後建立一個OPCBrowser對象,主要用于展開伺服器的“樹枝”和“葉子”,如下圖↓就是伺服器那邊所有的節點。

C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤C#利用自動化接口編寫OPC用戶端,OPC Client,不需要積分,源碼直接放網盤

4.建立一個組和設定組的屬性

5.添加一個節點(通過選擇清單裡的節點達成添加操作)

6.編寫訂閱事件,當伺服器端的資料有變化時,會把第5步添加的節點的對應值傳回來,并顯示在對應的文本控件上

7編寫異步寫入事件,可以通過寫入按鈕将對應文本框的值寫入到服務端

耐心的同學可以在部落格慢慢看,不習慣的同學直接拉到末尾下載下傳源碼,然後在軟體裡看會舒服很多

完整代碼

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using OPCAutomation;

namespace OPCtest4
{
    public partial class Form1 : Form
    {
        OPCServer KepServer;
        OPCGroups KepGroups;
        OPCGroup KepGroup;
        OPCItems KepItems;
        OPCItem KepItem;
        bool opc_connected = false;//連接配接狀态
        int itmHandleClient = 0;//用戶端的句柄,句柄即控件名稱,如“張三”,用來識别是哪個具體的對象,此處可了解為每個節點的編号
        int itmHandleServer = 0;//伺服器的句柄
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            GetLocalServer();
        }

        /// <summary>
        /// 擷取本地的OPC伺服器名稱
        /// </summary>
        public void GetLocalServer()
        {
            IPHostEntry host = Dns.GetHostEntry("127.0.0.1");
            var strHostName = host.HostName;
            try
            {
                KepServer = new OPCServer();
                object serverList = KepServer.GetOPCServers(strHostName);

                foreach (string turn in (Array)serverList)
                {
                    cmbServerName.Items.Add(turn);
                }

                cmbServerName.SelectedIndex = 0;
                btnConnServer.Enabled = true;
            }
            catch (Exception err)
            {
                MessageBox.Show("枚舉本地OPC伺服器出錯:" + err.Message, "提示資訊", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }


        /// <summary>
        /// "連接配接"按鈕點選事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnConnServer_Click(object sender, EventArgs e)
        {
            try
            {
                if (!ConnectRemoteServer(txtRemoteServerIP.Text, cmbServerName.Text))
                {
                    return;
                }

                btnSetGroupPro.Enabled = true;

                opc_connected = true;

                GetServerInfo();

                RecurBrowse(KepServer.CreateBrowser());

                if (!CreateGroup())
                {
                    return;
                }
            }
            catch (Exception err)
            {
                MessageBox.Show("初始化出錯:" + err.Message, "提示資訊", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }


        /// <summary>
        /// 連接配接伺服器
        /// </summary>
        /// <param name="remoteServerIP">伺服器IP</param>
        /// <param name="remoteServerName">伺服器名稱</param>
        /// <returns></returns>
        public bool ConnectRemoteServer(string remoteServerIP, string remoteServerName)
        {
            try
            {
                KepServer.Connect(remoteServerName, remoteServerIP);

                if (KepServer.ServerState == (int)OPCServerState.OPCRunning)
                {
                    tsslServerState.Text = "已連接配接到-" + KepServer.ServerName + "   ";
                }
                else
                {
                    //這裡你可以根據傳回的狀态來自定義顯示資訊,請檢視自動化接口API文檔
                    tsslServerState.Text = "狀态:" + KepServer.ServerState.ToString() + "   ";
                }
            }
            catch (Exception err)
            {
                MessageBox.Show("連接配接遠端伺服器出現錯誤:" + err.Message, "提示資訊", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 擷取伺服器資訊,并顯示在窗體狀态欄上
        /// </summary>
        public void GetServerInfo()
        {
            tsslServerStartTime.Text = "開始時間:" + KepServer.StartTime.ToString() + "    ";
            tsslversion.Text = "版本:" + KepServer.MajorVersion.ToString() + "." + KepServer.MinorVersion.ToString() + "." + KepServer.BuildNumber.ToString();
        }

        /// <summary>
        /// 展開樹枝和葉子
        /// </summary>
        /// <param name="oPCBrowser">opc浏覽器</param>
        public void RecurBrowse(OPCBrowser oPCBrowser)
        {
            //展開分支
            oPCBrowser.ShowBranches();
            //展開葉子
            oPCBrowser.ShowLeafs(true);
            foreach (object turn in oPCBrowser)
            {
                listBox1.Items.Add(turn.ToString());
            }
        }


        /// <summary>
        /// 建立組,将本地組和伺服器上的組對應
        /// </summary>
        /// <returns></returns>
        public bool CreateGroup()
        {
            try
            {
                KepGroups = KepServer.OPCGroups;//将服務端的組集合複制到本地
                KepGroup = KepGroups.Add("S");//添加一個組
                SetGroupProperty();//設定組屬性
                
                KepItems = KepGroup.OPCItems;//将組裡的節點集合複制到本地節點集合

                KepGroup.DataChange += KepGroup_DataChange;
                KepGroup.AsyncWriteComplete += KepGroup_AsyncWriteComplete;
            }
            catch (Exception err)
            {
                MessageBox.Show("建立組出現錯誤:" + err.Message, "提示資訊", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return false;
            }
            return true;
        }

      
        /// <summary>
        /// 設定組的屬性,從對應的控件裡擷取
        /// </summary>
        public void SetGroupProperty()
        {
            KepServer.OPCGroups.DefaultGroupIsActive = Convert.ToBoolean(txtGroupIsActive.Text);//激活組
            KepServer.OPCGroups.DefaultGroupDeadband = Convert.ToInt32(txtGroupDeadband.Text);// 死區值,設為0時,伺服器端該組内任何資料變化都通知組
            KepGroup.UpdateRate = Convert.ToInt32(txtUpdateRate.Text);//伺服器向客戶程式送出資料變化的重新整理速率
            KepGroup.IsActive = Convert.ToBoolean(txtIsActive.Text);//組的激活狀态标志
            KepGroup.IsSubscribed = Convert.ToBoolean(txtIsSubscribed.Text);//是否訂閱資料
        }


        /// <summary>
        /// 異步寫方法
        /// </summary>
        /// <param name="TransactionID">處理ID</param>
        /// <param name="NumItems">項個數</param>
        /// <param name="ClientHandles">OPC用戶端的句柄</param>
        /// <param name="Errors">錯誤個數</param>
        private void KepGroup_AsyncWriteComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)
        {
            lblState.Text = "";
            for (int i = 1; i <= NumItems; i++)
            {
                lblState.Text += "Tran:" + TransactionID.ToString() + "   CH:" + ClientHandles.GetValue(i).ToString() + "   Error:" + Errors.GetValue(i).ToString();
            }

        }


        /// <summary>
        /// 資料訂閱方法
        /// </summary>
        /// <param name="TransactionID">處理ID</param>
        /// <param name="NumItems">項個數</param>
        /// <param name="ClientHandles">OPC用戶端的句柄</param>
        /// <param name="ItemValues">節點的值</param>
        /// <param name="Qualities">節點的品質</param>
        /// <param name="TimeStamps">時間戳</param>
        private void KepGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
        {
            for (int i = 1; i <= NumItems; i++)//下标一定要從1開始,NumItems參數是每次事件觸發時Group中實際發生資料變化的Item的數量,而不是整個Group裡的Items
            {
                this.txtTagValue.Text = ItemValues.GetValue(i).ToString();
                this.txtQualities.Text = Qualities.GetValue(i).ToString();
                this.txtTimeStamps.Text = TimeStamps.GetValue(i).ToString();
            }
        }


        /// <summary>
        /// 選擇清單時觸發的事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                if (itmHandleClient != 0)
                {
                    this.txtTagValue.Text = "";
                    this.txtQualities.Text = "";
                    this.txtTimeStamps.Text = "";

                    Array Errors;
                    OPCItem bItem = KepItems.GetOPCItem(itmHandleServer);
                    //注:OPC中以1為數組的基數
                    int[] temp = new int[2] { 0, bItem.ServerHandle };
                    Array serverHandle = (Array)temp;
                    //移除上一次選擇的項
                    KepItems.Remove(KepItems.Count, ref serverHandle, out Errors);


                    itmHandleClient = 1;//節點編号為1
                    KepItem = KepItems.AddItem(listBox1.SelectedItem.ToString(), itmHandleClient);//第一個參數為ItemID,第二個參數為節點編号,節點編号可自定義
                    itmHandleServer = KepItem.ServerHandle;//擷取該節點的伺服器句柄
                }
                else
                {
                    itmHandleClient = 1;//節點編号為1
                    KepItem = KepItems.AddItem(listBox1.SelectedItem.ToString(), itmHandleClient);//第一個參數為ItemID,第二個參數為節點編号,節點編号可自定義
                    itmHandleServer = KepItem.ServerHandle;//擷取該節點的伺服器句柄

                }
               
            }
            catch (Exception err)
            {
                //沒有任何權限的項,都是OPC伺服器保留的系統項,此處可不做處理。
                itmHandleClient = 0;
                txtTagValue.Text = "Error ox";
                txtQualities.Text = "Error ox";
                txtTimeStamps.Text = "Error ox";
                MessageBox.Show("此項為系統保留項:" + err.Message, "提示資訊");
            }
        }


        /// <summary>
        /// 設定組屬性的按鈕點選事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnSetGroupPro_Click(object sender, EventArgs e)
        {
            SetGroupProperty();
        }


        /// <summary>
        /// “寫入”按鈕點選事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnWrite_Click(object sender, EventArgs e)
        {
            OPCItem bItem = KepItems.GetOPCItem(itmHandleServer);
            int[] temp = new int[2] { 0, bItem.ServerHandle };
            Array serverHandles = (Array)temp;
            object[] valueTemp = new object[2] { "", txtWriteTagValue.Text };
            Array values = (Array)valueTemp;
            Array Errors;
            int cancelID;
            KepGroup.AsyncWrite(1, ref serverHandles, ref values, out Errors, 2009, out cancelID);
            //KepItem.Write(txtWriteTagValue.Text);//這句也可以寫入,但并不觸發寫入事件
            GC.Collect();
        }


        /// <summary>
        /// 關閉視窗事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!opc_connected)
            {
                return;
            }

            if (KepGroup != null)
            {
                KepGroup.DataChange -= new DIOPCGroupEvent_DataChangeEventHandler(KepGroup_DataChange);
            }

            if (KepServer != null)
            {
                KepServer.Disconnect();
                KepServer = null;
            }

            opc_connected = false;
        }
    }
}

           

至此,一個可讀可寫的用戶端就編寫好了,大家要是做得更好(比如多個節點的資料同時顯示在ListView控件上)可以分享出來大家一起學習。

源碼百度雲下載下傳↓

連結:https://pan.baidu.com/s/1BmCa622CAdUQRbW8ahXtMg 提取碼:yvdw

該分享連接配接期限為永久,送給愛學習的人。如果大家發現該源碼裡的BUG,或者有更好的解決方案可以在評論區說出來。最後由衷感謝那位不知名的大佬,在網上提供了初版代碼,讓我可以跌跌撞撞地入門學習。