使用C++访问OPC Server的简单方法

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 str uct 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-&gt;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, // 读取的值的状态
&amp;timestamp ); // 读取的事件戳
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;
longdim[ 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;
}

### 回答1: 构建 OPC Server 服务端需要经过以下步骤: 1. 确保拥有 OPC 服务器开发工具包(OPC Server Development Toolkit),该工具包包含了开发 OPC Server 所需的必要库和组件。 2. 创建一个新的 OPC Server 项目。在项目中添加所需的标签(Tags)和变量(Variables),这些标签和变量将会与客户端进行数据交流。 3. 配置 OPC Server 的通信设置。根据需要选择使用 DCOM(分布式组件对象模型)或其他通信协议,以实现与客户端的连接和数据传输。 4. 实现 OPC Server 的功能和逻辑。根据需求编写代码来读取和写入标签和变量的数据,以及处理客户端发送的请求和命令。 5. 配置 OPC Server 的安全设置。根据需要设置访问控制和权限管理,以确保数据的安全性和可信度。 6. 测试和验证 OPC Server 的功能和稳定性。使用 OPC Client 工具或自定义的客户端应用程序来连接和通信,检查数据的准确性和响应性。 7. 部署和启动 OPC Server。将编译好的 OPC Server 部署到目标机器上,配置好运行参数和选项,然后启动服务。 8. 监控和维护 OPC Server。定期检查服务器的运行状态和日志,排查和修复可能存在的问题和故障。 以上是构建 OPC Server 服务端的基本步骤,具体实施时还需要根据不同的开发环境和工具进行适当调整和操作。 ### 回答2: 构建 OPC Server 服务端是为了实现数据采集和共享,并与其他 OPC 客户端进行实时通信。在构建 OPC Server 服务端时,需要完成以下几个步骤: 1. 确定设备和数据源:首先需要明确要连接的设备和数据源,这可以是传感器、仪表、PLC等设备,数据源可以是设备本身产生的数据或者由其他系统提供的数据。 2. 选择合适的 OPC 服务器软件:根据设备和数据源的特点和要求,选择一款合适的 OPC 服务器软件。市面上有很多不同的 OPC 服务器软件可供选择,如KEPServerEX、MatrikonOPCOPC Factory Server等。 3. 安装和配置 OPC 服务器软件:根据选择的 OPC 服务器软件的安装指南,进行软件的安装和配置。通常情况下,需要指定连接设备和数据源的参数、选择数据传输协议(如OPC DA、OPC UA等)、配置访问权限等。 4. 配置数据点和标签:在 OPC Server 中需要配置数据点和标签,以便与其他 OPC 客户端进行数据交互。数据点对应需要采集或共享的数据,标签则用于标识和组织数据点。根据设备和数据源的不同,可以根据需要配置不同类型的数据点和标签。 5. 进行数据采集和共享:配置完成后,OPC Server 服务端可以开始进行数据采集和共享。数据采集可以通过连接设备和数据源,读取数据并将其传递给 OPC 客户端。共享数据可以将从设备和数据源采集到的数据发布给其他 OPC 客户端,实现实时数据交换和共享。 6. 进行测试和调试:在完成配置和数据采集共享后,需要进行测试和调试,确保 OPC Server 服务端的正常工作。可以使用 OPC 客户端软件连接到 OPC Server,并进行数据读写操作,检查数据是否正常传递和交换。 总之,构建 OPC Server 服务端需要选择合适的软件,进行安装和配置,配置数据点和标签,进行数据采集和共享,并进行测试和调试,以保证其正常工作。这样可以实现数据的稳定采集和共享,提高生产过程的效率和准确性。 ### 回答3: 构建 OPC Server 服务端是在计算机或网络环境中搭建一个运行 OPC Server 的设备或软件。OPC Server 负责将物理设备中的数据和信号转换为 OPC 标准的数据,通过网络传输给 OPC Client。 构建一个 OPC Server 服务端的过程可以分为以下几个步骤: 1. 选择合适的 OPC Server 开发工具:根据需要选择适合的开发工具,比如OPC Foundation 提供的开发工具包OPC Foundation 提供的工具包:OPC Core Components、OPC SDK、OLE for Process Control (OPC) 和 OPC UA。 2. 创建数据模型:根据实际情况定义 OPC Server 具备的数据模型,包括设备类型、变量、属性和方法等。 3. 实现 OPC Server 接口:通过使用相应的开发工具,编写代码实现 OPC Server 接口。这些接口包括:OPC DA (Data Access) 接口、OPC HDA (Historical Data Access) 接口、OPC AE (Alarms and Events) 接口、OPC XML-DA (XML Data Access) 接口等,根据具体需求进行选择。 4. 配置 OPC Server:根据实际情况进行 OPC Server 的参数配置,如端口设置、用户权限、安全策略等。 5. 测试和调试:在构建 OPC Server 服务端之后,进行测试和调试工作,确保 OPC Server 能正常运行、数据可靠。 6. 文档和支持:编写相关文档,提供使用帮助和技术支持,供用户参考和解决问题。 在构建 OPC Server 服务端时,需要考虑以下几个方面:设备所支持的 OPC Server 版本、网络环境的稳定性、安全性和性能要求等。同时还需要注意遵守 OPC 标准和相关规范,确保 OPC Server 能与 OPC Client 正常通信。 构建一个高质量的 OPC Server 服务端,需要充分理解 OPC 技术和相关规范,具备一定的开发经验和技术实力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值