|
|
运动控制卡发展越来越讯速,不同类型的控制卡亦多种多样,使软件项目经理具备越来越灵活的方案选择。而对于软件开人员,对不同的类型卡的性能测试工作无疑也越来越麻烦,通常各类型的控制卡其驱动库函数各不一样,都需要用户自己进行整理封装。本文的目的就是通过C++方式编程,探讨一下实现多类型卡编程方式,以供开发人员参考。
一、利用C++虚拟函数机制 除了手动去单个修改替换以前卡的驱动函数之外,此方法最易被C++程序员所想起,其方法是,首先需要建立一个通用的基类(父),然后不同卡封装的类通过重写基类的虚拟函数来实现,代码看上去大致如下: class CDviece//用户根据自己的设置建立的基类 { public: virtual int InitBoard(); //在此仅以初始化函数为例 … (略) };
class CDmc1000Card: public CDviece // 设该卡为雷赛的DMC1000卡 { public: virtual int InitBoard() { return dmc1000_init_board(); }//改进基类的InitBoard函数 …(其它改写略去) }
class COMS: public CDviece //设该卡为美国的OMS卡 { public: virtual int InitBoard() {
} …(其它改写略去) }
在程序中,若想设备使用DMC1000控制卡,可定义如下: CDviece *pMineDviece = new CDmc1000Card; 改为OMS控制卡则一样: CDviece *pMineDviece = new COMS; 而其它代码调用InitBoard函数可以不去改动,照常使用: if( pMineDviece->InitBoard() ) { …(Do s.th) }
使用此方法,需要根据当前设备配置情况,完整的写好CDviece的所有函数,同样,从CDviece派生的控制卡类,也需要将父类虚拟函数全部改写完毕,对函数返回值,参数都需要进行规范,同时,修改完成之后,将整个软件工程全部编译一次。 利用虚拟函数方法,会带来虚拟函数表的成本开销,随着CDivece需要的函数增加,其成本会相应的增加,事实上,MFC的消息机制就是以代码的方法实现了虚拟函数的机制,只不过虚拟函数的处理是通过C++编译器来完成的。 并且,CDviece的需求发生变化时,其派生类的函数相应的也要发生变化,这一点是程序员需要注意的。
二、利用DLL动态库实现 从COM组件编程过来的人,很容易想到DLL的实现方案。看重这一点是的只要主程序框架写得正确,改变DLL即可实现主程序不编译,即可获得不同类型卡的选择。 事实上,这是一种美好的愿望,要实现起来并不容易,而且很需要程序员有熬夜的精神。大致伪代码实现如下:
class CDviece { private: HANDLE dllHandle; public: typedef int( *P_InitBoard)(void ); P_InitBoard InitBoard; …(其它所有定义及声明略去)
int InitFunction( char *dllFileName )//调用DLL文件 { dllHandle = LoadLibaray( dllFileName ); InitBoard = (P_InitBoard)FindFunction(dllHandle, “Init_Board”); … (其它略去) } } 由于不同卡的函数名都不一样,故需要程序员按照CDviece所需函数进行另一DLL的编写,若需要DMC1000控制卡时,则需要生成一个MDMC1000.dll如下:
int InitBoard() { return dmc1000_board_init(); } …(其它略去)
生成OMS控制卡的MOMS.dll如法炮制: int InitBoard() { } …(其它略去)
在代码中调用DMC1000控制卡,使用如下: CDviece mineDviece; mineDviece.LoadLibaray(“Mdmc1000.dll”); if( mineDviece.InitBoard() ) { …(Do s.th) } 使用OMS控制卡如下: mineDviece.LoadLibaray(“Moms.dll”); …(其它略去)
看似简单,实质上略去的部分将是程序员的一场恶梦,想一想那一大堆的定义和声明,一碰到CDviece的变动,哪怕是最小的函数返回值或参数变动,则其它相应的所有DLL都需跟全部编译。这无疑给软件项目的整个维护带来极大的难度。
三、利用模板类实现 探讨到现在,本人比较推荐的就是使用此方法,它较虚拟函数相比无额外成本的开销,更不有因为虚拟函数带来的效率问题。 与DLL方法相比,定义声明,及变动性都非常良好,整个维护的成本也较低。 其编程特点即是,通过模板定义一个空壳类,然后根据需要可以邦定不同类型的控制卡。其伪代码实现方法如下:
//定义一个空壳类 template class CDviece: public CARD { public: CDviece(){} ~CDviece(){} }//简单吧,了无秘密可言
以下定义DMC1000封装类: class CDmc1000Card //无需继承 { public: int InitBoard( void ) { return dmc1000_board_init(); } …(其它略去) }
以下定义OMS封装类: class COMS { public: int InitBoard( void ) { } }
在程序中应用时,调用DMC1000控制卡如下:
typedef CDviece DEVIECE; DEVIECE mineDviece; if( mineDviece.InitBoard() ) { …(Do s.th) } 当然,DEVIECE的定义也可以如下形式: class DEVIECE: public CDviece { public: DEVIECE(){} ~DEVIECE(){} } 调用OMS控制卡如下: typedef CDvieceDEVIECE; …(其它同上)
可以看到,此方法还是让人感到兴奋的。即使参数或声明发生变化或忘记定义,那就让编译器来查错吧! 顺带提一下,由于控制卡封装时,总有一些数据结构是通用的,对此解决方法也很简单,实现如下: struct tag_CARD { …(定义通用数据,如每转脉冲数,最大速度值,行程范围等等之类) } 再改变一下CDmc1000Card如下形式: class CDmc1000Card: public tag_CARD //其它不变,OMS的定义同理
四、设立一个卡类型变量 此方法最为简单,缺点是需要同时链接全部的控制卡驱动库函数,且在程序安装时,还需要考虑各种控制卡的驱动程序或其相应的动态库是否存在。其伪代码形式下如: enum{ NOCARD=0,DMC1000=1, OMS=2 …}; class CCtrlCard { public: int m_nCardType;//记录控制卡类型变量
CCtrlCard():NOCARD(0){} ~CCtrlCard(){}
int InitBoard( int nCardType ) { m_nCardType = nCardType; switch( nCardType ){ case DMC1000: if( d1000_board_init() <= 0 ) m_nCardType = NOCARD;//初始化失败 else{ } break; case OMS: … break; default: m_nCardType = NOCARD; } return m_nCardType; } //其它省略 };
调用时伪代码形式如下: CCtrlCard card; if( card.InitBoard( DMC1000 ) != DMC1000 ) //初始化失败 else //找到控制卡
|
|
状 态:
离线
公司简介
产品目录
|
|
公司名称:
|
深圳市雷赛智能控制股份有限公司
|
联 系 人: |
梁邦敏
|
电 话: |
755-26401178
|
传 真: |
|
地 址: |
深圳市南山区登良路天安南油工业区2栋3楼 |
邮 编: |
518000 |
主 页: |
|
|
|
|
|