2010年3月14日 星期日

LabVIEW 的資料流

最近在實驗室寫一些很鳥的 LabVIEW 程式,遇到幾個愚蠢的問題,有幾個是因為 LabVIEW 程式語言 -- G language 的中心思想 -- 資料流的關係,這裡就來談談這種非常特別的程式邏輯。

LabVIEW 程式的寫法非常特別,在人機界面上放好一些控制元件,比如說按鈕或是文字輸入框,寫程式碼 (好吧...不算程式 "碼" XD) 的 Block diagram 裡就會出現對應那些控制元件的 vi 方塊,此時就會用到資料流的概念,舉一個例子好了,如下圖 :


上圖有三個控制項 (框框是 Event structure,不是此處的重點故先不提),資料流的觀念很簡單,就是整個平面的資料一起流動,而資料流動的方向就是程式執行的方向及順序,換句話說,上圖三個控制項是同時執行的。可是有點程式概念的人都知道,哪有什麼同時執行這種事情 ? (OK,我們先不提多執行緒平行處理這種深奧的玩意) 電腦執行程式就是程式碼一行一行地去跑阿 ! 果然上次聽 NI 的課程時,主講人就說,其實沒有人知道哪個先執行,電腦只是隨機抓一個先跑,也就是說還是有先後差別的 [1]。

那要怎麼決定程式執行的順序 ? 有好幾種方式,最基本的就是用牽線的方式,因為 Block diagram 裡所有元件都是左邊輸入,右邊輸出,如果像下圖這樣牽線...


表示資料從 Numeric 流至 Numeric 3,先執行的當然是 Numeric,這樣就決定了執行順序。另外就是使用 Stack structure :


這個像膠卷的框框代表執行的順序,上圖有 0 跟 1 兩頁,所以第 0 頁的 Numeric 執行完之後,要先執行第 1 頁的其他元件才會輪到 Numeric 3。

聽起來好像不錯,這種程式好寫,程式邏輯性也不錯,有什麼不足之處呢 ? 我立刻問一個問題,而且這還是幾乎每個程式都會遇到的 : 要怎麼把一個控制項的值輸出給自己 ? 這很常見吧 ! 你隨時有可能對某個輸入的數值或字串等等資料作一些處理,然後再輸出回原本的輸入框。但是在 LabVIEW 裡,以資料流的邏輯來看,這是很忌諱的事情,所以預設是辦不到的。試想,如果可以將一個控制項同時接上輸出跟輸入的線,這樣變成一個資料流的無限迴圈,程式怎麼判斷哪裡是起點 ? 這就是關鍵了。可是一般的程式語言哪有這種事情 ? 程式跑到該變數的那一行時,就去抓一塊記憶體區塊來用,之後不管對這變數幹嘛,輸入、輸出、修改,都取這一塊記憶體的資料就是了,再簡單不過的事情,在資料流的觀念下完全是罩門。

那要怎麼解決 ? LabVIEW 提供一種方法 -- 區域變數 (Local variable)也就是將該控制項複製一份,做成一個分身,這個分身就可當作輸出或輸入,如下圖 :


但是由於你複製了一份,所以會讓記憶體多一倍的耗損,這可是非常糟糕的,但是沒有辦法,這是資料流天生的限制。除了這個需求,Event structure 也增加區域變數的需要性 [2],如下圖 :


Event structure 可讓程式執行後,依照使用者的某些動作 (event) 產生反應,所以很明顯地,邏輯上各 event 間必須互相獨立,你不可以讓控制項的資料從一個 event 流到另一個 event,因為你不能判定哪一個 event 會先發生。至於將控制項放在 Event structure 外面也不行,因為你觸發 event 前修改的值就讀不到,到頭來不用區域變數都不行。

不過還是有例外,例如兩個不同的迴圈要同時執行,由於迴圈有不執行完就不停止的特性,所以看起來也必須用區域變數 :


可是此時可以用一種高級技術 Quene 來連接,就可以免於使用區域變數,但是很多人沒在使用這東西吧,這算是比較進階的技巧了,我個人也不是很想去深究就是 [3]....

LabVIEW 資料流的構想很好,圖形化的 Block diagram 使得寫程式變得簡單,有些天生的限制也沒什麼好奇怪的,就當作應有的代價也可以讓人接受。只是邏輯上還是跟一般程式語言有差,有時候要小心別犯邏輯錯誤,這種錯誤要 debug 可是很累人的 [4]。

附註
1. 這裡我就犯過一個愚蠢的邏輯錯誤,GPIB Write 跟 GPIB Read 絕不能放在同一個平面上,不然 Read 先執行了就會產生 I/O Error。
2. 我在之前的文章曾說過 Event structure 是非常重要的東西,居然到了第八版才出來,NI 到底在想什麼 ? 沒有這東西,LabVIEW G language 只配稱作一個執行得很慢的 script language 而已。
3. 坦白說這種糟糕的程式語言....我離開這個實驗室之後就不會再用了吧...
4. 邏輯錯誤比起語法錯誤更麻煩,因為程式開發工具沒辦法幫你找錯誤,要靠自己一點一點地檢查。

1 則留言:

  1. 其實回頭一想, Event Structure 之所以這麼晚出來, 有可能是因為這東西會造成區域變數的濫用, 而以資料流的概念不希望看到區域變數在程式裡跑來跑去, 但是沒有這個又會淪為 script language....真為難 NI 了阿~ XD

    回覆刪除