OPC,全称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
C#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类
C#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类
C#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;
}
}
}
修改异步写
C#//同步写
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;
}
}
异步读
C#//异步读
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();
}
}
数据改变事件
C#MyOpcGroup.IsSubscribed = true; //只有为真事件才有效
C#MyOpcGroup.DataChange += MyOpcGroup_DataChange; //绑定事件
C#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 关闭事件
C#MyOpcServer.ServerShutDown += MyOpcServer_ServerShutDown;
C#private void MyOpcServer_ServerShutDown(string Reason)
{
}
界面代码
C# 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()
{
}
}
}
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!