测量与测试世界 专注 LabVIEW 传感器 仪器仪表 数据采集 工业控制 虚拟仪器 系统集成 知识 设计 应用 开发 学习 交流 测控产品应用

国内最专业
工业自动化测量与测试最新资讯社区

操作者框架和面向对象编程用于解决软件工程中的重大问题

LabVIEW使得工程师和科学家均能够编写代码。 部分工程师仅用它来编写概念验证代码和一次性测量(程序)。 但是LabVIEW是一种成熟的编程语言,可支持复杂的软件工程实现各种应用。 本文阐述了使用LabVIEW进行更复杂编程时需要了解的概念。我们将通过LabVIEW面向对象编程和操作者框架范例来指导您编写高质量的代码*。

 *高质量的代码是指可调整(scalable)、模块化(modular)、可复用(reusable)、可扩展(extendable)且简单(simple)(简称smores)的代码。

1. 什么是接口、实现和API?

接口:两个系统、主体、组织或实体衔接和交互的点。 在软件中,接口是指一个软件模块与另一个软件模块的交互方式。 在LabVIEW中,最简单的接口是VI连接板。 当我们有多个进程时,我们可以添加序列、通知器、用户事件或其他信息发送方法。 接口就是我们可以使用这些工具来发送的信号。 在操作者框架中,接口是指我们发送的消息对象。

实现: 实现其实就是“如何做”。 它是程序的内容——队列消息处理器框架的操作,或操作者框架消息执行的方法。

应用程序编程接口(API):API规定了某些软件组件如何与其他组件的交互方法,是两个程序之间交互的规则。 任何足够大的软件都是由许多相互之间交互的小程序构成。其中任何一个组成部分都不用很复杂,但也没有任何一个组成部分可以独立完成所有工作。 这些组成部分协同配合才能完成任务。 API定义了与程序配合工作的方式。 在操作者框架中,API是指发送给操作者的一系列消息以及操作者回复的一系列消息。


2. 在LabVIEW操作者框架中定义接口

程序之间的互动是双向的。 程序之间相互发送消息。 这些消息就是接口。 操作者可以发送和接收消息。

程序通常需要调用其他程序才能完成任务。 这就是大型软件应用程序的开发方法,我们让程序调用子程序,子程序再调用它们的子程序,以此类推。

操作者消息

可复用的程序必须具有通用性,且不依赖于调用它的程序。 假设我们希望追踪时间,我们设计了一个已用时间定时器。 如果我们拥有一个使用定时器的进程,那么通过该进程启动定时器。 调用定时器的进程可识别该程序的接口,即“开始定时器”、“停止定时器”、“重置定时器”。 程序接受的消息就是定时器接口的接收部分。

图1. 已用时间定时器的LabVIEW库。 该库包含定时器、方法、定时器的消息类和定时器对调用程序的应答消息。

针对调用程序的抽象消息

定时器不应依赖于调用该定时器的进程。 定时器应向调用它的进程简单地报告自己的状态(已用时间、完成状态、需要复位)。 在软件工程方面,我们希望尽量减少耦合。 问题是: 操作者通过发送消息来回复调用进程。 该消息可包含提供消息详情的数据。 操作者需要特定类型的消息才能将数据嵌入消息中。 但是调用进程也需要特定的消息类型才能读取该数据。 如果操作者总是使用调用进程所需的类型,那么其他操作进程将无法复用该操作者。 我们需要设计一种方式来让操作者根据调用进程选择要使用的类型。

解决方案之一: 一种直接的解决方案是要求操作者定义应答消息的类型。 然后调用进程定义该回复消息的新子类型。 运行时,调用进程为操作者提供该子类型消息的实例。 该子类仍将回复数据存储到特定消息中,但子消息知道要从指定调用程序调用方法。

在本例中,定时器操作者创建了一个用于报告定时器状态的消息。 调用进程创建了该消息的子消息,并覆盖Do.vi,以调用特定于该调用进程的方法。

定时器并不知道哪个进程对其进行调用,但是调用进程知道自己调用了一个定时器。

图2. 定时器应答信息的自定义簇类型。

图3. 定时器用于向调用程序发送其状态的方法。 注意:定时器并不知道调用进程的类型。

将继承与应答消息类一起使用可实现定时器的复用,因为新的进程可以创建消息的新子类,并将其交给定时器。 定时器并不知道子类型是否位于连接线上。 定时器将数据放入父类容器。 调用进程接收消息并访问父类消息的数据。

图4. 程序调用定时器并将应答消息类放入定时器属性。 这是定时器应答消息类的子类。

图5. 调用程序接收的定时器应答消息和通过父类容器访问的数据。

 

图6. 定时器应答消息的类层次结构和该定时器调用程序专用的子类。

至页首

3. 认真思考接口

为了最大程度利用软件的通用部分,考虑常见情况是非常重要的。对于定时器,这可能非常明了。消息可能是“开始”、“停止”、“重置”、“设置时间限制”。 对于其他程序,接口可能就比较复杂。 比如用于让程序进入特定状态而不是进入某种设置的命令,如:“Initialize”(初始化)命令或“Turn On” (打开)命令。 这些命令比“Start At XYZ Setpoint”(从XYZ设置点开始)更为通用。 您可尝试更有效地利用这些非专用型命令。想一下一般情况可处理的命令。 将这些命令添加到接口中。

如果你技术娴熟的话,可将被调用的程序替换为另一个同类型的程序,而且无需改变调用程序。假设使用另一种类型的定时器 - 如果该定时器包含所有相同的命令,那么调用程序不需要重写就可使用该定时器。

采用特定实现的命令也是可以的。 这些可以成为特定实现所需的消息。 特定实现可以包含可发送给调用进程的其他消息。这其实是扩展了接口。 使用这样的消息可能会限制可复用性,但复用性在软件工程中并不总是具有最高优先级。 换句话说,可在全面考虑具体情况之后使用特定实现的消息。

至页首

4. 实际(更大型)范例


我们在凤凰核实验室(Phoenix Nuclear Labs)搭建了粒子加速器。 我们采用不同的配置——功能不完全的测试系统、软件测试需要仿真非正常条件等。 我们使用带有用户界面的PC和两个带有各种C系列I/O模块的NI CompactRIO控制器构建了一个系统。系统运行依靠三个主要执行程序。 两个实时执行程序部署至CompactRIO控制器,另一个执行程序运行于控制室的PC上。 这里总共有超过5,000个VI。为了使问题更容易理解,我们将其分解为多个小问题。 该系统由五个子系统组成。 每个子系统由多个组件构成。 每个系统需要控制9-15个电源,最大的电源电压可达300 kV DC。 我们开始分解问题。

图7.凤凰核实验室内的加速器列和差分泵抽式终端。

我们选择采用模型-视图-控制器模式 (MVC) 架构。 因此,物理硬件也有软件“模型”。 这里的重点在于这些模型会向调用程序报告它们的状态,且模型可以接受命令,继而进入某个状态。

图8. 模型-视图-控制器图。 用户界面仅仅是该系统的一个“视图”。 命令可以通过用户界面发送到系统控制器。 系统控制器和系统模型决定着响应行为。

图9. 项目的系统、子系统及组件级别。

  

图10. 系统状态菜单。

系统向子系统发送命令:Go to State A(进入状态A)。系统并不知道子系统是如何实现的,只知道可以发出命令。 子系统负责处理状态请求并回复自身状态,如“Going to State A”(正在进入状态A)或“In State A” (已处于状态A),甚至是“Cannot go to Requested State”(无法进入请求的状态)。 子系统必须遵循接口。 子系统必须向系统发送系统预期的应答。 如果你更换子系统,则你需要知道新的子系统必须满足的一系列要求。 具体来说,子系统必须接受特定类型的状态更改命令并回复特定类型的状态应答。 该架构还意味着系统只需关注子系统的状态信息就可以决定系统的状态。 系统并关心实现的情况。

图11. 离子源状态菜单。

图12.加速器列菜单。

图13. 离子透镜状态菜单。

图14. 终端状态菜单。

 

类似地,子系统可能会调用不同的组件。 这里主要有两种情况:组件替换成其他模型或对组件进行仿真。 在两种情况下,我们都不希望为了使用新组件而重新编程子系统。 我们希望”插入“新组件并复用其他组件。 为了实现这一目的,我们必须让组件“模型”处理状态请求并向调用程序报告状态。 我的电源模型状态包括 “Steady state”(稳态)、“Transitioning”(正在转换) 、“Error”(错误)等。 我也获得了预期的一系列电源数据值,具体来说就是设置值和实际电压/电流。 这些是电源的“接口”: State(状态)、Setpoint(设置值)、Actual Voltage(实际电压)、Actual Current(实际电流)。如果我需要新的电源,我就知道该模型的要求。 该模型必须接受状态更改命令并回复预期的数据。 满足这些要求后,我就可以使用不同的电源,知道该电源可正常工作,且不需要更改子系统逻辑和系统逻辑。 与系统级一样,子系统只需知道对应组件的状态信息就可计算子系统的状态。 每个级别的逻辑都极大简化了。 而且,子系统不需要了解特定实现的情况,而且可根据需要换入/出各种不同的实现。


[+] 放大图片

图15. 可“实现”的各种不同电源

图16. 抽象化电源的“Enable”(启用)方法。

图17. 具体化通过FPGA控制的模拟电源的“Enable” (启用)方法。

图18. 具体化高压电源(HVPS)EthernetIP接口的“Enable” (启用)方法。

大的软件问题其实只是一系列小软件问题的集合。 将问题分割成不同的小问题并非易事。 希望本文介绍的概念能够帮助您更有效地分解问题。

分享:

相关推荐

评论