C# Opcda 應(yīng)用詳解,你學(xué)會(huì)了嗎?
OPC,一句話DA用的越來(lái)越少了。全稱OLE For Process Control,是一種工業(yè)標(biāo)準(zhǔn),旨在解決不同供應(yīng)商設(shè)備與應(yīng)用程序之間的接口標(biāo)準(zhǔn)化問(wèn)題。隨著工業(yè)自動(dòng)化和控制系統(tǒng)的不斷發(fā)展,各種設(shè)備和應(yīng)用程序不斷涌現(xiàn),其之間的通信變得越來(lái)越復(fù)雜。傳統(tǒng)的設(shè)備和應(yīng)用程序之間的通信主要依靠編程和調(diào)試,但這種方式效率低下、不靈活、成本高,也限制了應(yīng)用程序的自由組合和開(kāi)發(fā)。
OPC的出現(xiàn)正是為了解決這些問(wèn)題。它采用一種簡(jiǎn)單的接口標(biāo)準(zhǔn),允許不同的供應(yīng)商設(shè)備和應(yīng)用程序之間進(jìn)行數(shù)據(jù)交換。通過(guò)使用OPC,應(yīng)用程序可以自由地組合不同的設(shè)備和應(yīng)用程序,從而實(shí)現(xiàn)更加靈活、高效和經(jīng)濟(jì)的工業(yè)自動(dòng)化系統(tǒng)。
OPC標(biāo)準(zhǔn)包括一組接口規(guī)范和協(xié)議,可以被用于不同類型的設(shè)備和應(yīng)用程序。OPC的接口規(guī)范包括以下幾個(gè)部分:OPC控制器規(guī)范、OPC數(shù)據(jù)服務(wù)規(guī)范、OPC應(yīng)用程序接口規(guī)范和OPC數(shù)據(jù)傳輸規(guī)范。這些規(guī)范提供了一種通用的接口,允許設(shè)備和應(yīng)用程序之間進(jìn)行數(shù)據(jù)交換。OPC的協(xié)議主要包括基于協(xié)議的和基于消息的兩種方式。
基于協(xié)議的方式是通過(guò)協(xié)議規(guī)范定義的消息格式進(jìn)行通信。每個(gè)設(shè)備和應(yīng)用程序都可以使用OPC協(xié)議生成自己的消息格式,從而實(shí)現(xiàn)與其他設(shè)備和應(yīng)用程序的通信?;谙⒌姆绞絼t是通過(guò)定義消息的格式和目的地來(lái)進(jìn)行通信。消息可以通過(guò)OPC服務(wù)器進(jìn)行傳輸,服務(wù)器可以是本地的或者遠(yuǎn)程的。
OPC標(biāo)準(zhǔn)的實(shí)現(xiàn)依賴于OPC基金會(huì),該基金會(huì)是一個(gè)非營(yíng)利性的組織,致力于推廣和發(fā)展OPC標(biāo)準(zhǔn)。基金會(huì)負(fù)責(zé)維護(hù)OPC標(biāo)準(zhǔn),開(kāi)發(fā)新的規(guī)范和協(xié)議,提供技術(shù)支持和培訓(xùn)等服務(wù)。OPC基金會(huì)的成員包括各種設(shè)備和應(yīng)用程序的供應(yīng)商、系統(tǒng)集成商、工程師和用戶。
利用驅(qū)動(dòng)器的系統(tǒng)連接
圖片
利用 OPC 控制的系統(tǒng)組成
圖片
OPC 的分層結(jié)構(gòu)
OPC 對(duì)象中最上層的對(duì)象是 OPC 服務(wù)器,一個(gè) OPC 服務(wù)器中可以設(shè)置一個(gè)以上的 OPC 組。OPC 服務(wù)器常對(duì)應(yīng)于某種特定的控制設(shè)備,如 DCS 以及 PLC 等。
OPC 組是可以進(jìn)行數(shù)據(jù)訪問(wèn)的多個(gè) OPC 標(biāo)簽的集合,OPC 應(yīng)用程序可以將需要的數(shù)據(jù)分組進(jìn)行批量讀取,也可以以組為單位啟動(dòng)或者停止數(shù)據(jù)訪問(wèn)。此外,OPC 組還提供組內(nèi) OPC 標(biāo)簽數(shù)據(jù)變化時(shí)向 OPC 應(yīng)用程序通知的事件。
圖片
OPC 與 OPC UA
OPC DA 與 OPC UA 都是 OPC 協(xié)議的標(biāo)準(zhǔn):
OPC 是一種通過(guò)微軟 COM/DCOM 技術(shù)來(lái)實(shí)現(xiàn)自動(dòng)化控制的協(xié)定,采用 C/S 架構(gòu)。開(kāi)發(fā)人員只需要按照 OPC 的標(biāo)準(zhǔn)編寫(xiě) OPC-Client 訪問(wèn) OPC-Server 進(jìn)行讀寫(xiě)操作即可實(shí)現(xiàn)與硬件設(shè)備的通信。OPC 的協(xié)定中包括:
DA (Data Access):訪問(wèn)數(shù)據(jù)的主要規(guī)范;
A&E (Alarm and Event):基于事件提供 Client 端訂閱,事件觸發(fā)后 Server 主動(dòng)提交數(shù)據(jù);
HDA (History Data Access):歷史數(shù)據(jù)訪問(wèn);
OPC UA 是 OPC 協(xié)議的新版,其不再依賴于 COM/DCOM 技術(shù),這意味著其具有跨平臺(tái)性,不再局限于 Windows 系統(tǒng)。OPC UA 提供了可靠的通信機(jī)制,接口簡(jiǎn)單一致。
先用KepServerEx 做個(gè)防真環(huán)境
模擬數(shù)據(jù)配制
線形變化型 RAMP(Rate, Low Limit, High Limit, Increment) Rate:變化率,單位毫秒 Low Limit:下限值 High Limit:上限值 Increment:變化量
隨機(jī)變化型
格式:RANDOM(Rate, Low Limit, High Limit)
Rate:變化率,單位毫秒
Low Limit:下限值
High Limit:上限值
三角函數(shù)型 SINE(Rate, Low Limit, High Limit, Frequency, Phase) Rate:變化率/變化周期,單位毫秒 Low Limit:下限值 High Limit:上限值 Frequency:三角函數(shù)頻率 Phase:三角函數(shù)偏移量
用戶自定義型 USER(Rate, User Value1, User Value2, User Value3,…User ValueN) Rate:變化率,單位毫秒 User Value1….ValueN:用戶自定義的值,可以有多個(gè)
OPCDA
OPCDAAuto.dll下載_解決OPCDAAuto.dll丟失問(wèn)題 OPCDAAuto.dll是微軟vc組件的一個(gè)重要dll文件,缺少它可能會(huì)造成部分軟件或游戲無(wú)法正常運(yùn)行。當(dāng)系統(tǒng)提示“沒(méi)有找到OPCDAAuto.dll”或“丟失OPCDAAuto.dll”等類似錯(cuò)誤信息 dll文件修復(fù)方法:1、解壓下載的文件。2、復(fù)制文件“OPCDAAuto.dll”到系統(tǒng)目錄下。3、系統(tǒng)目錄一般為:C:\WINNT\System32 64位系統(tǒng)為C:\Windows\SysWOW64 4、最后點(diǎn)擊開(kāi)始菜單-->運(yùn)行-->輸入regsvr32 OPCDAAuto.dll 后,回車即可解決錯(cuò)誤提示。
圖片
注意:這個(gè)組件在.net core下項(xiàng)目需要是x86下才能正常使用。
一個(gè)例子
圖片
做一個(gè)自定義控件UPanel。
圖片
public partial class UPanel : UserControl
{
public event Action<string,string> ValueClick;
public UPanel()
{
InitializeComponent();
}
protected override void OnInvalidated(InvalidateEventArgs e)
{
base.OnInvalidated(e);
ResetLocation();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
ResetLocation();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
ResetLocation();
this.Invalidate();
}
private void ResetLocation()
{
lblTitle.Left = (this.Width - lblTitle.Width) / 2;
lblValue.Left = (this.pnlMain.Width - lblValue.Width) / 2;
lblValue.Top = (this.pnlMain.Height - lblValue.Height) / 2;
}
public string Title
{
get
{
return lblTitle.Text;
}
set
{
lblTitle.Text = value;
}
}
public string Value
{
get
{
return lblValue.Text;
}
set
{
lblValue.Text = value;
}
}
private void pnlMain_Click(object sender, EventArgs e)
{
valueClick(Value);
}
private void lblValue_Click(object sender, EventArgs e)
{
valueClick(Value);
}
private void valueClick(string value)
{
if (ValueClick != null)
{
lblValue.Visible = false;
TextBox txt = new TextBox();
txt.Multiline = true;
this.pnlMain.Controls.Add(txt);
txt.Dock= DockStyle.Fill;
txt.Focus();
txt.MouseLeave += (obj, e) =>
{
txt.Visible=false;
lblValue.Visible = true;
if(value!=txt.Text && txt.Text != "")
{
Value = txt.Text;
this.ValueClick(Value, Title);
}
};
}
}
}
申明一個(gè)Dev類
public class Dev
{
public Int32 Bom1 { get; set; }
public string Part1 { get; set; }
public Int32 Qty1 { get; set; }
public Int32 Speed1 { get; set; }
public bool Status1 { get; set; }
public bool Status2 { get; set; }
public bool Status3 { get; set; }
public bool Status4 { get; set; }
public Int32 Times1 { get; set; }
public Int32 Weight { get; set; }
public int OnLine { get; set; } = 0;
public int Offline { get; set; } = 0;
public void Status()
{
OnLine = 0;
Offline = 0;
var properties= this.GetType().GetProperties()
.Where(x => x.Name.Contains("Status"));
foreach (var item in properties)
{
if ((bool)item.GetValue(this, null))
{
OnLine++;
}
else
{
Offline++;
}
}
}
}
OpcDev類
public class OpcModel
{
public Action<Dev> ReadFinish=null;
public Dev dev { get; set; } = new Dev();
OPCServer MyOpcServer;
OPCGroup MyOpcGroup;
OPCItem MyOpcItem1;
OPCItem MyOpcItem2;
OPCItem MyOpcItem3;
OPCItem MyOpcItem4;
OPCItem MyOpcItem5;
OPCItem MyOpcItem6;
OPCItem MyOpcItem7;
OPCItem MyOpcItem8;
OPCItem MyOpcItem9;
OPCItem MyOpcItem10;
//連接初使化
public bool Connect()
{
try
{
MyOpcServer = new OPCServer();
//ProgID是一個(gè)字符串,唯一標(biāo)識(shí)注冊(cè)的真實(shí)OPC數(shù)據(jù)訪問(wèn)服務(wù)器(實(shí)現(xiàn)自定義接口)
//The Node name can specify another computer to connect using DCOM.
MyOpcServer.Connect("Kepware.KEPServerEX.V6", "127.0.0.1");
MyOpcGroup = MyOpcServer.OPCGroups.Add("MyGroup1");
MyOpcGroup.IsActive = true;
//是否可以Change事件
MyOpcGroup.IsSubscribed = true;
MyOpcGroup.DeadBand = 0;
MyOpcGroup.UpdateRate = 100;
MyOpcItem1 = MyOpcGroup.OPCItems.AddItem("Channel1.Dev1.Bom1", 0);
MyOpcItem2 = MyOpcGroup.OPCItems.AddItem("Channel1.Dev1.Part1", 0);
MyOpcItem3 = MyOpcGroup.OPCItems.AddItem("Channel1.Dev1.Qty1", 0);
MyOpcItem4 = MyOpcGroup.OPCItems.AddItem("Channel1.Dev1.Speed1", 0);
MyOpcItem5 = MyOpcGroup.OPCItems.AddItem("Channel1.Dev1.Status1", 0);
MyOpcItem6 = MyOpcGroup.OPCItems.AddItem("Channel1.Dev1.Status2", 0);
MyOpcItem7 = MyOpcGroup.OPCItems.AddItem("Channel1.Dev1.Status3", 0);
MyOpcItem8 = MyOpcGroup.OPCItems.AddItem("Channel1.Dev1.Status4", 0);
MyOpcItem9 = MyOpcGroup.OPCItems.AddItem("Channel1.Dev1.Times1", 0);
MyOpcItem10 = MyOpcGroup.OPCItems.AddItem("Channel1.Dev1.Weight1", 0);
//異步讀完成
MyOpcGroup.AsyncReadComplete += MyOpcGroup_AsyncReadComplete;
return true;
}
catch
{
return false;
}
}
//同步讀取信息
public void ReadValue()
{
object ItemValues;
object Qualities;
object TimeStamps;
MyOpcItem1.Read(1, out ItemValues, out Qualities, out TimeStamps);
dev.Bom1 = (Int32)ItemValues;
MyOpcItem2.Read(1, out ItemValues, out Qualities, out TimeStamps);
dev.Part1 = ItemValues.ToString();
MyOpcItem3.Read(1, out ItemValues, out Qualities, out TimeStamps);
dev.Qty1 = (Int32)ItemValues;
MyOpcItem4.Read(1, out ItemValues, out Qualities, out TimeStamps);
dev.Speed1 = (Int32)ItemValues;
MyOpcItem5.Read(1, out ItemValues, out Qualities, out TimeStamps);
dev.Status1 = (bool)ItemValues;
MyOpcItem6.Read(1, out ItemValues, out Qualities, out TimeStamps);
dev.Status2 = (bool)ItemValues;
MyOpcItem7.Read(1, out ItemValues, out Qualities, out TimeStamps);
dev.Status3 = (bool)ItemValues;
MyOpcItem8.Read(1, out ItemValues, out Qualities, out TimeStamps);
dev.Status4 = (bool)ItemValues;
MyOpcItem9.Read(1, out ItemValues, out Qualities, out TimeStamps);
dev.Times1 = (Int32)ItemValues;
MyOpcItem10.Read(1, out ItemValues, out Qualities, out TimeStamps);
dev.Weight = (Int32)ItemValues;
dev.Status();
}
//同步寫(xiě)
public void WriteValue(string title, object value)
{
int[] tmp = new int[] { 0, MyOpcItem1.ServerHandle };
Array serverHandles = (Array)tmp;
Array Errors;
int cancelID;
object[] v = new object[] { "", value };
Array values = (Array)v;
MyOpcGroup.SyncWrite(1, ref serverHandles, ref values, out Errors);
switch (title)
{
default:
break;
}
}
}
修改異步寫(xiě)
//同步寫(xiě)
public void WriteValue(string title, object value)
{
int[] tmp = new int[] { 0, MyOpcItem1.ServerHandle };
Array serverHandles = (Array)tmp;
Array Errors;
int cancelID;
object[] v = new object[] { "", value };
Array values = (Array)v;
MyOpcGroup.SyncWrite(1, ref serverHandles, ref values, out Errors);
switch (title)
{
default:
break;
}
}
//異步寫(xiě)
public void WriteValueAsync(string title, object value)
{
int[] tmp = new int[] { 0, MyOpcItem1.ServerHandle };
Array serverHandles = (Array)tmp;
Array Errors;
int cancelID;
object[] v = new object[] { "", value };
Array values = (Array)v;
MyOpcGroup.AsyncWrite(1, ref serverHandles, ref values
, out Errors, 1, out cancelID);
switch (title)
{
default:
break;
}
}
異步讀
//異步讀
public void ReadValueAsync()
{
int[] tmp = new int[] { 0, MyOpcItem1.ServerHandle, MyOpcItem2.ServerHandle
, MyOpcItem3.ServerHandle, MyOpcItem4.ServerHandle, MyOpcItem5.ServerHandle
, MyOpcItem6.ServerHandle, MyOpcItem7.ServerHandle, MyOpcItem8.ServerHandle
, MyOpcItem9.ServerHandle, MyOpcItem10.ServerHandle };
Array serverHandles = (Array)tmp;
Array Errors;
int cancelID;
MyOpcGroup.AsyncRead(10, ref serverHandles, out Errors, 1, out cancelID);
}
//異步讀完成后的回調(diào)
private void MyOpcGroup_AsyncReadComplete(int TransactionID, int NumItems
, ref Array ClientHandles, ref Array ItemValues
, ref Array Qualities, ref Array TimeStamps, ref Array Errors)
{
dev.Bom1 = (Int32)MyOpcItem1.Value;
dev.Part1 = MyOpcItem2.Value.ToString();
dev.Qty1 = (Int32)MyOpcItem3.Value;
dev.Speed1 = (Int32)MyOpcItem4.Value;
dev.Status1 = (bool)MyOpcItem5.Value;
dev.Status2 = (bool)MyOpcItem6.Value;
dev.Status3 = (bool)MyOpcItem7.Value;
dev.Status4 = (bool)MyOpcItem8.Value;
dev.Times1 = (Int32)MyOpcItem9.Value;
dev.Weight = (Int32)MyOpcItem10.Value;
if (ReadFinished != null)
{
ReadFinished();
}
}
數(shù)據(jù)改變事件
MyOpcGroup.IsSubscribed = true; //只有為真事件才有效
MyOpcGroup.DataChange += MyOpcGroup_DataChange; //綁定事件
private void MyOpcGroup_DataChange(int TransactionID, int NumItems
, ref Array ClientHandles
, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
{
dev.Bom1 = (Int32)MyOpcItem1.Value;
dev.Part1 = MyOpcItem2.Value.ToString();
dev.Qty1 = (Int32)MyOpcItem3.Value;
dev.Speed1 = (Int32)MyOpcItem4.Value;
dev.Status1 = (bool)MyOpcItem5.Value;
dev.Status2 = (bool)MyOpcItem6.Value;
dev.Status3 = (bool)MyOpcItem7.Value;
dev.Status4 = (bool)MyOpcItem8.Value;
dev.Times1 = (Int32)MyOpcItem9.Value;
dev.Weight = (Int32)MyOpcItem10.Value;
if (DataChanged != null)
{
DataChanged();
}
}
Opc server 關(guān)閉事件
MyOpcServer.ServerShutDown += MyOpcServer_ServerShutDown;
private void MyOpcServer_ServerShutDown(string Reason)
{
}
界面代碼
public partial class FrmMain : Form
{
OpcModel opc = new OpcModel();
System.Timers.Timer timer= new System.Timers.Timer();
public FrmMain()
{
InitializeComponent();
timer.Interval = 1000;
timer.Elapsed += Timer_Elapsed;
}
private void Timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
{
opc.ReadValueAsync();
}
private void Opc_ReadFinished()
{
this.Invoke(() =>
{
uPanel1.Title = "Bom1";
uPanel1.Value = opc.dev.Bom1.ToString();
uPanel2.Title = "Part1";
uPanel2.Value = opc.dev.Part1.ToString();
uPanel3.Title = "Qty1";
uPanel3.Value = opc.dev.Qty1.ToString();
uPanel4.Title = "Speed1";
uPanel4.Value = opc.dev.Speed1.ToString();
uPanel5.Title = "Status1";
uPanel5.Value = opc.dev.Status1.ToString();
uPanel6.Title = "Status2";
uPanel6.Value = opc.dev.Status2.ToString();
uPanel7.Title = "Status3";
uPanel7.Value = opc.dev.Status3.ToString();
uPanel8.Title = "Status4";
uPanel8.Value = opc.dev.Status4.ToString();
uPanel9.Title = "Times1";
uPanel9.Value = opc.dev.Times1.ToString();
uPanel10.Title = "Weight";
uPanel10.Value = opc.dev.Weight.ToString();
uPanel1.Refresh();
uPanel2.Refresh();
uPanel3.Refresh();
uPanel4.Refresh();
uPanel5.Refresh();
uPanel6.Refresh();
uPanel7.Refresh();
uPanel8.Refresh();
uPanel9.Refresh();
uPanel10.Refresh();
});
}
private void btnStart_Click(object sender, EventArgs e)
{
opc.Connect();
opc.ReadFinished += Opc_ReadFinished;
opc.DataChanged += Opc_DataChanged;
//timer.Start();
}
private void Opc_DataChanged()
{
Opc_ReadFinished();
}
private void pnlValueClick(string value,string title)
{
opc.WriteValueAsync(title, value);
opc.WriteFinished -= Opc_WriteFinished;
opc.WriteFinished += Opc_WriteFinished;
}
private void Opc_WriteFinished()
{
}
}
}