图 2 Enum型和Ring型控件
(1) enum型代表的值只能够为U8、U16和U32型,而ring型代表的值允许为I8、I16、I32、I64、U8、U16、U32、U64、EXT、SGL、DBL和FXP型;
(2) ring型实质上是一种numeric型,而enum型是一种独立于numeric之外的数据类型;
(3) 当把ring或enum型控件分别连接到case时,对ring型而言,case结构的选择端子只能够显示数值;而对enum型而言,case结构的选择端子能够显示具体的枚举值;
(4) ring的strings[]属性可以在程序运行时被修改,而enum的strings[]属性在程序运行时却无法被修改;
(5) 当把ring型和enum型控件分别制作成自定义类型控件(Type Def.)时,ring的控件实例可以任意设置其strings[]属性的值,而enum的控件实例却无法设置strings[]属性的值,如图 3所示;
(6) 当把ring型和enum型控件分别制作成自定义类型控件(Type Def.)时,改变ring的Type Def中控件的strings[]属性的值,但是其对应的实例的strings[]属性却不会改变;而改变enum的Type Def中控件的strings[]属性的值,其对应的实例的strings[]属性会随之发生变化。
(7) ring型控件对应的各个状态可以表示任何值(在控件的property>>Edit Items对话框中),而enum控件对应的各个状态只能够从0开始顺序表示(在控件的property>>Edit Items对话框中)。
由于应用程序的各个状态在设计时就是相对固定的,不会在应用程序中进行修改。对比以上ring型和enum型的区别,可以看出在基本状态机中,enum更适合来标记状态。首先当把enum直接与case相连时,case的选择端会立刻显示enum的各个状态值,有利于程序的理解和维护;其次,当把enum制作成一个Type Def型自定义控件时,日后如果需要增加新的状态则只需要修改Type Def型的strings[]属性,此时其各个实例的strings[]属性会随之改变。
图 3 Enum型和Ring型控件对比
反观我们在本章开头提到的10个问题,使用
labview状态机模式是否能够回答上述问题呢?也就是说基本状态机模式有什么样的缺点呢?
(1) 状态的分类不清晰。试想,如果有几十个状态,那么case结构的选择端会显得没有条理。事实上,我们是可以对状态进行分类的,如数据采集、数据分析状态可以均属于对数据的操作。其实并没有统一的规定如何对状态进行分类,其目的在于使程序能够清晰明了。
(2) 缺乏数据共享和错误处理机制。例如在数据采集之后还需要增加一个数据分析的状态,那么如何将采集得到的数据提供给数据分析模块呢(使用局域变量、全局变量、共享变量或其它)?这一点并不能称为基本状态机的缺点,只是在上面的例程中没有实现,所以单独列出。
(3) 每一个状态分支只能够决定后面的一个状态,而无法决定一个状态序列(多个状态)。假如状态机有三个状态A、B、C,前面板上有三个按钮依次为B1、B2和B3。如果单击B1时需要使得三个状态按照A→B→C的顺序执行,当单击B2时需要使得三个状态按照B→A→C的顺序执行,当单击B3时需要使得三个状态按照C→A→B的顺序执行。这种情况是无法使用基本状态机模式解决的。
(4) 程序一直在占用CPU资源。即使在Idle状态下,仍然需要对前面板的控件值进行监控以确定对哪一个状态进行响应。
(5) 无法响应更多的前面板事件。如当单击窗口右上角的×时,弹出一个确认退出的对话框。当鼠标在前面板拖曳时,捕获这个事件。这种情况是无法使用基本状态机模式解决的。
(6) 任何时刻只能有一个状态在运行。如果用户需要在数据采集过程(acquire状态)中查看“关于&帮助”对话框(about状态),那么基本状态机模式只能暂停数据采集而显示对话框,却无法实现在查看“关于&帮助”对话框的同时仍然进行数据采集。
状态机是一种最为经典的程序设计模式,在LabVIEW 7.1(含)之前它几乎统治了大部分的LabVIEW主程序。最基本的状态机结构如图 1所示。状态是状态机运行的经脉,在开始使用状态机模式撰写程序时需要将应用分为若干个状态。