總結自己在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還是很值得一學的。
分享一首好聽的歌 錯都錯了 ~~~