来源:http://blog.csdn.net/gjack/article/details/5641794

C#实现 OPC历史数据存取研究 (原文)
Research of Accessing the OPC Historical Data by Using C# 
文孟飞[1]
,何海江[2]
,阳春华[3] 
WEN meng-fei
[1]
, HE hai-jiang[2]
, YANG chun-hua
[3]
 
(1、长沙广播电视大学;2、长沙大学计算机中心;3、中南大学信息科学与工程学院) 
摘要:在 OPC.NET COM  包装器和 OPC.Net API 的基础上,用 C#实现一个实例,从 OPC
历史数据服务器抽取数据,对数据聚合运算。结果表明,该技术能应用于工业过程数据仓库,
综合分析自动化系统所产生的历史数据,取得了满意的效果。 
关键词:OPC 历史数据;工业过程;.NET 
中图分类号:TP312         文献标识码:A 
 
Abstract: One application coded by C# on the basis of OPC.NET COM Wrapper and OPC.NET 
API is realized,it can be used to extract and aggregate data from the OPC historical data server. 
The results show its good performance when used in the industrial process data warehouse to 
analyze synthetically the historical data produced by the automation system. 
Key Words: OPC historical data; industrial process;.NET 
1  前言 
目前工业界面临激烈的全球竞争,企业为了生存,需要各种分析和决策去占领和控制市
场。为提高竞争力,许多企业使用 DCS、SCADA、PLC 等自动化设备和软件,来实现生产过
程自动化[1]
。 现在大多数这一类工业企业都保存了大量的过程数据,但由于这些自动化系统属
于不同的厂商,数据往往分散在异构的计算机或控制系统上,各个自动化系统所拥有的历史数
据都只能为本身使用,不能统一的存储、调用和管理,造成数据资源的极大浪费,许多问题因无
法得到足够的数据进行综合分析而难以得到合理快捷的解决。本文遵循 OPC 历史数据规范,
开发了一个历史数据抽取的程序,为工业过程历史数据分析软件提供基础,文章分五个部分介
绍实现过程。 
2 OPC历史数据规范 
OPC 历史数据服务器实现两个逻辑意义上的对象,每个对象包含一个或多个接口。
IOPCHDA_Server、IOPCHDA_SyncRead 和 IOPCHDA_Browser(后文简称 HDA项)是这两
个逻辑对象包含的重要接口。历史数据客户端软件根据功能要求,获取对应的接口,再调用接
口,从服务器获得所需要的数据。 
3 服务器的枚举和连接 
OPC基金会[2]
对会员提供了OpcRcw动态链接库、OPC NET COM 包装器和OPC NET 
API 1.1版本,前两者完成了COM编排过程中遇到的包括数据类型转换、接口实现、参数传递
等复杂的技术工作,后者将OPC复杂的规范封装成简单易用的C#类。本文在此两种技术的基
础上,建立客户端软件,与OPC历史数据服务器交换数据。遵照Visual Studio.NET的要求,引用
组件OpcNetApi.dll和OpcNetCom.dll,在程序中使用using,加入这些命名空间[3]
。 
下面的代码用来浏览某台计算机上已安装的历史数据服务器。 
 Opc.IDiscovery m_discovery = new OpcCom.ServerEnumerator(); 
Opc.Server[] servers = m_discovery.GetAvailableServers(Specification.COM_HDA_10, host, 
null);// COM_HDA_10历史数据1.0版本,host为计算机名,null表示不需要任何网络安全认证。
servers即为需要连接的OPC历史数据服务器的集合。找到服务器xpServer后,可建立与该服务
器的连接。 
Opc.Hda.Server xpServer=null;//定义历史数据服务器 
…//将从前文游览到的某一个OPC历史数据服务器赋给xpServer。 
 Try{ 本课题得到国家自然科学基金(60574030)资助。 
…//将从前文浏览到的某一个OPC历史数据服务器赋给xpServer。 
try{ 
  xpServer.Connect();//建立连接。 
         … 

 catch (Exception f){//捕获错误,提高软件的健壮性,后文的代码都省略这一段。 
  …//错误处理 
 } 
4  地址空间  
地址空间由服务器的IOPCHDA_Browser接口提供,客户端由此可查找到服务器中哪些
项拥有历史数据。与数据存取规范相同,OPC历史数据服务器提供两种方式的地址空间:平直
型(Flat)、层次型(Hierarchical)。要在.NET中激活COM对象,需要通过RCW(运行库可调用包
装)在托管的.NET代码和未托管的COM代码之间生成一个代理[4]
,手工编排COM中的接口定
义语言。IOPCHDA_Browser接口的编排方法如下。 
[Guid("1F1217B1-DEE0-11d2-A5E5-000086339399)"],//接口IOPCHDA_Browser的GUID 
 InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]  
internal interface IOPCHDA_Browser{ 
void  GetEnum([in]int dwBrowseType, [out]Intptr ppIEnumString );  
void  ChangeBrowsePosition([in]int dwBrowseDirection,  
[in, MarshalAs(UnmanagedType.LPWStr)]string szString );  
void  GetItemID ([in, MarshalAs(UnmanagedType.LPWStr)]string szNode,  
[out]Intptr pszItemID); 
void  GetBranchPosition ( [out]Intptr pszBranchPos ); 
 } 
用IOPCHDA_Browser中的方法递归调用,可搜索地址空间。 
5 HDA项的历史数据 
OPC NET组件使用两种方法读HDA项的数据, 一是通过Trend,一是直接操作HDA项。
先介绍 Trend 方法,Trend 是 HDA 项的集合,相当于数据存取规范中的组 Group。客户端创建
Trend 后,再将 HDA 项加入其中,对 Trend 进行 ReadRaw(读原始数据) 、ReadProcessed(读
聚合数据)等操作,则 Trend 内所有 HDA项的相应操作完成。如下方法可创建一个 Trend。 
Trend trend = new Trend(m_server); //在服务器m_server中创建Trend。 
trend.Name = "分配台"; //Trend名不进行聚合运算,修改此值后,可对其实行各种聚合运算。 
trend.AggregateID = AggregateID.NOAGGREGATE;  
trend.StartTime = new Time(new DateTime(2002,10,12,15,43,08,0));//起始时间 
trend.EndTime = new Time(new DateTime(2002,11,12,15,44,28,0)); //终止时间 
trend.MaxValues = 0;//一次最多读值个数,0表示读时间段内所有数据。 
trend.IncludeBounds = false;//不包括边界。 
     m_server.Trends.Add(trend); //将trend添加到服务器m_server中。 
在名为“分配台”的Trend中增加三个HDA项。 
 Item[] items = new Item[3]; //Item为HDA项的类,创建一个数组。 
 for( int i=0;i<3;i++ ) { 
 items[i] = new Item(); 
 items[i].ItemName = m_strItemName[i]; //m_strItemName在地址空间内找到的HDA项名。     items[i].ItemPath = null;//该HDA项没有路径。 
   items[i].ClientHandle = Guid.NewGuid().ToString(); //生成客户端句柄。 
  } 
  IdentifiedResult[] results= m_server.CreateItems(items);//在m_server创建HDA项。 
  if (results != null){ //创建成功 
    foreach (IdentifiedResult item in results){ 
     if (item.ResultID.Succeeded()){ 
      trend.Items.Add(new Item(item)); //***将HDA项加入到trend中。 
            } } } 
完成前述工作后,一条语句m_results = trend.ReadRaw()即可完成读操作,并将结果存放
在类型为ItemValueCollection[]的数组m_results中。下面的代码在视图中显示第一个HDA项
的结果。 
ItemValueCollection i_r_s = m_results[0]; 
foreach (ItemValue iValue in i_r_s) { 
   string strTime = Opc.Convert.ToString(iValue.Timestamp);//时间戳 
   string strValue = Opc.Convert.ToString(iValue.Value); //值 
   string strQuality = Opc.Convert.ToString(iValue.Quality); //原始的数据品质 
//将枚举型的历史数据品质转换为字符串。 
   string strHdaQuality = Opc.Convert.ToString(iValue.HistorianQuality);  
…… 

第二种方法直接操作HDA项则不涉及到Trend的操作。先在历史数据服务器中创建HDA
项,成功后再将这些HDA项放到一个Item数组中。具体代码除两个变化外与Trend操作第二段
相同,增加两个变量Item[] m_readItems =null; j=0;将用*标记的一句替换为m_readItems[j++] = 
new Item(item);读操作变为: 
Opc.Hda.Time start = new Time(new DateTime(2002,10,12,15,43,08,0)); //起始时间 
Opc.Hda.Time end = new Time(new DateTime(2002,10,12,15,44,28,0)); //终止时间 
m_results = m_server.ReadRaw(start,end,0,false,m_readItems); //读所有数据,不包括边界值 
历史数据服务器在2002年10月12日15时43分0秒到15时45分0秒之间有43:03、43:08、
43:13、43:18、43:23…44:23、44:28…44:58这些时间的历史数据。前文代码中的MaxValues
和IncludeBounds为0和true时,则返回的数据包括43:08、43:13…44:23、44:28;IncludeBounds
为false时,左边界值43:08返回, 右边界值44:28则没有;MaxValues和IncludeBounds为3和true时,
则返回的数据只有三个43:08、43:13、43:18。 
6  平均值和记数 
  同样,组件使用Trend和直接操作两种方法读HDA项的聚合值,包括求平均值、 总和、 方差、
插值等,文章以求平均值和记数为例。Trend方法和前文相同,创建Trend后,再将HDA项加入其
中,对Trend进行聚合运算。下面的代码直接操作HDA项,不用到Trend,求平均值。 
Opc.Hda.Time start = new Time(new DateTime(2002,10,27,15,43,08,0)); //起始时间 
  Opc.Hda.Time end = new Time(new DateTime(2002,10,27,15,43,27,0)); //终止时间 
  foreach(Item item in m_readItems) 
  item.AggregateID = AggregateID.AVERAGE;//求平均值,COUNT为记数。 
  m_results = m_server.ReadProcessed(start,end,5,m_readItems);//时间间隔为5秒。 
返回的结果 m_results 使用前述方法显示。历史数据服务器的某一 HDA 项在 2002 年
10月27日15时43分08秒到15时43分27秒之间有表一所示的数据,表中及后文省略时间戳中的日期。 
表一  已有的历史数据(2004-1-29) 
时间戳  值 
原始数
据品质 
历史数
据品质 时间戳 值 原始数
据品质
历史数
据品质 
15:43:09 4.8 good Raw 15:43:174.7good Raw 
15:43:11 4.4 bad Raw 15:43:194.5good Raw 
15:43:13 4.9 good Raw 15:43:214.7good Raw 
15:43:15 4.5 good Raw     
15:43:23 起没有数据 
求平均值的聚合运算将原始数据品质为 good 的数据求和,再除以数据个数,返回结果的
时间戳为每一个时间段的起始。前述的执行结果返回四个值,15:43:08 到 15:43:12 这 5 秒钟
(时间间隔)内有两个值,15:43:11 的值不参与计算,因为其原始数据品质为 bad,则平均值为
4.8,时间戳为15:43:08;15:43:13到15:43:17这5秒钟内有三个值,平均值= (4.9+4.5+4.7) /3=4.7;
15:43:18 到 15:43:22 这 5 秒钟内有两个值,平均值=(4.5+4.7)/2=4.6;15:43:23 到 15:43:27 内
没有数据。最终显示表二所示的结果: 
表二  平均值                                   表三 
记数是统计时间段内原始数据品质为 good 的数据个数,将代码中的 AggregateID. 
AVERAGE 改为 AggregateID. COUNT 时,则表二变成表三。 
7  结束语 
实例在 Windows XP专业版,.NET  框架 1.1,Visual Studio.NET 2003 下调试通过,在实际
应用中运行良好。工业过程的数据抽取是一个十分复杂的过程,遇到不提供 OPC 历史数据
服务器的自动化系统,则要求厂商提供文件或数据库表的格式,即算得到格式,工作量比文章
介绍的方法大得多。本文作者创新点是实现了在.net 环境下使用 OPC 规范获取历史数据,
为工业过程历史数据分析软件提供基础。 
参考文献 
[1]  日本横河公司电子文档.1B30_09.pdf 
[2] OPC Foundation·OPC Historical Data Access Specification V1.20 [EB]·2003.12 
[3]  何海江.C#程序与基于 COM 的 OPC 数据存取服务器交换数据研究[J].微计算机信
息,2004,20(10) :112-113 
[4] Microsoft·MSDN 2003 
作者简介:文孟飞,男,1975年生,硕士,讲师,主要研究方向为计算机应用技术、智能系统、自动
控制.E-mail:wmfdcf@126.com..何海江,男,1970 年生,硕士,副教授,主要研究方向为数据仓库、
组件技术.阳春华,女,1965 年生,博士,教授,博士生导师,主要研究领域为复杂过程建模与优化
控制、智能自动控制系统与装置以及实时系统容错调度技术。 
Author Brief introduction : Wen, Meng-fei (1975-), male, M.A. in computer applied technology, 
docent. taking on research on computer applied technology, intelligence-system, 
e-mail:wmfdcf@126.com. He, Hai-jiang (1970-), male, adjunct professor, taking on research on 
时间戳  值 
原始数据
品质 
历史数据
品质 
时间戳  值 原始数据
品质 
历史数据 
品质 
15:43:08 4.8 good Calculated15:43:081 good Calculated 
15:43:13 4.7 good Calculated15:43:163 good Calculated 
15:43:18 4.6 good Calculated15:43:242 good Calculated 
15:43:23  bad NoData 
 
15:43:320 good Calculated data-godown, joint-technology.Yang, Chun-hua (1965-), female, PHD tutor, taking on research on 
modeling and optimized controlling of complicated process, intelligent autocontrol system and 
installation, and real-time attemper technology of systematic fault tolerance. 
 (1、长沙广播电视大学   长沙  410005;2、长沙大学计算机中心   长沙  410003;   3、
中南大学信息科学与工程学院   长沙  410083)文孟飞[1]
,何海江[2]
,阳春华[3] 
(1、 Shangsha Radio & TV Unversity, Changsha, 410005; 2、 Computer Teaching Center, Changsha 
University, Changsha, 410003; 3、College of Information Science and Engineering, Central South 
University, Changsha, 410083) WEN meng-fei
[1]
, HE hai-jiang[2]
, YANG chun-hua
[3]
 
通信地址: (410005   长沙市浏正街 126 号长沙广播电视大学理工部)文孟飞

C#实现 OPC历史数据存取研究的更多相关文章

  1. zabbix历史数据相关表研究

    zabbix历史数据相关表研究 history和trends相关表 history和trends都是存储历史数据的地方.一般是通过监控项(item)配置里.匹配更新监控项(item)和设置HouseK ...

  2. OPC协议解析-关于OPC协议的几个问题

    1    什么是OPC协议? 为了便于自动化行业不同厂家的设备和应用程序能相互交换数据,定义了一个统一的接口函数,就是OPC协议规范.有了OPC就可以使用统一的方式去访问不同设备厂商的产品数据. OP ...

  3. C#使用OpcNetApi.dll和OpcNetApi.Com.dll操作OPC

    本人学习了一下.Net,恰好,51自学网,又要用这个.而网上很多VC6,VB6,VB .Net的但,很少C#的.现在研究一下,给出例子: 测试平台,是VS2008,KEPServer,OpcNetAp ...

  4. OPC协议

    详解OPC协议-工业控制和自动化领域的接口标准     摘要:OPC全称是OLEforProcessControl,即用于过程控制的OLE,是针对现场控制系统的一个工业标准接口,是工业控制和生产自动化 ...

  5. OPC UA 统一架构) (一)

    OPC UA 一 .OPC UA简介 OPC UA(OPC Unified Architecture)是下一代OPC统一体系架构,是一种基于服务的.跨越平台的解决方案. OPC UA具有如下特点: 1 ...

  6. OPC协议解析-OPC客户端与服务器通讯解析

    1      OPC服务器 OPC服务器, 是指按照OPC基金组织规定的OPC规范群开发的软件驱动.OPC服务器作为中间媒介负责从数据源读取数据再跟另外一端的客户端通信.在 OPC客户端/服务器 的结 ...

  7. 从 OPC 到 OPC UA

    [前言]OPC是一个工业标准,所属国际组织是OPC基金会,现有会员已超过220家,包括世界上所有主要的自动化控制系统.仪器仪表及过程控制系统的公司. [经典 OPC]经典OPC规范基于微软Window ...

  8. OPC接口相关资料地址

    OPC官方网址:https://opcfoundation.org/ OPC中国官网: http://www.chinaopc.org/ ------------------------------- ...

  9. 关于OPC连接读写下位机PLC(转)

    原文转自:http://blog.csdn.net/u012252959/article/details/49736285?locationNum=11 开发OPC客户端程序时,首先应该生成OPC服务 ...

随机推荐

  1. lspci能看到ifconfig -a看不到网卡

    随着宽带技术的快速发展,服务器使用万兆网卡的概率越来越高.最近装了几台服务器都用的万兆网卡,为了图便宜,网卡和模块都是淘宝上买的,这部还真遇到不少问题. 我的服务器都是centos6.4 64位的,网 ...

  2. 012——数组(十二) 数组range array_count_values array_pad array_product

    <?php /*range () 生成包含指定范围的数组,第三个参数是步长值 */ /*$arr=range(1,10,2); print_r($arr);//输出:Array ( [0] =& ...

  3. 【第三方类库】underscore.js源码---each forEach 每次迭代跟{}比较的疑惑

    var each = _.each = _.forEach = function(obj, iterator, context) { if (obj == null) return; //首先判断是否 ...

  4. 【ecmascript】Javascript 严格模式详解【转】

    一.概述 除了正常运行模式,ECMAscript 5添加了第二种运行模式:"严格模式"(strict mode).顾名思义,这种模式使得Javascript在更严格的条件下运行. ...

  5. 如何在非Spring管理的类中使用Spring加载的bean

    <dependencies> <dependency> <groupId>org.springframework.boot</groupId> < ...

  6. trigger 触发器(mysql)

    /* 触发器 tigger 引出触发器: 在进行数据库应用软件的开发的时候,我们有时候会碰到表中的某些数据改变,同事希望引起其他相关数据改变的需求,这时候就需要使用触发器. 运用触发器可以简化程序,增 ...

  7. MySQL 不开启slave如何完成异地复制

    1,分批次通过远程的binlog来进行数据加载 业务新需求,线上数据库数据拉到本次,但是不允许开启slave服务,不建立直接外网的数据库账号,也不能打通数据库对外网的网络,所以我们测试环境无法通过普通 ...

  8. PHP循环嵌套例子

    循环嵌套1.实现如下效果:第一行第二行第三行第四行第五行1 2 3 4 51 2 3 4 51 2 3 4 51 2 3 4 52.实现如下效果图:第一行第二行第三行第四行第五行1 2 3 4 56 ...

  9. PHP convet class to json data

    /********************************************************************* * PHP convet class to json da ...

  10. 使用dlopen和dlsym来使用C++中的类

    http://my.oschina.net/u/1450061/blog/204608