基于C#的数据采集组件的设计与实现.docx

上传人:b****6 文档编号:3491215 上传时间:2022-11-23 格式:DOCX 页数:7 大小:20.15KB
下载 相关 举报
基于C#的数据采集组件的设计与实现.docx_第1页
第1页 / 共7页
基于C#的数据采集组件的设计与实现.docx_第2页
第2页 / 共7页
基于C#的数据采集组件的设计与实现.docx_第3页
第3页 / 共7页
基于C#的数据采集组件的设计与实现.docx_第4页
第4页 / 共7页
基于C#的数据采集组件的设计与实现.docx_第5页
第5页 / 共7页
点击查看更多>>
下载资源
资源描述

基于C#的数据采集组件的设计与实现.docx

《基于C#的数据采集组件的设计与实现.docx》由会员分享,可在线阅读,更多相关《基于C#的数据采集组件的设计与实现.docx(7页珍藏版)》请在冰豆网上搜索。

基于C#的数据采集组件的设计与实现.docx

基于C#的数据采集组件的设计与实现

基于C#的数据采集组件的设计与实现

摘要:

近年来,组态软件在各行各业都有广泛的应用,而数据采集则是组态软件运行的前提和基础,是组态软件不可或缺的重要组成部分。

数据采集系统能否高效地同组态软件相结合,是否能方便地扩展,是否能灵活支持多样的硬件设备等一系列问题成为组态系统的核心问题。

论述了利用C#设计实现数据采集组件所涉及到的关键技术,提出一种可行的结构设计思路。

  关键词:

数据采集;组态;C#;NET互操作

  1数据采集与组态软件

  数据采集(DataCapture),又称数据获取,是指从传感器或其它待测设备等模拟和数字被测单元中自动采集非电量或者电量信号,送到上位机进行分析、处理。

  组态软件,又称组态监控软件,即进行数据采集与过程控制的专用软件。

组态软件利用可视化的方式将工作环境中的硬件设备、数据组织起来,通过醒目、灵活的操作界面供用户对设备进行监视和控制。

目前来说,组态软件在众多行业均有广泛的应用,比如电力系统、给水系统、建筑、石油、化工等都可以见到组态软件的身影。

  数据采集是组态系统运行的前提、其采集到的数据是组态系统加工和处理的对象。

由此可见,数据采集是组态系统的核心基础。

数据采集系统能否及时、准确地采集到所需数据,能否方便地进行功能上的扩展,以及对于层出不穷的硬件设备能否灵活予以支持等一系列问题,成为系统工程师需要攻克的重点。

  2C#与组件设计

  C#是微软公司发布的一种面向对象的程序设计语言,其运行的基础是微软的.NETFramework。

C#是由C和C++衍生出来的编程语言,其继承了C和C++的强大功能,但同时掩盖了一些复杂的特性。

C#有强大的操作能力、语言风格优雅、有创新的语言特性,并融入了Java、VB等高级语言的特点。

C#语法简洁,是彻底的面向对象的编程工具,其与Web紧密集合,拥有强大的安全性机制,是目前系统开发的首选之一。

  组件有很多定义。

比如卡耐基梅隆大学的科学家将组件定义为“一个不透明的功能实体,能够被第三方组装,且符合一个构件模型”。

计算机百科全书将组件定义为“软件系统中具有相对独立功能、接口由契约指定、和语境有明显依赖关系、可独立部署、可组装的软件实体”。

从上述定义可以看出,组件不仅封装了数据而封装了处理逻辑,从而形成功能单一、独立、可装配的灵活构件。

组件设计是目前系统设计工作的重点部分,组件是否有灵活的结构,是否提供方便的调用接口等直接影响组件的质量。

  C#为组件的设计提供了强大的支持。

在C#中,组件是以类的方式呈现的,但组件类必须实现System.ComponentModel.Icomponent接口,或者从实现Icomponent的类直接或间接派生。

  3C#封装C接口的硬件驱动

  目前的硬件设备种类繁多,硬件厂商提供的驱动程序总共有两种:

一种是以DLL(DynamicLinkLibrary,即动态链接库)的形式提供,以C或C++接口居多;另外一种是以ActiveX的形式提供。

本文着重讨论DLL与C#的配合。

C#运行于.NETFramework之上,其代码是“安全的”,被称为托管代码。

而C或C++接口的DLL属于非托管代码。

若计划在托管代码内调用非托管代码的接口则需要一些特殊的处理方式。

  在托管代码中调用非托管代码的一般步骤如下:

  

(1)获取非托管函数的信息;

  

(2)声明该非托管函数,设置P/Invoke所需属性;

  (3)调用该函数。

  完成第一步可用dumpbin.exe或depends.exe工具,这两个工具在VC++.NET部分提供。

例如查看user32.dll内的导出函数信息可采用如下命令:

  dumpbin.exe/exportsuser32.dll>d:

\\DllInfo\\funclist_user32dll.txt。

这将user32.dll内的所有导出函数信息输出到funclist_user32dll.txt文件中。

下面给出完整的互操作调用例子。

  若已存在一个VC++6.0创建的DLL文件,文件名称为“DevAgent.dll”其内包含被导出函数extern"C"HRESULT__stdcallDoWork(constINTiDevID,INTiICmdID);在托管代码中使用此函数的代码如下:

  \[DllImport(“DevAgent.dll”)\]

  staticexternintDoWork(constintiDevID,intiICmdID);

  声明之后就可以直接调用了。

注意,在声明时必须使用extern修饰,目的为告知编译器,此函数是在外部实现的方法体。

另外,还需用static修饰,表明无需实例化即可调用。

  调用非托管函数时,.NET可以直接捕获到非托管代码内出现的异常,调用时直接将可能发生异常的非托管代码放入try…catch语句块内即可。

除此之外,.NET仍可以捕获由非托管代码抛出的异常。

如在非托管代码内定义抛出异常函数:

  VoidThrowUnexpectException(){

  RaiseException(EXCEPTION_ACCESS_VIOLATION,

  EXCEPTION_NONCONTINUABLE,0,0);

  }在托管代码内使用如下方法:

  \[DllImport(“DevAgent.dll”)\]

  StaticexternvoidThrowUnexpectException();……

  try{

  ThrowUnexpectException()

  }catch(SEHExceptionSEHExc)

  {……//异常处理}

  catch(AccessViolationExceptionexc)

  {……//异常处理}

  4多媒体定时器和线程的配合

  定时器是工控软件的必要元素,若需要比较精确的定时,可选用多媒体定时器。

多数硬件系统采用轮询方式提供数据,因此,在进行此部分设计时必然会用到线程的循环。

所以将多媒体定时器和循环线程配合在一起来实现硬件的轮询将会有很高的效率。

  4.1事件与委托

  委托是一个类,它定义了方法的类型,使用委托可以将方法当作另一个方法的参数进行传递。

若为其分配了方法,则委托与该方法具有完全相同的行为,这非常类似于C语言的函数指针,只是它是类型安全代码。

若要使用委托,首先应创建一个方法,并使该方法的返回值、参数和声明的委托相同;然后要创建委托的一个实例,并将创建的方法传入;之后要调用该实例,并传入所需参数。

当开发过程中同一个委托有多个实例的使用时,可以采用委托数组的形式。

除此之外,当需要调用不同的方法时,可以采用多播委托技术。

  事件一般用于通知用户进行某种操作的时机,而声明一个事件即声明了被封装了的委托类型的变量。

一个事件可以被多处订阅,而一个订阅者可以处理多个发行者的事件。

在系统中一般将事件用于同步线程。

  4.2多媒体定时器的引用

  工控系统对于定时有很高的要求,而一般的定时方法,比如采用Timer控件,其定时精度在有些情况下是不能满足需求的。

高精度定时一般采用多媒体定时器的方式。

若要在C#开发的系统内引用多媒体定时器需要调用系统API函数timeSetEvent、timeKillEvent、timeGetTime、timeBeginPeriod和timeEndPeriod,然后配合事件与委托来完成高精度的定时工作。

本文中将这些函数封装在类中。

  publicclassMMTimer:

IDisposable

  {

  //系统API

  \[DllImport("Winmm.dll",CharSet=CharSet.Auto)\]

  staticexternuinttimeSetEvent(uintuDelay,uintuResolution,TimerCallbacklpTimeProc,UIntPtrdwUser,uintfuEvent);

  \[DllImport("Winmm.dll",CharSet=CharSet.Auto)\]

  staticexternuinttimeKillEvent(uintuTimerID);

  \[DllImport("Winmm.dll",CharSet=CharSet.Auto)\]

  staticexternuinttimeGetTime();

  \[DllImport("Winmm.dll",CharSet=CharSet.Auto)\]

  staticexternuinttimeBeginPeriod(uintuPeriod);

  \[DllImport("Winmm.dll",CharSet=CharSet.Auto)\]

  staticexternuinttimeEndPeriod(uintuPeriod);

  \[Flags\]

  publicenumfuEvent:

uint{

  TIME_ONESHOT=0,

  TIME_PERIODIC=1,

  TIME_CALLBACK_FUNCTION=0x0000,

  }

  /回调委托

  delegatevoidTimerCallback(uintuTimerID,uintuMsg,UIntPtrdwUser,UIntPtrdw1,UIntPtrdw2);

  TimerCallbackthisCB;

  protectedvirtualvoidOnTimer(EventArgse){//定时调用某种处理

  TimeDoneEvent.Set();//比如设置事件信号

  ……

  }

  ……

  publicvoidStop(){

  lock(this)

  {

  if(id!

=0)

  {

  timeKillEvent(id);

  Debug.WriteLine("MMTimer"+id.ToString()+"stopped");

  id=0;

  }

  }

  }

  publicvoidStart(uintms,boolrepeat){

  Stop();

  fuEventf=fuEvent.TIME_CALLBACK_FUNCTION|(repeat?

fuEvent.TIME_PERIODIC:

fuEvent.TIME_ONESHOT);

  lock(this)

  {id=timeSetEvent(ms,0,thisCB,UIntPtr.Zero,(uint)f);

  if(id==0)

  {

  thrownewException("timeSetEventerror");

  }

  Debug.WriteLine("MMTimer"+id.ToString()+"started");

  }

  }

  voidCBFunc(uintuTimerID,uintuMsg,UIntPtrdwUser,UIntPtrdw1,UIntPtrdw2){

  OnTimer(newEventArgs());}

  使用时需定义一个MMTimer类变量,privateMMTimermulitTime=newMMTimer();开始定时调用mulitTime.Start((uint)iTimeElpase,true);停止定时调用mulitTime.Stop()。

  4.3多媒体定时器与线程的配合

  在需要定义轮询的模块部分定义privatestaticManualResetEventTimeDoneEvent;构造时以false为参数传入,如TimeDoneEvent=newManualResetEvent(false);表示其为一个手工设置事件。

然后定义轮询线程,并在其内调用事件的WaitOne函数等待事件信号被置位。

  privatevoidScanDevsFun()

  {

  while(this.SCANNING)

  {

  //等待定时器的时间到通知

  boolflag=TimeDoneEvent.WaitOne(GlobalItem.THREADWAITTIME,false);

  ……//进行设备操作

  TimeDoneEvent.Reset();//重设事件无信号

  }

  }

  5数据存储之线程池技术

  线程池是多线程处理技术的一个典型应用,处理中首先将工作任务添加到队列内,然后创建并启动线程执行池内任务。

线程池适合执行实时性要求不是很高、工作性质重复、工作之间无顺序要求的任务。

如在数据采集系统中,为了方便地绘制曲线,经常会以设备采集点为单位进行采集数据的存储。

而很多情况下一套采集系统的数据采集点可能会数以百计,在不适合采用数据库进行存储的情况下,采用一般的方法同时操作这么多的文件会出现数据丢失的情况,这时可采用线程池技术解决这个问题。

  采用线程池技术可以自行定义并实现,也可以采用系统线程池实现。

建议使用系统线程池,这样可以减少很多的编码和调试工作。

使用系统线程池需要首先定义工作函数,如:

  staticvoidWorkThreadFunc(WorkItemObjectWorktem){

  ……//工作项操作

  }

  然后定义工作项,并将其作为参数同工作函数一起抛入线程池内:

  WorkItemObjectwi=newWorkItemObject(DeviceID,ChanID,DataPack);

  if(ThreadPool.QueueUserWorkItem(newWaitCallback(WorkThreadFunc),wi)){

  Console.WriteLine("TheworkItemhasbeeninsertedintothreadpool.");

  }

  else{

  Console.WriteLine("UnabletoqueueThreadPoolrequest.");

  }

  6结语

  数据采集组件的设计对于一个涉及多种硬件设备的系统来说是至关重要的,只有灵活、高效的组件结构才能方便地对系统进行扩展。

本文所论述的技术经过了若干工程项目的验证,是有效的、稳定的架构方式,希望可助工控开发人员一臂之力。

  参考文献:

  [1]黄际洲.精通NET互操作[M].北京:

人民邮电出版社,2009.

  [2]王小科.C#编程宝典[M].北京:

人民邮电出版社,2011.

  [3]JeffreyRichter.Windows核心编程[M].北京:

清华大学出版社,2008.

  [4]孟凡姿.基于VC++工控组态软件的总体设计与实现[J].工业控制计算机,2008(6).

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1