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;
}

使用C++访问OPC Server的简单方法相关推荐

  1. C#创建OPC Client来访问OPC server

    最近一个项目,需要跟PLC通讯,所以测试使用了OPC server.现主要记录使用C#编写的Client例程,其它方面不作详细描述. 第一步,OPC Server使用的是KEPServer 5版本,网 ...

  2. sql活动监视器 死锁_监视SQL Server死锁–简单方法

    sql活动监视器 死锁 SQL Server is a very powerful tool and wherever I go, I see the tool being way much unde ...

  3. linux python连接oracle数据库_Linux下通过python访问MySQL、Oracle、SQL Server数据库的方法...

    本文档主要描述了Linux下python数据库驱动的安装和配置,用来实现在Linux平台下通过python访问MySQL.Oracle.SQL Server数据库. 其中包括以下几个软件的安装及配置: ...

  4. 访问Windows 11恢复环境的5种简单方法

    问Windows 11恢复环境的5种简单方法 原文地址:我使用的第二个访问Windows 11恢复环境的5种简单方法 Windows 11恢复环境可以帮助您排除故障.恢复或引导设备,但如何访问设备并运 ...

  5. C# OPC DA 协议同步及异步读取数据,支持局域网访问其 他OPC server

    C# OPC DA 协议同步及异步读取数据,支持局域网访问其 他OPC server

  6. Linux访问外网(内含简单方法)

    这里先介绍Linux利用桥连模式访问外网 1.先对虚拟机进行设置(右键虚拟机,点击设置) 2.选择网络适配器,然后在右边的网络连接中选择桥连模式,然后点击确定 3.查看虚拟机默认配置 [root@lo ...

  7. 用Delphi开发OPC客户端工具的方法研究

    用Delphi开发OPC客户端工具的方法研究[1]<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:o ...

  8. 本机连接opc server有部分数据不刷新_实时数据库PI在企业MES系统中的应用

    实时数据库是计算机控制系统和上层生产管理系统数据存储和展示的核心.结合河南天冠燃料乙醇有限公司MES系统应用实例,介绍了实时数据库PI的安装部署,建立信号量集和数据导入,以及客户端接口配置,数据库测试 ...

  9. c# typescript_在任何IDE中从C#,Java或Python代码获取TypeScript接口的简单方法

    c# typescript by Leonardo Carreiro 莱昂纳多·卡雷罗(Leonardo Carreiro) 在任何IDE中从C#,Java或Python代码获取TypeScript接 ...

最新文章

  1. COCO 2019挑战赛,旷视研究院拿下三项计算机识别冠军 | ICCV 2019
  2. P3648-[APIO2014]序列分割【斜率优化】
  3. OpenCV中直方图对比
  4. Tuxera NTFS使用教程:关于Tuxera NTFS mac还有你不知道的用法
  5. 微信月活9亿的高效运维之路
  6. 探究foreach对于迭代变量的封装性的研究
  7. centos7 部署elasticsearch
  8. [转帖]达梦数据库(DM6)和ORACLE 10g的异同点
  9. 环境判断:区别h5打开还是weixin打开?
  10. 做了三年Java,java参考文献近五年图书
  11. visio中公式太小_多元醇羟值、羟基含量与分子量之间的关系及计算公式
  12. 蛐蛐播放器 android,蛐蛐五线谱播放器
  13. MySQL生成测试数据相关脚本(持续更新)
  14. 博士读一半决定放弃了,丢人吗?
  15. 几个网站收录提交入口,让自己的博客被搜索引擎收录(最新版)
  16. TM1622的程序设计实现
  17. Windows、Linux以及银河麒麟系统的一些操作系统知识
  18. 读《码农翻身:用故事给技术加点料》
  19. 这次,大数据工程师赢了!
  20. 小鱼发现玩机械臂的小姐姐越来越多了。。。再说说手眼标定那些事~

热门文章

  1. php nginx配置404页面,Nginx实现404页面的几种方法
  2. spring java配置_Spring基于java的配置
  3. 白光干涉衍射实验的计算机仿真,白光干涉_衍射实验的计算机仿真_蓝海江.pdf
  4. 做技术的为什么很难发财?
  5. PCB上走100A电流的方法
  6. 一个电子工程师的完美人生!
  7. 自己动手写CPU(2)流水线数据相关问题
  8. 基于物品的协同过滤推荐算法_《推荐系统实践》3.基于物品的协同过滤算法
  9. iphone打字怎么换行_除了打字,iPhone的键盘还有这12个功能
  10. python yield from yield_python yield和yield from用法总结详解 python yield和yield from用法总结...