1、通常一个 VI 若包含三、四十个以上的 subVI(不包含 LabVIEW 本身在 Functions 中提供的VI)时,就可算是一个中大型的软件计划 (software project)了。虽然比起软件工程中的一些作业环境软件(如 Windows 系列 )或大型应用软件(如 Word、Excel)等仍算是小工程,但其复杂性亦在一定程度之上,若没有事先想好在撰写程序时的一些规划与方法,想要完成这类中大型的软件绝对不是一件简单的事。尤其这类软件通常不是由一个人,而是由一个团队所共同完成的,因此整个软件的结构,就要能让团队中的每一成员都能清楚的了解,而且要够简单,才算是好的软件结构。以下将参考由
2、Rick Bitter 等人所着”LabVIEW Advanced Programming Techniques”,中之第4 章的部分内容,介绍所谓软件计划中的三层式结构(the Three-Tiered Structure)的概念及其优点。 需要软件结构的主要原因,是当软件人员发展软件到某一阶段时,若没有计划或无意的创造了许多 subVI,但各 subVI 之间有许多部分其实是重复撰写的;或各 VI 相互间呼叫时没有一定的纪律,使得在 VI Hierarchy 中所看到的各 VI 间的联机是错综复杂,像个盘丝洞一般,这将可能会使多人发展的软件计划增加所耗费的时间和可能出错的机会、减低程序的效
3、率,以及增加 debugging 时的困难。为了改善上述的情形,所以要提倡三层式结构的概念。三层式结构由上而下依次为:Main Level、Test Level 和 Driver Level,这种结构是由经验中得来的,在多人发展的软件计划中显得简单明了,当大家都能遵照这个结构来写程序时,这种结构就可以充分显现出它的优点。那这三个阶层到底如何区别呢?以通俗的比喻来说,假设我们如果要组织一个篮球队参加全国比赛,每个球员要练习基本动作及体能,如何跑、如何跳、手脚该如何放置才是正确位置等,这就相当于系统中 Driver Level 所做的事情;接下来,将各球员组合练习某一套防守或进攻的战术,如二三区域
4、联防、人盯人防守,每个人该在什么位置才能正确接应等,则像是 Test Level 中一项项的 test 了;而最后比赛时,场上的战略运用,包括何时要用什么战术组合、如何更换球员、何时喊暂停、终场前是不是要故意犯规或采拖延战术等等,对照过来,就像是在 Main Level 中,如何将 Test Level 中各test 做最有效能的整合与排列组合等的工作。简单来说,Driver Level 包含了程序与所有仪器、组件、马达或其它应用软件的沟通、控制等较低阶的事情,使其可完成某一项基本的动作,例如初始化、马达走到 home 位置、雷射以设定的能量及频率发射光束等。可注意到我们在这边所说的 driv
5、er,并不像一般在别处所称驱动程序的那种 driver 那么低阶,真正最低阶的工作还是要有现成的 VI 来帮忙才行;在 Test Level 中,则是如何连接各个 Driver VI 的基本动作,使可做完出一套连续、有意义的流程,来执行某项测试,例如让手臂由 A 点走到 B 点,下降夹取一个螺丝,再走至 C 点装到某面板上,然后回到 A 点等待,类似这样控制一个流程的进行,便是 Test VI 的工作内容;Main Level 则包含了使用者接口 (User Interface)或称人机接口(Man-Machine Interface) ,目的是整合各项测试和例外处理(Exception Ha
6、ndling)等,将它们以适当的顺序及流程组合,很容易地让使用者操作。当一个软件计划严格的遵照上述的三层结构来撰写时,最大的优点是可使程序代码的再使用(code reuse)达到最大化,在不同的 Test VI 中,可重复使用相同的 Driver VI;而在不同程序的 Main Level 中,又可重复使用相同的 Test VI,这将使得程序维护或修改的时间与精力大幅减少;同时当我们已有一个程序的样板(template)后,可增加软件版本更新的速度。另一个很重要的好处是,当我们在撰写某一个 level 中的程序时,并不需要关心在另一个level 中有什么其它的程序是如何执行的,而只要专注在自己
7、的这个 level 的程序上就可以了,这使得由团队来共同完成一个大型计划的工作变得容易许多。以下将依 Driver Level、Test Level、Main Level 的顺序,来介绍在各 level 写程序时的原则与心得(1) Driver Level通常在 Driver Level 所写的程序是比较低阶的,其功能是把一些最基本的 I/O 动作按某一顺序连接起来,形成基本动作,大致分为三大类a. 组态(configuration) :开启或关闭与仪器的连结、将仪器初始化及设定组态等。b. 量测(measurement) :由仪器读出测量值或特定的资料。c. 动作状态(action/stat
8、us):一个流程的启始或结束动作、检查错误等。简单来讲,Driver Level 的内容是藉由一些接口(卡) ,经 digital I/O 或 analog I/O 由仪器取得上一些 sensor 的电压值,或传出一些电压值来控制机器、仪器或马达等的组态与动作,使得仪器或机器能够正确设定及做出一些基本的动作。另外程序与仪器间的资料传递或在自身计算机的存取动作,规定档案的格式以及存取等等,也都属于 Driver Level 中应该完成的事项。但是真正最低阶的工作还是需要 NI 提供的 VI 来进行,我们所写的 Driver VI 仍是属于较高阶的。以下举一个利用7344卡来控制马达动作,而使 B
9、M7移动的 Driver VI 实例首先要确定马达所有的动作有哪些,我们可简单分类如下初始设定、找寻 Home 点、移到指定位置、以及回报现在位置等。当然我们必须先在 MAX 上做细部的设定,接下来再回到LabVIEW 中来撰写 Driver VI。看起来我们必须写四个 Driver VI 来控制马达,但这样往往造成 subVI 过多的情形,因此我的建议是将关于同一个仪器或装置的 Driver VI 合并为一个。合并的方法为利用一个enumerated type 的 control 加上 case 结构即可。完成后如下图利用名为 Action 的 enum control 来控制此 Drive
10、r VI 要进行何项动作。记得在编辑 icon 时,要将此 enum control 加到 input terminal 即可。在 Test Level 中呼叫时的方法就可如下图所示了如此一来,我们便可以利用一个 subVI 来完成数个动作了,减少了一些管理 subVI 的辛苦。但请注意,这样的 Driver VI 要看情形而在其 File VI Properties Execution 中设定为reentrant,因它很有可能同时被数个 Test VI 呼叫。在大型程序计划发展时,一个观念很重要:Driver Level 中的 VI 是整个软件计划中最重要的部分,它们的好坏直接决定了这个软件
11、计划的成功与否和完成后的品质,就像要盖高楼大厦,打地基、灌水泥以及基本用料的好坏才是决定建筑物好坏最重要的地方,若基础的部分不好,无论大楼外观有多雄伟或多漂亮,假使地震一来,都有可能会倒塌。Driver VI 若不先规划,往往会造成程序效率减低,以及重复的程序代码过多等现象。虽然在最后主程序执行时并看不到 Driver VI,但它们可是最重要的无名英雄,所以在设计 Driver VI 时需要多费点心思喔!(2) Test Level:在 Test Level 中的 VI 此处暂称为 Test VI,它可以呼叫 Driver Level 的 VI,但只能被 Main Level 中的 VI 呼叫
12、。有一个不成文的规定:Test VI 间不可以互相呼叫;否则会使三层结构又被破坏了。可是在写程序时,有时又觉得难以避免,或为了图方便想说就暂时先呼叫一下吧,若有这情形发生,那就要请重新检视一下 Driver Level 中的 VI,是否要重写某些部分或在写一些新的 Driver VI,以避免上述的情形发生。有一个重要的原则,一个 Test VI 内只要安排一套完整的测试即可,不要在同一个 Test VI中去完成两个(以上)的测试,否则未来若整个计划要作修改时, Test VI 可能就又要修改了。一个完整的 Test VI 当然要包含对仪器设备的初始化,组态设定等,它是一个可以单独执行的 VI,
13、也就是说,即使此 Test VI 不放到 Main Level 之下,它也一样可以单独执行来完成一项测试。另一个重要观念是,各个 Test VI 间是不要有什么关联的,因为当在 Main Level中的某个 Test VI 执行时,它并不确定前一个 Test VI 结束时的机器状态是否合于要求,因此要重新设定,或是要重新检查一下,以避免不能执行或有预料外的状况发生。流程图对于我们来撰写 Test Level 中的 VI 是特别有用,因流程图的概念也正好就是LabVIEW 中所谓 data flow 的概念,因此当一项测试的流程图清楚的画出来并能解释其流程时,即使我们还没有开始写程序,我们几乎可
14、以说这个 Test VI 的程序设计已完成60% 以上了,这一点也不夸张,因剩下的部分只是将流程图中各方块的连接,换成 LabVIEW 中各function VI 或 Driver VI 的连接而已。(3) Main Level:Main Level 又称人机接口(Man-Machine Interface, MMI),设计 Main Level 程序的中心观念是不仅要能完成测试外,而且操作上要越能 user-friendly 越好,因为当使用者在操作仪器设备时,他其实并不见得很关心细节的部分是如何运作的,他或许只希望能很轻松愉快的尽快完成工作,然后轻松愉快的下班回家。例如,使用者希望手臂能够
15、走到某特定位置去夹取一个螺丝,最好是按下某个屏幕上的按钮就好了,只要看着屏幕上一切正常的讯息,说不定他还可以有时间悠闲地喝杯咖啡呢!通常 Main Level VI 的设计往往利用 while 循环不断的 polling,大部分的时候也不只一个while 循环。其内容要包含几个重点:a) 可让操作者设定或更改操作参数:例如可选择何项测试及执行顺序、接口的地址、档案的路径等等,但也请注意,需设定的选项并非越多越好,太多的选项容易使人分散注意力而容易出错。b) 在特定的情况下使用适当的 Control:有时 Control 需加些心思来点变化,以表示其不同的重要性,最简单的当然是以大小、颜色来区别
16、,当然在执行时也可利用 property node 中闪烁的效果来强调,不过一般而言,常用的重要 Control 通常用按钮放在 Front Panel 上显眼的地方;而较不常用的 Control,通常利用放在 cluster 或 tab control 中,利用 invisible 的功能或换至其它页面使其平常不出现在Front Panel 上。较不常用的按钮,也较不用按钮的形式,而可在 Controls Classic Controls Boolean 中选择 Radio Button 或 Checkbox 来使用。请记得一个原则,在 Front Panel 上可看见的 Control 越
17、少越好,因出现越多的 Control,设定的参数就越有可能因不小心而改变,进而造成错误发生,要避免这种情形,可将 Control 连上另一 Indicator 后,在将Indicator 放到 Front Panel 显示其值即可。c) 要将众多的 Control 及 Indicator 依使用功能分类,并适当地利用页面切换来显示。d) 在执行程序时可以选择 cancel 或 abort:这对操作者而言是十分重要的,但却容易被程序设计者所忽视,因程序设计者会不经意的假设操作者是非常了解他所写的程序,又非常熟练,而且一定照正确步骤不会按错按键。但实际上操作者可能并不熟练或很粗心等等,有时若一旦按
18、下某个按钮就不能后悔的话,很容易造成万劫不复的悲剧。请注意,程序设计者一定要在程序中加入在执行中跳出程序的方法,而尽量避免由操作者去按下 toolbar 上的 abort(红色圆形按钮)来跳出程序。e) 在 Front Panel 上多使用图形,避免过多的文字或数据。f) 在 Main Level 中统一处理所有的 exception massage:这部分在程序设计中又称 exception handling,在软件工程上也是需要专门课程来讨论的,同时,这部分对于产品的商品化是非常重要的,在此只先简单叙述,之后会有专门的专题来讨论。先谈建立 exception handling 机制的好处:
19、它可告知操作者哪里有出错,或需要注意,使不是十分熟练的新手操作者,减低发生状况的机会;也方便制造厂商与程序设计者容易做除错及故障排除的动作,使得整个系统的开发及维护能较有效且所短时间。我们希望 exception handling 可达到下列几个功能:当测试过程有错误状况发生时,程序可以自动修复错误,并继续完成测试或重新测试一次;或当测试过程有错误状况发生,且此错误不能修复时,程序能自动跳过有问题的部分,并继续完成测试;或测试过程有错误状况发生时,程序会将整个系统暂停或终结,并告知操作者错误发生处。上面叙述起来似乎很简单,但实际上它需要程序设计者看到实际操作后的结果,再一项一项加到整个程序中,
20、然后再故意发生错误来测试,也因为如此,它可算是整个软件计划发展过程中非常耗时耗力的一部分。若藉用前述所提过的 State Machine 的写法,会使得 exception handling 的程序设计较为简单,因 State Machine 中原本就有安排一个 error state,可让程序设计者在那里统一撰写处理各种 exception/error message,然后在 error state 中判断,是回到原来的测试中,或是走到 close state 去结束此项测试。因此是强烈推荐采用 State Machine 的写法。以上简单叙述了三层式软件结构的设计概念,及各个 level
21、中所应要包含的重点,这边再写一些个人的实战心得:三层式结构它是一个原则,并非一个绝对的规定,但千万要了解他的精神所自,它的精神是:各个 VI 是属于那一个 level 要区分清楚,同一个 level 间的 VI 不要互相呼叫,程序的 code reuse 要最大化。这样一来,当你要维护或修改这个软件时就会比较容易了;同时,就算有团队中的成员突然插入修改的工作也能很快了解整个架构而上手。实际上在采用三层式结构时,可由 Main Level 的 VI Hierarchy 图中,来看你是否有按照这种写法,通常为了好看起见,不同 level 中的 VI 其 icon 我们分别用不同的颜色来区别。例如下
22、两图,左图的程序就有符合标准,很清楚的三层式结构,使整个软件计划的架构一目了然。右图则较不好,因其在 Test 1中又呼叫了属于同一 level 的 Test 2,而 Main 这个 VI 又直接呼叫了 Driver 2来使用,因此造成原分别属于不同 level 各 VI 间的分界并不明显,这样也就达不到三层式结构的目的,也显现不出其优点了。或许有人会说反正写出来的程序可以执行,也可以达到正确的结果不就好了,但上两图只是以较小型的程序为例而已,实际上熟悉三层式结构这种写法的目的是在于较容易发展及维护大型的程序计划,因此在平时写较小型的程序时就要多加练习及养成习惯,切莫以解决眼前的小程序为满足,
23、而不去练习及体会它的精神所在。请相信一点,三层式结构并不是只为了VI Hierarchy 图好看而已,它确实能增进设计程序的速度及效率,越大型的程序就越能显现其优点。在实际运用三层式结构的写法时,有时会发生一个情况,只有三层可以选择,实在是无法将所有的 VI 只分成三个 level 的话怎么办?那就要秉持三层式结构设计的精神,仔细规划一下到底需要几层,四层?五层?在个人的经验里,用到四层则是有的,尚未有用到五层的例子,例如可将 Driver Level 再分为 Upper Driver Level 及 Lower Driver Level(这是我自己的命名,仅供参考) ,这样就变为四层式的结构了。请记得坚持同一 level 中的 VI 不要互相呼叫的原则,这样即使分为四层或五层,我想也是可以很容易且清楚的了解整个软件计划的架构的。