将上图的程序架构包成 Sub VI 就是一个可随时随地被呼叫使用的 Functional Global Variable ,其有以下特点:
(1) 只执行一次的 Loop 。
(2) 未初始化的 Shift Register 。
(3) Enum Control 和 Case Structure 。
(4) 禁止 Reentrant execution 。
看起来 Functional Global Variable 和一般常用的 Local Variable 或 Global Variable 的功能很像。但不同的是 Functional Global Variable 本身就是一个 Sub VI ,所以在 VI Properties 中的 Execution 可以去设定是否允许 Reentrant execution ,而预设值是取消的,如下图。
在禁止 Reentrant execution 的状态下, Sub VI 可以随时随地使用,但如果同时有两个人在呼叫它的时候,并不会同时写入资料,而是会排队等先呼叫的人执行完,另一个人才能进去使用它。如此可避免同时去读写资料而造成 Race Condition ,甚至可以在程序里面增加功能去纪录资料是在何时何地被读写的。
接下来同样沿用前两篇的红绿灯程序来做为范例,在开始写程序之前,先规划一下会需要用到那些功能模块。首先是要有一个可以写入红绿灯状态并且可以读取显示的模块,另外就是要有一个计时功能的模块,设定好时间后,会等到时间到了再执行下一个动作。下面我会将这两个模块先写成 Functional Global Variable ,如下图。
上图是 Timing Module 的程序,在 Reset 的状态中,会将输入的 Wait Time (s) 以及将目前时间当作 Start Time 写入 Shift Register 中。
在 Check 的状态中,每次会等待 50ms ,并将目前的时间减掉 Start Time 计算经过时间,再与一开始设定的 Wait Time 相比较,输出为是否到达时间 Done ?
上图是 Traffic Light Module 的程序,其中 Shift Register 内存放的是红绿灯的状态, State Control 指定执行的方法,最后会输出下一次要执行的状态 Next State ,以及目前状态要等待的时间 Wait Time (s) 。当执行 Read 状态时,会将 Shift Register 中的资料读取出来并输出。
看到这边大家应该会有似曾相识的感觉,没错这是 State Machine 的写法,但是又会困惑为何循环每次只执行一次。因为接下来要示范的是 State Machine 的另外一种写法- Queued State Machine 。
主程序如上图所示,是由 Queue 所架构而成的,其所传递的 element 为主程序执行的状态 Queue State 以及 Traffic Light State 所包成的 Cluster 。而在程序一开始就预先 Enqueue element 进去一笔 Write 的指令,将 Traffic Light Module 状态设定为 Start 。当程序执行 Write 指令时,会将 TL State 写入前面所写好的 Traffic Light Module ,并且将 Wait Time 设定于 Timing Module 。再将 Read 的指令以及 Write 设定红绿灯的 Next State 的指令先后排入 Queue 里面。
接下来执行 Read 指令时,会读取目前红绿灯的状态并且更新界面上的 Indicator 。此时在 Queue 里面还有一笔 Write 的指令,是要将红绿灯的 Next State 写入。但我们必须等 Wait Time 到达时才能执行,所以就要使用 Enqueue Element At Opposite End 将 Wait 的指令插队排到 Write 之前来执行。