(Last Update: 2020/Oct./30)

先講結論,

要看教學的,強烈建議看Tom McQuillan的[LabVIEW Actor Framework Tutorial Series]影片

(他曾是英國NI 的FAE,現在是LabVIEW講師,雖然他是英國腔,但總比某些印度人清楚XD)

這系列有12集,他用一個「簡化的文字聊天程式」去當範例,算是蠻深入淺出的教學!

 


==========

本文開始...

 

大部分小型專案,或單一功能的小程式,

用QSM(Queue State Machine)就可以解決,

若希望程式在處理某項耗時的功能時,還能即時響應使用者的操作,

那通常會用QSM-PC(Queue State Machine-Producer/Consumer)設計架構,

類似架構也有人稱為EDQSM(Event Driven Queued State Machine),

 

 

無論它叫什麼名字,基本上都是State machine (狀態機) 的進階變形,

也就是以狀態機為基礎,導入Event結構和Queue去應對兩種需求:

(1 )圖形化使用者介面(GUI):

用LabVIEW寫程式,最大優點就是能快速寫出GUI,和使用者互動,

使用者通常會期待,前面板上有數個按鈕,

能讓他進行硬體資源選擇、初始化、

擷取資料、存檔、分析等基本功能,

這時候就很適合導入Event結構:

當有按鈕被按下,就會觸發某個動作,甚至一連串任務。

 

2) 「複雜任務」其實是由多個「簡單任務」(單一功能函數),排列組合而成的

舉例來說,一個電子元件的量測任務可能包含:   

驅動電源供應器、設定供電電壓,設定DAQ、開始量測、分析、顯示、存檔。

但也有可能是以0.1V遞增電壓,

然後依序量測不同電壓下的電流數值,全部量完後,才取平均,存檔。

這種情境下,就可以引入Queue,

來讓每一個「簡單任務」,以不同順序去「排隊」依序執行,組合成不同的「複雜任務」。

 

但如果專案內容比較大,牽涉多執行緒並行處理,又希望未來好擴充好維護,

那可能就要考慮使用Actor Framework去實作。

由於網路上關於AF的中文參考資料不多,

所以我把目前看到的英文資料都盡量看了一次,做個筆記。



I.必要準備:  要了解AF,最好要滿足2條件:

  (1)二年以上用QSM-PC開發專案的經驗,了解Queue、Event等概念和實作方式。

  (2)基本的LabVIEW OOP(物件導向)概念,至少了解Inherit, Override。

 

 

II.概觀

1. AF其實是QSM-PC的變形,但非常的複雜且隱晦不明( non-trivial),

我甚至覺得AF把LabVIEW圖形化語言的最大優點 - 「容易瞬間看懂全貌」給掩蓋了。

因為它把原本在case結構中的每一個函數/功能整個打散,隱藏在各個Actor的method內部,

(但,這就是OOP的「封裝」,把所有圍繞著某個功能的函數,放在同一Actor類別內部)

要執行該函數,還要透過send message去間接呼叫Do方法,然後再執行該函數,

這一連串的設計,已經讓人無法一眼看穿程式的流程了~~
 

此外,整個AF底層在運作時還做了很多事,

譬如,當你呼叫一個Actor起來運作的時候,

它會先呼叫該Actor的Pre Initial.vi 方法 (我們可以override它,在裡面放一些初始化函數,譬如呼叫一些initial函數,或建立user event,但你也可以讓它維持空白,什麼都不做。)

然後再動態呼叫其Actor Core.vi (這是每一個Actor的核心,可以響應其他Actor的呼叫,這個一定要override),

接著其內部又會用Call parent node去呼叫父類的Actor Core.vi使其在背景執行;

最後關閉Actor時,還會自動呼叫Stop Core方法 (通常此方法會將Actor使用的相關資源釋放掉,以免佔用記憶體或產生error),這個方法雖然不一定要放東西在裡面,可保持空白,但實作上都會寫。

 

然後,雖然有些教學文章跟你說,你只要知道怎麼用AF,不用管他底層怎麼運作!

但,不知道底層在幹嘛,你在實作的時候就會茫茫然,知其然而不知其所以然...

所以還是要花時間去看一下整個AF函式庫中,每個類別有啥預設的方法(Method)。

 

2.AF專案組成:

一個Launch VI(必要)、一個Root Actor(必要)、N個Nested Actor
(N=0,1,2,...,這表示不一定要呼叫Nested Actor,但實際上最少會有1個)

=================

(1)Launch VI

顧名思義,它只是個單純的VI,而非Actor物件,

主要功能就是「呼叫Root Actor」,

 

實作上就是會拉一個Root Actor的instance進來,

把它連到Launch Root Actor.vi這函數,

就可以讓這個Root Actor運作起來了。

 

這VI通常不當作GUI和使用者互動,所以不需顯示Front panel畫面。

但也可以根據需要,放一個Loading bar顯示載入進度,

或是單純顯示公司LOGO維持幾秒鐘,

然後就把自己關掉(使用Invoke node的FP.close)。

 

(2)Root Actor

通常在專案中,這是獨一無二的Actor物件,一切的起源!

專案中其他所有功能,也就是所有的Actor們,都是由這個Root Actor去呼叫起來的!

它就像整棵樹的樹根一樣,所以叫做Root Actor。

此Actor的Front Panel通常也是使用者最初看到的GUI,姑且稱它為Main UI。

常見的用法是,在Main UI放一個SubPanel外框元件,

在執行階段把任意VI的Front Panel插入此SubPanel去顯示,

這樣就能先設計好各種GUI,然後在執行階段動態載入,

這樣就能達成GUI的模組化,且便於重複使用~

 

(3)Nested Actor

根據功能分類,只要是一個獨立的任務(Task),就建成一個Actor。

由於這些Actor是由Root Actor所呼叫起來的,

漸漸擴展組成一個樹枝形狀或是網狀,所以都稱為Nested Actor。

 

譬如,

USB DAQ的所有功能就做成一個Nested Actor,

某一型號的馬達控制所有功能也做成一個Nested  Actor。

 

然後,每一個Actor都會有很多行為(Action),

譬如:

選擇DAQ裝置名稱,就是一個行為;

設定DAQ AI通道,就是一個行為;

設定取樣率,也是一個行為...

依此類推,可能有數十個。

 

然後,一個行為就建立一個方法(Method),

最後,針對每個方法,

建立一個對應的Message類別=>這一步很重要!!!


在較舊的LabVIEW版本上,實作步驟比較繁瑣,

但在LabVIEW 2015之後,有腳本工具可大幅簡化時間!

==>

只要在該Method上按下右鍵->Actor Framework->Create Message,

LabVIEW就會執行腳本工具,

自動產生一個對應的Message類別,

還有該類別的兩個方法:  Send<XYZ>.vi和 Do.vi

 

這裡的<XYZ>指的就是Method的名稱;

譬如Method名稱叫做<DAQ Acquire>,

那對應的Message類別就是<DAQ Acquire> Msg.lvclass,

其內部兩個方法就是Send <DAQ Acquire>.vi和 Do.vi 。

 

然後,當其他的Actor要呼叫特定Nested Actor底下的某個Method時,

就得先執行對應的Send<XYZ>.vi,

他的腳色其實就是DQMH中的Enqueue命令的功能,

然後它會呼叫對應的Do.vi,

最後Do.vi內部才又呼叫<XYZ>Method。

(感覺很迂迴、很間接,但就是個介面,裡面封裝了Enqueue的行為)

 

#注意: 在實作Actor Core.vi的時候,他會預設使用Call Parent Method去呼叫父類方法,

如果你要加入其他程式碼,譬如: 呼叫method去設定該actor的Private data,

請把Call Parent Method放在整個資料流的最後面,而不能放第一個,

不然,這個Actor Core.vi就會卡住! 無法正常運作!

而且,若要建立一個Event Loop(或泛稱為Helper Loop)來處理event的觸發,

則是要讓此結構與Call Parent Method能平行執行,而非一前一後。

 

你可能會問為啥?

因為用Call Parent Method呼叫的父類Actor Core.vi方法的內部,

有一個「Msg處理迴圈」會一直在背景執行,

其功能就是在等待並處理外部丟進來的message,

然後去呼叫對應的Do.vi。

也就是說,一個Actor要能響應命令,它對應的「Msg處理迴圈」就不能停! 

所以在Actor.vi中,Call Parent Method要放在整個error cluster資料流的最後一個。

 

此外,還要注意一點,若Do.vi有error,

那就會造成這個內部隱藏的「Msg處理迴圈」被強制終止,

這樣就無法響應任何Msg了!

 

還有一件事,就是如何停止一個Actor?

尤其是Actor Core中通常會放置一個平行的事件處理迴圈(EHL--Event Handle Loop),

或稱為Helper Loop,也要能同時停止。

 

實作上會採用一個迂迴的方式,讓這個迴圈在各種狀況都能優雅的停止!

Step1: 在Actor的private data,新增一個User Event Refnum

Step2: Override此Actor的Pre Launch Init方法,

在內部新增Create User Event,然後將其refnum寫入Actor物件的private data。

Step3: 在Actor Core中,將此User Event註冊到EHL中,

這個case內部,放一個True布林常數,連到迴圈的條件端點,結束迴圈。

Step4: Override 該Actor的stop core.vi,

在內部呼叫Generate User event,去觸發Step3 註冊的User Event。然後Destroy User Event。


[建議閱讀資料]

1.Tom McQuillan的頻道,2019錄製的AF教學,目前有12段,講得非常好!
使用了一個Chat room聊天軟體的範例來講解AF的設計!
不過他有點英國腔,且無任何字幕,考驗大家的英文聽力XD

https://www.youtube.com/playlist?list=PLmF-6jvwRvVNFzBjzh4bQDjFbv6lShcth


2. 內建樣板的說明文件!

請打開LabVIEW (一定要選2015以上的版本,才是最新的AF函式庫),

執行Create Project->Template->Actor Framework,

專案名稱改一下,譬如AF_Template,按enter下一行,讓他自動更改目錄,

Actor名稱使用預設的Alpha/Beta。

這樣就會自動產生一個專案,

然後看Project Manager,最上面有個虛擬目錄「Document」,

裡面有2個檔案(HTML格式),

一個叫Actor Framework(推薦這篇!必看!),

另一個叫White paper (白皮書,這個當補充資料)。

 

3.Moore Good Idea 的教學:

2015年底寫的,有Step by step教你操作!

但他在文中有提到AF的一些缺點,

然後偷偷置入,廣告自己的MGI模組,說要用他這個工具會更好開發!

但我還沒用過所以不評論。

====================[其他參考資料]

1.NI官方論壇的至頂整理文READ THIS FIRST:

這篇編排很亂,看不出重點,

但他其實就是叫你去看內建樣板的說明文件。

 

2.Bloomy的教學: 不錯的Step by step,從QMH引導,但因用2014版本,過時了,看個概念就好。

3.Labvolution的心得:  不錯的Step by step心得文,但用2013版,過時了,看個概念就好。

4.Delacor的一篇分享文:  Delacor是一個女工程師Fabiola De la Cueva開的公司,它們基於NI的QMH,開發了DQMH這個很有名的模組。這篇文是2019的,沒有AF教學,只是單純AF和DQMH的比較文,有點像是在業配DQMH。此文建議使用者不一定要用AF,因為AF架構的觀念有點抽象,核心程式碼隱藏得太深了,無法一眼就看懂,很難把程式交給後人維護。而DQMH的架構還是比較容易看懂,只要知道生產者消費者就好。我沒用過DQMH,所以暫時抱持懷疑態度。

arrow
arrow

    sky 發表在 痞客邦 留言(1) 人氣()