加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

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

(2011-02-20 09:31:45)
分类: OPC
    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,               // 读取的值的状态
        &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;
    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

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有