4.4.1 状态机简介
在我看来,状态机结构绝对是图形化语言程序设计中必不可少的结构,我设计的应用程序中,几乎无一例外都使用了状态机结构。也可以这样说:在图形化语言中,状态机是解决有序控制的最好方法。状态机结构是在LabVIEW那个版本里出现的,我不清楚,估计是在LabVIEW5后,它一直是以工具包的形式提供给使用者的。2007或2008年NI发布了新的“状态图工具包”,所以在2008年NI的开发者套件中已经看不到状态机工具包了。给我的感觉是新版的“状态图工具包”过于“抽象”,反而不太好理解(主要问题还是没使用过,没法理解它所带来的好处)。
由于状态机工具包是NI开发者套件中的一个附加工具包,所以先前出版的绝大部分介绍LabVIEW的中文书籍中都没有包含这部分内容。只有最近出版由江建军等编著的《LabVIEW程序设计教程》一书对此讲述的比较详细。此外这本书的内容相当于中高级教程,有兴趣的读者不妨找来看看。
考虑到状态机在程序设计中的实用性,所以我觉得还是有必要将它简要的介绍给大家。另外,LabVIEW State Diagram Toolkit User Guide中会有更精确的描述,鉴于我的英文水平不高,所以没办法将其内容完整的表述给大家。这里仅仅是我在实际应用中的体会,并且对它的应用也仅仅体现在程序架构设计中。其实在仪器控制(GPIB)等方面它应该更具有使用价值,因为GPIB从本质上讲,就是状态的不断变迁。
状态机工具包提供了在LabVIEW开发环境下,根据程序的需求抽象出状态图,并将设计好的状态图自动转换成LabVIEW状态机架构。“LabVIEW状态机架构”是指LabVIEW开发环境下的状态机机制的图形化语言程序代码。
状态机的基本概念
“状态机”的概念大约出现在上个世纪30-40年代,在学习数字电路设计时也涉及到了状态机,在数字逻辑设计中,“状态机”是一个系统设计的规范方法。在程序设计中引入状态机的概念,可使复杂的程序看起来更清晰,程序修改起来更容易。由此可见,状态机是降低软件复杂度的比较好的方法。
状态机的基本定义:状态机是一种具有指定数目的状态的概念机,它在某个指定的时刻仅处于一个状态。状态的改变是由输入事件引起的状态变化。作为对输入事件的响应,系统可能转变到相同或不同的状态,而输出事件可能是任意产生的。
——摘自A software engineering Approach to LabVIEW 的中文译本
所谓“状态图”就是用图形化的方法来描述:状态及状态之间的联系和转换关系,有点类似于通常程序设计中的流程图。
本节主要讨论图形化语言中“状态机”的基本概念、原理和架构以及如何使用状态机工具包实现程序设计。当然,在较大的开发项目中,如果使用状态机工具包会进行设计,将会提供方便、简洁、灵活的应用程序架构。
“程序”与“状态机”间的关系
所谓“程序”就是指挥计算机并通过计算机来表达或实现我们意念(想法、要求)的一堆代码;“状态机”是用来抽象地表示这堆代码的一种简便机制(方法)。
实际上,我们可以将程序看作是有许多种状态单元,这些状态相互连接,状态之间的转换是通过某些事件发生或状态结束来触发的。
状态机的实质就是:状态、状态的转换和处理。状态的转换来自于某些事件发生或状态结束来触发。状态的处理就是你想要解决的问题的程序代码。
如果对于上面这种说教式的表述方式,初学者往往还是很难理解清楚的。其实最主要的问题就是不知如何确定、抽象出程序所需的“状态”。这里先用一个非常简单的生活事例让我们来看看如何抽象出状态和状态的转换和处理 !
假如,我们早上被闹钟叫起,起床后要把被子叠好,穿好衣服,拉开窗帘打开窗户置换室内的空气;然后开始刮胡须、洗脸、刷牙;之后到餐厅边吃早餐边听广播或看电视新闻节目;早餐后穿好外衣出门上班。基本上就是这么个程序(也可能比这个还复杂随你想象:比如把手机、钱包、驾照带好等等)。
问题是:有没有这样的时候,起来晚了,连洗脸、刷牙都顾不上了,吃点东西赶紧出门上班,可能会有这样的情况。
问题是:有没有这样的时候,起来晚了,连洗脸、刷牙、吃点东西都顾不上了,赶紧出门上班,也可能会有这样的情况。
看到了吧,整个过程是多么的复杂!其实将我们所关心的事情进行抽象化处理后是很简单的,也就是这么几件事(状态及状态的改变):起床、洗漱、早餐、上班。例图给出了上述状态的状态图。
如果“出门上班”是我们最终的目的,从例图上可以看出共有三种途径能够达到这个目的。
一是:按部就班的“起床”-“洗漱”-“早餐”-“出门上班”;
二是:时间较紧“起床”-“洗漱”-“出门上班”;
三是:时间更紧“起床”-“出门上班”。
注意:这是抽象出来的状态图,根据不同的需求,抽象的结果也不同。为了使大家有更清晰的理解,有必要再重申一下:状态机不是程序,是从程序中抽象出来的程序构架,真正的程序代码应该在存在于例图中的状态椭圆。
而如何抽象出我们所关心的状态,才是设计状态机的关键。比如:在初始化状态椭圆中,导致起床的事件可能有:“闹钟响起”、“恶梦惊醒”、“生物钟作用”、“内急”、“意外的响动”等等,可以有N个事件源。所有这些事件源,只要你对此关注,都应该包含在初始化状态中,它们中任何一个事件的发生都导致“起床”(从一个状态到另一个状态)。
绿色椭圆——初始化
每个状态机的状态图中,必须有一个初始化状态,也是程序的初始状态,用绿色表示也说明与其它的状态不同。
通常的测控程序开始工作时也都要有一个初始化状态,避免系统发生瞬变或混乱,比如:初始化时做系统复位操作、指定某些I/O的状态等等。程序也是从初始化开始从新运行。
本例中,导致起床的事件并没有具体标出,其实可以是“恶梦惊醒”、“生物钟的作用”等等原因。
黄色椭圆——状态节点
黄色椭圆应该有多少个,取决于满足程序要求的前提下,对程序抽象的程度。比如:完全可以把洗漱包含在起床这个状态中,那就缺少了“来不及洗漱、早餐”这个事件。
避免遗漏所关注的事件,是整个抽象过程中的最基本的基本原则。
红色椭圆——终点节点
也是我们抽象出的一个状态,是程序的终点,所以用红色的椭圆表示。这也是状态机(图)中所要求的。
Default连线
节点间状态转移的流程,默认的意思就是:执行完该节点的内容后,它将自动转移到下一个它所连接的状态节点,或理解为程序的正常流程。
事件连线
表示导致状态发生改变的事件源。比如:像例图中的:“来不及早餐”和“来不及洗漱、早餐“等等。
上面我们通过生活中的实际事例了解了状态机,同时也了解了状态图的结构。下面我们结合应用程序设计中的一个简单实例来进一步加深对状态机的理解。