Workers框架入门知识详解

电子说

1.2w人已加入

描述

0. Workers的安装

Peter Scarfe, Creator of Workers for LabVIEW, 在VIMP中搜索Worker进行安装即可,本文档基于Workers 3.1.1版本进行说明。

API

1. Workers的设计背景

虽然LabVIEW可以很容易地以非常高效的方式开发小型和独立的任务,但对大多数人来说,开发一个更复杂的多线程应用程序,仍然是一个比较困难且耗时的过程。需要多个独立循环、且健壮、可扩展、易于调试的应用程序需要时间和经验去开发。

Actor Framework采用LVOOP封装设计,提供了增强的可伸缩性、可扩展、和优先级队列,但是它对新手开发人员来说理解相对复杂。所以作者希望可以做更多事情来帮助新手开发人员以一种更简单、快速且轻松的方式开发出高质量的应用程序,而无需具备LVOOP基础,或是学习一些新的不熟悉的架构,于是在使用广泛且被大家熟悉理解的QMH框架基础上,并且为了提高开发效率集成一些脚本工具,便创建了Workers。

作者希望通过Workers可以提高新手和高级开发人员的生产力,让他们以更低复杂度和更短的时间开发更大的项目。

2. Workers的特征

2.1 模块化

一个Worker就是一个独立的QMH(Queued Message Handler)模块,类似搭积木一样将它们组织在一起,通过多个Worker的协作,可以很轻松地创建它们的层次结构来构建应用程序的框架。模块化设计还使得它们可以通过Create/Add Worker Tool被更好的复用和扩展,Workers被设计的小而灵活,并且可以被静态或动态加载。

2.2 可扩展

Workers允许你通过使用Create/Add Worker Tool快速创建多个Worker来构建程序的功能框架,大大减少开发时间和未来需要扩展的工作量,可以很方便地使用其它项目中开发好的Worker模块,或创建自己的Worker模板在该脚本工具中进行使用。

2.3 优先级消息队列

参考Actor Framework中消息队列设计改编而来,性能和限制基本与之一样。它提供了一个多优先级的消息队列,包括Low、Normal(default)、High、Critical(Framework Only)四个优先级,提供了更加灵活的消息优先级处理方式。

2.4 调试器

一个框架的好坏取决于它的调试器!Workers Debugger一开始就是和Worker框架一起开发的,旨在帮助让运行的代码更加透明且易于导航。Task Manager显示了所有正在运行的Workers的当前状态,并允许你跳转到正在运行的克隆副本中以进行进一步调试。Message Log按时间顺序列出所有Workers消息处理循环之间发送的消息,消息入队和出队列的时间和地点,以及Workers中哪里发生了错误,借助调试器可以很容易定位问题并直接跳转到它们,以便立刻修复它们。

2.5 层次视图工具

应用程序中所有的Workers的调用链层次关系是怎样的?它们是被其它的Worker静态还是动态进行加载的?层次视图工具可以为你展示这些信息,通过平面视图以树状图为你展示Workers之间的关系。

2.6 分支标签

用来定义消息处理循环的分支,它是一个带字符串输出的VI,用来替代以往用的字符串常量,相比字符串常量更具优势,方便修改,查找跳转追踪消息流向,在Workers中提供了Quick Drop插件来自动创建,很实用的设计考虑。

2.7 支持使用LVOOP进行扩展

虽说Workers适合没有LVOOP基础的新手实用,但了解LVOOP相关知识可以更好地实用Workers框架,它采用LVOOP进行设计,提供了非面向对象编程所不具有的一些优点,所有的Workers都继承自Workers.lvclass,主要的一些公共API都包含其中,框架已经帮我们实现好了,大大减少了重复代码,当开发的多个Workers有相同的行为时,可以引入中间层,新增公共方法代码或重新Workers的公共API,这样就可以被继承自它的Workers使用了。

3. Worker的构成

3.1 Worker的定义

简单定义:模块化的消息队列处理程序

高级定义:一个继承自Workers.lvclass的类,其中包含一个消息处理程序及其相关支持代码和控件。

API

每个新创建的Worker都包含下面几个部分:

API

Initialization Data.ctl:自定义簇,Worker在初始化时期望接收的任何数据的定义。

Main.vi:包含QMH的VI,也就是Worker的核心程式码。

3.2 Workers的层次关系

典型的应用程序都是由多个Worker分层组合构建而成,一个Worker可以在Main.vi中调用一个或多个其它的Workers,如下图由三个Workers构成:

API

Worker A:最顶层的Worker,通常称为 "Head Worker",应用程序通过启动它的Main.vi开始运行。它也被称之为 Sub Worker 的 "Manager",它负责管理它的所有Sub Workers的启动、初始化和关闭。

Worker B / Worker C:它们的Main.vi在Worker A中被调用,因而被称作是Worker A的 "Sub Worker"。

Manager 和 Sub Worker两个术语用来描述Workers直接调用与被调用者之间的关系。

API

3.3 Workers中的重要分支(框架需要,不能删除)

3.3.1 Default

QMH中的默认分支,里面有调用一个叫Common Case.vi的VI,其中包含了所有Workers的一些公共代码分支,框架需要用到必须存在。

3.3.2 Initialize

Worker运行起来后会第一个被执行的分支,在该分支中会接收到它的 Manager 发送给它的一些初始化数据,也就是在 Initialization Data.ctl 中定义的数据。在该分支中会调用 Write(Set) Initialization Data - Worker Name.vi 将初始化数据加载到缓存中 (如果需要的话),随后会调用Initialize subWorkers.vi 给所有的 Sub Workers发消息执行 “Initialize"分支,如果已经缓存了初始化数据会一起捆绑到消息中被发送过去。

如果该Workers没有 Sub Workers,Initialize subWorkers.vi会调用执行 "All subWorkers Initialized" 分支。

3.3.3  All subWorkers Initialized

当所有 Sub Workers 都初始化完成时会执行该分支(当一个Worker初始化完成时它会调用 Initialize Notify.vi 通知它的 Manager Worker),如果你想在所有 Sub Worker初始化完成后执行某些任务,可以在该分支中进行。

3.3.4 Start Exiting

通过调用 Start Exiting Worker.vi 会执行该分支,在该分支中会调用 Start Exiting subWorkers.vi 通知所有 Sub Workers 执行 ”Start Exiting“ 分支开始退出操作。

如果该Workers没有 Sub Workers,Start Exiting subWorkers.vi 会调用执行 "All subWorkers Exited" 分支。

3.3.5 All subWorkers Exited

当所有的 Sub Worker都成功退出时,框架会自动执行该分支(当一个Worker结束运行是它会调用 Exited Notify and Cleanup.vi 通知它的 Manager Worker),如果你想在所有 Sub Worker都结束后执行某些任务,可以在该分支中进行。

当你想退出Worker停止运行时,需要调用 Exit.vi (stop loop? 会赋值 True),它会引起 MHL (和 EHL ,如果有的话) 的结束,从而结束Worker的运行。

3.4 初始化和退出的时序

通过一个包含4个Workers的简单应用来进行说明。

API

3.4.1 初始化时序

应用程序的初始化通常是从 Head Worker 的 “Initialize” 分支执行开始。整个初始化过程大致如下:

通过 Launcher VI 启动 Worker A。

Worker A执行 “Initialize” 分支,然后调用 Initialize subWorkers.vi。

所有Worker A的 Sub Worker 都执行 “Initialize” 分支,然后它们分别调用 Initialized Notify.vi 通知 Worker A它们已经完成初始化。

Worker A 接着就会执行 “All subWorkers Initialized" 分支,最后它也会调用 Initialized  Notify.vi 告知框架自己已经完成初始化。

API

3.4.2 退出时序

应用程序的退出通常是从 Head Worker 的 “Start Exiting’” 分支执行开始。整个初始化过程大致如下:

在Worker A中,用户通过调用 Start Exiting Worker.vi 开始执行退出操作,如在EHL的 "Panel Close?" 分支中调用。

Worker A执行 “Start Exiting” 分支,然后调用 Start Exiting subWorkers.vi。

所有Worker A的 Sub Worker 都执行 “Start Exiting” 分支,然后它们退出时分别调用 Exited Notify and  Cleanup.vi 通知 Worker A它们已经退出停止运行。

Worker A 接着就会执行 “All subWorkers Exited" 分支,最后它也会调用 Exited Notify and  Cleanup.vi 告知框架自己也已经退出停止运行。

API

4. Workers中的消息

Workers中使用了两种类型的消息,标准消息(异步)和回调消息(异步或同步)。

4.1 标准消息

标准消息是异步的,可以通过使用Enqueue Standard Message.vi来进行发送。该VI是一个多态VI,包括两种可选的VI:

Enqueue Message to Self v2.vi

给当前的worker自己发送消息。

Enqueue Message to Queue.vi

给指定队列的Worker发送消息。

4.2 回调消息

接收方Worker在收到消息后,可以直接向发送方Worker发送回复消息,而无需访问其队列。

框架中支持两种类型的回调消息,通过调用Enqueue Callback Message.vi来进行发送,该VI是一个多态VI,包含四种可选的VI:

Enqueue and Collect.vi(同步)

给指定队列的Worker发送同步的”Callback“消息,该线程想被挂起直到收到接收方Worker回复的 ”Callback Reply“消息或超时(默认5000ms)。

Enqueue and Collect - Silent Mode.vi (静默模式)

Enqueue and Forget.vi(异步)

给指定队列的Worker发送异步的”Callback“消息。

Enqueue and Forget - Silent Mode.vi (静默模式)

通过Get Callback Message.vi找到回调消息并保存,然后调用 Callback Reply.vi给发送方Worker发送回复消息,框架中提供了Callback Easy Reply.vi方法直接使用。

4.3 静默模式

API

上述消息入队列VI都支持静默模式(带有静音的图标),功能上没有区别,只是使用该模式发送的消息不会记录到调试器的消息日志中,当你不想调试器被一些重复且频繁发送的消息填满时,使用该模式将会很有用。

4.4 消息的构成

每条消息都包含一些特定的数据类型,它们会打包在一起进行发送,如下所示:

API

API

其中包括:

Case Lable

指定接收方Worker的MHL中分支名称,通过Quick Drop 进行创建。

Data(Optional)

将任意数据类型转成变体进行发送,接收方需要按照指定的类型进行转换,可选。

Auxiliary(Optional)

作为附加数据进行发送,与Data类似,可选。

MetaData(hidden)

框架自身需要用到的一些数据,用来标识消息的发送方和接收方,这些数据会被发送到调试器中。

4.5 消息优先级

API

框架中设计的初始化和退出时序中使用的消息优先级是“Critical”,该优先级不对开发人员开发,原因就是当Worker的消息队列过载时,框架仍然能够抢占消息并关闭应用程序,从而提高程序的可靠性及性能。

4.6 Case Label

API

创建Case Label

确保Worker的Main.vi程序框图窗口处于激活状态,并将MHL中的条件分支切换到对应的分支,比如“Inset to Main Panel"。

API


2. 激活Quick Drop,按Ctrl + 9进行创建。

API

分支跳转

选中要跳转的Case Label。

激活Quick Drop,按Ctrl + 8,将会自动跳转到对应的条件分支中。

5. Worker的加载

一个Worker可以以静态或动态两种方式被另一个Worker进行加载。

5.1 静态加载

大部分时候,通过脚本工具 “Create/Add Worker" 创建或添加的 Sub Worker会自动以静态的方式在 Manager Worker中进行加载。静态加载是用得最多的方式。

API

5.2 动态加载

有时你期望通过编程的方式加载一些没有事先指定的Worker,那么使用动态加载会很有用,框架中提供了Dynamically Load Worker.vi来实现该功能,一旦Worker被动态加载,它会自动集成到Manager Worker中,其行为方式将于静态加载的Worker一致。

API

6. Workers中的调试器

Workers调试器在使用该框架开发的应用程序中扮演着不可或缺的一部分。不论一个框架多么好用,如果它创建的代码难以导航和调试,那么框架带来的高效性将大打折扣。调试器必须在Workers应用程序运行之前启动,可以直接通过菜单 “Tools >> Workers tools...”启动,或以编程的方式通过调用Debugger Loader.vi进行加载。

6.1 Task Manager

在Task Manager页面可以看到所有加载的Workers层级列表,并行能看到它们的Clone ID (Workers的Main.vi是共享副本的) 和状态。

API

框架中定义的一些状态有:

Queue Created

在Manager Worker中调用执行Setup  subWorkers.vi 已经完成了Worker的消息队列创建(或是在Head Worker中调用 Setup Head Worker.vi完成了Head Worker的消息队列创建)。

Pre-initialized

Worker的Main.vi已经被加载,而且能将收到的消息出队列,处于初始化前的一个就绪状态。

Initialized called

Worker已经跳转到“Initialize”分支执行。

Initialized

Worker已经通过调用Initialized Notify.vi告知它的Manager Worker自己已经完成了初始化。

Start Exiting called

Worker已经跳转“Start Exiting”分支开始执行(通过调用Start Exiting Worker.vi或是在Manager Worker中调用Start Exiting subWorkers.vi.)。

Exited

The Worker已经通过调用Exited Notify and Cleanup.vi告知它的Manager Worker自己已经成功退出。

Stopped (Critical Error)

Worker中已经发生了严重的错误,Worker已经停止且不能再被访问。

(Aborted)

当应用程序提前终止时,追加到上述状态的字符串。

6.2  Task Manager的右键菜单

API

Open Running VI

会显示当前正在运行的Workers的 Main.vi副本程序框图,如果该VI不再运行,那么将打开可编辑模式的Main.vi。

Filter as Enque Worker

右键选中的Worker作为 Enque Worker对Message Log页面中的信息进行过滤显示。

Filter as Deque Worker

右键选中的Worker作为 Deque Worker对Message Log页面中的信息进行过滤显示。

6.3 Message Log

每当一条消息在Worker中出队列时,消息中的MetaData都会被发送到调试器中记录,在该页面提供了以下功能:

按照时间先后顺序显示MHL之间的消息流向。

显示应用程序中的错误,并告知发送的时间和位置。

允许用户直接跳转到Message Log中显示的任何Worker的条件分支中。

API

Enque Worker

发生消息的Worker的ID。

Enque Case

Worker中发送消息的分支。

Deque Worker

接收消息的Worker的ID。

Deque Case

Worker中接收消息的分支。

Message

一个字符串可以作为消息发送到调试器中. 消息被发送到调试器中有以下方式:

用户通过调用 Send Debugger Message.vi 进行发送。(在Workers的函数选板中可以找到)。

当框架运行时发生了错误,被Error Handler.vi检测到时框架会自动发送到调试器。

API

6.4 Message Log的右键菜单

API

Filter String (Double Click)

该列会通过单元格中的字符串进行过滤,或在指定的单元格进行双击即可执行相同的操作。

Open Running VI

显示当前正常运行的Worker’s Main.vi (clone) 的程序框图。

Go to Case

会跳转到Workers编辑模式的Main.vi中特定分支,由鼠标单击时的单元格中分支名称决定。(该操作对Worker中的基本分支无效)

7. Workers Tools

Tools >> Workers tools...

API

Create/Add Worker

API

添加Sub Worker

API

Worker层级视图

API

Worker的调试器界面

API

8. Workers Demo

产品老化测试,10个槽并行测试,记录老化过程中的温度、电压和电流,每个槽的数据需在界面中显示,并保存所有老化数据,支持数据按时间和槽进行查询。

API




审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分