使用C++访问OPC Server的简单方法
(2011-02-20 09:31:45)
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 Files\Matrikon\OPC\Common\OPCAuto.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的构造函数在所有对象的构造函数调用之前调用,
// 析构函数在所有对象的析构函数调用之后调用
IOPCAutoServerPtr
opcSvr;
// 这些智能指针类型在OPCAuto.tlh中定义
IOPCGroupsPtr
opcGrps;
IOPCGroupPtr
opcGrp;
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_t
quality;
_variant_t
timestamp;
SAFEARRAY
*pServerHandles;
SAFEARRAY
*pValues;
SAFEARRAY
*pErrors;
SAFEARRAYBOUND
rgsabound[ 1
];
long
dim[ 1
];
long
svrHdl;
vector<_variant_t>
values;
vector<long>
errs;
int
i;
_variant_t
value;
long
err;
//
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 = 0; i < 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,
// 读取的值的状态
×tamp
);
// 读取的事件戳
for( i = 1; i <= 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 = 0; i < 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_t
quality;
_variant_t
timestamp;
SAFEARRAY
*pServerHandles;
SAFEARRAY
*pValues;
SAFEARRAY
*pErrors;
long
dim[ 1
];
long
svrHdl;
int
i;
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 = 0; i < 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;
}
喜欢
0
赠金笔
加载中,请稍候......