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