OPC技术的上位机与PLC之间的通信现代工业控制系统通常以PC机为上位机

更新时间:2023-12-26 17:17:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

OPC技术的上位机与PLC之间的通信现代工业控制系统通常以PC机为上位机,通过与现场工控设备如PLC的数据交换与处理,实现对生产过程的自动控制。对于小型控制系统,采用专门的组态软件成本太高,用VB设计监控系统则可以降低成本,但首先要解决上位机与PLC之间通信问题。以往使用较多的进程间通信方式是DDE(动态数据交换)方式,随着OPC技术的发展和普及,它已成为工业过程控制的通信标准。OPC服务器有两类接口,其中自动化接口主要用于VB、Delphi等开发工具。本文利用罗克韦尔公司提供的OPC接口,用VB编写了客户端应用程序,实现了上位机与AB可编程控制器之间的数据交换。

2 OPC技术简介

OPC(OLE for Process Control—用于过程控制的对象连接与嵌入)是一套以微软对象连接与嵌入OLE、组件对象模型COM、分布式组件对象模型DCOM(Distributed COM)技术为基础,基于Windows操作平台,为工业应用程序之间提供高效的信息集成和交互功能的组件对象模型接口标准。OPC实际上是提供了一种机制,通过这种机制,系统能够以服务器/客户端标准方式从服务器获取数据并将其传递给任何客户应用程序。这样,只要生产商开发一套遵循OPC规范的服务器与数据进行通信,其他任何客户应用程序便能通过服务器访问设备。 OPC服务器有两类接口:定制接口和自动化接口。定制接口为C++程序服务,自动化接口为VB这一类可使用自动化对象的程序服务。定制接口是服务商必须提供的,而自动化接口则是可选的,不过OPC基金会提供了一个叫做“自动化包装器”的动态连接库,用于在两者间转换。

OPC数据存取规范规定的基本对象有三类:服务器(server)、组(group)和数据项(item)。服务器对象包含服务器的所有信息,也是组对象的容器,一个服务器对应于一个OPC server,即一种设备的驱动程序。组对象除了包含它自身信息外,还负责管理数据项。每一个数据项代表到数据源的一个连接,但它没有提供外部接口,客户端程序无法对数据项直接进行操作,应用程序必须依靠数据项的容器组对象来对它进行操作。

3 通信实现

3.1 RSLinx的配置

RSLinx是AB可编程控制器在Windows环境下建立工厂所用通信方 案的工具,它不仅提供了多种网络驱动程序,而且提供了最快速的OPC、DDE和Custom C/C++接口。本设计中上位机与Logix5550控制器采用RS-232串口方式连接,在RSLinx中要对DF1网络驱动程序组态,设置串口特性: COM1、波特率19200bps、一个停止位、无奇偶校验、全双工、BCC校验码。此外要使用RSLinx的OPC接口作为服务器,还要在RSLinx 中对OPC进行配置。 3.2 安装OPC自动化接口服务

若要用VB开发OPC应用程序,必须安装OPC自动化接口服务,保证计算机系统目录下有OPCDAAuto.dll。OPC基金会提供了一个叫做“自动化包装器”的动态连接库,从OPC基金会的网站(www.opcfoundation.org)可下载。在VB环境中,按“工程”的子菜单“引用?”后,弹出对话框,选择其中的“RSLinx OPC Automation 2.0”项,这样才能使用自动化接口。 3.3 程序设计

用VB编写了OPC客户端应用程序,实现了上位机与Logix5550控制器之间的通

信。主要程序代码如下: (1) 连接OPC服务器

Dim WithEvents MyOPCServer As OPCServer ‘定义服务器对象变量MyOPCServer

Dim WithEvents MyOPCGroup As OPCGroup ‘定义OPC组对象变量MyOPCGroup

Set MyOPCServer = New OPCServer

MyOPCServer.Connect \RSLinx 的OPC服务器 (2) 添加OPC组对象

Set MyOPCGroup=MyOPCServer.OPCGroups.Add(\‘添加OPC组对象MyOPCGroup.IsSubscribed= True ‘设置该组数据为后台刷新 MyOPCGroup.IsActive = True ‘设置该组为激活状态

MyOPCGroup.UpdateRate=1000 ‘设置数据刷新时间为1000 (3) 添加数据项

Dim abItemIDs() As String ‘项标识符

Dim abClientHandles() As Long ‘客户端句柄

Dim abServerHandles() As Long ‘服务器端句柄

Dim abErrors() As Long Dim i As Long ItemCount=5

Dim oOPCItem As RSLinxOPCAutomation.OPCItem For i = 1 To 5

abItemIDs(i) = \‘将Topic名和标签名赋给项标识符

abClientHandles(i) = i ‘给客户端句柄赋值 Next i

MyOPCGroup.OPCItems.AddItems ItemCount, abItemIDs, abClientHandles, abServerHandles, abErrors ‘添加数据项操作 (4) 同步数据读写

OPC数据存取有同步方式和异步方式两种。异步读写数据复杂,需要与事件结合使用,与同步相比速度慢但准确性高。同步读写数据简单,直接使用OPCItem的方法即可。

Dim One As OPCItem Dim Index As Long ‘Index为标签顺序号

Dim OneRead As String Dim Xie As String

Set One = MyOPCGroup.OPCItems(Index) One.Read OPCCache OneRead = One.Value ‘读数据

One.Write (Xie) ‘写数据

若只读取数据,可以使用DataChange事件,当控制器中所要访问的数据一旦发生改变时将会触发该事件,并将该数据自动读到TxtValue文本框。

Private Sub MyOPCGroup_DataChange(ByVal TransactionID As Long, ByVal NumItems As Long, ClientHandles() As Long, ItemValues() As Variant, Qualities() As Long, TimeStamps() As Date) ‘自动刷新数据 Dim i As Long

For i = 1 To NumItems

txtValue(ClientHandles(i)) = ItemValues(i) ‘获取项的值

txtTime(ClientHandles(i)) = TimeStamps(i) ‘获取项的时间戳

txtQuantity(ClientHandles(i))=GetQualityString(Qualities(i)) ‘获取项的品质 Next i End Sub

(5) 断开OPC服务器

MyOPCServer.OPCGroups.RemoveAll ‘移除所有OPC Group,空出资源 Set MyOPCGroup = Nothing MyOPCServer.Disconnect ‘断开连接

4 结束语

利用本文介绍的方法,在实验室实现了上位机和PLC之间的通信,图1是OPC客户端设置界面。此外我们还设计了城市供水监控系统,鉴于篇幅,监控界面不再一一列出。

在上一次发表的<运用VC#编程通过OPC方式实现PC机与西门子PLC通讯>主要讲的是同步通讯,本文将主要讲解如何编程实现异步通讯,通过讲解你也将会知道同步通讯与异步通讯的区别,以及在什么情况下使用异步通讯。

1、 配置OPC服务器

对于服务器的配置与同步通讯的配置一样,这里不需再讲解,若有不清楚的,可以参阅之前发布的<运用VC#编程通过OPC方式实现PC机与西门子PLC通讯>

2、 OPC编程

变量组、项的命名规则与同步通讯的一样,这里不再描叙,下面主要就开发一个异步通讯类 AsynServer来讲解如何编程。

<1>、引用

在VC#开发环境中添加对OpcRcw.Da库以及OpcRcw.Comn库的引用,该库属于.NET库,不属于COM库,西门子虽然编写了类库,以提 供对.NET平台的支持,但这些类库仍然难于编程,里面包含了大量的在托管和非托管区传输数据,因此我们需要在它的基础上再开发一个类库,以简化以后的编 程,首先在类的开头使用命名空间: using OpcRcw.Comn; using OpcRcw.Da;

using System.Runtime.InteropServices; using System.Collections;

<2>、编程

异步编程的原理就是在OPC服务器那边检测当前活动的变量组,一但检测到某一个变量,譬如变量Q0.0从1变成0,就会执行一个回调函数,以实现针对变量发生变化时需要实现的动作,在这里可以采用委托来实现该功能。

1、 在命名空间的内部、类 AsynServer声明之前添加委托的申明: // 定义用于返回发生变化的项的值和其对应的客户句柄 public delegate void DataChange(object[] values,int[] itemsID);

2、 该类继承于西门子提供的库接口IOPCDataCallback public class AsynServer:IOPCDataCallback 在类的开头部分声明变量: struct groupStru {

public int groupID; public object groupObj; }

internal const int LOCALE_ID = 0x407; //本地语言 private Guid iidRequiredInterface; private string serverType=\

private int hClientGroup = 0; //客户组号

private int nSvrGroupID; // server group handle for the added group

private Hashtable hashGroup; //用于把组收集到一起 private int hClientItem=0; //Item号

3、编写构造函数,接收委托参数已确定当数据发生变化时需要执行的方法入口点: //创建服务器

//svrType 服务器类型的枚举

//dataChange 提供用于在数据发生变化时需要执行的函数入口 public AsynServer(ServerType svrType,DataChange dataChange) {

switch(svrType) {

case ServerType.OPC_SimaticHMI_PTPRO: serverType=\case ServerType.OPC_SimaticNET: serverType=\case ServerType.OPC_SimaticNET_DP: serverType=\case ServerType.OPC_SimaticNET_PD: serverType=\case ServerType.OPCServer_WinCC: serverType=\ }

hashGroup=new Hashtable(11); dtChange=dataChange; }

4、创建服务器

// 创建一个OPC Server接口 //error 返回错误信息

//若为true,创建成功,否则创建失败 public bool Open(out string error) {

error=\Type svrComponenttyp ;

//获取 OPC Server COM 接口

iidRequiredInterface = typeof(IOPCItemMgt).GUID;

svrComponenttyp = System.Type.GetTypeFromProgID(serverType); try {

//创建接口

pIOPCServer =(IOPCServer)System.Activator.CreateInstance(svrComponenttyp); error=\}

catch (System.Exception err) //捕捉失败信息 {

error=\错误信息:\}

return success; }

5、 编写添加Group的函数 ///

/// 添加组 /// /// 组名

/// /创建时,组是否被激活

/// //组的刷新频率,以ms为单位 /// 返回错误信息

/// 若为true,添加成功,否则添加失败

public bool AddGroup(string groupName,int bActive,int updateRate,out string error) {

error=\

int dwLCID = 0x407; //本地语言为英语 int pRevUpdateRate; float deadband = 0;

// 处理非托管COM内存 GCHandle hDeadband;

IntPtr pTimeBias = IntPtr.Zero;

hDeadband = GCHandle.Alloc(deadband,GCHandleType.Pinned); try {

pIOPCServer.AddGroup(groupName, //组名 bActive, //创建时,组是否被激活

updateRate, //组的刷新频率,以ms为单位 hClientGroup, //客户号 pTimeBias, //这里不使用 (IntPtr)hDeadband, dwLCID, //本地语言

out nSvrGroupID, //移去组时,用到的组ID号

out pRevUpdateRate, //返回组中的变量改变时的最短通知时间间隔 ref iidRequiredInterface,

out pobjGroup1); //指向要求的接口 hClientGroup=hClientGroup+1; groupStru grp=new groupStru();

grp.groupID=nSvrGroupID;grp.groupObj=pobjGroup1; this.hashGroup.Add(groupName,grp);//储存组信息 // 对异步操作设置回调,初始化接口

pIConnectionPointContainer = (IConnectionPointContainer)pobjGroup1; Guid iid = typeof(IOPCDataCallback).GUID;

pIConnectionPointContainer.FindConnectionPoint(ref iid,out pIConnectionPoint); pIConnectionPoint.Advise(this,out dwCookie); }

catch (System.Exception err) //捕捉失败信息 {

error=\错误信息:\}

finally {

if (hDeadband.IsAllocated) hDeadband.Free(); }

return success; }

6、 编写激活、或者取消激活组的函数

在同步编程中对于组的激活或者取消激活没有实质的意义,但在异步通讯编程中却异常重要,这是因为OPC服务器只对当前处于活动状态的组中的变量进行监 控,同时这也是很有必要的,因为我们可以把不同界面中的变量编程不同的组,即同一界面中的变量规成一个组,而在某一时刻提供给用户的只有一个界面,让该界 面中用到的组处于活动状态,这样执行委托调用时只会执行于该界面中有关的变量检测,而如果让所有的组处于活动状态,则当前没有显示给用户的界面用到的变量 若发生变化也会触发对委托函数的调用,这根本是没有必要的,同时会大大降低程序的性能,请严格控制组的激活。 ///

/// 激活或者取消激活组 ///

/// 指定组名

/// true为激活,false为取消激活 /// 若有错误,返回错误信息

/// 若为true,添加成功,否则添加失败

public bool AciveGroup(string groupName,bool toActive,out string error) {

error=\//通过名称获取组

object grp=((groupStru)hashGroup[groupName]).groupObj; IOPCGroupStateMgt groupStateMgt=(IOPCGroupStateMgt)grp; //初始化传递参数

IntPtr pRequestedUpdateRate = IntPtr.Zero; //由客户指定的Item更新间隔时间 int nRevUpdateRate = 0; //由服务器返回的能够更新的最短时间间隔 IntPtr hClientGroup = IntPtr.Zero; //客户组 IntPtr pTimeBias = IntPtr.Zero; IntPtr pDeadband = IntPtr.Zero;

IntPtr pLCID = IntPtr.Zero;

// 激活或者取消激活组 int nActive = 0;

GCHandle hActive = GCHandle.Alloc(nActive,GCHandleType.Pinned); if(toActive)

hActive.Target = 1; else

hActive.Target = 0; try {

groupStateMgt.SetState(pRequestedUpdateRate,out

nRevUpdateRate,hActive.AddrOfPinnedObject(),pTimeBias,pDeadband,pLCID,hClientGroup); }

catch(System.Exception err) {

error=\错误信息:\}

finally {

hActive.Free(); }

return success; }

7、 向指定的组中添加变量的函数 ///

/// 向指定的组添加一系列项 ///

/// 指定组名

/// 完整的item名数组

/// 由服务器返回读写数据时需要使用的item号 /// 无错误,返回true,否则返回false

public bool AddItems(string groupName,string[] itemsName,int[] itemsID) {

bool success=true;

OPCITEMDEF[] ItemDefArray=new OPCITEMDEF[itemsName.Length]; for(int i=0;i {

hClientItem=hClientItem+1; //客户项自动加1

ItemDefArray[i].szAccessPath = \可选的通道路径,对于Simatiic Net不需要。 ItemDefArray[i].szItemID = itemsName[i]; // ItemID, see above ItemDefArray[i].bActive = 1; // item is active

ItemDefArray[i].hClient = hClientItem; // client handle ,在OnDataChange中会用到

ItemDefArray[i].dwBlobSize = 0; // blob size

ItemDefArray[i].pBlob = IntPtr.Zero; // pointer to blob

ItemDefArray[i].vtRequestedDataType = 4; //DWord数据类型 }

//初始化输出参数

IntPtr pResults = IntPtr.Zero; IntPtr pErrors = IntPtr.Zero; try {

// 添加项到组

object grp=((groupStru)hashGroup[groupName]).groupObj;

((IOPCItemMgt)grp).AddItems(itemsName.Length,ItemDefArray,out pResults,out pErrors);

int[] errors = new int[itemsName.Length]; IntPtr pos = pResults;

Marshal.Copy(pErrors, errors, 0,itemsName.Length); for(int i=0;i {

if (errors[i] == 0) {

OPCITEMRESULT result = (OPCITEMRESULT)Marshal.PtrToStructure(pos, typeof(OPCITEMRESULT)); itemsID[i] = result.hServer;

pos = new IntPtr(pos.ToInt32() + Marshal.SizeOf(typeof(OPCITEMRESULT))); } else {

String pstrError;

pIOPCServer.GetErrorString(errors[0],0x407,out pstrError); success=false; break; } }

SetItenClient(groupName,itemsID,itemsID); //要求始终只有一个组被激活,才不会引起冲突。 }

catch (System.Exception err) // catch for error in adding items. {

success=false;

//error=\错误信息:\}

finally {

// 释放非托管内存

if(pResults != IntPtr.Zero)

{

Marshal.FreeCoTaskMem(pResults); pResults = IntPtr.Zero; }

if(pErrors != IntPtr.Zero) {

Marshal.FreeCoTaskMem(pErrors); pErrors = IntPtr.Zero; } }

return success; }

说明:使用该函数时,在类的开头,应该先声明整数数据,以用于保存由本函数返回的服务器对每一项分配的Item ID号:

8、 下面编写的是一个最重要的重载函数,当检测到当前活动组中的某个变量发生变化时,就会调用委托。

//数据变化时处理的问题

public virtual void OnDataChange ( Int32 dwTransid , Int32 hGroup ,

Int32 hrMasterquality , Int32 hrMastererror , Int32 dwCount , int[] phClientItems , object[] pvValues , short[] pwQualities ,

OpcRcw.Da.FILETIME[] pftTimeStamps , int[] pErrors ) {

dtChange(pvValues,phClientItems); }

该函数的代码只有一句,即调用委托函数。 以上编写的是需要实现监控的最重要的方法,当然不完善,还有许多方法和重载函数可以编写,这里就不详细介绍。

9、 编写基本的测试程序,用于检测上面编写的异步类AsynServer

<1>、 重新创建一个工程,添加对上面编写的异步类的引用,并在类的开头部分添加变量声明: //声明委托

private S7Connection.DataChange dt;

//声明服务器

S7Connection.AsynServer server;

<2>、初始化服务器数据

dt=new S7Connection.DataChange(DataChange);

server =new AsynServer(S7Connection.ServerType.OPC_SimaticNET,dt); string err;

server.Open(out err);

server.AddGroup(\server.AddItems(\

server.AddGroup(\server.AddItems(\nt[0]=nt1[0];nt[1]=nt1[1];

<3>、添加两个单选按钮,用于选择某个组,并编写相应的程序 string err,err1;

if(server==null) return; if(radioButton1.Checked) { nt[0]=nt1[0];nt[1]=nt1[1];

server.AciveGroup(\server.AciveGroup(\ } else {

nt[0]=nt2[0];nt[1]=nt2[1];

server.AciveGroup(\server.AciveGroup(\ }

<4>、添加文本框、按钮等,并编写委托执行函数: private void DataChange(object[] obj,int[] itemsID) {

for(int j=0;j {

if(itemsID[j]==nt[0])

this.textBox1.Text=obj[j].ToString(); if(itemsID[j]==nt[1])

this.textBox4.Text=obj[j].ToString(); } }

其中参数obj用于返回当前发生变化的变量的结果值,而itemsID返回当前发生变化的

变量的ID号,其与添加变量时服务器返回的ID号对应。以上就是一个基本的测试函数,其相对同步编程来说,应该还简单一些。

3、 同步编程与异步编程的使用场合

一般来讲,同步编程需要使用定时器来循环检测变量,而异步编程则不需要,当服务器检测到数据发生变化时,可以直接调用传入的函数,从这方面来讲,使用异 步编程更简单一些,但同步编程使用外部的定时器控制,编程则会更加灵活,一般只监控变量时可以使用异步编程,而当需要写入数据时可以使用同步编程,但这也 不是绝对的,我曾编写了一个标准监控程序,没有使用异步编程。

4、 关于开发监控界面的说明

毫无疑 问,我们应该开发一系列控件,用于简化界面的设计,否则工作量会异常大。设计一个标准模块,用于第一次运行监控软件时添加变量,并可以设定当前已经组态的 界面中的各控件元素与之关联,这样在以后再运行该软件时,不需要再设定,就可以直接连接变量,并进行相应的变化。否则若在编程时编写代码进行关联,其工作 量将会异常大。

其实该类我早已经开发了,但一直没有时间写成文章,本来想开发一系列标准控件和标准模块,但由于换到上海工作,可能不会再有时间搞这方面的研究了。

作者:吴向阳

wuyang26@sohu.com

如何开发OPC Server

首先我们先来看一下什么是OPC

OPC (OLE for Process Control——用于过程控制的OLE)是基于Microsoft公司的DNA(Distributed Internet Application)构架和COM(Component Object Model)技术的一个工业标准接口,是根据易于扩展性而设计的。 正在装载数据?? 再来了解一下OPC的用途

OPC主要适用于过程控制和制造自动化等应用领域。 OPC是以OLE/COM机制作为应用程序的通讯标准。OLE/COM是一种客户/服务器模式,具有语言无关性、代码重用性、易于集成性等优点。OPC规范了接口函数,不管现场设备以何种形式存在,客户都以统一的方式去访问,从而保证软件对客户的透明性,使得用户完全从低层的开发中脱离出来

然后我们再来看看OPC Server的组成

一个设备的OPC Server主要有两部组成,一是OPC标准接口的实现;二是与硬件设备的通信模块。

实现OPC 标准接口

[图1]

在这些接口中,IOPCServer是OPC Server的主接口,通过它实现OPC Server在操作系统中的安装和注册。此接口是必须要实现的,其所有方法也必须实现。其它的接口都是可选的我们就不做介绍了,下面主要来介绍如何实现IOPCServer接口。

在IOPCServer接口中共有六个法: 1、 IOPCServer::AddGroup

HRESULT AddGroup( [in, string] LPCWSTR szName,

[in] BOOL bActive,

[in] DWORD dwRequestedUpdateRate, [in] OPCHANDLE hClientGroup, [unique, in] LONG *pTimeBias, [in] FLOAT * pPercentDeadband, [in] DWORD dwLCID,

[out] OPCHANDLE * phServerGroup, [out] DWORD *pRevisedUpdateRate, [in] REFIID riid,

[out, iid_is(riid)] LPUNKNOWN * ppUnk );

此方法是在OPC Server上建立一个组。下在我们来实现这个方法:

?. ?.

首先要对组名(szName)进行检查,看是否有效或是否已经有这个组。 if (szName != NULL) {

RequestedName = szName; if (RequestedName == \

RequestedName = pSvrObject->DefaultGroupName(); } else

RequestedName = pSvrObject->DefaultGroupName(); for (i=0; iNumbrGroups(); i ) {

pGroup = pSvrObject->GetGroup(i); if (RequestedName == pGroup->Name) return (OPC_E_DUPLICATENAME); }

这需要在内存中维护OPC Group(组)的列表(还要有OPC 项的列表)。

如果szName(组名)正确并且没有建立过该组,就开始根据传过来的参数进行组的建立,建立好后将该组加到自己的组列表中以备后用。 最后将新建组的接口指针返回给客户端。 2、IOPCServer::GetErrorString

HRESULT GetErrorString( [in] HRESULT dwError,

[in] LCID dwLocale,

[out, string] LPWSTR *ppString );

为Server的错误代码返回相应的错误字符串。 代码略

3、 IOPCServer::GetGroupByName

HRESULT GetGroupByName( [in, string] LPCWSTR szName,

[in] REFIID riid,

[out, iid_is(riid)] LPUNKNOWN * ppUnk );

通过指定的组名(由同一客户端建立的)找到该组的接口指针。

此方法实现比较简单,只要根据提供的名子循环从组列表中找到该组的接口指针,并返回给客户端 4、 IOPCServer::GetStatus

HRESULT GetStatus( [out] OPCSERVERSTATUS ** ppServerStatus ); 返回当前Server的状态信息。

此方法比较简单,但要注意的是在使用OPCSERVERSTAUS前要进行内存分配。 5、 IOPCServer::RemoveGroup

HRESULT RemoveGroup( [in] OPCHANDLE hServerGroup,

[in] BOOL bForce );

从服务器中删除指定组

在组列表中找到指定的组,并将其删除。

6、 IOPCServer::CreateGroupEnumerator

HRESULT CreateGroupEnumerator( [in] OPCENUMSCOPE dwScope,

[in] REFIID riid,

[out, iid_is(riid)] LPUNKNOWN* ppUnk );

为Server上所提供的组建立不同的列举器。

上面是直接使用COM技术进行开发的,这要求你必须熟悉COM技术。如果你对COM不是很了解那也没关系,可以选择OPC Server的开发工具,你只需要简单的调用开发工具的函数就可以实现OPC Server中的所有接口。

我们虽然实现了IOPCServer接口中的所有方法,但它只是OPC Client与我们通信的一个桥梁,最主要的还是我们要自己维护好OPC Group和OPC Item列表。这样我们才可以跟OPC Client进行真正的数据通信。

与硬件设备进行通信

接口已经实现好了,这回我们该从硬件设备中读取数据提供给OPC Client了。

同硬件设备通信有多种方法,如果你是设备的制造商就可以直对硬件进行数据操作了;你也可以通过设备驱动程序和硬件厂商提供的API或是通过TCP、串口等方法进行操作,这就要看硬件设备提供的与软件通信的接口是什么样的了。 不管你用什么方法只要把数据从硬件设备中读取上来并与OPC 项进行关联,就可以实现OPC Server了。

下面我们为清华同方的RH2000系统来做一个OPC Server(如图2)。 [图2]

因为RH2000系统由一个叫易视的软件系统进行调控的,它下面控制多种下位机(硬件设备)。我们只需要与易视进行通信就可以实现对硬件设备的操作(易视提供了TCP/IP通信方式)。首先与易视建立好Socket连接,然后发送控制命令读取与易视相连的硬件设备的信息(也就是设备点信息)。我们根据读取到的不同的设备点相应的建立OPC项,这样OPC Client只要读取到OPC 项就可以读取到设备信息。如果OPC Client对OPC 项进行修改,我们收到OPC项的变化就向易视发送相应命令对设备进行操作,这样就实现了OPC Client对硬件设备的操控了。

结束语

虽然使用开发工具也可以开发OPC Server,但还是希望大家能多了解COM技术,这样对OPC Server的扩展和维护都有好处。并多到OPC基金会的官方网站上去了解OPC的最新知识和下载相关资料,网址是 http://www.opcfoundation.org。

由于时间和作者水平的限制,难免有错误和不妥之处,敬请大家批评和指正。我的邮件是 yanghongtao@thtf.com.cn。最后感谢清华同限份有限公司的周洪波博士和卢盛融经理给我开发和学习OPC的机会,使我了解OPC并写下这篇文章。

本文来源:https://www.bwwdw.com/article/axzx.html

Top