OPC是Object Linking and Embedding(OLE)for Process Control 的缩写,它是微软公司的对象链接和嵌入技术在过程控制方面的应用。OPC以OLE/COM/DCOM技术为基础,采用客户/服务器模式,为工业自动化软件面向对象的开发提供了统一的标准,这个标准定义了应用Microsoft操作系统在基于PC的客户机之间交换自动化实时数据的方法,采用这项标准后,硬件开发商将取代软件开发商为自己的硬件产品开发统一的OPC接口程序,而软件开发者可免除开发驱动程序的工作,充分发挥自己的特长,把更多的精力投入到其核心产品的开发上。
SimaticNet是西门子全集成自动化系统中的一个重要组成部分,它为完善的工业自动化控制系统的通讯提供部件和网络,同时提供多个OPCServer,为数据的外部访问提供接口,本文主要以OPC.SimaticNET为例说明。
采用不同的通信方式,通过OPC.SimaticNET,现场数据可以方便地提供给用户:
OPC的读写方式
在实际使用中,主要包括对现场数据的读写操作。
OPC读数有三种方式:同步、异步、订阅。
同步通讯时,OPC客户程序向OPC服务器进行请求时,OPC客户程序必须等到OPC服务器对应的响应全部完成以后才能返回,在此期间OPC客户程序一直处于等待状态,若进行读操作,那么必须等待OPC服务器响应后才返回。因此在同步通讯时,如果有大量数据进行操作或者有很多OPC客户程序对OPC服务器进行读操作,必然造成OPC客户程序的阻塞现象。因此同步通讯适用于OPC客户程序较少,数据量较小时的场合。
异步通讯时,OPC客户程序对服务器进行请求时,OPC客户程序请求后立刻返回,不用等待OPC服务器的响应,可以进行其它操作。OPC服务器完成响应后再通知OPC客户程序,如进行读操作,OPC客户程序通知OPC服务器后离开返回,不等待OPC服务器的读完成,而OPC服务器完成读后,会自动的通知OPC客户程序,把读结果传送给OPC客户程序。因此相对于同步通讯,异步通讯的效率更高。
订阅方式时,OPC客户程序对服务器进行请求时,OPC客户程序操作后立刻返回,不用等待OPC服务器的操作,可以进行其它操作, OPC 服务器的Group组在组内有数据发生改变时,自动根据更新周期刷新相应的客户端数据,如下图,客户端只向OPC服务发送一次请求,之后不再对服务器请求。
OPC写数有两种方式:同步、异步。区别与上面讲的机制一样,在生产应用中,如果写数据参与控制,一般采用同步方式。
OPC访问接口方式:
OPC主要包含两种接口:CUSTOM标准接口和OLE自动化标准接口,自定义接口是服务商必须提供的,而自动化接口则是可选的。
自定义接口是一组COM接口,主要用于采用C++语言的应用程序开发;
自动化接口是一组OLE接口,主要用于采用VB,DELPHI,Excel等基于脚本编程语言的应用程序开发。
许多OPC服务器,包括OPC.SimaticNet,是在COM平台开发的,从而对于基于.NET 框架下的C#语言,作为客户端程序语言访问OPCServer,需要解决两个平台间无缝迁移的问题。OPC基金会对会员提供了OpcRcw动态链接库,OPC NET COM包装器和OPC NET API,将OPC复杂的规范封状成简单易用的C#类,可以比较容易地实现数据访问。
本文中通过实验,逐步讲解了通过C#编写客户端程序,访问OPC.SimaticNet,对PLC数据进行读写的实现过程。自定义接口及自动化接口都进行了测试,但基于C#的语言特性,建议采用自定义接口访问,同时有很多OPCServer服务商,对外是不提供自动化接口的,西门子的SimaticNet及WinCC的OPCServer都提供自动化接口。
OPC Server端组态配置:
如上图建立连接S7_connection_1,然后在OPC Scout测试连接的正确性。
从上面可以看到数据访问都是正常的。
C#自动化接口
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using OPCSiemensDAAutomation;//引用连接库
第一步,添加下列命名空间(首先在COM组件中添加相应组件)
using OPCSiemensDAAutomation;
第二步,定义OPC相关变量
OPCServer MyOpcServer; //定义OPCServer
OPCGroup MyOpcGroup; //定义组
OPCItem MyOpcItem1; //Item
OPCItem MyOpcItem2; //值
long[] ServerHandle = new long[2]; //Item的句柄
第三步,建立连接及对象
MyOpcServer = new OPCServer();
MyOpcServer.Connect("OPC.SimaticNet", "192.168.0.102");
MyOpcGroup = MyOpcServer.OPCGroups.Add("MyGroup1");
MyOpcItem1 = MyOpcGroup.OPCItems.AddItem("S7:[S7 connection_1]DB10,INT0",1);
MyOpcItem2 = MyOpcGroup.OPCItems.AddItem("S7:[S7 connection_1]DB10,INT2", 2);
ServerHandle[0] = MyOpcItem1.ServerHandle;
ServerHandle[1] = MyOpcItem2.ServerHandle;
第四步,同步读数据,
private void Btn_Read_S_Click(object sender,EventArgs e)//同步读数据
{
……
MyOpcItem1.Read(1,out ItemValues,out Qualities, out TimeStamps);
//ItemValues,Qualities,TimeStamps分别是值,质量码
//也可以通过调用SyncRead函数,参数可参考异步读函数
……
}
第四步,同步写数据
private void Btn_Write_S_Click(object sender,EventArgs e)
{
……
MyOpcItem1.Write(Txt_W1.Text);
//也可以通过调用SyncWrite函数,参数可参考异步写函数
……
}
第五步,异步事件定义,
在异步操作情况下,需要定义相应的异步事件
MyOpcGroup.DataChange +=new
DIOPCGroupEvent_DataChangeEventHandler(MyOpcGroup_DataChange); //
//订阅方式下数据改变
iteComplete +=new
DIOPCGroupEvent_AsyncWriteCompleteEventHandler(MyOpcGroup_WriteComplete);
//写完成事件
MyOpcGroup.AsyncReadComplete += new
DIOPCGroupEvent_AsyncReadCompleteEventHandler(MyOpcGroup_ReadComplete);
//读完成事件
MyOpcGroup.AsyncCancelComplete += new
DIOPCGroupEvent_AsyncCancelCompleteEventHandler(MyOpcGroup_CancelComplete);
//取消操作事件
在使用中注意,其事件函数要按照特定接口:
void MyOpcGroup_DataChange(int TransactionID, int NumItems,ref Array ClientHandles,
ref Array ItemValues,ref Array Qualities, ref Array TimeStamps)
void MyOpcGroup_WriteComplete(int TransactionID, int NumItems, ref Array ClientHandles,
ref Array Errors)
void MyOpcGroup_ReadComplete(int TransactionID, int NumItems,ref System.Array
ClientHandles,ref System.Array ItemValues,ref System.Array Qualities,
ref System.Array TimeStamps,ref System.Array Errors)
void MyOpcGroup_CancelComplete(int CancelID)
第六步订阅方式读
void MyOpcGroup_DataChange(int TransactionID, int NumItems,ref Array ClientHandles,
ref Array ItemValues,ref Array Qualities, ref Array TimeStamps)
{
……
//注意数据改变时,Item数量要通过NumItems得到,也就是说只有数据改变时,才对一
遍,所以降低了服务器负担。要注意读语句写法。
……
}
第七步异步读
private void Btn_Read_A_Click(object sender,EventArgs e)//异步读事件
{
int[] handle = new int[3] {ServerHandle[0], ServerHandle[1],0};//注意方式
Array MyServerHandles = (Array)handle;
Array errors;
int cancelID;
……
MyOpcGroup.AsyncRead(2, ref MyServerHandles, out errors, READASYNC_ID, out
cancelID);
……
}
void MyOpcGroup_ReadComplete(int TransactionID, int NumItems,ref System.Array
ClientHandles,ref System.Array ItemValues,ref System.Array Qualities,
ref System.Array TimeStamps,ref System.Array Errors)
{
……
//注意TransactionID的对应
……
}
注意array在函数内部做参数时,数据下标是从1开始的,所以要考虑将第0位空出
来,n个Item,就要定义n+1列数组,添加一个0,但在函数使用时,又是从左开始读的。
否则会报错。
第八步异步写
private void Btn_Write_A_Click(object sender,EventArgs e)
{
……
MyOpcGroup.AsyncWrite(2,ref MyServerHandles, ref Myvalues,out errors,
WRITEASYNC_ID,out cancelID);
……
}
void MyOpcGroup_WriteComplete(int TransactionID, int NumItems, ref Array ClientHandles
ref Array Errors)
{
……
}
同样要注意Array在函数内部做参数的传递。
第九步释放对象
private void Btn_Disconn_Click(object sender,EventArgs e)
{
……
}
OPCItem的数据类型:
在通过自定义接口访问时,
ItemArray[1].szAccessPath = "";
ItemArray[1].szItemID = "S7:[S7 connection_1]DB10,Real4";//地址,不同数据类型表示
ItemArray[1].bActive = 1;//是否激活
ItemArray[1].hClient = 2;//表示ID
ItemArray[1].dwBlobSize = 0;
ItemArray[1].pBlob =IntPtr.Zero;
ItemArray[1].vtRequestedDataType = 5;
ItemArray[2].szAccessPath = "";
ItemArray[2].szItemID = "S7:[S7 connection_1]DB10,STRING26.10";//地址,不同数据类型表
示方法不同
ItemArray[2].bActive = 1;//是否激活
ItemArray[2].hClient = 3;//表示ID
ItemArray[2].dwBlobSize = 0;
ItemArray[2].pBlob =IntPtr.Zero;
ItemArray[2].vtRequestedDataType = 8;
全部0条评论
快来发表一下你的评论吧 !