Peter Scarfe, Creator of Workers for LabVIEW, 在VIMP中搜索Worker進行安裝即可,本文檔基于Workers 3.1.1版本進行說明。
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的類,其中包含一個消息處理程序及其相關支持代碼和控件。
每個新創建的Worker都包含下面幾個部分:
Initialization Data.ctl:自定義簇,Worker在初始化時期望接收的任何數據的定義。
Main.vi:包含QMH的VI,也就是Worker的核心程式碼。
3.2 Workers的層次關系
典型的應用程序都是由多個Worker分層組合構建而成,一個Worker可以在Main.vi中調用一個或多個其它的Workers,如下圖由三個Workers構成:
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直接調用與被調用者之間的關系。
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的簡單應用來進行說明。
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 告知框架自己已經完成初始化。
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 告知框架自己也已經退出停止運行。
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 靜默模式
上述消息入隊列VI都支持靜默模式(帶有靜音的圖標),功能上沒有區別,只是使用該模式發送的消息不會記錄到調試器的消息日志中,當你不想調試器被一些重復且頻繁發送的消息填滿時,使用該模式將會很有用。
4.4 消息的構成
每條消息都包含一些特定的數據類型,它們會打包在一起進行發送,如下所示:
其中包括:
Case Lable
指定接收方Worker的MHL中分支名稱,通過Quick Drop 進行創建。
Data(Optional)
將任意數據類型轉成變體進行發送,接收方需要按照指定的類型進行轉換,可選。
Auxiliary(Optional)
作為附加數據進行發送,與Data類似,可選。
MetaData(hidden)
框架自身需要用到的一些數據,用來標識消息的發送方和接收方,這些數據會被發送到調試器中。
4.5 消息優先級
框架中設計的初始化和退出時序中使用的消息優先級是“Critical”,該優先級不對開發人員開發,原因就是當Worker的消息隊列過載時,框架仍然能夠搶占消息并關閉應用程序,從而提高程序的可靠性及性能。
4.6 Case Label
創建Case Label
確保Worker的Main.vi程序框圖窗口處于激活狀態,并將MHL中的條件分支切換到對應的分支,比如“Inset to Main Panel"。
2. 激活Quick Drop,按Ctrl + 9進行創建。
分支跳轉
選中要跳轉的Case Label。
激活Quick Drop,按Ctrl + 8,將會自動跳轉到對應的條件分支中。
5. Worker的加載
一個Worker可以以靜態或動態兩種方式被另一個Worker進行加載。
5.1 靜態加載
大部分時候,通過腳本工具 “Create/Add Worker" 創建或添加的 Sub Worker會自動以靜態的方式在 Manager Worker中進行加載。靜態加載是用得最多的方式。
5.2 動態加載
有時你期望通過編程的方式加載一些沒有事先指定的Worker,那么使用動態加載會很有用,框架中提供了Dynamically Load Worker.vi來實現該功能,一旦Worker被動態加載,它會自動集成到Manager Worker中,其行為方式將于靜態加載的Worker一致。
6. Workers中的調試器
Workers調試器在使用該框架開發的應用程序中扮演著不可或缺的一部分。不論一個框架多么好用,如果它創建的代碼難以導航和調試,那么框架帶來的高效性將大打折扣。調試器必須在Workers應用程序運行之前啟動,可以直接通過菜單 “Tools >> Workers tools...”啟動,或以編程的方式通過調用Debugger Loader.vi進行加載。
6.1 Task Manager
在Task Manager頁面可以看到所有加載的Workers層級列表,并行能看到它們的Clone ID (Workers的Main.vi是共享副本的) 和狀態。
框架中定義的一些狀態有:
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的右鍵菜單
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的條件分支中。
Enque Worker
發生消息的Worker的ID。
Enque Case
Worker中發送消息的分支。
Deque Worker
接收消息的Worker的ID。
Deque Case
Worker中接收消息的分支。
Message
一個字符串可以作為消息發送到調試器中. 消息被發送到調試器中有以下方式:
用戶通過調用 Send Debugger Message.vi 進行發送。(在Workers的函數選板中可以找到)。
當框架運行時發生了錯誤,被Error Handler.vi檢測到時框架會自動發送到調試器。
6.4 Message Log的右鍵菜單
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...
Create/Add Worker
添加Sub Worker
Worker層級視圖
Worker的調試器界面
8. Workers Demo
產品老化測試,10個槽并行測試,記錄老化過程中的溫度、電壓和電流,每個槽的數據需在界面中顯示,并保存所有老化數據,支持數據按時間和槽進行查詢。
審核編輯:劉清
-
LabVIEW
+關注
關注
1974文章
3655瀏覽量
324036 -
API
+關注
關注
2文章
1502瀏覽量
62101 -
調試器
+關注
關注
1文章
305瀏覽量
23758
原文標題:Workers框架入門教程
文章出處:【微信號:LabVIEW QT 修煉之路,微信公眾號:LabVIEW QT 修煉之路】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論