现在的位置: 首页 > 综合 > 正文

vega程序设计

2012年04月16日 ⁄ 综合 ⁄ 共 4736字 ⁄ 字号 评论关闭
2)vega应用程序的主框架
Vega编程类似于C编程,实际接口是C,包括完整的C语言应用程序接口,为软件开发人员提供最大程度的软件控制和灵活性。对于Windows NT平台上的Vega应用,主要有三种类型:控制台程序、传统的Windows应用程序和基于MFC(Microsoft Foundation Classes)的应用。但无论是哪一种应用,建立Vega应用的三个必需的步骤为:
1 初始化:这一步初始化Vega系统并创建共享内存以及信号量等;
2 定义:通过ADF应用定义文件创建三维模型或是通过显式的函数调用来创建三维模型;
3 配置:通过调用配置函数来完成配置设置完Vega系统后,就开始了Vega应用的主循环,主循环的作用是对三维视景进行渲染驱动。它主要分两步,对于给定的帧速进行帧同步和对当前的显示帧进行必要的处理。
以下显示了一个最小的Vega应用程序:
main(  ) {  
vgInitSys();                //初始化
vgDefineSys(myapp.adf);     //定义
vgConfigSys();           //配置
while(bContinuing) {   
vgSyncFrame();  //同步帧   
vgFrame();   //帧内处理   
//application specific code  }
这就是一个控制台的应用程序。但是vega只是一个包含十几种不同模块的函数集,并没有窗口函数(虽然Vega函数库中提供了一些窗口和事件管理的函数,但这些函数在实际应用中还远远不够),它缺乏面向对象能力,不符合当前流行的软件设计思想,因此需要一个窗口系统来完成Vega实时仿真的程序设计。而且在Windows平台上开发,那么具有良好图形用户界面的应用程序将更受到欢迎。对于基于窗口的应用来说,Vega系统通过提供一个窗口初始化函数调用来完成,即只要把上述的初始化函数vgInitSys( )替换成vgInitWinSys()即可。该函数通过获得窗口句柄来初始化Vega的显示窗口。
3)基于MFC的Vega应用的程序结构
考虑到Vega函数是用C++语言写的以及在Windows平台上进行开发,所以VC++作为开发工具成为首选。在VC++中的MFC类库已是一个相当成熟的类库,特别是其基于文档/视结构的应用程序框架,已成为开发Windows应用程序的主流框架结构。该框架结构能够将程序中的数据和显示部分进行有效的隔离,并能将一个文档与多个视进行对应。这种设计方法在设计模式中被称为观察者模式(Observer)。它是一种对象行为型模式,定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并自动更新。
为了便于开发者能较容易的开发出基于MFC的Vega应用程序,Vega通过继承MFC中的CView类而派生出一个子类zsVegaView。这个zsVegaView类提供了启动一个Vega线程最基本的功能,还以虚函数的形式定义了特定的应用要进行操作的通用接口,因此用户的应用程序只需从zsVegaView派生出新类并根据需要重载必要的虚函数即可。从设计模式的观点来看,它是采用了模板方法模式(Template Method)。模板方法模式是一种类行为模式,其意图是定义一个操作算法的骨架,而将一些具体步骤延迟到子类中去,这是代码重用的一种基本技术。模板方法导致了一种反向的控制结构,即指的是一个父类调用一个子类的操作,而不是相反,它的实现依赖于对象特性中的多态性。
1: 基于MFC的Vega应用的线程分析
通过采用模板方法将用户开发一个基于MFC应用的工作量减少到最低,这是Vega系统提供的方法,其初衷是好的,但由于MFC类库并不是一个支持多线程访问的类库。用户派生的子类通过在OnInitialUpdate(  )函数中调用基类的runVega(  )来启动Vega线程,并将派生类的指针作为参数传给新启动的子线程,这恰是问题的所在。由于源代码中的类空间和线程运行时的线程空间是可以相互交迭的,也就是说不同的线程在运行时可以访问相同的类的实例——对象,而MFC类库本身设计时并未考虑到多线程访问,因此在线程间传递视类CView(根据“ISA” 规则,它的子类对象也是一个CView对象)将是危险的,实践也证明了这一点。在单文档多视中在Vega线程中改变文档数据后通过调用函数UpdateAllViews(  )来更新所有相应的视图时旋即出现了访问保护异常的错误。 回头看一下前面的设计,用类CView的派生类来嵌入与Vega功能有关的功能代码,而这些功能代码中主要是启动Vega的三个主要步骤,模型渲染主循环和便于用户扩展的几个虚函数接口,其对于CView的关联仅仅是使用了CView类所拥有的视窗口的句柄而已。这种设计其实违反了软件设计的基本准则:高内聚,低耦合。为了既能保持由模板方法设计带来的好处,又能达到高内聚、低耦合的目标,最好的方法就是进行切割分离。将所有与Vega系统有关的数据和操作单独作为一个基类CVega来实现,并保持用户的扩展借口。这样用户只需从CVega类派生出自己的类并将视口的句柄作为参数进行传递就可以了。那么当Vega线程改变数据后如何通知各个视口及时的进行更新呢?好在Windows平台的消息驱动机制,使得可以利用视口的句柄发送消息来实现。
2基于MFC的Vega线程中的问题
Vega应用程序可以通过应用定义文件(.ADF)来载入三维场景模型。在实际开发的应用中,当用户打开应用程序时,在不退出该应用的前提下进行三维场景切换是十分必要的。从Vega的应用程序框架中我们可以看出,框架主要由它的配置三步曲和渲染主循环构成。当用户已经打开一个场景后需要切换到另一个场景时,程序正处于渲染主循环中,本应只需用配置三步曲中的第二步导入另一个.ADF文件再进行第三步设置或三步曲皆重新作过再进入渲染主循环即可,遗憾的是由于某些未知的具体原因(并非程序结构的实现技术问题)而行不通。由于Vega系统提供的是链接库形式的函数调用,没有源码,从设计软件系统的角度来看,这种为了保证整个系统的完整性,系统外部接口的一致性,系统内部更新的独立性以及系统内部实现技术的保密性等而进行的封装是完全有必要的。然而在保证以上特性的前提下,给开发用户提供必要的诊错手段也是必不可少的。在这方面Windows的API调用以及MFC类库等做的十分好,它们在许多操作的结果通常都返回一个值,以告诉用户是否成功或是失败还是出现异常,更完善的是Windows系统提供了一个全局的函数调用GetLastError(  )操作来获取每一步操作后的结果是否成功。从设计模式的观点来看,这是使用了单件模式(Singleton),它使得在程序的任何位置都能直接访问到该函数。而在Vega系统里却没有提供如此完善的诊错手段,它的大部分操作没有返回结果,因此用户只能认为此次操作成功了。然而一旦程序出现了问题,用户将会不知所措。而且,由于Vega将类的接口定义都“封装”起来了,用户所能获取的仅仅是一个可用来充当句柄的类的指针而已,一些与面向对象有关的继承、多态、消息操作[4]等皆无明显体现,这大概与Vega旨在最大限度的兼容现有程序的移植目标有关吧。
若在Vega线程未退出时进行重新配置将导致失败,那么在切换场景时让正在运行的线程自然终止而重新开启另一个线程应该不会有错了。然而结果是令人沮丧的,出现一个地址访问保护的错误。导致这种错误的主要原因是引用的无效的指针,该指针所指的空间要么是分配空间后已被释放,要么是指向其它线程的局部地址空间。为什么会出现这种错误呢?在第二次初始化Vega系统时,前一个Vega线程不是已经终止了吗?所以一定存在遗留问题。经调试跟踪显示,问题就出在Vega配置三步曲的第一步vgInitWinSys()上,该函数的作用主要是初始化Vega系统并创建共享内存以及信号量等,除此之外它还在后台做了一件重要的事情——就是又开启了一个Vega窗口子线程,该子线程根据传送的窗口句柄参数创建一个与该句柄对应窗口一相同大小的窗口二,并将它蒙(覆盖)在窗口一上,这是Vega开发指南上未有提及的,这样Vega系统的渲染窗口就可以嵌入到基于MFC的视口上了。该子线程的创建是必不可少的,但却没有终止它的办法——它是Vega函数内部创建的。又因为线程隶属于进程,只有进程结束了,所有的线程才全部退出。所以即使创建它的父线程已死,它却还“活”着,不过因此而保留了一些无用的参数如无效的指针等。而更糟的是当第二个线程启动时,函数vgInitWinSys()却不再启动新的子线程而是继续保留原来没死的窗口线程,因此错误的发生是注定的。
由Windows线程的一些特性可以得出解决问题的有效方法就是进一步隔离——用一个单独的Vega进程来实现。从模式的观点来看,这可以作为一个新的设计模式应用于软件的设计当中去,不妨称之为栅栏模式(Fence)。它通过将同一个应用中的不同部分进行相互黑箱化,彼此之间仅通过定义好的接口进行访问,这使得各部分之间的相互影响减少到最低限度。从操作系统平台考虑,Windows不是一个实时操作系统,但却是一个较稳定的系统。由于FVSS系统是一个实时的虚拟仿真系统,在它的视景驱动模块中将处理大量的实时仿真结果数据,如何保证该模块中的两个进程之间能快速有效地进行通信将成为问题。
1 Windows NT上的进程间通信
在Windows NT平台上,进程间通信的主要方式有:动态数据交换(DDE),网络动态数据交换(NetDDE),Windows套接字(Windows Sockets),命名管道(Named Pipes),内存映射文件(Memory-Mapped Files),NetBIOS,远程过程调用(RPC)以及磁盘文件等。在特定的场合下,应选用适当的通信方式以最佳地满足应用性能上的要求和便于功能上的实现及扩展。在视景驱动模块中,由于Vega进程和MFC主进程将运行在同一台PC上,所以主要应用于网络环境中PC间的进程间通信的NetDDE、Windows Sockets、NetBIOS以及RPC皆不予考虑。而通过磁盘文件来进行数据交换显然是行不通的,这使得直接在内存中进行通信成为必要。因为在Vega进程和MFC主进程间除了要传递大量的视景驱动数据外,还要进行一些控制信息的传递,鉴于实现的快捷性、进程通信速度上的性能要求以及便与功能的扩展等方面的综合考虑,采用内存映射文件的方式将是最佳的。
3 基于MFC的Vega应用的进程实现
在Windows NT平台上采用文件映射对象,使得Vega进程与MFC界面主进程之间能够进行有效的数据交换。图5显示了视景驱动模块中两个进程实现的流程图,图中略去了MFC主进程中的用户界面控制部分。 因为分离出来的Vega进程是一个控制台程序(尽管它还创建了自己的渲染窗口),所以它没有消息泵,不能通过消息机制响应应用要求。而Vega应用框架中的渲染主循环恰恰可以插入一些操作用来模拟一个消息泵,以完成MFC的主进程的要求操作,这个被传递的消息同样可以放在文件映射对象中。反之,由于MFC主进程是一个窗口应用,它本身具有接收消息的功能,因此只需直接向其发送Windows消息即可。图6显示了实现两进程间主要功能的内存映射文件中的数据结构。至此,通过Windows内存管理中的文件映射对象,不仅解决了两个进程间的共享数据和传输数据的速率问题,还提供了Vega进程一个虚拟的消息泵来完成主进程的要求操作。

抱歉!评论已关闭.