电子测量仪器通用绘图模块设计
2018-04-18郑恢康
郑恢康 朱 伟
(中国电子科技集团公司第四十一研究所 山东 青岛 266555)
0 引 言
目前很多电子测量仪器,如示波器、频谱分析仪、矢量网络分析仪等,其测量结果以曲线形式进行显示。这些仪器软件通常包括数据采集、数据处理、数据显示3部分。动态波形的绘制是测试仪器中极为重要的一环[1]。其用于实现数据显示的绘图模块具有较多共通性,含有窗口、多迹线、信息显示、光标等要素。但因绘图模块和系统其他部份相互关联(例如:绘图模块调用采集模块获得测量数据),且以源码的形式嵌入整个系统中,使得绘图相关的代码难以复用[2]。
针对上述设计的不足,本文从通用性和复用性的角度出发,应用面向对象的组件化软件设计方法,设计并实现了一种通用的绘图模块。该模块基于动态链接库(DLL)技术,方便地实现复用、集成和扩展,满足测量仪器领域的绘图需求,提高软件开发效率。
1 通用绘图模块总体设计
通用绘图模块是在Windows下使用Visual Studio 2008开发环境, C++语言开发实现的,它基于MFC类库实现图形绘制功能。因此,需在开发环境中创建一个“MFC常规DLL”工程,最终经编译得到Dll和Lib文件,与相关的头文件一起提供给用户。
设计通用绘图模块有两个关键点:一是避免模块依赖于软件系统,使模块独立;二是支持扩展绘图,满足特殊的绘图需求。
为了使模块独立,模块应设计成由上层配置、调用、启动的方式,而减少模块对软件系统的调用[3]。另外,应用面向对象编程依赖倒置的原则[4],使系统与模块之间不直接依赖于具体实现,而是依赖于接口。
为支持绘图扩展,应用面向对象编程开闭原则,在模块内实现共通的、基本的绘图功能,其他绘图则通过绘图组件的方式进行扩展实现,使模块向修改关闭,向扩展开放[5]。
对于电子测量仪器领域,基本绘图任务就是曲线的绘制,另外该领域通常以光标的方式进行测量数据的读取与显示,这两部份可作为基本绘图需求在模块内实现。而系统功能中涉及到的一些特殊绘图需求,例如提示信息、参数设置框等,可透过绘图组件的形式来实现。
基于以上分析,定义以下主要类:
(1) 绘图接口类CGraphInterface绘图模块对外提供的访问接口。
(2) 绘图类CGraph实现对迹线、光标、绘图组件的管理。
(3) 绘图组件接口CGComponentInf用户继承该接口定制绘图组件,并将指针传给绘图类,由绘图类进行绘制。
(4) 迹线类CTrace实现迹线数据及网格等的绘制。
(5) 光标管理类CMarkerManager实现光标功能,用于绘制光标和光标信息,显示测量数值。
这几个主要类之间的关系如图1所示。
图1 绘图模块UML类图
另外,对于测量仪器,通常要绘制动态迹线,为了避免绘图时闪烁,可采用双缓冲绘图技术[6],在CGraph类中实现。
2 各功能模块设计
2.1 迹线绘制模块
该模块完成测量迹线、坐标网格、刻度标尺等的绘制,将这些视为迹线的属性,封装为CTrace类。
2.1.1CGraph类相关设计
为支持多迹线的绘置,在CGraph类组合CTrace类的成员变量,CGraph内部调用CTrace接口进行迹线相关图形绘制,CGraph对外提供相应的接口进行迹线相关配置。
在CGraph中定义成员变量:
CList
在CGraph中定义迹线操作接口:
(1) 添加迹线void AddTrace(CArray
其中data为传入的绘图数据,nTrID为上层为该迹线分配的ID,Graph其他接口通过该ID对指定迹线进行相关操作。
(2) 删除迹线void DeleteTrace(int nTrID);
(3) 设置迹线刻度void SetTraceScale(double fTop,double fBtm,int nTrID);
(4) 选择当前迹线void SelectCurTrace(int nTrID);
2.1.2CTrace类设计
CTrace类主要实现测量的数据坐标转换,以及网格、标尺相关的绘制。
CTrace类定义成员变量,对原始数据和转换后的绘图数据进行存放:
CArray
CArray
为支持对多种的坐标格式,应用面向对象编程多态技术[7-8],将与坐标、及坐标转换相关的函数定义为虚函数,在CTrace派生类中实现。
virtual void DrawGrid(CDC* pDC) = 0;
virtual void DrawScale(CDC* pDC) = 0;
virtual CPoint CalcOnePoint(double fXVal,
double fYVal) = 0;//坐标转换
2.2 光标功能模块
该模块用于读取迹线上的数值。包括光标状态及位置的设置、光标位置的绘制、光标信息的绘制,封装为CMarker类。为支持多个光标,定义CMarkerManager类对一组光标进行管理。由CGraph类集合CMarkerManager类的成员变量,并由CGraph调用CMarkerManager相应接口实现光标功能。
2.2.1CGraph提供的光标功能配置接口
(1) 设置光标显示状态void ShowMkr(UINT nMkrID,BOOL bOn);
(2) 设置光标位置void SetMkrPos(UINT nMkrID,UINT nPos);
(3) 光标信息接口void SetMkrInfoInterface(CMkrInfoInterface* pInf);
由于光标信息的格式是因项目而异,因此定义通用的光标信息接口,由CGraph的使用者进行实现,并配置给CGraph。绘图模块在绘置光标信息时,调用该接口,以获得要显示的光标信息。
CMrkInfoInterface定义接口:
void GetMkrInfo(
UINT nMkrID, //光标ID
UINT nMkrPos,//光标位置
double data[2], //数值
int nTraId, //迹线ID
CStringArray& infoAry, //光标信息
CWordArray& widthAry//光标信息区域划分
);
2.2.2CMarkerManager类设计
该类定义一组的光标类CMarker成员变量,并提供对指定光标进行设置的接口。
CMarkerManager定义成员变量:
static enum {MKR_NUM = 6};//光标个数
CTrace* m_pTrace; //关联迹线
CMarker m_mkrs[MKR_NUM]; //光标
CMkrInfoInterface* m_pInfoInf;// 光标信息接口
CMarkerManager提供的接口主要有:
void DrawMkrs(CDC* pDC);
void DrawMkrsInfo(CDC* pDC,CRect& rect);
void AssociateToTrace(CGTrace* pTrace);
void SetMkrInfoInterface(CMkrInfoInterface* pInf);
2.2.3CMarker类设计
该类实现对单个光标位置、状态、绘制的封装。
CMarker类定义成员变量:
BOOL m_bOn;//光标状态
UINT m_nPos;//光标位置
CMarker类提供的接口主要有:
void DrawMkr(CDC* pDC,CPoint pt);
void DrawMkrInfo(CDC* pDC,
CArray
CStringArray& strInfo);
void SetPos(UINT nDataIdx);
2.3 信息区配置
该模块用于配制绘图区顶部和底部的显示信息。由CGraph类实现信息区的划分,通过位置索引的方式来确定显示的位置,在CGraph类中实现对该信息的绘制。
定义结构体对信息内容和位置进行绑定:
typedef struct _XGraphInfo
{
CString strText; //信息区显示内空
UINT nRowIdx; //信息区位置横向索引
UINT nColumIdx;//信息区位置纵向索引
}XGraphInfo;
在CGraph类中,定义下列成员变量存放顶部和底部信息区划分:
UINT m_nTopInfoRowNum;
UINT m_nTopInfoColumNum;
UINT m_nBtmInfoColumNum;
UINT m_nBtmInfoColumNum;
在CGraph类中,定义下列成员变量存放顶部和底部信息配置:
CArray
CArray
在CGraph类中,定义下列接口进行信息区相应的配置:
void DivideTopInfoZone(UINT nRow,
UINT nColum);//划分顶部信息区
void DivideBottomInfoZone(UINT nRow,
UINT nColum);//划分底部信息区
void SetTopInfo(UINT nRowIdx,
UINT nColumIdx,CString str);//设置顶部信息
void SetBottomInfo(UINT nRowIdx,
UINT nColumIdx,CString str);//设置底部信息
2.4 绘图组件化扩展
绘图模块以绘图组件化扩展的方式来支持使用者在绘图区自绘制图形。在Graph中定义队列成员变量,对绘图组件的指针进行存储:
CList< CGComponentInf *, CGComponentInf *> m_pComponentList;
2.4.1CGraph提供的绘图组件配置接口
void AddComponent(
CGComponentInf* pGComponent);//添加组件
void RemoveComponent(
CGComponentInf* pGComponent);//删除组件
2.4.2绘图信息接口类CGInfoInf
绘图组件需要知道绘图区域划分情况,因此定义绘图信息接口类,提供以下接口:
virtual CRect GetGridRect()=0;//获取网格区
virtual CRect GetGraphRect()=0;//获取整个绘图区
virtual CPoint CalcGraphPoint(double fXValue,
double fYValue) = 0;//数值转换为绘图点
这些由CGraph实现,并将指针赋给绘图组件。
2.4.3绘图组件接口类CGComponentInf
为了支持在基本绘图不同纵向位置进行绘制,将基本绘图分为背景层、网格层、迹线绘制层。绘图组件接口类提供不同在绘图层中进行绘制的接口:
virtual void DrawUponBk(CDC* pDC,
CGInfoInf* pGInfo);//在背景层之上绘制
virtual void DrawUponTrace(CDC* pDC,
CGInfoInf* pGInfo);//在迹线层以上绘制
virtual void DrawUponGrid(CDC* pDC,
CGInfoInf* pGInfo);//在网格层之上绘制
以上接口由派生类实现,在CGraph中调用以实现组件绘图。
3 绘图模块的DLL封装与导出
对于使用该模块进行开发的用户而言,无需知道上述主要类的实现,只需知道CGraphInterface、CGComponentInf的定义。但模块需对外提供CGraph类对象的创建函数,该函数返回CGraphInterface类型指针。因此,在工程中新建GraphDll.h、GraphDll.cpp,在GraphDll.h中定义该输出函数,在GraphDll.cpp中实现该函数。
4 应用实例
该绘图模块已应用在某微波综合测量仪器中,该类型仪器含有:矢量网络分析仪器、频谱仪、矢量电压计、功率计等多种仪表,透过使用该模块的基本绘图功能和组件化绘图扩展方式,成功地满足了该项目的绘图需求。如图2-图4所示,证明该模块具有较好的通用性。
图2 使用基本绘图功能绘制迹线
图3 使用绘图组件实现参数设置标签绘制
图4 使用绘图组件实现不同绘图层的绘制
5 结 语
本文设计了一种应用于电子测量仪器领域的通用绘图模块,给出模块的总体设计,各功能模块的数据结构以及接口设计。该绘图模块可实现基本的曲线绘制功能,支持以绘图组件的方式进行实现自定义绘图。经过测试, 表明具有易维护、通用性强等优点, 可满足电子测量仪器的图形绘制需求。
[1] 杨乐,王厚军,戴志坚.测试仪器中的动态波形绘制技术[J].仪器仪表学报,2006,27(S2):1159-1160.
[2] 王泽,朱金伟,曲政.基于COM技术的通用绘图模块的构建[J].计算机与数字工程,2006,34(8):158-160.
[3] 王湘文,陈建伦,陈纪铭.分层软件架构设计及其应用研究[J].福建电脑,2011,27(6):55-56.
[4] 高松,牛治永.敏捷设计原则与设计模式的编程实践——单一职责原则与依赖倒置原则[J].计算机应用,2011,31(S2):149-152.
[5] 郭荣.浅谈软件设计模式中的设计原则[J].信息安全与技术,2015(11):5-6.
[6] 王强,汤小慷,谢存,等.基于GDI与双缓冲技术的雷达PPI显示器的仿真[J].科技视界,2016(12):123-123.
[7] 周宇,张立群.面向对象技术的多态性研究[J].电脑与信息技术,2006,14(6):16-19.
[8] 李明明,管志伟.浅析C++多态的作用及实现原理[J].无线互联科技,2014(7):116-116.