LabVIEW教程——程序框架与设计模式详解


前言

在LabVIEW开发中,当你完成一些程序后,某天可能会意识到:代码编写不是简单的堆程序,也不能没有规则的写VI,因为会极易造成混乱,导致模块间耦合严重,扩展性差,维护较为困难;因此就需要学习“框架设计”,将程序的组织进行抽象和规划。本文面向具有LabVIEW基础的开发者,详细解释了LabVIEW 程序框架与常见设计模式,帮助你基于规范化的组织方式构建维护性更好、可扩展性更强的应用程序。

设计模式(Design Patterns),又称软件设计模式,是针对软件工程中常见问题所总结出的可复用解决方案。它为开发者提供了通用的设计思路,有助于提升软件的开发效率、代码的可读性、系统的可扩展性与可维护性。采用广泛认可的设计模式,能够使程序结构更清晰,便于其他熟悉该模式的开发者理解和协作。

LabVIEW的软件设计模式一般分为基本设计模式、中级设计模式、高级设计模式。各模式细分如下:

模式类别模式细分
基本设计模式状态机、事件处理结构、主从设计模式、生产者消费者模式
中级设计模式QMH(队列消息处理器)
高级设计模式面向对象的设计模式、面向操作者的设计模式

一、基本设计模式

基本设计模式处理的是至少包含一个循环的简单模式,或者是多个循环之间仅在一个方向上进行通信的模式。常见的有单循环多循环,其中单循环包含状态机、事件处理结构,多循环包含主从结构、生产者/消费者结构。

1.1 单循环

1.1.1 状态机

1.1.1.1 概述

状态机是 LabVIEW 中最基本且常用的一种设计模式,它用来实现复杂的决策流程,通常通过状态图或流程图来表示。简单来说,状态机就是程序根据当前“状态”来决定下一步做什么,适合用在那些有不同工作阶段或步骤的场景中。

状态机是LabVIEW开发人员经常用来快速构建应用程序的基本架构之一。它特别适合用于实现复杂的决策逻辑,这类逻辑通常可以用状态图或流程图来表示。状态机可以完全依靠 LabVIEW 自带的基本函数实现,无需额外安装任何工具包或模块,简单高效,易于上手。

1.1.1.2 什么是状态机

在 LabVIEW 里,状态机一般是用一个包含在 while 循环里的 case 结构来实现的。程序开始时会根据输入的状态选择第一个要执行的“状态”。然后,程序根据用户的操作或者当前状态的计算结果,决定下一个要进入的状态。状态之间的切换是靠“移位寄存器”来控制的——它就像一个传递信息的管道,把当前状态传给下一次循环,告诉程序下一步要执行哪个状态。这样,程序就可以按照设计好的顺序,一步步执行不同的操作。

image-20250726103340276

状态机的优点在于可以将多个步骤按顺序连接起来,让每个步骤都能被清晰地执行或调试。对于刚入门的程序员来说,状态机的一个潜在问题是可能会出现状态间随意跳转的情况,导致状态流变得复杂和混乱,这有点像传统编程中容易引发混乱的 goto 语句。不过,尽管存在这些风险,状态机依然是非常实用且高效的设计工具。

状态机可以用整数、字符串或者枚举类型来表示状态。上图所示为不同类型的状态机示意图。状态机常用字符串枚举类型表示状态,它们各有优缺点:

  • 基于枚举的状态机优势是如果枚举类型发生变化(如果枚举是自定义类型控件),状态机会自动对应更新;缺点是新增或删除状态时,需要仔细检查相关代码,避免遗漏。
  • 基于字符串的状态机优势是添加或删除状态比较灵活方便;缺点是容易因为拼写错误导致程序出错。不过可以通过设置“默认”状态来捕捉这些错误,比如当遇到未识别的状态时弹出提示框,帮助发现拼写问题。如下图所示,这种方法能让状态机更加稳健,降低新手出错的风险。
image-20250726104056770

第一张图展示了LabVIEW状态机常见的三种实现方式。每种方法各有特点,适合不同的开发需求。

第一种(虽然不推荐)是使用整数来控制状态跳转。做法是将当前 case 的输出整数连接到下一个 case 的选择器输入。这种方式虽然简单,但不利于代码的可读性和维护,因为数字本身没有语义,程序逻辑不直观,case 名称也失去了意义。

更推荐使用以下两种方法之一:

1. 使用字符串作为状态选择器
这种方式通过字符串名称来标识每个状态,例如 “Init”、“ReadData”、“Process” 等。这样可以为每个 case 添加有意义的标签,让程序逻辑更清晰,也具有“自文档化”的效果。另一个好处是你不需要一次性写完所有状态,后续想添加新状态也很方便。
缺点是容易发生拼写错误,导致程序进入错误状态。解决方法是添加一个“默认” case,一旦遇到未定义的字符串,就弹出对话框提醒用户,这样可以及时发现问题。

2. 使用枚举类型(Enum)作为状态选择器
这是 LabVIEW 中非常常见且推荐的做法,特别是将枚举定义为类型定义(Type Definition)后,所有用到该枚举的地方会随着定义的更改自动更新。它也保留了字符串方式的可读性,并能有效避免拼写错误。
缺点是,在添加或删除状态时需要格外小心。此外,如果你在初期没有写完所有状态,建议加一个默认 case,以防止程序执行出错。

总的来说,无论使用字符串还是枚举,都是比整数更可维护、更安全的状态控制方式。选择哪种方法可以根据项目规模、团队协作习惯或个人喜好来决定。

1.1.1.3 为何使用状态机

状态机适用于具有多个明确状态的应用程序。在这种架构中,每个状态都可以根据条件转移到一个或多个其他状态,或者直接结束整个流程。状态的切换通常由用户输入或当前状态中的计算结果决定。

在很多应用中,状态机会从一个“初始化”状态开始,接着进入一个“默认”状态。默认状态中可以根据不同情况执行多种操作,具体行为由前一个状态、当前输入以及内部状态综合决定。此外,状态机中还常包含一个“关闭”状态,用于程序结束前的资源释放和清理操作。

除了强大的决策逻辑实现能力,状态机还体现了程序功能的有序组织结构,是应用程序设计的重要组成部分。随着应用规模的扩大和逻辑的复杂化,对良好结构设计的需求也越来越高。此时,结合状态图或流程图进行规划将非常有帮助,甚至在某些项目中是必不可少的。状态机不仅有助于梳理设计思路,还易于搭建和扩展,非常适合构建结构清晰、逻辑严谨的程序。

用例说明:

在以下类型的应用程序中,使用状态机模式能带来清晰的结构和良好的可维护性,尤其适合逻辑分支较多或需要响应用户操作的场景:

  • 单页或多标签页的对话框界面
    每个选项卡可以视为一个状态,用户点击不同的选项卡时,状态机会根据选择切换到相应状态。在每个状态中处理对应选项卡的功能逻辑,使界面控制更清晰、操作更稳定。
  • 自动取款机(ATM)程序
    ATM 操作流程自然适合状态机建模。例如可以包含以下状态:等待用户输入、验证账户、检查余额、吐钞、打印凭条等。每一步都有明确的输入和输出,适合用状态机按流程组织。
  • 测量控制类应用程序
    比如:启动一次测量、保存数据、显示结果,并等待用户下一步操作。可以设计为:等待输入 → 执行测量 → 记录数据 → 显示数据 → 返回等待。状态机让流程清晰、有条不紊。
  • 带用户界面的程序逻辑控制
    在用户界面开发中,不同的用户操作(如按钮点击、菜单选择等)对应不同的处理逻辑段落,每段逻辑可以作为一个状态。程序通过持续检测用户动作,决定进入哪个状态,从而处理相应功能。
  • 流程测试系统
    测试流程中,每个测试步骤可以看作一个状态。根据当前测试结果,状态机会判断是否进入下一个测试步骤,或者跳转到错误处理等其他状态。这种结构便于对测试过程进行深入分析和自动化控制。

这些例子展示了状态机在多种场景下的灵活性和实用性,尤其适合需要流程控制、用户交互或条件判断的应用程序。

1.1.1.4 范例程序

使用LabVIEW**创建项目(Create Project)**弹出对话框选择”Simple State Machine“,下图所示为对话框选择界面。按向导保存项目后,可自行学习。

haha

1.1.2 事件处理结构

1.1.2.1 概述

事件处理程序设计模式为处理用户与 LabVIEW 之间的交互提供了一种高效而灵活的架构。它可以用来响应各种用户操作,例如控件值的改变、鼠标的点击与移动、键盘输入等事件。

标准的事件处理程序通常由一个 While 循环和其中的事件结构(Event Structure)组成。事件结构可以被配置为监测特定类型的事件,每类事件对应一个 Case。当某个事件被触发时,程序就会自动跳转到相应的 Case 并立即执行其中的处理逻辑。

这种设计方式能让程序对用户操作作出快速响应,同时保持结构清晰,特别适合用于图形界面交互密集的应用程序。

Event_Handler_Design_Pattern
1.1.2.2 范例程序

可通过菜单栏”文件(File)---->新建(New)“,在弹出对话框选择Dialog Using Events可以创建简单事件处理结构,可用于简单学习。

image-20250726113337888

1.2 多循环

1.2.1 主从设计模式

1.2.1.1 概述

主从(Master/Slave)设计模式是 LabVIEW 开发中常用的另一种基本架构,适用于需要并行处理多个任务的应用程序。该模式通常由多个并行运行的循环组成,每个循环可以独立以不同的速率执行各自的任务。在这些循环中,其中一个被指定为主循环,负责协调和控制程序的整体流程;其余的则为从循环,负责具体的功能模块或子任务。

主循环通过消息传递机制与从循环进行通信,传递方式有队列(Queue)、用户事件(User Event)或通知器(Notifier)等方式。主循环发送指令或数据,从循环接收后执行对应操作。这样可以实现各模块之间的解耦,使程序结构更清晰,也便于后期扩展与维护。

下图所示为基本主从(Master/Slave)程序结构,新版本LabVIEW改为了Controller/Worker,只是名称更改,代码相同。

Master-Slave_Design_Pattern
1.2.1.2 应用场景

主从(Master/Slave)设计模式在开发多任务 LabVIEW 应用程序时非常有用。它通过将程序拆分为多个并行循环,让每个任务都可以独立执行,从而使整个程序更加模块化、易于管理。

在 LabVIEW 中,每个并行循环都可以看作是一个独立的“线程”,也就是程序中可以单独运行的一段逻辑。如果不使用并行结构,整个程序将被系统当作一个单线程运行,也就意味着所有任务只能一个接一个地执行;而将程序拆分为多个线程后,它们可以同时运行,系统会合理分配处理时间,使程序运行更加高效。这种结构的一个最大优势是可以更灵活地控制每个任务的执行频率,从而提升程序的整体性能和响应速度,也能带来更好的用户体验。LabVIEW 天然支持并行操作,因此非常适合实现主从结构。

举个例子:

假设我们正在做一个数据采集程序,既要采集波形,又要测量电压。最简单的方法是把这两个功能放在同一个循环中,例如每 50 次波形采集后测量一次电压。但这样会遇到问题:电压测量和记录可能比采集波形花的时间更长,结果导致波形采集也被拖慢,下一次采集必须等电压处理完才能进行。而且,如果我们后来想调整波形采集的频率,就可能不得不连带修改电压测量的逻辑,增加了程序维护的复杂度。

解决方法:

采用主从设计模式,我们可以把波形采集和电压测量分别放到两个独立的循环中(作为“从循环”),让它们并行执行。再用一个“主循环”来处理用户界面的操作,比如检查参数有没有变化。主循环可以通过局部变量、队列或事件与从循环进行通信,告诉它们何时更新设置或开始操作。

这样一来,不同的任务互不干扰,就算弹出对话框或用户操作有延迟,也不会影响采集过程的连续性。

1.2.1.3 范例程序

可通过菜单栏”文件(File)---->新建(New)“,在弹出对话框选择Master/Slave Design Pattern或Controller/Worker Design Pattern可以创建主从范例,用于简单学习。

Master-Slave_Design_Pattern1

1.2.2 生产者/消费者模式

1.2.2.1 概述

生产者/消费者设计模式是在主从模式基础上发展而来,主要用于优化多个以不同速率运行的循环之间的数据共享。它通过将数据生成和数据处理分离,解决了生成速度与消费速度不匹配的问题。

在该模式中,程序并行运行两类循环:

  • 生产者循环,负责生成数据;
  • 消费者循环,负责消费和处理数据。

这种结构有效地将生产和消费过程解耦,使得两者可以独立运行,提升系统的稳定性和性能。下图所示为基础生产者消费者框架示意图。

Producer-Consumer_Design_Pattern
1.2.2.2 为何使用生产者/消费者

生产者/消费者模式能够轻松支持多个进程同时运行,并允许它们以不同速率独立迭代。这个模式的核心优势在于它提供了进程间的缓冲通信机制,确保数据在生产和消费之间顺畅传递。当多个进程运行速度不同时,缓冲通信可以有效防止数据丢失或处理瓶颈,保证系统的稳定性和高效运行。以下为生产者/消费者在不同场景的引用。

缓冲通信
当多个进程以不同速度运行时,采用进程间缓冲通信非常合适。只要缓冲区容量足够大,生产者循环可以以高于消费者循环的速度运行,而不会造成数据丢失。

举例来说,假设一个应用程序包含两个进程:第一个进程负责数据采集,第二个进程负责将数据传输到网络。若数据采集进程的速度是网络传输进程的三倍,使用生产者/消费者模式时,数据采集进程作为生产者,网络传输进程作为消费者。拥有足够大的通信队列后,消费者可以访问并处理生产者采集的大量数据,极大地减少了数据丢失风险。

数据采集和处理
在需要按顺序处理多组采集数据的应用中,生产者/消费者模式非常适用。假设程序需要一边接收数据,一边按照接收顺序处理它们。由于数据生成(生产)速度往往远快于数据处理(消费)速度,生产者/消费者模式允许消费者循环以自己的节奏处理数据,而生产者循环则继续将数据排队等待处理。

如果生产者和消费者都在同一循环中运行,为了保证数据处理速度,数据采集速度就不得不降低。将数据采集和处理拆分成两个独立进程,就是生产者/消费者模式的优势所在。

网络通信
网络通信中经常需要两个以不同速率运行的进程:一个不断轮询网络并接收数据包,另一个提取并分析这些数据包。在这种情况下,轮询网络的进程是生产者,提供数据;分析数据的进程是消费者。使用生产者/消费者模式,两者通过排队通信实现数据缓存,尤其在网络繁忙时,缓冲可以保证数据包被顺畅接收和处理,防止因处理速度跟不上而丢包。

队列消息处理
队列消息处理架构是生产者/消费者模式的特殊形式,数据队列用于在生产者和消费者循环间传递数据。队列不仅实现了数据交换,还起到了缓冲的作用,保证数据在不同速率循环间的稳定传输。

1.2.2.3 范例程序

可通过菜单栏”文件(File)---->新建(New)“,在弹出对话框选择Producer/Consumer Design Pattern可以创建生产者/消费者范例,用于简单学习。

image-20250726144025421

二、中级设计模式

中级设计模式处理的是多循环模式,通常通过实现消息传递机制在循环之间进行双向通信。常见的有QMH(队列消息处理器)、JKI State Machine、AMC(Asynchronous Message Communication)。本次中级设计模式主要详细介绍QMH(队列消息处理器)架构。

2.1 QMH(队列消息处理器)

2.1.1 概述

QMH(队列消息处理器)设计模式是将生产者/消费者架构与事件处理架构结合而成的一种高效程序结构。它将用户界面的事件响应与后台任务处理清晰地分离,提升了程序的可维护性和响应效率。

2.1.2 QMH详解

在 QMH中:

  • 生产者循环被称为事件处理循环(Event Handler Loop,EHL),它包含一个事件结构,用于监听用户界面上的各种操作(如按钮点击、控件变化等)。
  • 当 UI 事件被触发时,EHL 会将相应的“消息”加入到一个队列中。
  • 消费者循环称为消息处理循环(Message Handler Loop,MHL),它从队列中依次读取消息,并根据不同的消息类型执行对应的处理操作。
  • 消息(从EHL到MHL),消息可以采用多种结构,通常包含两个部分,分别是消息名称和数据,其中消息名称通常使用字符串。
  • 用户事件(从MHL到EHL),用户事件用于反方向通信,即从 MHL 向 EHL 发送信息。用户事件的数据类型或结构可以是任意类型。

QMH 是构建中大型 LabVIEW 应用程序的推荐模式之一。

1200px-Queued_Message_Handler_Design_Pattern

上图所示为基于队列和用户时间构建的基础QMH框架,请注意QMH 中存在两个或更多并行循环。如果其中一个循环停止,而其他循环未同步停止,程序将会无响应。因此,任何一个循环的停止条件都应确保其他循环也一并停止。

  • 如果在 EHL 中发起停止操作,应发送一条消息命令 MHL 停止;
  • 如果在 MHL 中发起停止操作,应发送一个停止类型的用户事件通知 EHL 停止。如果还有其他 MHL,由 EHL 向它们发送停止消息。

2.1.3 创建QMH应用

LabVIEW提供封装后的QMH框架模板,可直接用于程序开发,通过启动LabVIEW>>Create Project>>Templates>>Queued Message Handler来找到该项目。默认情况下,此模板包括一个事件处理循环和一个消息处理循环。下图为QMH程序框图,新手可用该模板尝试简单编程,理解整体思路。

image-20250608144809618

2.1.4 范例程序

通过启动LabVIEW>>Create Project>>Sample Projects>>Continuous Measurement and Logging来找到该示例项目。该示例项目是基于上述 QMH 框架开发的。能够看懂并理解这个程序,基本上就算是入门 LabVIEW 开发了。由此可见,QMH 框架是 LabVIEW 学习过程中不可或缺的重要程序结构。

随着学习的深入,你会逐渐接触到更多高级的架构,比如 JK 状态机、AMC、DQMH 以及 Actor Framework。仔细观察就会发现,这些架构在设计思路上或多或少都融合了 QMH 的理念。因此,掌握 QMH 不仅是 LabVIEW 学习的基础,也为后续理解和使用更复杂的架构打下了坚实的基础。

该实列程序前面板和程序框图如下:

image-20250608155558243
2

Continuous Measurement and Logging 示例项目可持续获取测量数据并将其记录到磁盘中。它并行执行 5 个循环:

  • 事件处理 (Main.vi)— 事件处理循环 (EHL),它根据前面板事件(例如用户单击**“开始**”或**“设置**”)生成消息。
  • 用户界面消息收发 (Main.vi)— 一种消息处理循环 (MHL),它从 EHL 接收消息,并通过向其他 MHL 发送消息来做出响应。
  • 采集数据 (Acquisition.lvlib:Acquisition Message Loop.vi)— 持续采集数据的 MHL。默认情况下,此模板模拟获取的数据。
  • 记录数据 (Logging.lvlib:Logging Message Loop.vi)— 持续记录采集数据的 MHL。
  • 显示数据 (Main.vi) - 使用采集的数据更新波形图的 While 循环。

此示例项目还提供了一个 Settings 对话框 (Settings.lvlib),可用于配置应用程序。

2.1.5 QMH应用经验

  • 不要在事件处理循环(EHL)中执行耗时操作代码。

​ 如果事件里的代码运行时间太长,会导致界面卡住,用户可能以为程序死机了。EHL 的主要任务是快速响应 用户操作,所以应该尽量保持它的运行流畅。

  • 如果存在多个 消息处理循环(MHL),应为每个 MHL 使用独立的队列。

​ 如果你有多个 MHL,不要让它们共用一个队列。因为队列的机制是“谁先取,谁得到”,而且无法控制是谁取 到了。如果多个 MHL 共用一个队列,某些消息可能就无法被正确处理。因此,一个队列对应一个 MHL 是最 安全的做法。

  • 如果使用字符串作为 Command 的数据类型,必须在 MHL 中处理默认(Default)分支。

    当 Command 拼写错误时,会进入默认分支,因此建议在默认分支中抛出错误或弹出提示框以识别无效命令。为避免此类问题,也可以改用枚举类型。

  • 如果使用枚举(Enum)作为 Command 的数据类型,应将其设为类型定义(TypeDef)。

    这样当枚举项发生更改时,所有相关的常量和控件都会自动更新。枚举类型也可以有默认分支,就像字符串命令一样。但只要你已处理了枚举中的所有项,就不需要默认分支。当枚举中添加了新项时,不使用默认分支的好处是 VI 会报错(VI 变为“断裂”状态),提醒你新添加的枚举项尚未被处理,让你自行决定如何添加处理逻辑,而不是默认为默认分支执行。

三、高级设计模式

高级设计模式涉及多个模块,每个模块包含多个循环,这些循环通过消息机制在循环之间以及模块之间进行通信。常见高级设计模式有两种,分别是面向对象设计模式、面向操作者(Actor)的设计模式。

3.1 面向对象设计模式

3.1.1 概述

面向对象编程(OOP)是一种常用的编程方式,LabVIEW 面向对象编程(LVOOP)借鉴了其他面向对象语言中的概念,包括类结构、封装和继承。你可以使用这些概念来创建更易维护和修改的代码,而不会影响应用程序中其他部分的代码。下表所示为可以应用于 LVOOP的面向对象设计模式

模式名称功能简介在LVOOP中的实现方式
聚合模式 (Aggregation)一个对象拥有或负责一个对象数组,并为该特定类型的数组定义特殊行为。在类的私有数据中嵌套包含其他类的对象,类之间通过组合而非继承复用功能。
命令模式 (Command)将操作请求封装为对象,包含数据和行为;通过统一父类接口执行命令,客户端无需关心具体实现;支持延迟执行和消息传递;便于扩展维护,每个命令对应一个子类重写执行方法;常用于消息处理,避免数据类型转换。创建一个父命令类,定义 Execute 方法,多个子类分别实现不同的行为,通过动态调度在运行时调用具体子类方法。
装饰器模式 (Decorator)通过一个中间对象(装饰器)静态或动态地给现有对象添加新行为,而不影响同类其他对象的行为。定义一个基类接口并使用子类对其包装,添加额外功能;装饰器类持有被装饰对象引用,调用原始方法并添加扩展行为。
委托模式 (Delegation)允许一个对象(委托者)通过组合另一个对象(委托对象)来复用功能,委托对象代表委托者执行操作,无需共享共同祖先。委托者持有委托对象实例,并将特定行为转发给它。类中包含委托对象的实例,方法调用时转发给委托对象,实现功能复用和职责分离,模拟面向对象语言中缺乏的委托机制。
工厂模式 (Factory)根据输入(如枚举或字符串)动态创建不同子类对象,隐藏实例化逻辑,客户端通过父类接口操作新对象,常用于插件或动态加载场景。创建一个工厂方法或工厂类,根据输入返回不同子类对象,支持运行时动态加载,实现代码复用和扩展。
模板模式 (Template)父类定义算法的执行流程,子类可重写其中某些步骤以改变具体行为,但不能改变算法整体结构或步骤顺序。适合用于行为固定但细节可变的流程控制场景。在父类中定义控制流程的主 VI(例如使用静态调度或调用多个动态调度方法),子类通过重写各步骤方法来自定义行为,实现算法可定制但结构不变。

在 OOP 中,**类(Class)是一个模板,它定义了数据(也叫属性)和处理这些数据的方法(也叫功能)。当你需要使用这个类时,就会创建一个对象(Object),也就是类的一个具体实例。**你可以通过对象来调用类里定义的方法来完成各种操作。

一个很重要的特点是:类的内部细节对外部(客户端)是隐藏的。这意味着,类内部如何实现功能,不会影响外部使用它的代码。即使类内部发生了改动,只要接口(方法)不变,外部代码都不需要修改。

此外,类还支持继承。也就是说,一个类可以继承另一个类的功能,还可以选择重写(Override)父类的方法,来实现不同的行为。
在使用时,外部代码可以把子类当作父类来使用,而不需要知道它到底是哪个具体的类。这就叫做多态性

LabVIEW 中还支持一种机制叫动态分派(Dynamic Dispatching)
当你调用一个方法时,如果子类中有同名方法,程序会自动调用子类的版本;如果没有,就会使用父类的方法。这使得程序更灵活、更易扩展。

3.1.2 LabVIEW中的面向对象编程

较早之前,LabVIEW已支持面向对象编程(OOP),并遵循面向对象的设计原则。为了更好的理解面向对象设计模式,以下针对LabVIEW中的面向对象编程做简单介绍。

3.1.2.1 LabVIEW的类

在 LabVIEW 中,类是由“类库(Class Library)”定义的,是一个以 *.lvclass 为扩展名的文件。它是一个 XML 文件,用于存储该类所拥有文件的信息。可通过 Project Explorer(项目资源管理器)来查看和编辑这些文件。不建议直接修改 XML 文件内容。类是基于 项目库(Project Library) 扩展而来的,它具备一些特殊能力,比如:

  • 能保存对象的数据(即私有数据)
  • 支持封装(保护内部实现)
  • 支持继承(可以扩展其他类的功能)

这些特性使得 LabVIEW 能实现标准的 面向对象编程(OOP)

同时,LabVIEW的类就像一种你自定义的数据类型。一个类包含两部分内容:

  • 私有数据控件(Private Data Control):用来保存对象内部的数据。
  • 方法(成员 VI):用来访问或操作这些数据的 VI(程序块),根据功能类型不同,这些VI有不同的称呼如下:
类型说明
访问器(Accessor)读写私有数据的 VI,比如“获取温度”、“设置速度”等
属性(Property)定义在“属性定义文件夹”中的访问器,类似 LabVIEW 中的“属性节点”功能
方法(Method)执行某些功能的 VI,通常会通过 Class 端口传入/输出对象
构造函数(Constructor)LabVIEW 没有专用构造函数节点,但你可以通过“常量对象”或封装初始化过程的 VI 来实现类似功能,通常称为“构造 VI”

每个方法都有访问范围,也就是说,不是所有人都能使用所有方法。常见的访问级别有:

  • Public(公开):任何地方都可以调用这个方法。
  • Protected(受保护):只有这个类和它的子类可以使用。
  • Community(社区):只有这个类和它的“友元类”可以使用。
  • Private(私有):只能在这个类内部使用。
3.1.2.2 成员VI的创建

可在 Project Explorer 中右键点击类,然后选择“New(新建)”并选择要创建的项,从而添加类成员VI。下图所示为创建过程。

image-20250726174803071

下表所示为各选项功能说明:

选项名称功能说明使用场景
VI创建一个空的 VI,但仍属于该类(类成员)。用于定义类的方法,比如内部算法、工具函数等。
Virtual Folder创建一个虚拟文件夹,用于在 Project Explorer 中组织类的成员。不会影响磁盘结构。用于分类成员(如:Methods, Properties, Utilities)。建议使用来保持工程整洁。
Property Definition Folder创建一个“属性定义文件夹”。将访问器放在此文件夹中,可使用属性节点(Property Node)调用。推荐将属性(如温度、电压)放在这里,便于模块化访问。
VI from Dynamic Dispatch Template创建一个 动态调度(Dynamic Dispatch)方法,可由子类重写。用于父类定义接口,子类实现差异化行为(典型多态用法)。
VI from Static Dispatch Template创建一个普通方法(Static Dispatch),不支持重写。适合用于不需要扩展的通用功能,如日志写入、配置加载等。
VI for Data Member Access…启动 Create Accessor Dialog,用于创建访问类私有数据的读写器(Accessor)或属性(Property)。用于封装类的数据字段(如电压、电流),避免直接操作私有数据。
VI for Override启动 New Override Dialog,选择父类/祖先类的动态方法进行重写(Override)。子类必须实现父类指定的接口(类似抽象类实现接口)。
Type Definition创建一个 .ctl 文件,并设置为类型定义(TypeDef)。用于定义结构(如 Cluster、Enum),在多个 VI 间共享。
3.1.2.3 LabVIEW的对象

对象是类的特定实例。在面向对象编程(OOP)中,**类(Class)**用来描述一类事物的通用特性。你可以把它看作是一个“模板”或“蓝图”。

比如,我们可以创建一个“汽车类”来表示所有汽车的共同特点。这个类会包含一些汽车的通用数据和行为,比如:

  • 数据(特性):车门数量、齿轮数量、颜色、品牌
  • 行为(方法):加速、刹车、鸣笛等

**对象(Object)**是类的一个具体实例。也就是说,如果“汽车类”是一个模板,那“某一辆具体的汽车”就是一个对象。

举个例子:

  • 类:汽车类(Car)
  • 对象1:张三的白色轿车,有4个车门、6个齿轮
  • 对象2:李四的蓝色卡车,有2个车门、10个齿轮

这两个对象虽然来自同一个类,但它们的数据可以不同。它们都可以执行“加速”“刹车”等行为,因为这些方法是由类定义的。

基于某个类可以生成多个对象,每个对象都具备这个类定义的数据结构和功能。

3.1.2.4 LabVIEW类的两种用户

在学习和使用 LabVIEW 的面向对象编程(OOP)时,我们可以把用户分为两类,这样更容易理解每个人的职责和技能要求:

  1. LabVIEW 类开发人员

这一类用户是编写和设计类的人,也可以理解为“类的作者”。

  • 通常是具备面向对象编程经验的开发者。
  • 他们负责创建类、定义数据(属性)和行为(方法),以及处理封装、继承等面向对象特性。
  • 需要深入了解 LabVIEW 类的结构和工作机制。
  • 编写的类最终是给其他人使用的(即“类用户”)。
  • 他们设计类的时候,应该保证即使类将来有所修改,也不会影响已经使用这个类的程序。

举个例子:像搭积木的人,设计了各种标准积木块,让别人可以自由组合使用,但积木的内部结构不用别人关心。

  1. LabVIEW 类使用人员

这一类用户是使用别人创建好的类来编写程序的人。

  • 不需要自己编写类,也不必理解类是如何实现的。
  • 即使对面向对象编程不熟悉,也可以使用这些类。
  • 可以通过类提供的接口(比如方法 VI)来调用功能。
  • 无法访问类的内部私有数据或逻辑,只能使用开发人员允许的部分。
  • 他们只需要知道如何使用类、类包含什么数据类型、有哪些可调用的功能
  • 还需要注意类升级后的兼容性问题,例如:新版本是否会影响已有程序。

可以理解为使用积木的人,只需要按照说明书拼装和使用积木,不需要关心积木内部是怎么做出来的。

3.2 面向操作者(Actor)的设计模式

3.2.1 概述

面向 Actor 的设计模式是利用 Actor 模型(例如 Actor Framework)的一类软件设计模式。在 LabVIEW 中,Actor 是构建大型、高度并行化、可扩展系统的强大工具。

从数据流转向消息流

  • 在传统 LabVIEW 编程中,数据流用于描述模块的执行逻辑。
  • 对于大型系统,这种方式难以描述模块之间的通信与协作
  • Actor 模型通过明确的消息传递机制来组织模块间的交互,适用于模块众多、交互复杂的系统。
  • 数据流依然适合组件内部生命周期短、逻辑简单的进程

3.2.2 Actor Framework 设计模式

Actor Framework 是一个功能强大的工具,它的实现方式有很多种。以下是三种基于 Actor Framework 实现的面向 Actor 的设计模式。但它们是以单一 Actor 的形式实现的。这些模式的真正优势体现在多个 Actor 协同工作的场景中。包括以下三种模式:

模式名称简要说明
状态机 Actor(SMA)使用消息链控制状态转换,不重写 Actor Core。逻辑纯粹由消息驱动。
带队列的状态机 Actor(QSM Actor)类似传统状态机,每次从消息队列中读取状态或命令,并依序执行。
带队列的消息处理器 Actor(QMH Actor)采用更明确的消息类别进行处理,适合响应多样的外部请求。

总结

本文更多的是整理讲述概念性的内容,对LabVIEW编程设计模式有一个清晰的认识。可关注博主,WeiXin同名,后续会针对一些常用框架,比如JKI State Machine、AMC、DQMH以及Actor Framework等架构做详细解析,帮助大家更好的学习和使用LabVIEW。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未央LabVIEW

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值