如何在C++中实现OPC访问

如何在C++中实现OPC访问,第1张

OPC的文档网上很多,我在这里要介绍的主题是使用C++通过自动化接口来访问OPC Server,写这篇文章的目的是我在网上没有搜索到这方面的文档,如果我有这方面的需要,我想在网上一定也有其他朋友有这个需要,希望能对这些朋友有一些帮助。渣正

使用C++来访问OPC Server, 相对于使用自定义接口来说,自动化接口要简单很多,因为这和Visual Basic使用的是同一个接口,使用过Visual Basic来访问OPC Server的朋友一定能有这个体会。首先是准备好开发环境,一般测试是在模拟环境中进行,这样比较保险,可以使用一些免费的模拟OPC Server。我这里准备的是Matrikon的模拟服务器,模拟器裂坦安装以后。编程环境是VC++ 6.0,使用200X和2010也都大同小异。

为了演示简单,新建一个Win32控制台工程agOPC,新建agOPC.cpp源文件并加到工程里。

// --------------------------------- agOPC.cpp -----------------------------------------------

//在agOPC.cpp开头添加如下一行

#import "C:Program FilesMatrikonOPCCommonOPCAuto.dll" no_namespace

//这是通过OPCAuto.dll里所包含的类型库信息产生C++能访问的头文件,此时在工程的Debug文件夹下产生OPCAuto.tlh和OPCAuto.tli两个文件。

//添加需要的头文件

#pragma warning( disable : 4786 ) // 为了避免vector报出的C4786警告

#include<comdef.h> // 使用到了_bstr_t,_variant_t,_com_error都在这个文件里定义

#include<iostream>

#include<vector>

using namespace std

//声明全局变量

typedef struct OLEInit {

OLEInit() { CoInitialize( NULL )}

~OLEInit() { CoUninitialize()}

} OLEInit

OLEInit oleInit // 必须在最前面定义,因为在使用COM之前必须初始化COM库,否则程序会崩溃

//如源悔 由于是全局变量oleInit的构造函数在所有对象的构造函数调用之前调用,

// 析构函数在所有对象的析构函数调用之后调用

IOPCAutoServerPtropcSvr // 这些智能指针类型在OPCAuto.tlh中定义

IOPCGroupsPtropcGrps

IOPCGroupPtropcGrp

vector<OPCItemPtr> opcItms // 使用vector来保存三个测试Item。

//连接到OPC Server, 我所使用的参数是"Matrikon.OPC.Simulation.1"

void agOPCConn( const char *opcSvrName ) {

HRESULT hr

hr = opcSvr.CreateInstance( __uuidof( OPCServer ) )

if( FAILED( hr ) ) {

cerr<<"OPCServer CreateInstance failed, hr = " <<hr<<endl

exit(1)

}

opcSvr->Connect( opcSvrName )

}

//断开和OPC Server的连接

void agOPCDisc() {

opcGrps->RemoveAll()// 删除所有的组, 这个演示实例只有一个组

opcSvr->Disconnect()// 断开和OPC Server的连接

}

//创建一个组

void agOPCCreateGroup() {

// OPCGroups是特殊的属性,执行的时候会调用OPCAuto.tlh中的IOPCGroupsPtr GetOPCGroups()

opcGrps = opcSvr->OPCGroups

opcGrp = opcGrps->Add( _variant_t( "group1" ) ) // 组名随意取

}

//在组里添加三个不同类型的测试Item, 类型可以从Item的名字可以看出

void agOPCAddItems() {

OPCItemPtr opcItm

opcItm = opcGrp->OPCItems->AddItem( _bstr_t( "Bucket Brigade.Int4" ), 1 )

opcItms.push_back( opcItm )

opcItm = opcGrp->OPCItems->AddItem( _bstr_t( "Bucket Brigade.Int2" ) , 1)

opcItms.push_back( opcItm )

opcItm = opcGrp->OPCItems->AddItem( _bstr_t( "Bucket Brigade.String" ) , 1)

opcItms.push_back( opcItm )

}

//用来显示读取的Item的值

void agDumpVariant(VARIANT *v)

{

switch(v->vt)

{

case VT_I2:

printf(" value(VT_I2) = %d ", v->iVal )

break

case VT_I4:

printf(" value(VT_I4) = %ld ", v->lVal )

break

case VT_BSTR:

printf(" value(VT_BSTR) = %ls ", v->bstrVal )

break

default:

printf(" value(unknown type:%d) ", v->vt )

break

}

}

//同步读取三个Item的值,同步在很多情况下都是简单有效的选择方案,其实读取的异步方式在C++中可以建立一个工作线程来执行同步读的 *** 作,等有新的Item值的时候再通过某种线程间通信的方式告诉主线程“数据改变”的事件

void agOPCReadItems() {

_variant_tquality

_variant_ttimestamp

SAFEARRAY*pServerHandles

SAFEARRAY*pValues

SAFEARRAY*pErrors

SAFEARRAYBOUNDrgsabound[ 1 ]

longdim[ 1 ]

longsvrHdl

vector<_variant_t> values

vector<long> errs

inti

_variant_tvalue

longerr

// VC数组索引从0开始,而在OPCAuto.dll需要中从1开始,所以是rgsabound[ 0 ].cElements = 4,而给pServerHandles赋值的时候应该给索引是1,2,3相应的赋值Server Handle

rgsabound[ 0 ].cElements = 4

rgsabound[ 0 ].lLbound = 0

pServerHandles = SafeArrayCreate( VT_I4, 1, rgsabound )//构建一个1维数组,类型是VT_I4

for( i = 0i <opcItms.size()i++ ) {

svrHdl = opcItms[i]->ServerHandle

dim[ 0 ] = i + 1

// 给数组的每个元素赋值,对应的索引值是1, 2, 3

SafeArrayPutElement( pServerHandles, dim, &svrHdl )

}

opcGrp->SyncRead( OPCDevice,

3, // 读取的Item数目

&pServerHandles,// 输入的服务器端句柄数组

&pValues, // 输出的Item值数组

&pErrors, // 输出的Item错误状态数组

&quality, // 读取的值的状态

&timestamp ) // 读取的事件戳

for( i = 1i <= opcItms.size()i++ ) {

dim[ 0 ] = i

SafeArrayGetElement( pValues, dim, &value )// 读取Item值在value中

SafeArrayGetElement( pErrors, dim, &err ) // 读取错误状态值在err中

values.push_back( value )

errs.push_back( err )

}

for( i = 0i <values.size()i++ ) {

agDumpVariant( &values[ i ] ) // 显示读取的Item值

cout<<", err = "<<errs[ i ]<<endl

}

SafeArrayDestroy( pServerHandles )

SafeArrayDestroy( pValues )

SafeArrayDestroy( pErrors )

}

// 写入3个Item的值,为了演示实例简单,参数传递3个对应的Item值

void agOPCWriteItems( vector<_variant_t>values) {

_variant_tquality

_variant_ttimestamp

SAFEARRAY*pServerHandles

SAFEARRAY*pValues

SAFEARRAY*pErrors

longdim[ 1 ]

longsvrHdl

inti

SAFEARRAYBOUND rgsabound[ 1 ]

rgsabound[ 0 ].cElements = values.size() + 1

rgsabound[ 0 ].lLbound = 0

pServerHandles = SafeArrayCreate( VT_I4, 1, rgsabound )

pValues = SafeArrayCreate(VT_VARIANT, 1, rgsabound)

for( i = 0i <values.size()i++ ) {

svrHdl = opcItms[i]->ServerHandle

dim[ 0 ] = i + 1

SafeArrayPutElement( pServerHandles, dim, &svrHdl )

SafeArrayPutElement( pValues, dim, &values[i] )

}

opcGrp->SyncWrite( 3,&pServerHandles, &pValues,&pErrors )

SafeArrayDestroy( pServerHandles )

SafeArrayDestroy( pValues )

SafeArrayDestroy( pErrors )

}

//main主程序

int main()

{

try

{

agOPCConn( "Matrikon.OPC.Simulation.1" )

agOPCCreateGroup()

agOPCAddItems()

// 第一次写和读

vector<_variant_t>values

values.push_back( ( long )156 )

values.push_back( ( short )11 )

values.push_back( "opc" )

agOPCWriteItems( values )

agOPCReadItems()

cout <<"---------------------------------------"<<endl

// 第二次写和读

vector<_variant_t>values1

values1.push_back( ( long )123456 )

values1.push_back( ( short )666 )

values1.push_back( "hello" )

agOPCWriteItems( values1 )

agOPCReadItems()

}

catch ( _com_error &e ) {

// 应该在上面的子函数里面捕捉异常,但为了演示简单,在主函数里面捕捉异常

_bstr_t bstrSource( e.Source( ) )

_bstr_t bstrDescription( e.Description( ) )

cout<<"Code = "<<e.Error()<<endl

cout<<"Code meaning = "<<e.ErrorMessage()<<endl

cout<<"Source = "<<( LPCTSTR ) bstrSource<<endl

cout<<"Description = "<<( LPCTSTR ) bstrDescription<<endl

}

return 0

}

OPC全称是Object Linking and Embedding(OLE) for Process Control,它的出现为基于Windows的应用程序和现场过程控制应用建立了桥梁。

在过去,为了存取现场设备的数据信息,每一个应用软件开发商都需要编写专用的接口函数。由于现场设备的种类繁多,且产品的不断升级,往往给用户和软件开发商带来了巨大的工作负担。

通常这样也不能满足工作的实际需要,系统集成商和开发商急切需要一种具有高效性、可靠性、开放性、可互 *** 作性的即插即用的设备驱动程序。

在这种情况下,OPC标准应运而生。OPC标准以微软公司的OLE技术为基础,它的制定是通过提供一套标准的OLE/COM接口完成的,在OPC技术中使用的是OLE 2技术,OLE标准允许多台微机之间交换文档、图形等对象。

扩展资料

OPC是世界上最受欢迎的基于标准的数据通信方法。它旨在解决自动化行业中的最大的挑战:如何摆脱传统的基于特制驱动器的通信方式,在不同设备、控制器、和/或应用程序之间实现通讯。

OPC之所以能够成功地创造真正独立于供应商的通讯是因为,OPC从双方提取了数据发送设备(例升链如PLC)和数据接收端(例如HMI)的执行细节,因此可以衡哪在它们之间进行数据交换而不需要了解彼此的本地通信协议和内部数据组织形式。

这与特制驱动器的要求满足只针对于通信方两端的编写方法形成了鲜明的对比。OPC可以代表为一个位于数据发送端和数据接收端之间的“提取”界面,这个界面允许在数据发送端和数据接收端之间交换数据而不需要对对方有任何了解。

OPC的“设备细节提取”是通过运用两个称为OPC客户端和OPC服务器的OPC构件得以实现的。其中每一个构件将在以下章节予以描述。需要注意的是,数据发送端和数据接收端能够彼此通过OPC进行通讯并不意味着它们各自的本地协议就不需要了,或者是被OPC取代了。

相反,这些本地协议和/或接口仍然存在,但只是与两个OPC构件的其中某一个通讯。然后,OPC构件之间进行数据交换,从而结束咐笑码数据传递。数据也可以从应用程序端被传输至设备,而不需要彼此直接联系。

参考资料来源:百度百科-opc

OPC是OLE for Process Control的缩写,即应用于过程控制的OLE。

OLE原意是对象连接和嵌入,随着OLE2的发行,其范围已远远超出了这个概念。现在的OLE包含了许多新的特征,如统一数据传输、结构化存储和自动化,已经成为独立于计算机语言、 *** 作系统甚至硬件平台的一种规范。

OPC建立于OLE规范之上,它为工业控制领域提供了一种标准的数据访问机制。工业控制族兆盯领域用猜漏到大量的现场设备,在OPC出现以前,软件开发商需要开发大量的驱动程序来连接这些设备。

扩展资料:

OPC以OLE/COM机制作为应用程序的通信标准,而OLE/COM是一种客户端/服务器模式,具有语言无关性、代码重用性、易于集成性等优点。

OPC服务器中的代码确定了服务器所存取的设备和数据、数据项的命兆和名规则和服务器存取数据的细节,不管现场设备以何种形式存在,客户都以统一的方式去访问,从而保证软件对客户的透明性,使得用户完全从低层的开发中脱离出来。

客户应用程序仅须使用标准接口和服务器通信,而并不需要知道底层的实现细节。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12347184.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-24
下一篇 2023-05-24

发表评论

登录后才能评论

评论列表(0条)

保存