使用OPC與PLC通訊 一


總結自己在opc與自控開發的經驗。首先介紹OPC DA模式下的OPC各種操作。

在使用opc時需要引用到 OPCDAAuto.dll 這個類庫。

在項目引用后需要注冊這個類庫,否則程序跑起來會報錯,“未找到工廠類 。。。”

將該dll文件放在任意目錄下,建議在引用程序的的同級目錄下。 

在 cmd 控制台  輸入regsvr32  Q:\PLCDataIntegration\packages\01OPCDaAuto\OPCDAAuto.dll 

注冊完成后電腦會提示注冊成功,這時,就可以使用工具類中的方法啦。

1.定義相關變量

        private OPCServer opcServer;
        private OPCGroups opcGroups;
        private OPCGroup opcGroup;
        private List<int> itemHandleClient = new List<int>();
        private List<int> itemHandleServer = new List<int>();
        private List<string> itemNames = new List<string>();
        private List<model> modelValues = new List<model>();
        private OPCItems opcItems;
        private OPCItem opcItem;
        private Dictionary<string, string> itemValues = new Dictionary<string, string>();

  

2.使用opc從plc中讀取數據。這個是使用OPC DAAuto中的Connect方法。Connect之前要先

創建OPCServer 對象

opcServer = new OPCServer(); 

OPCServer.StartTime:服務器的啟動時間

OPCServer.CurrentTime:服務器的當前時間,各個客戶端可以通過這個屬性值完成一些同步的操作

//strHostIP 主機IP,DA模式下通常為127.0.0.1;
//strHostName opc服務名,通常為字符串,例如kepsserver 的opc名稱為 Kepware.KepServerEX.V6
 private bool ConnectServer(string strHostIP, string strHostName)
        {
            try
            {
opcServer = new OPCServer(); opcServer.Connect(strHostName, strHostIP); }
catch (Exception ex) { SaveCommand("連接到OPC服務器失敗!" + ex.Message); return false; } txtLog.Text += "連接到OPC服務器成功!" + "\r\n"; return true; }

 

3.連接成功后就可以用opcServer這個對象了。

根據自己開發OPCServer 和OPCClient的經驗,這里的OPCGroups與OPCGroup可以用樹來理解,OPCGroups下可以有很多OPCGroup,OPCGroup下添加很多測點item。

OPC API 里AddItem 就是往OPCGroup下添加測點

4.OPCGroups與OPCGroup

  opcGroups = opcServer.OPCGroups;
 opcGroup = opcGroups.Add("YoursGroupName");

這里從OPCServer中獲取OPCGroups信息,保持一致。

而OPCGroups下的OPCGroup可自行添加,然后自己注冊的測點屬性都在這個組下面

可以給OPCGroup設置如下屬性,這將影響到這個組下面的測點數據采集頻率等。

     private void SetGroupProperty(OPCGroup opcGroup, int updateRate)
        {
            opcGroup.IsActive = true;
            opcGroup.DeadBand = 0f;
            opcGroup.UpdateRate = updateRate;
            opcGroup.IsSubscribed = true;
        }
UpdateRate是一個整型值,影響組中測點數據更新頻率。相關屬性如下
OPCGroups.Count:下屬組(Group)的數量
DefaultGroupIsActive(新添加的OPC組的活動狀態的默認值。默認初始值是活動狀態)
DefaultGroupUpdateRate(新添加的OPC組的默認數據更新周期,默認初始值是1000亳秒)、
DefaultGrouPDeadband( 新添加的OPC組的默認不敏感區的默認值,即能引起數據變化的最小數值百分比,默認值是0%)
DefaultGroupLocaleID(新添加的OPC組區域標識符的默認值)
DefaultGroupTimeBias(新添加的OPC組的時間偏差的默認值)等。
 
        

5.設置OPCGroup屬性

  opcGroups = opcServer.OPCGroups;
  opcGroup = opcGroups.Add("MYOPCGROUP");
  SetGroupProperty(opcGroup, UpdateRateOPC);
  opcItems = opcGroup.OPCItems;

 

6.注冊opc監控測點。根據項目實際情況將監測點取出,以字符串數組傳遞。

        public void AddItems(string[] itemNamesAdded)
        {
            itemHandleServer.Clear();

            for (int i = 0; i < itemNamesAdded.Length; i++)
            {
                itemNames.Add(itemNamesAdded[i]);
                itemValues.Add(itemNamesAdded[i], "");
            }
            for (int j = 0; j < itemNamesAdded.Length; j++)
            {
                try
                {
                    itemHandleClient.Add((itemHandleClient.Count == 0) ? 1 : (itemHandleClient[itemHandleClient.Count - 1] + 1));
                    opcItem = opcItems.AddItem(itemNamesAdded[j], itemHandleClient[itemHandleClient.Count - 1]);
                    itemHandleServer.Add(opcItem.ServerHandle);
                }
                catch (Exception ex)
                {
                    SaveCommand(itemNamesAdded[j] + ex.Message);
                }
            }
        }

 

7.注冊讀寫或數據變化事件,opc的事件很多,常用的有以下幾個。

opcGroup.AsyncWriteComplete //測點寫入完成后觸發
opcGroup.AsyncReadComplete //讀取指令完成后觸發
opcGroup.DataChange  //opcc測點數值變化后觸發,通常用於實時數據上報

 //注冊事件,然后在自己的方法里實現相關操作
opcGroup.AsyncWriteComplete += new DIOPCGroupEvent_AsyncWriteCompleteEventHandler(opcGroup_AsyncWriteComplete);
opcGroup.DataChange
+= new DIOPCGroupEvent_DataChangeEventHandler(KepGroup_DataChange);
opcGroup.AsyncReadComplete += new DIOPCGroupEvent_AsyncReadCompleteEventHandler(GroupAsyncReadComplete);

//實現方法 KepGroup_DataChange

        /// <summary>
        /// 數據變動轉儲
        /// </summary>
        /// <param name="TransactionID"></param>
        /// <param name="NumItems"></param>
        /// <param name="ClientHandles"></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)
        {
            string itemID = string.Empty;

            try
            {
                string text = "hello rabbit";
                int i = 0;
                for (i = 1; i <= NumItems; i++)
                {
                    //SetLogInfo($"{ItemValues.GetValue(i)}", Color.Green);

                    if (ItemValues.GetValue(i) == null) { continue; }

                    text = ItemValues.GetValue(i).ToString();

                    //SetLogInfo($"本次發布消息{text}", Color.Green);

                    itemID = opcItems.GetOPCItem(itemHandleServer[(int)ClientHandles.GetValue(i) - 1]).ItemID;

                    string message = itemID + ":" + text;

                     MessageBox.Show(mesage);
                }
                
            }
            catch (Exception ex)
            {                
                SaveCommand(ex.Message + itemID);
            }
        }

 

實現方法 GroupAsyncReadComplete

        private void GroupAsyncReadComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps, ref Array Errors)
        {

            //SaveCommand("**********GroupAsyncReadComplete***********");

            string itemID = string.Empty;
            string empty = string.Empty;

            try
            {

                int i = 0;

                for (i = 1; i <= NumItems; i++)
                {
                    //非空判斷,防止程序異常中斷
                    if (ItemValues.GetValue(i) != null)
                    {
                        empty = ItemValues.GetValue(i).ToString();
                    }

                    //獲取監測點id
                    itemID = opcItems.GetOPCItem(itemHandleServer[(int)ClientHandles.GetValue(i) - 1]).ItemID;

                    string text = itemID + ":" + empty;

                    MessageBox.Show("讀取完成,結果為"+text);
                }

                MessageBox.Show($"本次讀取完成,共{i}個", Color.Blue, 2);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"GroupAsyncReadComplete:{ex.Message},itemID:{ itemID }");
            }
        }

 

7.使用監測數據,在成功讀取到數據后就可以按自己的需求使用數據了,可以存到數據庫,放到Redis,或者推到Rabbit等消息隊列都沒問題。

 

8.通過opc寫入測點值實現,遠程控制plc

其實遠程控制,在代碼層面來看並沒有那么復雜,因為前面的工作都做好后,opc通道已經打開的情況下,控制只是一個寫入操作。把指定的值寫到指定的測點中,就是這么簡單。實現控制難點不在控制本身,重點難點在於保證通信網絡的穩定,和監測狀態的

實時返回。總而言之,控制本身不復雜,復雜在安全性和穩定型的保證上。使用opc多為IT與OT融合的情景之下,IT平台本身缺乏對底層設備的有效監管,需要借助於opc客戶端充當代理中介角色。這時OPC客戶端本身的穩定性,和時效型顯得尤為重要。這點

需要研發人員多多考慮。這點大家有好的方法可以評論分享下。

         //調用示例

          string[] array2 = {"LCU1.PLC_GATE1_OPEN"};//點號開啟1號閘門
          string[] array3 = {"1"};

         AsyncWrite(array2, array3);      

/// <summary> /// 異步寫方法,支持批量操作也可以調用opc單點讀寫方法 /// </summary> /// <param name="writeItemNames"></param> /// <param name="writeItemValues"></param> public void AsyncWrite(string[] writeItemNames, string[] writeItemValues) { try { OPCItem[] array = new OPCItem[writeItemNames.Length]; for (int i = 0; i < writeItemNames.Length; i++) { for (int j = 0; j < itemNames.Count; j++) { if (itemNames[j] == writeItemNames[i]) { array[i] = opcItems.GetOPCItem(itemHandleServer[j]); break; } } } int[] array2 = new int[writeItemNames.Length + 1]; array2[0] = 0; for (int k = 1; k < writeItemNames.Length + 1; k++) { array2[k] = array[k - 1].ServerHandle; } Array ServerHandles = array2; object[] array3 = new object[writeItemNames.Length + 1]; array3[0] = ""; for (int l = 1; l < writeItemNames.Length + 1; l++) { array3[l] = writeItemValues[l - 1]; } Array Values = array3; opcGroup.AsyncWrite(writeItemNames.Length, ref ServerHandles, ref Values, out Array _, 2009, out int _); GC.Collect(); } catch (Exception ex) { MessageBox.Show(ex.Message); } }

 

至此OPC基本操作都完成了,OPC提供的官方API很深奧,有很多可以學的東西。OPC現在已經是自動化領域,跨域提供數據的潮流了,尤其時OPC UA發布后,自動化(OT)與信息化(IT)日趨融合,opc在傳統自動化企業轉型中應該會擔任比較重要的角色。

所以OPC還是很值得一學的。

 

分享一首好聽的歌 錯都錯了 ~~~


免責聲明!

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



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