Labview程序设计模式
- 格式:docx
- 大小:1.31 MB
- 文档页数:21
LabVIEW程序设计模式(五)—生产者/消费者模式(3)_LabVIEW程序的动态调用LabVIEW程序设计2009-05-19 17:11:09 阅读696 评论0 字号:大中小订阅简单而言,动态调用指的是通过程序控制另外一个程序的运行、停止、赋值和获取值等。
LabVIEW提供了多种动态调用的方式,从底层而言是通过VI Server 技术实现的。
图31所示为LabVIEW中的Application Control选板,动态调用所使用的节点都位于这个选板。
当调用一个在硬盘、内存甚至是网络路径上的vi时,首先要使用Open VI Reference以将该VI载入内存并获取VI的“句柄(Reference)”;然后再使用该句柄进行其它的控制操作;最后再关闭该VI的句柄避免内存泄漏,这就完成了一次对VI的调用。
图31 Application Control选板图32是一个动态调用的具体实现代码,首先使用Open VI Reference获取被动态调用VI的Reference(例子中是C:\average.vi);再使用Call By Reference Node 节电动态运行该VI;最后关闭VI的Reference。
在使用Call By Reference Node 时需要事先指定被调用VI的输入输出接口,也就是说这种动态调用的前提是必须知道被调用VI的输入输出接口,否则无法进行动态调用。
图32 VI的动态调用Open VI Reference的路径输入是一个多态的输入口,也可以使用String输入,如图33所示。
此时被调用的VI必须在内存中,且输入的是被调用VI的文件名。
值得一提的是这种“文件名”调用方式在可执行程序中是无法被调用的,因此建议最好采用路径的调用方式。
图33 Open VI Reference的多态性【应用5】本例将使用LabVIEW的动态调用方式实现斐波那契数列(Fibonacci数列)。
Labview简易程序设计Labview简易程序设计概述Labview(Laboratory Virtual Instrument Engineering Workbench)是一种用于虚拟仪器设计和控制系统的开发环境和语言。
它的特点是图形化的编程方式,使得用户无需编写繁琐的代码,就能够完成复杂的测量和控制任务。
本文将介绍Labview的简易程序设计方法。
Labview程序结构Labview程序由多个虚拟仪器(VI)组成,每个VI由输入、处理和输出三个核心部分组成。
输入部分负责从外部设备或传感器中获取数据,处理部分对输入数据进行计算和逻辑处理,输出部分将处理结果发送给外部设备或在界面中显示。
Labview程序的整体架构通常是基于数据流图(Block Diagram)的,其中各个VI之间通过数据流连接进行数据传递。
数据流连接将结果从一个VI的输出端传递到另一个VI的输入端,从而实现整个程序的协同工作。
Labview程序设计步骤1. 创建新的Labview程序打开Labview软件,“新建”按钮创建一个新的项目。
选择适当的模板或空项目来开始新的程序设计。
2. 添加VI在新建的项目中,右键“当前程序”文件夹,选择“新建”->“虚拟仪器”。
给新建的VI命名,并双击打开它。
3. 添加输入在VI的数据流图上,选择需要的输入控件或函数。
例如,可以添加一个“数字输入框”来接受用户输入的数值,或者添加一个“传感器读取”函数来获取外部设备的数据。
4. 添加处理在VI的数据流图上,选择需要的处理函数或操作。
例如,可以添加一个“加法”函数来对输入的两个数值进行求和,或者添加一个“循环结构”来进行重复计算。
5. 添加输出在VI的数据流图上,选择需要的输出控件或函数。
例如,可以添加一个“数字显示”控件来显示处理结果的数值,或者添加一个“数据保存”函数来将结果保存到文件中。
6. 连接数据流将输入、处理和输出部分通过数据流连接连起来,确保数据能够流动并得到正确的处理。
LabVIEW程序设计模式(三)—用户界面事件模式针对基本状态机模式的第(4~5)个问题,需要对模式进行改进。
本节将一一分析这些问题对应的解决方案,并最终形成一种新的状态机模式——用户界面事件模式。
(1)程序一直在占用CPU资源。
(2)无法响应更多的前面板事件。
熟悉LabVIEW的工程师应该能够很容易地解决这两个问题,在LabVIEW 7.0以后的版本中提供的事件结构(Event Structure)能够让我们非常便捷地处理这两类问题。
在LabVIEW中事件结构的使用并不是一件难事,根据事件的发出源,事件可以抽象地分为用户界面事件和用户自定义事件。
相关的基本知识可以参考有关的书籍,这里不再阐述事件结构的使用方法。
图14所示的结构称为用户界面事件模式,它能够很便捷地响应各种事件并且不占用CPU的资源,这是由LabVIEW中事件结构本身的特性决定的。
图14 用户界面事件模式【应用3】本例要模拟一个简单的画图板功能。
它有4个功能选项:点(point)、线(line)、圆(circle)和椭圆(oval),一次完成的绘画过程是:在画布上单击鼠标开始绘制→按住鼠标的同时在画布上拖动鼠标→在画布上放开鼠标结束绘制。
程序的前面板如图15的样式,下面是画布,右上方的图15 画图板前面板由于系统需要响应鼠标在画布上单击、移动和释放事件,因此使用状态机模式是无法解决的,只能通过事件结构。
因此本例将使用用户界面事件模式实现上述的画图板功能。
程序的背面板如图164个事件。
(1)Panel Close?:响应前面板的(2)Picture <Mouse Down>:表示绘画的开始。
(3)Picture <Mouse Move>:表示绘画的路径和轨迹。
(4)Picture <Mouse Up> <Mouse Leave>:表示绘画的结束,此时一定要加入<Mouse Leave>事件,因为当鼠标移动到画布的外面时就可以认为是绘画结束了,并不需要一定要求鼠标在画布中释放。
精讲LabVIEW设计模式培训概述LabVIEW是一种图形化编程语言,用于数据采集、控制、仪器仪表通信、图像处理等领域。
设计模式是一种经过验证的最佳实践方法,用于解决特定问题。
本文将精讲LabVIEW设计模式培训,帮助读者了解LabVIEW设计模式的基本概念和应用。
设计模式的概念设计模式是在软件工程中,根据问题的特点和需求的约束,提供一套解决方案的模式。
它可以提高代码的可读性、可维护性和可扩展性。
设计模式分为三大类:创建型模式、结构型模式和行为型模式。
在LabVIEW中,常用的设计模式包括状态机模式、发布-订阅模式、命令模式等。
状态机模式状态机模式是一种通过定义对象的状态来解决特定问题的设计模式。
在LabVIEW中,状态机模式常被用于处理事件驱动的程序。
它通过不同的状态和状态之间的转换来实现特定功能。
例如,一个简单的状态机模式可以用于控制流程的顺序执行,通过定义不同的状态和状态之间的转换条件,实现不同的程序逻辑。
发布-订阅模式发布-订阅模式是一种实现对象间松耦合的设计模式。
在LabVIEW中,发布-订阅模式被广泛应用于多任务编程和消息传递。
它通过将消息的发布和订阅分离,实现不同模块之间的通信。
例如,一个发布-订阅模式可以用于实现观察者模式,让观察者模块监听某个对象的状态变化。
命令模式命令模式是一种将请求封装为对象,以此来参数化客户端的设计模式。
在LabVIEW中,命令模式常被用于实现撤销和重做功能。
它通过将动作封装成命令对象,实现对动作的参数化和执行。
例如,一个命令模式可以用于实现对仪器的控制,每个命令对象代表一个具体的操作,可以被撤销和重做。
实例讲解下面,我们将通过一个简单的实例来讲解LabVIEW设计模式的应用。
假设我们需要编写一个程序来控制一个自动化实验装置,包括采集数据、处理数据和输出结果。
我们可以使用状态机模式来实现流程的顺序控制,使用发布-订阅模式来实现模块间的通信,使用命令模式来实现对仪器的操作。
LabVIEW程序设计模式,这个相对学术化的词语是对一系列用于LabVIEW程序设计结构的归纳和总结。
在建造房子时,需要针对房子的用途设计整个房屋的架构,确保房子在这个架构上的坚固性和可建造性。
写程序时同样如此,不同的应用需要使用不同的程序设计结构。
例如我们在LabVIEW中构建一个用户界面型程序时,往往首先在背面板中加入一个大的while循环以使程序持续运行。
如果需要响应用户界面事件则还需要加入一个Event事件结构。
那么我们是否曾经考虑过以下的这些问题:(1)应用中是否存在并行响应的情况?如在持续的数据采集过程中,是否需要同时响应单击菜单的事件?(2)底层获取的数据如何与上层的数据显示部分进行数据交互?(3)上层的界面如何受底层程序的控制?(4)同一个循环中采用哪种方式进行数据交换?是局域变量、全局变量、共享变量还是移位寄存器?(5)程序是否具有可扩展性?(6)如果程序运行过程中,发生系统错误或者硬件通讯错误,是否会停止运行?待错误排除后是否会继续运行?(7)如何组织程序中的核心数据结构?是否需要采用面向对象程序设计?(8)如何记录测试数据并生成报表?如何保存用户配置参数?(9)如何处理程序运行中的断电情况?重新启动时的继续运行?数据的最低丢失?(10)如何实现运行过程的采样触发和多点采样的同步?当然,也许只是使用LabVIEW临时地调试或开发某个小的应用,无需考虑上述的问题。
但是,如果使用LabVIEW开发一个典型应用的程序却无法回避这些问题。
因此,有必要对各种程序开发的应用进行归纳和总结,提取它们对应的LabVIEW程序结构中的共性。
此外,针对这些共性研究哪种结构更加适合于应用。
这些结论综合起来就形成了程序设计的模式。
对于初学者而言,理解和掌握程序设计模式往往能起到事半功倍的效果;而对高级用户而言,归纳各种程序设计模式又能够不断完善程序中遇到的问题,并衍生一套符合特定应用的特有的程序设计模式。
LabVIEW程序设计模式(五)—生产者/消费者模式(3)简单而言,动态调用指的是通过程序控制另外一个程序的运行、停止、赋值和获取值等。
LabVIEW 提供了多种动态调用的方式,从底层而言是通过VI Server 技术实现的。
图31 所示为LabVIEW 中的Application Control 选板,动态调用所使用的节点都位于这个选板。
当调用一个在硬盘、内存甚至是网络路径上的vi 时,首先要使用Open VI Reference 以将该VI 载入内存并获取VI 的句柄(Reference);然后再使用该句柄进行其它的控制操作;最后再关闭该VI的句柄避免内存泄漏,这就完成了一次对VI 的调用。
图31 Application Control选板图32 是一个动态调用的具体实现代码,首先使用Open VI Reference 获取被动态调用VI 的Reference(例子中是C:average.vi);再使用Call By Reference Node 节电动态运行该VI;最后关闭VI 的Reference。
在使用Call By Reference Node 时需要事先指定被调用VI 的输入输出接口,也就是说这种动态调用的前提是必须知道被调用VI 的输入输出接口,否则无法进行动态调用。
图32 VI的动态调用Open VI Reference 的路径输入是一个多态的输入口,也可以使用String 输入,如图33 所示。
此时被调用的VI 必须在内存中,且输入的是被调用VI 的文件名。
值得一提的是这种文件名调用方式在可执行程序中是无法被调用的,因此建议最好采用路径的调用方式。
图33 Open VI Reference 的多态性【应用5】本例将使用LabVIEW 的动态调用方式实现斐波那契数列(Fibonacci 数列)。
斐波那契数列指的是这样一个数列:1,1,2,3,5,8,13,21 这个数列从第三项开始,每一项都等于前两项之和。
LabVIEW程序设计模式(五)—生产者/消费者模式(4)_生产者/消费者循环本节将使用“多循环”来解决程序并行运行的问题,那么程序中的两个循环如何进行数据交互和共享呢?最普通的方式是采用全局变量或局域变量,但是当两个循环执行的速率不相等时,必然会造成数据的丢失或重复。
如前所述,LabVIEW提供了队列操作函数,允许数据的发送者和接受者之间建立一条缓冲通道,这样就避免了循环不同步带来的影响。
如图37所示,将整个过程与供水系统进行类比,在数据产生/采集端(供水局)产生数据后,并不直接向终端用户供水,因为前者产生水的速率与后者消耗水的速率并不相同。
此时需要建造蓄水池将供水局产生的水放入到蓄水池中,同理获取的数据也放入该缓冲区中。
当终端用户需要用水时,直接从蓄水池中获取就可以了,同理在进行数据显示和分析时直接从数据缓冲区中获取就可以了。
图37 生产者/消费者模型当然,上面的模型也会存在一个问题:数据缓冲区/蓄水池的容量?假定供水局不停地产生自来水,而终端用户却不消耗水,这样便会导致蓄水池装满而溢出。
反之当终端用户耗水量太大时,导致没有水可用。
LabVIEW中的队列函数提供了一种很好的方式规避了这个问题,由于队列中的元素是“先进先出”的,因此确保了接收到的数据是有序的。
在LabVIEW的队列操作中(入列和出列函数),提供了timeout选项以处理数据缓冲区的溢出或不足。
当数据溢出时,入列函数(数据进入队列)将停止发送数据(处于等待状态),直到缓冲区存在数据空间或者达到了timeout设置的时间;而当数据不足时,出列函数(数据流出队列)将停止接收数据(处于等到状态),直到缓冲区进入了新的数据或者达到了timeout设置的时间。
【应用6】本例将演示生产者/消费者循环的一些基本特性和队列操作的特点。
如图38所示,生产者与消费者之间传递的数据是一个连续的sine波形,二者靠大小为20个点的缓冲区连接。
右下角是“停止”按钮,用户控制程序的停止执行。
Labview简易程序设计1.引言本文档旨在提供关于LabVIEW简易程序设计的详细教程。
LabVIEW(Laboratory Virtual Instrument Engineering Workbench)是一款流行的图形化编程语言和开发环境,用于快速开发各种控制、测量和数据采集应用程序。
通过本文档,读者将学习如何使用LabVIEW进行基本的程序设计和开发。
2.环境准备在开始使用LabVIEW进行程序设计之前,您需要准备以下环境:2.1.安装LabVIEW开发环境:并安装LabVIEW最新版本,根据操作系统选择32位或64位版本。
2.2.硬件设备:连接需要控制或采集数据的硬件设备,如传感器、运动控制器等。
2.3.了解LabVIEW界面:学习基本的LabVIEW界面元素,如面板(Front Panel)和图表(Block Diagram)等。
bVIEW基础在本章节中,将介绍LabVIEW的基本概念和基本操作:3.1.程序结构:LabVIEW程序的基本结构,包括面板和图表的布局。
3.2.数据流编程:学习LabVIEW的数据流程图编程方式,理解数据流和控制流的概念。
3.3.数据类型和变量:LabVIEW中的数据类型以及如何创建和使用变量。
3.4.控制结构:学习条件语句、循环结构和事件结构的使用方法。
3.5.函数和VI:了解LabVIEW的函数和虚拟仪器(Virtual Instrument)的概念,学习如何使用和创建自定义VI。
4.数据采集与处理本章介绍LabVIEW的数据采集与处理功能:4.1.设备驱动程序:了解如何安装和配置硬件设备的驱动程序。
4.2.仪器控制:学习如何使用LabVIEW控制仪器进行数据采集和输出控制。
4.3.数据采集:介绍如何使用LabVIEW进行数据采集,包括模拟信号和数字信号的采集。
4.4.数据处理:学习LabVIEW中常用的数据处理方法,如平滑、滤波、数字信号处理等。
5.图形界面设计本章介绍LabVIEW的图形界面设计功能:5.1.面板设计:学习如何设计具有用户交互界面的LabVIEW面板。
LabVIEW程序设计模式(五)—生产者/消费者模式(5)结合状态机模式、事件结构和动态调用技术,能够归纳出针对较复杂应用程序的通用设计模式。
对常见的测试测量程序而言,主要由数据采集、数据分析、外围菜单项响应、报表生成、数据显示这五个部分组成。
其中数据采集是相对独立和长时间运行的一个模块,可以与其它的模块同时运行。
因此,在大多数持续采集的程序设计中需要将它单独作为一个模块运行。
与此同时,子程序也需要一条数据通道发送一些反馈命令给主程序。
于是可以构成如图44 所示的一个通讯回路。
图44 通讯回路LabVIEW 提供了多种主程序与子程序之间的通讯方式,如队列、Reference、事件等。
为了介绍这些方式的具体使用方法,将结合最常用的数据采集实例进行阐述。
【应用7】本例以计算机组件测试为应用介绍消费者和生产者循环的具体使用方法和数据交互过程。
例子并不是为了说明计算机组件测试的过程和方法,而是重在强调对该应用而言应该采用什么样的程序设计模式。
因此,例子中使用了多种数据交互方式,这些交互方式的选择并不是唯一的,可以根据实际情况选择合适的数据交互方法。
假设计算机的整个测试过程由CPU、RAM、CDROM、Power.等等数项子测试项组成,程序需要充分考虑可扩展性要求,使得后期增加新的待测组件时对主程序的影响不大或者没有影响。
测试过程应能够实现暂停和提前停止的功能,并且测试过程不受其它界面操作的影响。
根据以上的测试要求,可以把整个测试程序分为两个部分:控制部分和执行部分。
其中前者是用户主界面,用来响应用户界面事件以及控制测试流程的执行;后者是执行程序,用来根据控制命令运行测试流程并且产生测试结果。
系统的结构如图45 所示。
图45 计算机组件测试系统结构从上图可以看出,该应用与消费者和生产者模式是相符的,不同的是还涉及到消费者(执行部分)向生产者(控制部分)的数据传输。
本例使用的。
Labview简易程序设计
Labview简易程序设计
介绍
准备工作
在开始LabVIEW编程之前,你需要安装LabVIEW软件。
LabVIEW 软件可以从官方网站,并按照安装向导进行安装。
安装完成后,你可以打开LabVIEW软件开始编程。
创建新项目
启动LabVIEW软件后,你可以选择“File”菜单中的“New Project”命令来创建一个新项目。
在新项目中,你可以添加多个程序文件,便于管理和组织代码。
添加控件
LabVIEW中的控件用于构建用户界面,你可以通过拖拽的方式
将控件添加到程序界面中。
常见的控件包括按钮、文本框、滑块等。
连接控件和程序逻辑
LabVIEW中的控件和程序逻辑可以通过拖拽和连线的方式进行
连接。
例如,你可以将一个按钮控件和一个程序逻辑节点相连,使得按钮被时触发程序逻辑。
编写程序逻辑
LabVIEW中的程序逻辑通过数据流图的方式进行编写。
数据流
图是由各种节点组成的,每个节点之间通过连线进行连接。
你可以在节点中实现各种功能,例如数学运算、条件判断、循环等。
运行程序
当程序编写完成后,你可以LabVIEW界面上的运行按钮来运行程序。
程序将按照你设定的逻辑执行,并在界面上显示相应的结果。
你可以通过调试按钮来单步调试程序,逐步查看程序执行过程。
LabVIEW程序设计方法为了提高程序设计效率、保证程序设计质量、便于程序的维护,在设计程序时应遵循一些基本的设计方法。
当开发一个较大的项目时,有必要使用由顶向下的设计方法。
LabVIEW语言在使用由顶向下的设计方法上,比其他语言有优势,因为可以先做出用户的接口,然后再逐渐完善它。
1.使用由顶向下的设计方法1)用户需求列表先列出一个表格,包括用于用户操作的面板(指大的程序中需要弹出的子程序的面板),这些面板上一般包含控制量与显示量类型,实时分析需求以及数据的表达等。
然后创建一个临时的前面板,提供给预期的用户。
通过一系列交互的过程,按用户的要求反复调整用户接口。
在这个阶段,可能还需要做一些底层的调研,确定可以达到预期的设计要求。
2)设计程序的层次结构LabVIEW语言的强大功能建立在它的层次特性之上。
每创建一个程序,就可以在高层程序的框图中把它当做子程序使用。
在这种层次结构下,从本质上说层数是无限的。
确定所需顶层模块之后,使用这些顶层模块创建程序代码。
为每个模块创建一个子程序,这个子程序不具备任何功能,只是代表未来子程序的一个模型。
每个子程序应有一个图标和一个包括所有必要的输入输出量的前面板。
但是暂时不必为它创建程序代码,而是确认它是否为顶层程序所必需的。
把这些模型程序组合在一起后,一般说来就应该去理解每个模块的功能,以及它们如何提供需要的结果。
研究每一个模块是否能提供后续程序所必需的信息,然后确认顶层程序代码包含了各程序间传递数据的正确连线。
尽量避免使用不必要的全局变量,因为它们会掩盖了程序间的数据依存关系。
对一个大的程序来说,如果依赖全局变量作为程序间传递信息的手段,会使调试变得困难。
3)程序编码在程序编制过程中,应按照工作的逻辑划分和考虑代码复用的可能性,通过创建子程序实现模块化的编程,把解决一般性问题与特殊问题相结合。
子程序创建以后及时调试。
高层的调试尽管是不可避免的,但是在一个小模块中发现程序缺陷要比在多个程序的层次上方便得多。
Labview简易程序设计Labview简易程序设计LabVIEW(Laboratory Virtual Instrument Engineering Workbench)是一种基于图形化编程语言G代码的数据采集、仪器控制和工业自动化系统软件平台。
本文介绍了LabVIEW的简易程序设计方法,以帮助读者快速入门LabVIEW开发。
1. LabVIEW简介LabVIEW是由美国国家仪器公司(National Instruments)开发的一种图形化编程语言和环境。
它通过将函数块在一起构成程序流程图,使得开发人员能够用图形化方式表达程序逻辑。
LabVIEW广泛应用于实验室测量与控制、工业自动化、生物医学工程等领域。
2. LabVIEW程序的基本结构LabVIEW程序由前端GUI界面和后台程序逻辑组成。
前端GUI界面包括用户界面控件,用于输入数据和显示结果;后台程序逻辑则是执行实际的计算、数据处理和仪器控制等任务。
2.1 GUI界面设计在LabVIEW中,GUI界面设计采用拖拽式编程,即通过将各种控件从工具栏拖拽到屏幕上并进行配置来构建用户界面。
常用的控件有按钮、文本框、图表等。
2.2 程序逻辑设计LabVIEW中的程序逻辑设计采用数据流方式,即程序的执行是根据数据的可用性自动进行的。
在程序中,各个函数块称为节点,其输入和输出通过连线连接起来。
使用节点和连线可以构成程序的流程图。
3. LabVIEW程序的基本编程元素3.1 节点节点是LabVIEW程序中的基本编程单元,代表了执行特定操作的函数或算法。
通过在程序中添加不同类型的节点,并将它们按照适当的顺序连接起来,可以构建出复杂的程序逻辑。
3.2 连线连线用于在节点之间传递数据。
在连接节点时,需要将输出端口和输入端口以连线的方式连接在一起,这样数据才能够在节点之间流动。
3.3 常量常量是指在程序运行过程中数值不变的值。
在LabVIEW中,可以使用常量节点来表示常量值,并将其输出连接到其他节点的输入端口。
LabVIEW 软件工程方法一、LabVIEW 编程模式编程模式,是指一些固定有用的程序结构模式,是编程经验的总结和提炼,并经过了多次成功验证。
使用被广泛接受的编程模式设计出的应用程序,很容易被其他开发人员读懂或修改,也是软件重用的重要基础。
常见的LabVIEW编程模式主要有状态机模式(Staste Machine)、队列消息模式(QueuedMessage Handler)、用户界面事件模式(UI Event Loop)、主/ 从结构模式(Master/Slave)和生产者/ 消费者模式(Producer/Consumer)等。
这5 种模式应用的非常普遍,并且在LabVIEW中,也可以较为方便地实现这5 种编程模式。
状态机模式是LabVIEW 程序设计中最常用的设计模式之一。
它可以用来清晰地实现任何以状态图描述的算法,它的每一种状态对应一种相应的操作。
状态机常用在决策算法中。
例如诊断、监测和控制等。
图1 所示为LabVIEW状态机程序框图的主框架。
由图1 可知,LabVIEW 标准状态机主要由一个While 循环和一个Case 结构构成,并利用移位寄存器来实现状态的跳转。
为了方便编程,可采用Typedef 来实现状态枚举值,这样当需要修改程序状态时,只需要改变Typedef 就可以改变所有的枚举变量。
图1 标准状态机程序框图队列消息模式同状态机模式有些类似,这种模式通常被用于需要特定处理顺序的场合。
用户界面事件模式采用事件结构来响应用户的操作,这种交互方式可以处理诸如鼠标移动、键盘操作、值改变等事件。
由于事件捕获的方式是采用中断方式实现的,因而避免了轮询导致的CPU 资源的浪费,而且会自动产生事件队列,避免了事件的丢失。
但由于它必须执行完前一个事件后才能执行下一个事件,因此任何一个事件结构的执行时间必须尽量地短,以避免用户的误操作。
主/ 从结构模式通常应用于同时有两个或多个以不同频率运行的循环并且循环之间存在有信息交换时的场合。
LabVIEW设计模式汇总本文归纳了LabVIEW中常用的几种设计模式,介绍了各种设计模式的特点及适用范围,并提供了每种设计模式对应的典型应用实例。
1 标准状态机1.1简介状态机(State Machine)是编程中经典的设计模式之一。
状态机对系统所有可能的状态进行罗列,在每个状态分支中执行该状态的代码,并指明系统要执行的下一个状态。
状态机能清晰和准确地完成与状态密切相关的任务。
1.2结构图1-1为典型的标准状态机结构。
系统包含“Initialize”,“Idle”,“Case1”,“Case2”,“Stop”五个状态;系统可以在“Initialize”中初始化系统参数,在“Idle”中专门做状态选择处理,“Case1”和“Case2”为用户自定义的状态分支,“Stop”状态使系统停止运行。
图1-1 标准状态机结构1.3要点(1)状态枚举常量该枚举常量包含了系统所有可能的状态,每次可以选择一个指定的状态。
(2)带移位寄存器的while循环状态机通过while循环上的移位寄存器传递下一个要执行的状态,每次循环只能执行一个条件分支。
(3)条件结构该条件结构的每个分支对应一个系统的可能的运行状态。
Tips:●可以将枚举常量设计为自定义控件类型。
当系统状态需要修改时,只需要修改一次“自定义控件”即可更新整个程序中所有的枚举常量。
●将枚举常量连接到条件结构的选择器接线端后,右击条件结构边框,选择“为每个值添加分支”,可以轻松地为条件结构实现分支配置。
1.4实例(1)情景:使用温度监控系统监测当前温度,当温度超过高温阈值时发出“高温警报”,当温度低于冷冻阈值时发出“冷冻警报”。
(2)代码:详见附件中的“标准状态机”项目文件。
图1-2 前面板设计图1-3 程序框图设计1.5小结标准状态的应用非常广泛,它的特点是:(1)系统的所有状态和转换条件都是可以提前预期设定的,而不是随机产生的;(2)系统一次只能执行一个状态,不适合做并行任务处理。
LabVIEW程序设计模式,这个相对学术化的词语是对一系列用于LabVIEW程序设计结构的归纳和总结。
在建造房子时,需要针对房子的用途设计整个房屋的架构,确保房子在这个架构上的坚固性和可建造性。
写程序时同样如此,不同的应用需要使用不同的程序设计结构。
例如我们在LabVIEW中构建一个用户界面型程序时,往往首先在背面板中加入一个大的while循环以使程序持续运行。
如果需要响应用户界面事件则还需要加入一个Event事件结构。
那么我们是否曾经考虑过以下的这些问题:(1)应用中是否存在并行响应的情况?如在持续的数据采集过程中,是否需要同时响应单击菜单的事件?(2)底层获取的数据如何与上层的数据显示部分进行数据交互?(3)上层的界面如何受底层程序的控制?(4)同一个循环中采用哪种方式进行数据交换?是局域变量、全局变量、共享变量还是移位寄存器?(5)程序是否具有可扩展性?(6)如果程序运行过程中,发生系统错误或者硬件通讯错误,是否会停止运行?待错误排除后是否会继续运行?(7)如何组织程序中的核心数据结构?是否需要采用面向对象程序设计?(8)如何记录测试数据并生成报表?如何保存用户配置参数?(9)如何处理程序运行中的断电情况?重新启动时的继续运行?数据的最低丢失?(10)如何实现运行过程的采样触发和多点采样的同步?当然,也许只是使用LabVIEW临时地调试或开发某个小的应用,无需考虑上述的问题。
但是,如果使用LabVIEW开发一个典型应用的程序却无法回避这些问题。
因此,有必要对各种程序开发的应用进行归纳和总结,提取它们对应的LabVIEW程序结构中的共性。
此外,针对这些共性研究哪种结构更加适合于应用。
这些结论综合起来就形成了程序设计的模式。
对于初学者而言,理解和掌握程序设计模式往往能起到事半功倍的效果;而对高级用户而言,归纳各种程序设计模式又能够不断完善程序中遇到的问题,并衍生一套符合特定应用的特有的程序设计模式。
状态机是一种最为经典的程序设计模式,在LabVIEW 7.1(含)之前它几乎统治了大部分的LabVIEW主程序。
最基本的状态机结构如图1所示。
状态是状态机运行的经脉,在开始使用状态机模式撰写程序时需要将应用分为若干个状态。
下面以图中的应用为例说明基本状态机的使用。
【应用1】前面板具有3个按钮(Control)和1个波形显示控件Chart(Indicator),功能分别是:1)开始采集:Label是start,单击后开始进行模拟数据采集程序(这里使用随机数代替)。
2)关于:Label是dialog,单击后弹出对话框以说明这个程序的版权、帮助等信息。
3)停止:Label是stop,单击后停止程序的运行。
4)Chart:用于显示获取的随机数。
这是一个非常简单的应用,但是具有一定的代表性。
根据要求,该应用至少包含以下5种状态结构。
1)Initial:初始化状态;2)Idle:空闲状态,用于响应各种用户界面操作;3)acquire:采集状态,用于持续模拟采集数据;4)about:用于弹出关于和帮助对话框;5)stop:停止状态,退出循环并中止程序。
(a)背面板(b)前面板图1基本的状态机结构背面板仔细分析图中的基本状态机,可以看出状态始终贯穿整个应用程序,并由移位寄存器进行值的寄存和传递。
当前状态分支的结果将决定下一个状态,如图中的Idle状态。
在这个状态中,程序将自动检测前面板的三个按钮是否被按下。
如果start被按下,则进入acquire状态;如果dialog被按下,则进入about状态;如果stop被按下,则进入stop状态;否则如果没有任何按钮被按下,则仍然进入当前的Idle状态继续检测。
在acquire状态中,为了保证程序的重复采集使得下一个状态仍然为acquire,但是这样会导致程序无法停止(中断采集)。
于是需要在acquire状态分支中加入stop的探测,如果stop被按下,则不再进入acquire状态而直接进入stop状态。
从应用1可以看出,基本状态机模式大体上能够满足主程序结构的需要。
该模式能够很好地使得应用程序的各个功能以状态的方式有顺序地执行,并且保证了程序的可读性(以状态图的方式显示清晰明了)和扩展性(日后只需要扩展状态即可扩展相应的功能)。
事例中使用的是“string”型结构来标记状态,事实上也可以使用其它的数据类型替换,如ring、numeric或enum。
从严格意义而言,ring并不属于一种数据类型,它只是一种特殊的numeric性,其性质与numeric基本上一样。
尽管ring与enum控件从外观上看是一样的,如图2所示,但是它们实质上是不同的。
主要体现为以下7点:针对基本状态机模式的第(1~3)个问题,需要对模式进行改进。
本节将一一分析这些问题对应的解决方案,并最终形成一种新的状态机模式——消息队列型状态机模式。
(1)状态的分类不清晰。
这是一个涉及各个状态分类管理的问题,是一个组织问题。
我们可以做一个类比,在一个书桌上有许多种类的书籍(通信、计算机、机械、法律等),这些书都摆放在书桌上很整齐。
但是我们在寻找一本书时并不会觉得很迅速和随意,因为书籍的摆放是无序的,每次寻找书籍我们不得不从第一本开始浏览直至找到我们想要的书籍。
或许可以做一些改变,我们设置一些书立,将不同种类的书使用书立分开。
并且在书立上标明这些书籍表示的种类。
这样我们在寻找某一种书籍时就不需要从第一本书开始寻找了,只需要找到对应的书立,在这些书立中寻找即可。
让我们回到程序,并给程序的状态设置一些“书立”。
如图4所示,系统共有9个有效状态(UI Initial、Data Initial、Instr Initial、Temperature、Power、FFT、JTFA、Data Clean、Exit)。
如果把这些状态混在一起,我们需要找到某一个状态时会比较困惑和麻烦。
如同上面所述,将这些状态分为4类并设置了4个“书立”(Initial、Acquire、Analyse、System)分隔这些状态。
在实际的状态控制中,需要确保程序只会进入实际的状态中运行而不会进入到“书立”分支中,因此对每个“书立”加入了“-------”以示区别。
图4状态分类尽管我们只是进行了少量的修改,但是这的确有利于程序状态的组织和阅读,尤其是当程序具有很多个状态的时候。
(2)缺乏数据共享和错误处理机制。
在层叠式的顺序结构中,数据在帧之间的传递是靠“顺序局域变量”实现的。
那么如果在case结构中如何传递不同分支的数据呢?这个问题似乎很容易解决,使用局域变量,全局变量或共享变量都能够解决,但是这些并不是最优的解决方案。
因为上述的方式会明显系统运行的内存空间和时间。
由于状态机的基本组成元素除了case结构之外还有循环,因此可以使用移位寄存器来传递数据。
如图5所示。
图5状态机中的数据传递图5使用移位寄存器进行数据共享和传递,将所有的数据封装在一个簇中并对每个数据命名,这样在使用数据时就可以使用“Unbundle by name”或“bundle by name”。
需要说明的是,即使使用一个数据需要共享,仍然希望采用簇的封装形式,这样当后续需要增加扩展数据的时候并不会影响现有的数据引用。
(3)每一个状态分支只能够决定后面的一个状态,而无法决定一个状态序列(多个状态)。
在基本状态机中之所以存在这个问题是因为状态的传递使用的是Scalar(标量)形式,如果需要传递一个状态序列,很明显可以使用队列或数组进行状态的传递。
在LabVIEW程序设计模式中将这种具备处理状态序列的状态机称为“消息队列型状态机”,它是在基本状态机基础上的改进。
顾名思义,这种模式就像银行办理业务时排队一样采用队列的方式。
当储户进入银行时,首先到叫号机处领取号码进行排队(进入队列)并等待。
然后,当前面的储户办理完业务后就可以到相应的窗口办理业务(退出队列)。
事实上,这种方式在现代生活中随处可见。
在LabVIEW中至少有两种实现消息队列的方法。
如图6所示。
前者使用数组函数实现队列元素的入列和出列;后者使用队列函数实现队列元素的入列和出列。
二者都能够实现队列的有序操作和状态的序列变化。
图6消息队列型状态机模式本节解决了基本状态机模式中的(1)~(3)个问题,为了更好地比较和使用这些特点,特使用一个实例说明消息队列型状态机的使用过程。
【应用2】本例要模拟一个自动贩卖机的工作过程。
它的一次正常交易过程为:投币→选择需要购买的商品→找币,当币值不足或商品已经销售完毕时则无法购买。
程序的前面板如图7所示。
在贩卖机的左上侧有4个按钮。
(1)1USD:单击时表示投入1美元的货币,2USD和5USD类同;(2)Change Back:表示找零,也就是将目前剩余的货币退还给用户。
程序的右侧是5个按钮,表示5种不同类别的可乐(这里均使用了可口可乐的图标),每种可乐的价格均是1美元。
可乐的下面数字表示贩卖机中剩余的该商品的数量,初始为每种20瓶。
Current money显示贩卖机中剩余的货币数,你可以继续购买可乐或者选择退回。
单击Stop按钮将退出应用程序。
本例将使用本节介绍的消息队列状态机模式解决这个应用(也可以使用其它的设计模式)。
系统的功能并不复杂,关键是要判断贩卖机中的剩余钱数和剩余的货物数以决定交易是否成功。
图7自动贩卖机前面板程序背面板如图8所示。
系统分为5个状态,并分为2大类。
(1)第一类:Initiala)UI Initial:前面板界面的初始化。
b)Data Initial:数据的初始化。
(2)第二类:Systema)Idle(Default):空闲状态。
b)CheckMoney:贩卖机中的剩余钱数和剩余的货物数以决定交易是否成功。
c)Exit:退出程序。
程序开始运行时进入UI Initial和Data Initial状态,完成初始化操作。
从图中可以看出系统采用数组函数处理消息队列。
图8自动贩卖机背面板在UI Initial中,系统给标题栏和说明栏赋值,并将前面板的商品设置为不可购买状态,因为在初始化时还没有完成投币动作。
如图9所示。
图9UI Initial分支在Data Initial中包含两个共享的数据:Money和GState,前者表示贩卖机中剩余的币值,初始化值为0;而后者表示贩卖机中各个商品剩余的数量,初始化值为20。
数据使用移位寄存器传递以便于在各个case分支中共享和使用,如图10所示。
图10Data Initial分支CheckMoney分支主要是为了防止不合法的交易(如投入的币值不足或商品数量不足),如图11所示。
图11CheckMoney分支当程序运行到Exit分支时,将停止循环并退出程序,如图12所示。
图12Exit分支Idle分支用来监控前面板各个按钮控件的变化并执行相应的状态。