OPC,一句话DA用的越来越少了。全称OLE For Process Control,是一种工业标准,旨在解决不同供应商设备与应用程序之间的接口标准化问题。随着工业自动化和控制系统的不断发展,各种设备和应用程序不断涌现,其之间的通信变得越来越复杂。传统的设备和应用程序之间的通信主要依靠编程和调试,但这种方式效率低下、不灵活、成本高,也限制了应用程序的自由组合和开发。
OPC的出现正是为了解决这些问题。它采用一种简单的接口标准,允许不同的供应商设备和应用程序之间进行数据交换。通过使用OPC,应用程序可以自由地组合不同的设备和应用程序,从而实现更加灵活、高效和经济的工业自动化系统。
OPC标准包括一组接口规范和协议,可以被用于不同类型的设备和应用程序。OPC的接口规范包括以下几个部分:OPC控制器规范、OPC数据服务规范、OPC应用程序接口规范和OPC数据传输规范。这些规范提供了一种通用的接口,允许设备和应用程序之间进行数据交换。OPC的协议主要包括基于协议的和基于消息的两种方式。
基于协议的方式是通过协议规范定义的消息格式进行通信。每个设备和应用程序都可以使用OPC协议生成自己的消息格式,从而实现与其他设备和应用程序的通信。基于消息的方式则是通过定义消息的格式和目的地来进行通信。消息可以通过OPC服务器进行传输,服务器可以是本地的或者远程的。
OPC标准的实现依赖于OPC基金会,该基金会是一个非营利性的组织,致力于推广和发展OPC标准。基金会负责维护OPC标准,开发新的规范和协议,提供技术支持和培训等服务。OPC基金会的成员包括各种设备和应用程序的供应商、系统集成商、工程师和用户。
利用驱动器的系统连接
图片
利用 OPC 控制的系统组成
图片
OPC 的分层结构
OPC 对象中最上层的对象是 OPC 服务器,一个 OPC 服务器中可以设置一个以上的 OPC 组。OPC 服务器常对应于某种特定的控制设备,如 DCS 以及 PLC 等。
OPC 组是可以进行数据访问的多个 OPC 标签的集合,OPC 应用程序可以将需要的数据分组进行批量读取,也可以以组为单位启动或者停止数据访问。此外,OPC 组还提供组内 OPC 标签数据变化时向 OPC 应用程序通知的事件。
图片
OPC 与 OPC UA
OPC DA 与 OPC UA 都是 OPC 协议的标准:
OPC 是一种通过微软 COM/DCOM 技术来实现自动化控制的协定,采用 C/S 架构。开发人员只需要按照 OPC 的标准编写 OPC-Client 访问 OPC-Server 进行读写操作即可实现与硬件设备的通信。OPC 的协定中包括:
DA (Data Access):访问数据的主要规范;
A&E (Alarm and Event):基于事件提供 Client 端订阅,事件触发后 Server 主动提交数据;
HDA (History Data Access):历史数据访问;
OPC UA 是 OPC 协议的新版,其不再依赖于 COM/DCOM 技术,这意味着其具有跨平台性,不再局限于 Windows 系统。OPC UA 提供了可靠的通信机制,接口简单一致。
先用KepServerEx 做个防真环境
模拟数据配制
线形变化型 RAMP(Rate, Low Limit, High Limit, Increment) Rate:变化率,单位毫秒 Low Limit:下限值 High Limit:上限值 Increment:变化量
随机变化型
格式:RANDOM(Rate, Low Limit, High Limit)
Rate:变化率,单位毫秒
Low Limit:下限值
High Limit:上限值
三角函数型 SINE(Rate, Low Limit, High Limit, Frequency, Phase) Rate:变化率/变化周期,单位毫秒 Low Limit:下限值 High Limit:上限值 Frequency:三角函数频率 Phase:三角函数偏移量
用户自定义型 USER(Rate, User Value1, User Value2, User Value3,…User ValueN) Rate:变化率,单位毫秒 User Value1….ValueN:用户自定义的值,可以有多个
OPCDA
OPCDAAuto.dll下载_解决OPCDAAuto.dll丢失问题 OPCDAAuto.dll是微软vc组件的一个重要dll文件,缺少它可能会造成部分软件或游戏无法正常运行。当系统提示“没有找到OPCDAAuto.dll”或“丢失OPCDAAuto.dll”等类似错误信息 dll文件修复方法:1、解压下载的文件。2、复制文件“OPCDAAuto.dll”到系统目录下。3、系统目录一般为:C:\WINNT\System32 64位系统为C:\Windows\SysWOW64 4、最后点击开始菜单-->运行-->输入regsvr32 OPCDAAuto.dll 后,回车即可解决错误提示。
图片
注意:这个组件在.net core下项目需要是x86下才能正常使用。
一个例子
图片
做一个自定义控件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);
}
};
}
}
}
申明一个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是一个字符串,唯一标识注册的真实OPC数据访问服务器(实现自定义接口)
//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();
}
//同步写
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;
}
}
}
修改异步写
//同步写
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;
}
}
//异步写
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);
}
//异步读完成后的回调
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();
}
}
数据改变事件
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 关闭事件
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()
{
}
}
}