2023年Labview學(xué)習(xí)筆記大全_第1頁
2023年Labview學(xué)習(xí)筆記大全_第2頁
2023年Labview學(xué)習(xí)筆記大全_第3頁
2023年Labview學(xué)習(xí)筆記大全_第4頁
2023年Labview學(xué)習(xí)筆記大全_第5頁
已閱讀5頁,還剩97頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

reference死鎖問題

LabVIEW中的引用經(jīng)常需要和“InPlaceElementStructure”配合使用。In

PlaceElementStructure對(duì)一個(gè)引用的數(shù)據(jù)進(jìn)行解決時(shí),為了保證多線程安全,

它會(huì)鎖住引用指向的數(shù)據(jù);其它線程若需對(duì)同一數(shù)據(jù)做操作,必須能這個(gè)InPlace

ElementStructure中所有代碼執(zhí)行完畢才可,這樣就避免了多線程讀寫同一

內(nèi)存數(shù)據(jù)所產(chǎn)生的競(jìng)爭(zhēng)問題。

舉例來說,下面這段程序的執(zhí)行時(shí)間是1秒:

僵tworeference.viBlockDiagram。I回

而下面這段程序的執(zhí)行時(shí)間則是2秒:

瑞SingleData.viBlockDiagramI°I回Ifl

由于第二段程序中的兩個(gè)InPlaceElementStructure必須順序執(zhí)行。

有了“鎖住”這個(gè)操作,就有不小心導(dǎo)致死鎖的也許。比如對(duì)于同一數(shù)據(jù)的引用,千

萬不能嵌套使用InPlaceElementStructure,否則就會(huì)死鎖:

0-

在上面這個(gè)示例中,程序運(yùn)營(yíng)至內(nèi)層的InPIaceElementStructure,就會(huì)停

在這里等外層InP1aceElementStructure運(yùn)營(yíng)結(jié)束,釋放它鎖住的數(shù)據(jù);

而對(duì)于外層InP1aceElementStrueture來說,它內(nèi)部的所有代碼要運(yùn)營(yíng)結(jié)束,它

才結(jié)束。因而它們互相等待,導(dǎo)致了死鎖。

PackedProjectLibraries2與Library的比較

ackedProjectLibrary從名字上來看,就是被包裝好了的Project

Library。ProjectLibrary是編程時(shí)候由程序員創(chuàng)建出來的。比如下圖這個(gè)工

程,我在里面創(chuàng)建了一個(gè)叫做“MyAlgorithmLibrary.lvlib”的工程庫。它包含兩

個(gè)VI,其中一個(gè)是私有的。

盤ProjectExplorer-MyAlgorit..

PaekedPrejectLibrary并不是手工創(chuàng)建的,他是通過一個(gè)項(xiàng)目的生成規(guī)范,

從ProjectLibrary編譯而來的。比如上圖的項(xiàng)目,我創(chuàng)建了一個(gè)Packed

Library類型的生成規(guī)范。我在這個(gè)生成規(guī)范中指定把“MyAlgorithm

Library.Ivlib”編譯成PackedProjectLibrary?

編譯的結(jié)果是在我指定的途徑下生成了一個(gè)名為“MyA1gorithm

Library.lv1ibp”的文獻(xiàn)。它的后綴名僅比PackedLibrary多了一個(gè)字母p。

雙擊這個(gè)文獻(xiàn),可以打開它,看到他里面包含的VI:

假如需要在其它項(xiàng)目中使用到這個(gè)PackedProjectLibrary,我們可以直接把

它加到另一個(gè)項(xiàng)目中去,下圖是一個(gè)演示項(xiàng)目:

PackedProjectLibrary看上去和ProjectLibrary非常相似,用

法也完全相同。

PackedProjectLibrary與ProjectLibrary

?都是將功能相關(guān)的一組VI封裝起來的方法;

?庫中的VI可以具有層次機(jī)構(gòu);

?庫中的VI都帶有名字空間,名字空間是帶有后綴名的庫名;

?都可以方便的放在項(xiàng)目管理器里使用

盡管它們十分相似,PackedProjectLibrary與ProjectLibrary相比,還

是有一些明顯區(qū)別的:

?PaekedProjectLibrary是通過編譯生成的;

?PackedProjectLibrary中的VI是編譯后產(chǎn)生的,它們不能被修

改;

?PackedProjectLibrary包具有私有VI,但用戶無法看到也不能

使用它們;

?PackedProjectLibrary把VI,.Ivlib以及其它用到的文獻(xiàn)都打

成一個(gè)壓縮包,用戶在磁盤上就只能看到一個(gè).1vlibp文獻(xiàn),看不到VI文

獻(xiàn);

?PackedProjectLibrary很適合作為最終產(chǎn)品發(fā)布給用戶使用;

?在項(xiàng)目中使用PackedProjectLibrary可以縮短編譯時(shí)間,由于

PackedProjectLibrary中的VI是已編譯好的,不會(huì)再隨項(xiàng)目編譯一

遍。(這一條先這樣寫上,但我還需要再進(jìn)一步研究一下)

LabVIEW中LVCIass數(shù)據(jù)轉(zhuǎn)換成XML格式的問題

前一段時(shí)間,一個(gè)同事的程序出了問題。他在程序中把一個(gè)LVCIass類型的數(shù)據(jù)

轉(zhuǎn)換成XML格式,再保存成文獻(xiàn)。但是從文獻(xiàn)中把數(shù)據(jù)轉(zhuǎn)回成LVC1ass時(shí),

卻出了問題:在調(diào)用“UnflattenXML”這個(gè)函數(shù)時(shí),程序有時(shí)犯錯(cuò),有時(shí)又不

犯錯(cuò)。他的程序中使用了大量的LVCIass,并且它們之間有著復(fù)雜的繼承與包含

關(guān)系,以至于花了兩三天的事件,才找出問題所在。其實(shí)是個(gè)簡(jiǎn)樸的問題,只是在

設(shè)計(jì)程序時(shí)他沒故意識(shí)到。

我做了一個(gè)簡(jiǎn)化的程序,可以重現(xiàn)這個(gè)問題:

一方面,給一個(gè)子類的對(duì)象設(shè)立一些數(shù)據(jù)。然后把它當(dāng)做父類類型的數(shù)據(jù),平化成

XML文本,存盤:

E

關(guān)閉LabVIEW,然后重新打開LabVIEW。再編寫一個(gè)反向程序,把XML數(shù)據(jù)轉(zhuǎn)

換成父類類型的數(shù)據(jù):

發(fā)現(xiàn)UnflattenFromXML函數(shù)返回一個(gè)錯(cuò)誤,value中是一個(gè)空的數(shù)據(jù)。

錯(cuò)誤產(chǎn)生因素如下:在把子類數(shù)據(jù)轉(zhuǎn)換成父類數(shù)據(jù)類型,這個(gè)類型雖然是父類的,但

其數(shù)據(jù)仍然是子類的。再轉(zhuǎn)換成XML格式,XML格式中記錄的仍然是子類的數(shù)

據(jù)。

在反向過程中,UnflattenFromXML拿到的數(shù)據(jù)是子類的,但它企圖轉(zhuǎn)換時(shí),

卻發(fā)現(xiàn)內(nèi)存中沒有子類的類型信息,因此它也就不知道如何轉(zhuǎn)換這個(gè)數(shù)據(jù),所以報(bào)

錯(cuò)。

假如這個(gè)程序稍微改動(dòng)一下,把XML數(shù)據(jù)直接轉(zhuǎn)換成子類的數(shù)據(jù),就不會(huì)犯錯(cuò)了:

事實(shí)上,子類的數(shù)據(jù)總是可以用父類來表達(dá)的。因此這個(gè)XML數(shù)據(jù)亦可以直接被

轉(zhuǎn)換成父類的類型,但前提是,-定要保證子類的類型別家在到內(nèi)存中去了。只要

在程序中放置一個(gè)子類的對(duì)象,自然就可以把子類加載至內(nèi)存。像下面這個(gè)程序就

可以正常工作:

a

這個(gè)實(shí)驗(yàn)反映出兩個(gè)問題:

1.把XML中的內(nèi)容假如是屬于某個(gè)LVClass類型的數(shù)據(jù),把這些數(shù)據(jù)轉(zhuǎn)換回

LVClass數(shù)據(jù)時(shí),那個(gè)LVC1ass一定要已經(jīng)存在于內(nèi)存才行。

2.在之前的一篇文章“LvClass的一個(gè)效率問題”中提到過:當(dāng)子類被加載如

內(nèi)存時(shí),它所有的父類也會(huì)被加載入內(nèi)存。但反過來并不成立。由于一個(gè)類

有哪些父類是擬定的,父類的地址就記錄在子類中。但一個(gè)類并不知道他會(huì)

有多少子類,任何人都可以從它派生出不同的子類來,因此它在裝入內(nèi)存

時(shí),不也許把自己的子類也都裝進(jìn)來。

LabVIEW中實(shí)現(xiàn)鏈表、樹等數(shù)據(jù)結(jié)構(gòu)

LabVIEW自帶的數(shù)據(jù)結(jié)構(gòu)只有數(shù)組和隊(duì)列。多數(shù)情況下,這兩種數(shù)據(jù)結(jié)構(gòu)足夠開

發(fā)者使用了。但是,我平時(shí)使用C++和C#語言更多一些,所以編寫程序時(shí)經(jīng)常會(huì)想

到使用其它編程語言中常見的數(shù)據(jù)結(jié)構(gòu)比如鏈表(List)、樹(Tree)等。

LabVIEW中也可以編程實(shí)現(xiàn)這些數(shù)據(jù)結(jié)構(gòu),一個(gè)比較直觀易懂的編程方法是基于

LabVlEW中的類和引用來實(shí)現(xiàn)各類數(shù)據(jù)結(jié)構(gòu)。我在《我和LabVlEW》一書的第

13.3.5節(jié)中介紹了一個(gè)簡(jiǎn)樸的鏈表容器的實(shí)現(xiàn)方法,它是基于LvClass編寫的,數(shù)

據(jù)流驅(qū)動(dòng)的一種容器。但是正如我在書中提到的,它雖然和有一些和文本編程語言

中的鏈表相類似的地方,但本質(zhì)并不相同。文本編程語言中的鏈表,樹等數(shù)據(jù)結(jié)構(gòu)

離不開引用(或指針),節(jié)點(diǎn)之間是通過引用來互相關(guān)聯(lián)的。LabVlEW可認(rèn)為

數(shù)據(jù)創(chuàng)建引用,因此也可以方便的實(shí)現(xiàn)與文本語言中功能相同的數(shù)據(jù)結(jié)構(gòu)。

這里插一段,介紹一下數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)容器的關(guān)系,我自己理解是這樣的:數(shù)據(jù)結(jié)

構(gòu)側(cè)重于數(shù)據(jù)的存儲(chǔ)方式,比如如何排序;數(shù)據(jù)結(jié)構(gòu)在加上與此結(jié)構(gòu)相關(guān)的操作

方法,比如添加刪除數(shù)據(jù)等方法,就構(gòu)成了一個(gè)數(shù)據(jù)容器。脫離了操作方法,單純的

數(shù)據(jù)結(jié)構(gòu)用處非常有限。因此,我文章中在提到數(shù)據(jù)結(jié)構(gòu)或者數(shù)據(jù)容器時(shí),指的

都是同一回事:數(shù)據(jù)結(jié)構(gòu)和相關(guān)的方法。

為了介紹如何在LabVIEW中實(shí)現(xiàn)一個(gè)數(shù)據(jù)結(jié)構(gòu),我打算以雙向鏈表為例,講解

一下如何編寫它。

雙向鏈表中每個(gè)節(jié)點(diǎn)都會(huì)記錄上一個(gè)節(jié)點(diǎn)和下一個(gè)節(jié)點(diǎn)的位置。因此,在雙向鏈表

中,可以從一個(gè)節(jié)點(diǎn)直接跳轉(zhuǎn)到它的上一個(gè)或下一個(gè)節(jié)點(diǎn)上去,也就是正向或反向

遍歷整個(gè)鏈表??梢灾庇^的想到,使用LvC1ass實(shí)現(xiàn)這樣的節(jié)點(diǎn),只要為這個(gè)

節(jié)點(diǎn)創(chuàng)建一個(gè)類ListNode,并且這個(gè)類有兩個(gè)成員變量,它們的類型都是

ListNode的引用,分別用于指向前一個(gè)和后一個(gè)節(jié)點(diǎn)就可以了:

ListNode

Data:int

Previous:ListNode

Next:ListNode

這樣的設(shè)計(jì)在文本編程語言中是沒有問題的,但在LabVIEW中行不通。其它

編程語言中,程序運(yùn)營(yíng)時(shí),才會(huì)對(duì)類的對(duì)象進(jìn)行初始化。LabVIEW中,VI一

打開,它上面的控件和常量就需要被初始化了。某個(gè)對(duì)象在初始化時(shí),它的成員變量

也要被初始化,若它的成員變量的類型還是這個(gè)類,這以初始化的過程就陷入了死

鎖:類需要它的成員變量先初始化;它成員變量需要這個(gè)類先初始化。

基于同樣的因素,一個(gè)類的成員變量的數(shù)據(jù)類型也不可以是這個(gè)類的子類:子類初

始化需要先對(duì)它的父類進(jìn)行初始化。但是,一個(gè)類的成員變量的數(shù)據(jù)類型可以是這

個(gè)類的父類:父類在初始化的時(shí)候,不需要理睬它的任何子類。

既然父類初始化時(shí),不依賴于子類的初始化;而子類的對(duì)象又可以被當(dāng)做父類的類

型來保存,咱們就可以運(yùn)用這一特性在LabVIEW中實(shí)現(xiàn)可以數(shù)據(jù)結(jié)構(gòu)的節(jié)點(diǎn)

了。只但是LabVIEW實(shí)現(xiàn)鏈表的節(jié)點(diǎn)要多一個(gè)環(huán)節(jié):我們需要為L(zhǎng)istNode類

再定義一個(gè)父類ListNodeVirtuaL這個(gè)父類不做任何實(shí)質(zhì)性的工作,它僅用

于保存相鄰節(jié)點(diǎn)的引用。

ListNodeVirtual

A

ListNode

Data:int

Previous:ListNodeVirtual

Next:ListNodeVirtual

以上兩個(gè)類是針對(duì)鏈表節(jié)點(diǎn)的雙向鏈表自身也需要做成一個(gè)類:

DoubleLinkedList類,這個(gè)類中封裝有鏈表的屬性和方法。比如它需要一個(gè)指向

鏈表表頭的引用,需要有為鏈表添加刪除數(shù)據(jù)的方法,為遍歷鏈表中的數(shù)據(jù),還需要

有一個(gè)迭代器……

作為演示,我只實(shí)現(xiàn)了鏈表的幾個(gè)簡(jiǎn)樸功能。演示程序工程結(jié)構(gòu)如下:

現(xiàn)ProjectExplorer-Listlvproj[o|口

FileEditViewProjectOperateToolsWindowHe

II七亡品IX哈gxll]富胃|畫▼番二

ItemsFiles

B-鼠Project:List.lvproj

0-5MyComputer

申?,ListNodeVirtual.IvcIass

7■?ListNode.IvcIass

:ListNode.ctl

j-Readdata.vi

沁圖Writedata.vi

[■-@Readprevious.vi

其Writeprevious.vi

k電Readnext.vi

'?-'?Writenext.vi

0,@DoubleLinkedListlvcIass

k?'DoubleLinkedList.ctl

;???圖AppendafterEnumerator.vi

????@GetAllData.vi

h@EnumeratorgoNext.vi

9?EnumeratorValue.vi

?凰,ResetEnumerator.vi

“反Demo.vi

k七'Dependencies

金B(yǎng)uildSpecifications

ListNode的成員變量涉及一個(gè)數(shù)據(jù),和兩個(gè)指向前后節(jié)點(diǎn)的引用:

DoubleLinkedList類的成員變量涉及指向鏈表頭節(jié)點(diǎn)的引用,迭代器指向的節(jié)點(diǎn)的

引用,并記錄了鏈表長(zhǎng)度

器DcuhlHink-一I=I回?ESjj

下面看一下鏈表中幾個(gè)重要方法是如何實(shí)現(xiàn)的。

一方面是AppendafterEnumerator.vi這個(gè)方法,它是鏈表里最復(fù)雜的一個(gè)

方法。它的輸入是鏈表中一個(gè)新的節(jié)點(diǎn),它把這個(gè)新節(jié)點(diǎn)添加在鏈表迭代器指向的

那個(gè)節(jié)點(diǎn)的后面。

在給鏈表添加數(shù)據(jù)時(shí),會(huì)碰到兩種情況。一方面,這個(gè)鏈表是一個(gè)空鏈表,那么被添

加的節(jié)點(diǎn)就是這個(gè)鏈表的首節(jié)點(diǎn),鏈表的迭代器也應(yīng)當(dāng)指向這一唯一的節(jié)點(diǎn)。

我設(shè)計(jì)的這個(gè)鏈表是一個(gè)環(huán)狀鏈表。當(dāng)鏈表中只有一個(gè)節(jié)點(diǎn)的時(shí)候,這個(gè)鏈表的上

一個(gè)和下一個(gè)節(jié)點(diǎn)都是它自己。

假如鏈表不是空的,就把新節(jié)點(diǎn)插在迭代器指向的節(jié)點(diǎn)的后面。因此:

新節(jié)點(diǎn)的前一節(jié)點(diǎn)指向的應(yīng)當(dāng)是迭代器指向的那個(gè)節(jié)點(diǎn);新節(jié)點(diǎn)的后一節(jié)點(diǎn)是迭代

器指向節(jié)點(diǎn)本來的后一節(jié)點(diǎn)。迭代器指向節(jié)點(diǎn)的新的后一節(jié)點(diǎn)應(yīng)當(dāng)是這個(gè)新節(jié)點(diǎn);

本來迭代器的后一節(jié)點(diǎn)的前一節(jié)點(diǎn)也應(yīng)當(dāng)換成這個(gè)新的節(jié)點(diǎn)。最后,我把迭代器也

指向了這個(gè)新的節(jié)點(diǎn),這樣連續(xù)添加新節(jié)點(diǎn)時(shí),它們會(huì)按照先后順序插入鏈表。

我的演示程序還用到了其它幾個(gè)方法。

ResetEnumerator.vi負(fù)責(zé)把迭代器復(fù)位,也就是指向鏈表的頭節(jié)點(diǎn):

HNoError

ListinListout

firstfirst

TQ自鳴Ll|

enumeratorenumerator

errorin(noerror)errorout

匚■4E]

EnumeratorgoNext,vi用于讓迭代器向后移動(dòng)一個(gè)節(jié)點(diǎn):

EnumeratorValue.vi返回迭代器指向的那個(gè)節(jié)點(diǎn):

使用這幾個(gè)方法就可以搭建出一個(gè)簡(jiǎn)樸的演示程序來看一下鏈表如何工作了。下面

這個(gè)演示程序中,分兩部分:第一部分是左面那個(gè)循環(huán),每次循環(huán)迭代就會(huì)創(chuàng)建出

一個(gè)新的ListNode對(duì)象,它的數(shù)值是當(dāng)前迭代的次數(shù);右半部分使用鏈表的迭代

器遍歷鏈表中的節(jié)點(diǎn)。在這個(gè)演示程序中,迭代器移動(dòng)次數(shù)比鏈表長(zhǎng)度多了兩次,

由于鏈表是環(huán)狀的,轉(zhuǎn)著圈訪問,鏈表中的頭兩個(gè)元素會(huì)被讀出兩遍。

程序運(yùn)營(yíng)后,data顯示了迭代器每一步所指向的節(jié)點(diǎn)的值:

data

|0|1|2|3|4_|0|1

回調(diào)VI

LabVIEW界面程序最常用的結(jié)構(gòu)就是循環(huán)事件結(jié)構(gòu)。用事件結(jié)構(gòu)截獲用戶在界面

上對(duì)控件的操作,然后做出相應(yīng)解決。

在文本語言中,常用的事件解決方法與LabVIEW是不同的。文本語言經(jīng)常使用

回調(diào)函數(shù)來解決界面事件。比如:某個(gè)按鈕按下時(shí),需要做一個(gè)fft運(yùn)算。那

么就寫一段函數(shù)來完畢這個(gè)fft運(yùn)算,再把這個(gè)函數(shù)與按鈕按下事件關(guān)聯(lián)起來。

開發(fā)語言通常已經(jīng)做好了對(duì)事件的監(jiān)控,一旦發(fā)現(xiàn)按鈕按下事件產(chǎn)生了,就去調(diào)用

與它關(guān)聯(lián)的fft運(yùn)算函數(shù)。這個(gè)有開發(fā)者編寫,被系統(tǒng)調(diào)用的函數(shù)就叫做回調(diào)函

數(shù)。

LabVIEW也可以采用與文本語言相類似的方法來解決事件:不是在事件結(jié)構(gòu)內(nèi)解

決,而是在程序開始時(shí),就為某事件注冊(cè)一個(gè)回調(diào)VI。在回調(diào)VI內(nèi)編寫相應(yīng)代

碼,一旦事件發(fā)生,這段代碼就會(huì)被執(zhí)行。

與事件結(jié)構(gòu)相比,回調(diào)VI編寫起來稍微麻煩一點(diǎn);但它的好處是,它和主VI是平

行運(yùn)營(yíng)的。假如事件解決過程比較耗時(shí),把它放在事件結(jié)構(gòu)中會(huì)阻塞整個(gè)程序,使

得程序界面暫時(shí)失去響應(yīng);而把它放在回調(diào)VI中,則不會(huì)影響程序其它部分的運(yùn)

營(yíng)。

比如下面這個(gè)例子。程序界面上有兩個(gè)儀表盤:左面那個(gè)始終在運(yùn)轉(zhuǎn),每10秒鐘旋

轉(zhuǎn)一圈;右邊那個(gè),由按鈕控制,按下按鈕才旋轉(zhuǎn)一圈。若把旋轉(zhuǎn)右表這個(gè)工作放到

事件結(jié)構(gòu)的按鈕按下解決分支中去做,它勢(shì)必會(huì)打斷左表的旋轉(zhuǎn),因此,考慮把它放

到回調(diào)VI中去做。

這是主程序界面:兩個(gè)表盤,和一個(gè)控制右表旋轉(zhuǎn)的按鈕。

程序的代碼也比較簡(jiǎn)樸。先看代碼的右半部份:這是一個(gè)典型的循環(huán)事件結(jié)構(gòu),用

來控制左表的旋轉(zhuǎn)。但是注意,右表的控制并不是在這個(gè)結(jié)構(gòu)中實(shí)現(xiàn)的。

再看程序左半部分:它為按鈕“右表旋轉(zhuǎn)一圈”的值改變事件注冊(cè)了一個(gè)回調(diào)VI。

注冊(cè)回調(diào)VI用的是節(jié)點(diǎn)“RegisterEventCa1Iback”,它在函數(shù)選板

“Connectivity-〉A(chǔ)ctiveX”上。這個(gè)節(jié)點(diǎn)重要是為了給ActiveX、.NET控

件的事件注冊(cè)回調(diào)VI。事件結(jié)構(gòu)無法截獲ActiveX、.NET控件的事件,因此只

能通過回調(diào)VI的方式來解決這些控件的事件。但是這個(gè)節(jié)點(diǎn)也可以用于給

LabVIEW自帶的控件注冊(cè)回調(diào)VIo

注冊(cè)回調(diào)VI節(jié)點(diǎn),有三個(gè)輸入?yún)?shù)從上至下分別是:事件的發(fā)出者、回調(diào)VI、

用戶自定義數(shù)據(jù)。

在我們這個(gè)例子中,需要截獲的是按鈕“右表旋轉(zhuǎn)一圈”的值改變事件,因此需要把

“右表旋轉(zhuǎn)一圈”控件的引用作為第一個(gè)參數(shù)傳遞給注冊(cè)回調(diào)VI節(jié)點(diǎn)。指定好

事件的發(fā)出者,接下來需要選擇事件的類型,鼠標(biāo)點(diǎn)擊注冊(cè)回調(diào)VI節(jié)點(diǎn)的第一個(gè)

參數(shù)的接線方塊,發(fā)現(xiàn)“右表旋轉(zhuǎn)一圈”按鈕的所有事件都已經(jīng)列在這里T,選擇''值

改變”事件。

第三個(gè)參數(shù)是用戶自定義數(shù)據(jù),可以是任意類型的數(shù)據(jù),在回調(diào)VI中需要用到的

數(shù)據(jù)都可以通過它來傳遞。由于我打算在回調(diào)VI中對(duì)控件“右表”做修改,因此,

在這里把“右表”的引用作為數(shù)據(jù)傳遞給回調(diào)VI。

第二個(gè)參數(shù)是回調(diào)VI的引用,假如已經(jīng)寫好了回調(diào)VI,把引用傳進(jìn)去就行了。

我還沒有編寫回調(diào)VI,因此可以在參數(shù)的接線端上點(diǎn)擊鼠標(biāo)右鍵,選擇

“CreateCal1backVI”創(chuàng)建一個(gè)空白的回調(diào)VI。

回調(diào)VI中寫一小段代碼,讓右表旋轉(zhuǎn)一圈,整個(gè)程序就完畢了。這時(shí),左右表

可以各自運(yùn)營(yíng),互不影響。

LvClass的一個(gè)效率問題

前幾天,聽到了一個(gè)客戶的抱怨:他編寫了一個(gè)LabVIEW程序,每次打開主程序

就要花費(fèi)幾分鐘的時(shí)間,這有點(diǎn)令他忍無可忍。我沒有見過他的源程序,但是據(jù)幫

他檢查過程序的同事講,他的問題很也許是使用了大量的LvClass導(dǎo)致的。在他

的項(xiàng)目中,包具有上百個(gè)類(LvClass)o我以前也聽說過LvC1ass在效率上

也許會(huì)有些問題,聽到了這個(gè)消息后,我自己做了一個(gè)實(shí)驗(yàn)。

LabVIEWSeripting中有一個(gè)屬性節(jié)點(diǎn)可以用來查看內(nèi)存中所有的VI,我就運(yùn)

用這個(gè)VI來查看一個(gè)程序到底在裝入些什么,令它啟動(dòng)如此之慢。

假設(shè)不存在子VI,假如打開某個(gè)不在LvClass中的VI(即便這個(gè)VI是屬于某個(gè)

Ivlib的),只有這個(gè)VI會(huì)被裝入內(nèi)存。但是,打開某一個(gè)LvClass中的VI,

我發(fā)現(xiàn)不僅這個(gè)VI會(huì)被裝入內(nèi)存,它所在的類中的所有其它的VI也都被調(diào)入內(nèi)

存。假如這個(gè)類尚有父類和祖先類,那么所有父類、祖先類中的VI統(tǒng)統(tǒng)都會(huì)被

調(diào)入內(nèi)存。

總結(jié)一下就是這樣:當(dāng)一個(gè)VI被裝入內(nèi)存

1.它的所有子VI都會(huì)被裝入內(nèi)存;

2.它所在的類中的所有的VI都會(huì)被裝入內(nèi)存;

3.它所在的類的父類中的所有的VI都會(huì)被裝入內(nèi)存。

以上3條可以是遞歸發(fā)生的,比如一個(gè)主VIA被裝入內(nèi)存,它的子VIB也會(huì)被

裝入內(nèi)存,和B同屬一個(gè)類的VIC也要被裝入內(nèi)存,C中有個(gè)子VID,D屬于類

E,E有個(gè)父類F,F中有個(gè)方法VIGo盡管G的功能和程序A八桿子都打不著

了,但也會(huì)被裝進(jìn)來。這大約就是那個(gè)用戶碰到的問題,表面上他的程序不算太大,

但是程序開始啟動(dòng)時(shí),卻需要把多于程序自身數(shù)倍的不相關(guān)的VI都裝入內(nèi)存,這一

過程會(huì)每次都浪費(fèi)他幾分鐘的時(shí)間。

鑒于LvClass的這一特性,設(shè)計(jì)使用它的時(shí)候一定要格外小心,否則很也許會(huì)導(dǎo)

致程序效率的低下。我想到了幾點(diǎn)需要注意的地方:

1.假如僅需要對(duì)一些VI進(jìn)行封裝,那么應(yīng)當(dāng)使用Ivlib,而不是1vclasso

兩者封裝的重要區(qū)別是,Ivclass可以封裝對(duì)象的屬性(也就是模塊用到

的數(shù)據(jù))。

2.類中的VI必須是高內(nèi)聚的,類中的方法共同完畢某一基本功能,不可再分

害U。應(yīng)用程序一旦用到這個(gè)類中的某個(gè)VI,就意味著程序?qū)?huì)使用到類中

幾乎所有的VI;而不是一個(gè)應(yīng)用程序也許只使用這個(gè)類中的某幾個(gè)VI。

3.繼承關(guān)系應(yīng)當(dāng)盡量簡(jiǎn)樸。沒有必要的時(shí)候盡量不使用繼承。LabVIEW不

支持接口,不應(yīng)創(chuàng)建一個(gè)純虛類,然后當(dāng)作接口來用。

4.盡量不要嵌套調(diào)用。比如在一個(gè)類的VI中又去調(diào)用另一個(gè)類中的VI。

5.打算使用多態(tài)這個(gè)特性時(shí)要注意,多態(tài)使得應(yīng)用程序在運(yùn)營(yíng)時(shí),根據(jù)對(duì)象的

類型選擇相應(yīng)的解決方法。但有些選擇應(yīng)當(dāng)是程序編譯時(shí)就做出的,它們不

適合套用在多態(tài)特性上。

舉一些例子:

?INI文獻(xiàn)讀寫這個(gè)模塊比較適合做成類,每個(gè)INI文獻(xiàn)相應(yīng)一個(gè)類的實(shí)

例。它有豐富的數(shù)據(jù)(文獻(xiàn)的內(nèi)容);它的方法有限,基本上只需要打開、

讀條目、寫條目、保存關(guān)閉,這四個(gè)方法,并且一般的應(yīng)用程序都會(huì)同時(shí)使

用到這四個(gè)方法。

?復(fù)雜儀器的驅(qū)動(dòng)程序不適合做成類。由于驅(qū)動(dòng)程序會(huì)提供非常多的功能,示

波器有各種觸發(fā)模式。而一個(gè)應(yīng)用程序通常只用到多種模式中的某一種就夠

用了。

?某測(cè)試程序可以生成測(cè)試報(bào)告給用戶。用戶可以選擇幾種不同的報(bào)告類型。

生成報(bào)告的模塊可以用Ivc1ass來設(shè)計(jì)。由于生成不同類型的報(bào)告的方法

間,可重用代碼很多,可認(rèn)為它們?cè)O(shè)計(jì)一個(gè)基類。并且,是程序運(yùn)營(yíng)時(shí),才選

擇生成報(bào)告類型的。

?某一測(cè)試程序,可以支持多種型號(hào)的儀器。由于不同用戶使用不同的硬件。

對(duì)不同型號(hào)儀器的支持不適合使用Ivclass來設(shè)計(jì),由于測(cè)試程序發(fā)布給用

戶時(shí).,用戶的硬件設(shè)備是固定的。對(duì)儀器的選擇應(yīng)當(dāng)是程序發(fā)布時(shí)就決定好

的,而不應(yīng)等到程序每次運(yùn)營(yíng)起來后判斷。

如何在程序中同時(shí)彈出多個(gè)子VI的界面,各自運(yùn)營(yíng)互不影響

回答網(wǎng)友一個(gè)問題:“我設(shè)計(jì)了一個(gè)labview界面子VI,我想在主VI中多次調(diào)用

該界面VI(同時(shí)執(zhí)行,單獨(dú)分派內(nèi)存),并顯示出多窗口,該如何設(shè)立?我嘗試將

子VI屬性設(shè)立成可重入,仍無法解決?!?/p>

這個(gè)問題其實(shí)挺常見的。若需要子VI打開多份實(shí)例,子VI必須是可重入的。所

以第一步要把子VI設(shè)立為可重入。但僅僅這樣還不夠,主程序運(yùn)營(yíng)到子VI

處,把子VI打開后,會(huì)一直等在這里,知道子VI運(yùn)營(yíng)結(jié)束,才繼續(xù)執(zhí)行主VI后

續(xù)的代碼。主VI既然已經(jīng)停在這里了,自然不會(huì)再繼續(xù)去打開其它子VI。解決的

辦法是在調(diào)用子VI的地方,改為動(dòng)態(tài)調(diào)用,并且不等待子VI運(yùn)營(yíng)結(jié)束。這樣一

來,主程序運(yùn)營(yíng)到這里,將子VI調(diào)起后,立即執(zhí)行后續(xù)代碼,又可以去調(diào)用其它的

子VI了。

需要注意的是,用于子VI是可重入的,需要給“OpenVIReferenee”函數(shù)設(shè)立一個(gè)

值為“8”的Options參數(shù)。主VI程序代碼如下:

|0x08:Prepareforreentrantrun.|

LabVIEW2023新功能-傳引用

以前版本的LabVIEW雖然也有多種方法可以讓數(shù)據(jù)以引用的方式在程序間傳遞,

但是用起來都有些麻煩。LabVIEW有了構(gòu)建數(shù)據(jù)傳引用的節(jié)點(diǎn),大大簡(jiǎn)化了傳引

用的程序代碼。

新添的有關(guān)傳引用的兩個(gè)節(jié)點(diǎn)在函數(shù)選板“Programing->Application

Control—>MemoryControl”中,分別是“NewDataValueReference"和"Delete

DataValueReferenee"。"NewDataVa1ueReference”用于創(chuàng)建一個(gè)數(shù)據(jù)的

引用,“DeleteDataVaiueReference”可以從引用中取回本來的數(shù)據(jù)。

引用最重要應(yīng)用于多線程程序中。假如兩個(gè)線程同時(shí)對(duì)同一份數(shù)據(jù)進(jìn)行修改,則必

須使用傳引用的機(jī)制。否則,使用值傳遞的方式,數(shù)據(jù)在數(shù)據(jù)線分叉的地方,就會(huì)編

程獨(dú)立的兩份,之后在兩個(gè)線程內(nèi)分別修改的是兩份完全獨(dú)立的數(shù)據(jù),沒辦法對(duì)同

一份數(shù)據(jù)進(jìn)行修改的。

比如下圖這個(gè)程序,程序輸入了一個(gè)數(shù)組,然后需要在兩個(gè)并行的子VI中同時(shí)對(duì)

這個(gè)數(shù)組中的數(shù)據(jù)進(jìn)行修改。每個(gè)子VI也許修改了數(shù)組不同的元素,程序運(yùn)營(yíng)結(jié)

束產(chǎn)生的數(shù)組應(yīng)當(dāng)把兩個(gè)子VI中的修改都包含進(jìn)來。因此,程序一開始需要數(shù)組

數(shù)據(jù)生成一個(gè)引用,然后把引用分別傳遞到兩個(gè)子VI中去。兩個(gè)子VI都運(yùn)營(yíng)結(jié)

束后,在從引用中取回?cái)?shù)據(jù)。

LabVIEW中已有的函數(shù)還都是為值傳遞設(shè)計(jì)的,所以使用值傳遞少不了把數(shù)據(jù)取

出、放回的過程。這以過程中,也許又會(huì)產(chǎn)生數(shù)據(jù)拷貝,效率會(huì)比較差。好在我們

可以使用“InPlaceE1ementStructure”結(jié)構(gòu)來解決從引用中取出、放回?cái)?shù)據(jù)

的過程。配合了“InP1aceElementStructure”結(jié)構(gòu)使用后,LabVIEW會(huì)盡

量使用數(shù)組原地址,而不是把從引用中取出的數(shù)據(jù)復(fù)制一份,這樣就做到了傳引用與

效率的兼顧。比如下面兩圖中的程序,功能是完全相同的,但LabVIEW會(huì)對(duì)下

面一幅圖中的程序進(jìn)行優(yōu)化,提高效率。

daU丫山,referencevtluereferenceout

美化程序-隱藏程序框圖上的大個(gè)cluster

在編寫某些程序的時(shí)候也許會(huì)碰到如圖1所示的情形:即用到了一個(gè)極為復(fù)雜的數(shù)

據(jù)類型常量。這個(gè)常量由于體積巨大,使得在程序框圖無論怎么擺放都讓人看起來

不太舒適。如何才干把這個(gè)程序改造得美觀一些呢?

圖1:體積巨大的常量會(huì)有礙觀瞻

要解決這個(gè)問題,只有設(shè)法把這個(gè)常量在主程序框圖上隱藏起來。通常可以用以下

兩種方法。

第一種方法:把這個(gè)常數(shù)變換成控件,再把控件隱藏起來。這種方法比較簡(jiǎn)樸,但是

也有弊病。①容易引起誤解:控件一般表達(dá)有值傳入,其別人讀程序讀到這里就

也許搞不清楚這個(gè)值是從哪里傳來的了;②假如要修改常量Cluster中某一個(gè)元素

的值,操作起來比較麻煩。

第二種方法,也就是我向大家推薦的:把它隱藏到更深層的子VI中去。具體操作

方法如下:

如圖2先給這個(gè)復(fù)雜數(shù)據(jù)類型建立一個(gè)StrictTypeDef。我的建議是為所

有程序中用到的Ouster都建立一個(gè)StrictTypeDefo這樣可認(rèn)為以后的程

序維護(hù)省去很多麻煩。

圖2:StrictTypeDef.

然后然后再建立一個(gè)新的VI,把我們要隱藏的這個(gè)個(gè)頭巨大的常量擺放在這個(gè)VI

中,并且連接一個(gè)Indicator,以把它的值傳出來。VI的接線板采用4-2-2-4格

式的,最下層第3個(gè)接線端用于傳出VI中唯一的數(shù)據(jù),如圖3所示。

6clusterdato.viFrontPanel上叵『X

圖3:用于隱藏個(gè)頭巨大常量的VI

這個(gè)VI的圖標(biāo)要做得小巧美麗,如圖4,圖標(biāo)不一定非要做成正方形。只要

B&W和256Colors中的圖標(biāo)形狀同樣,我們就可以畫出不規(guī)則圖標(biāo)了。具

體方法可以參考《制作不規(guī)則圖形的子VI圖標(biāo)》。

圖4:常量數(shù)據(jù)VI的圖標(biāo)

把這個(gè)新造出來的常量數(shù)據(jù)VI拖到程序框圖上,把它的輸出鏈接到剛才鏈接常量

的地方,再把位置擺放好。現(xiàn)在我們的程序是不是美麗多了

圖5:改造后的程序框圖

Caption和Labe1的書寫規(guī)范

LabVIEW控件的Caption和Label的特性和用途很相似,都是給了控件一個(gè)故

意義的名字。因此,在很多場(chǎng)合沒有必要刻意區(qū)分他們。

Caption和Label的最重要區(qū)別在于,Caption可以在程序運(yùn)營(yíng)的時(shí)候改變;而

Label則不可以,一旦程序運(yùn)營(yíng),就固定不變了。鑒于這一點(diǎn),Caption和

Labe1的用途也略有區(qū)別。Label應(yīng)當(dāng)是給程序自己用的,比如在程序中需要根

據(jù)控件的名字找到它,那就得跟據(jù)Label來找,而不能用Caption來找;

Caption是為了給用戶看的,有時(shí)控件的名字在運(yùn)營(yíng)到不同狀態(tài)下需要發(fā)生改變,

此時(shí)顯示在界面上的就應(yīng)當(dāng)是Caption()

推薦大家按照下面的規(guī)范使用C叩tion和Label。

先給VI分一下類:

1.底層VI:用戶不會(huì)直接使用到的VI,作為subVl隨程序一起發(fā)布。

2.用戶界面VI:VI前面板是給用戶看的程序界面的一部分。

3.程序接口VI:VI是提供應(yīng)用戶,在他們編程時(shí),當(dāng)作API被調(diào)用。

對(duì)于Caption和Label一個(gè)共同的書寫規(guī)范是:使用故意義的文字,在使

用英語短語命名時(shí).,單詞之間用空格分隔,不應(yīng)當(dāng)有重名。

不同點(diǎn)列于下表:

Labe1Caption

使用LabVIEW的默認(rèn)狀態(tài),即

底層VI顯示出來

Caption為空。

用戶界面隱藏顯示A多語言版本中,使用本地化語

VI多語言版本中,只使用英語

顯示

程序接口隱藏A多語言版本中,只使用英語多語言版本中,使用本地化語言

VI不用標(biāo)注控件的默認(rèn)值在后面加一括號(hào),括號(hào)內(nèi)標(biāo)注控件

的默認(rèn)值和數(shù)據(jù)單位

在LabVIEW中實(shí)現(xiàn)VI的遞歸調(diào)用

LabVIEW中使用遞歸調(diào)用不是很方便。但是遞歸并不是編程必須程序結(jié)構(gòu),任何

需要使用遞歸調(diào)用的地方,都可以用循環(huán)結(jié)構(gòu)來代替。但是在某些情況下,使用遞

歸調(diào)用的確可以大大簡(jiǎn)化程序代碼,對(duì)縮短編程時(shí)間、提高程序可讀性都非常有幫

助,所以學(xué)習(xí)一下遞歸的實(shí)現(xiàn)方法還是有好處的。

一、為什么VI不可以被靜態(tài)的遞歸調(diào)用

LabVIEW不能通過靜態(tài)調(diào)用的方法(把子VI直接放到另一VI的程序框圖上)

來實(shí)現(xiàn)遞歸。

對(duì)于一個(gè)非可重入的subVI,在每一個(gè)時(shí)間,這個(gè)subVI這能被運(yùn)營(yíng)一次。

LabVIEW需要借此來保證多線程時(shí)的數(shù)據(jù)安全。對(duì)于被遞歸調(diào)用的代碼,是需要

在它執(zhí)行到中間的時(shí)候,就再次被調(diào)用的。所以默認(rèn)設(shè)立下的VI不能被靜態(tài)遞歸

調(diào)用。

對(duì)于被設(shè)立為可重入的VI,是可以被同時(shí)調(diào)用多次的,但也不能被靜態(tài)的遞歸調(diào)

用°

除非是通過VIServei?動(dòng)態(tài)的調(diào)用VI,否則,LabVIEW是在一個(gè)程序被調(diào)入

內(nèi)存,開始運(yùn)營(yíng)之前就為它的所有VI分派好內(nèi)存空間的,涉及數(shù)據(jù)區(qū)。假如一個(gè)

VI不是可重入的,LabVIEW會(huì)在這個(gè)VI運(yùn)營(yíng)時(shí)局部變量所在的數(shù)據(jù)區(qū)開辟在

這個(gè)VI所在的空間內(nèi);對(duì)于可重入的VI,LabVIEW把它的數(shù)據(jù)區(qū)開辟在調(diào)

用者VI上,這樣就可以保證這個(gè)可重入VI在不同的地方被同時(shí)調(diào)用時(shí)使用不同的

數(shù)據(jù)區(qū),以防止多線程運(yùn)營(yíng)時(shí)數(shù)據(jù)混亂。

因此,可重入VI雖然可以被同時(shí)多次調(diào)用,但是被調(diào)用的次數(shù)是運(yùn)營(yíng)前就擬定

的。而遞歸運(yùn)算時(shí)的調(diào)用次數(shù)是運(yùn)營(yíng)時(shí)決定的。這樣,假如是靜態(tài)調(diào)用,LabVIEW

主線沒有辦法為提前為參與遞歸的VI開辟好數(shù)據(jù)區(qū)。

二、用動(dòng)態(tài)調(diào)用方法實(shí)現(xiàn)遞歸

圖1是一個(gè)采用遞歸算法計(jì)算階乘的例子,可以點(diǎn)擊后面的連接直接下載示例

VI:^Bl.raro

圖1:運(yùn)用遞歸結(jié)構(gòu)計(jì)算階乘

正如前文說過的,所有的遞歸都可以使用循環(huán)來代替,計(jì)算階乘也可以使用循環(huán)結(jié)

構(gòu),但是這里介紹的是使用遞歸結(jié)構(gòu)的方法。由于n!=n*(n-l)!,所以我們只

要編寫一個(gè)VI實(shí)現(xiàn)功能F(n)=n*F(n-l)就可以了。

程序中,遞歸調(diào)用VI自身的結(jié)構(gòu)由三個(gè)VI動(dòng)態(tài)調(diào)用節(jié)點(diǎn)實(shí)現(xiàn):OpenVI

Reference,CallByReferenceNode,CloseReference。這三個(gè)節(jié)點(diǎn)分

別負(fù)責(zé)動(dòng)態(tài)打開一個(gè)VI(本例中就是這個(gè)VI自身),運(yùn)營(yíng)這個(gè)VI,再關(guān)閉它。

使用CallByReferenceNode需要在打開VI句柄的時(shí)候就要知道VI連線

板(ConnectorPane)的布局,因此,我們?cè)谟肙penVIReference打開VI的

時(shí)候要提供VI連線板的布局信息,在例子中就是OpenVIReference節(jié)點(diǎn)

上方的那個(gè)常量。

三、使用遞歸時(shí)的幾點(diǎn)注意事項(xiàng)

遞歸調(diào)用的退出或結(jié)束條件,本例中當(dāng)輸入數(shù)據(jù)小于1時(shí)?,就需要結(jié)束遞歸調(diào)用返

回最底層的值了。假如遞歸調(diào)用的退出條件設(shè)立不妥,也許會(huì)引起程序死循環(huán)甚至

崩潰。

LabVIEW中也可以實(shí)現(xiàn)A調(diào)用B,B又調(diào)用A這種用多個(gè)VI互相調(diào)用

的遞歸結(jié)構(gòu)。

參與遞歸調(diào)用的VI必須被設(shè)立為可重入。

動(dòng)態(tài)調(diào)用的需要把VI在運(yùn)營(yíng)時(shí)調(diào)入內(nèi)存,這個(gè)過程是比較耗時(shí)的。因此遞歸結(jié)構(gòu)

的運(yùn)營(yíng)效率遠(yuǎn)不如可實(shí)現(xiàn)相同功能的循環(huán)結(jié)構(gòu),內(nèi)存占用也會(huì)更大一些。決定使用

遞歸結(jié)構(gòu)之前要考慮到這些因素。

在LabVIEW中實(shí)現(xiàn)V工的遞歸調(diào)用

LabVIEW中使用遞歸調(diào)用不是很方便。但是遞歸并不是編程必須程序結(jié)構(gòu),任何

需要使用遞歸調(diào)用的地方,都可以用循環(huán)結(jié)構(gòu)來代替。但是在某些情況下,使用遞

歸調(diào)用的確可以大大簡(jiǎn)化程序代碼,對(duì)縮短編程時(shí)間、提高程序可讀性都非常有幫

助,所以學(xué)習(xí)一下遞歸的實(shí)現(xiàn)方法還是有好處的。

一、為什么VI不可以被靜態(tài)的遞歸調(diào)用

LabVIEW不能通過靜態(tài)調(diào)用的方法(把子VI直接放到另一VI的程序框圖

上)來實(shí)現(xiàn)遞歸。

對(duì)于一個(gè)非可重入的subVL在每一個(gè)時(shí)間,這個(gè)subV:[這能被運(yùn)營(yíng)一次。

LabVIEW需要借此來保證多線程時(shí)的數(shù)據(jù)安全。對(duì)于被遞歸調(diào)用的代碼,是需要在

它執(zhí)行到中間的時(shí)候,就再次被調(diào)用的。所以默認(rèn)設(shè)立下的VI不能被靜態(tài)遞歸

調(diào)用。

對(duì)于被設(shè)立為可重入的VI,是可以被同時(shí)調(diào)用多次的,但也不能被靜態(tài)的遞歸調(diào)

用。

除非是通過VIServer動(dòng)態(tài)的調(diào)用VI,否則,LabVIEW是在一個(gè)程序被調(diào)入內(nèi)

存,開始運(yùn)營(yíng)之前就為它的所有VI分派好內(nèi)存空間的,涉及數(shù)據(jù)區(qū)。假如一個(gè)

VI不是可重入的,LabVIEW會(huì)在這個(gè)VI運(yùn)營(yíng)時(shí)局部變量所在的數(shù)據(jù)區(qū)開辟

在這個(gè)VI所在的空間內(nèi);對(duì)于可重入的VI,LabVIEW把它的數(shù)據(jù)區(qū)開辟在調(diào)

用者VI上,這樣就可以保證這個(gè)可重入VI在不同的地方被同時(shí)調(diào)用時(shí)使用不同的

數(shù)據(jù)區(qū),以防止多線程運(yùn)營(yíng)時(shí)數(shù)據(jù)混亂。

因此,可重入VI雖然可以被同時(shí)多次調(diào)用,但是被調(diào)用的次數(shù)是運(yùn)營(yíng)前就擬定

的。而遞歸運(yùn)算時(shí)的調(diào)用次數(shù)是運(yùn)營(yíng)時(shí)決定的。這樣,假如是靜態(tài)調(diào)用,

LabVIEW主線沒有辦法為提前為參與遞歸的VI開辟好數(shù)據(jù)區(qū)。

二、用動(dòng)態(tài)調(diào)用方法實(shí)現(xiàn)遞歸

圖1是一個(gè)采用遞歸算法計(jì)算階乘的例子,可以點(diǎn)擊后面的連接直接下載示例

VI:^Bl.rar。

圖1:運(yùn)用遞歸結(jié)構(gòu)計(jì)算階乘

正如前文說過的,所有的遞歸都可以使用循環(huán)來代替,計(jì)算階乘也可以使用循環(huán)結(jié)

構(gòu),但是這里介紹的是使用遞歸結(jié)構(gòu)的方法。由于n!=n*(n—l)!,所以我們只要

編寫一個(gè)VI實(shí)現(xiàn)功能F(n)=n*F(n-l)就可以了。

程序中,遞歸調(diào)用VI自身的結(jié)構(gòu)由三個(gè)VI動(dòng)態(tài)調(diào)用節(jié)點(diǎn)實(shí)現(xiàn):OpenVI

Referenee,CallByReferenceNode,CloseReference。這三個(gè)節(jié)點(diǎn)分

別負(fù)責(zé)動(dòng)態(tài)打開一個(gè)VI(本例中就是這個(gè)VI自身),運(yùn)營(yíng)這個(gè)VI,再關(guān)

閉它。

使用Ca11ByReferenceNode需要在打開VI句柄的時(shí)候就要知道VI連線

板(ConnectorPane)的布局,因此,我們?cè)谟肙penVIReference打開

VI的時(shí)候要提供VI連線板的布局信息,在例子中就是OpenVIReference節(jié)點(diǎn)

上方的那個(gè)常量。

三、使用遞歸時(shí)的幾點(diǎn)注意事項(xiàng)

遞歸調(diào)用的退出或結(jié)束條件,本例中當(dāng)輸入數(shù)據(jù)小于1時(shí),就需要結(jié)束遞歸調(diào)用返回

最底層的值了。假如遞歸調(diào)用的退出條件設(shè)立不妥,也許會(huì)引起程序死循環(huán)甚至崩

潰。

LabVIEW中也可以實(shí)現(xiàn)A調(diào)用B,B又調(diào)用A這種用多個(gè)VI互相調(diào)用的遞

歸結(jié)構(gòu)。

參與遞歸調(diào)用的VI必須被設(shè)立為可重入。

動(dòng)態(tài)調(diào)用的需要把VI在運(yùn)營(yíng)時(shí)調(diào)入內(nèi)存,這個(gè)過程是比較耗時(shí)的。因此遞歸結(jié)構(gòu)

的運(yùn)營(yíng)效率遠(yuǎn)不如可實(shí)現(xiàn)相同功能的循環(huán)結(jié)構(gòu),內(nèi)存占用也會(huì)更大一些。決定使用

遞歸結(jié)構(gòu)之前要考慮到這些因素。

一個(gè)XContro1的實(shí)例

XControl與.ctl用戶定義控件相比,其最大的提高就在于它不僅可以定義控件

的外觀,還可以定義控件的行為。

在XControl出現(xiàn)之前,同樣可以在程序中編寫代碼,控制程序的行為。在《用

XControl實(shí)現(xiàn)面向組件的編程》一文中提到了,這種方法在程序模塊劃分上有缺

陷。假如用戶想發(fā)布一個(gè)帶有特定行為的控件也是不也許的,由于控制控件行為的

代碼,是同其它代碼混雜在一起的。

運(yùn)用XControl可以解決上面提到的問題,這里以一個(gè)例子說明一下如何運(yùn)用

XContro1實(shí)現(xiàn)一個(gè)有特定行為的控件。

Windows風(fēng)格的工具條上的按鈕有一個(gè)特點(diǎn),就是當(dāng)鼠標(biāo)移動(dòng)到按鈕上方,按鈕就

會(huì)變亮或浮起。LabVIEW中默認(rèn)的按鈕沒有這樣的特性,但是實(shí)現(xiàn)這一點(diǎn)是

很容易的。

以鼠標(biāo)移上,按鈕變亮為例:在程序中,當(dāng)按鈕的MouseEnter事件發(fā)生時(shí):

把按鈕的顏色設(shè)立為淺顏色;當(dāng)按鈕的MouseLeave事件發(fā)生時(shí),把按鈕的顏

色設(shè)立為深色即可?,F(xiàn)在把界面上的按鈕和控制顏色的代碼都封裝在一個(gè)

XControl中。這樣,其別人在使用這個(gè)XContro1時(shí),就無需修改他的代碼,

而直接獲得這種顏色變化的特性了。

一、簡(jiǎn)樸行為的XControl

一方面創(chuàng)建一個(gè)空的XControL

茂ProjectExplorer-Untitle...茂ProjectExplorer-Untitle...

圖1、2:創(chuàng)建一個(gè)新的XControl

新的XControl中有四個(gè)VI。

Data.ct1定義XC。ntrol的數(shù)據(jù)類型。比如我們要做一個(gè)按鈕,數(shù)據(jù)類型應(yīng)當(dāng)是布

爾型。假如要作一個(gè)工具條,數(shù)據(jù)類型就應(yīng)當(dāng)是布爾型數(shù)組了。

State.ctl定義XControl內(nèi)部要用到的一些數(shù)據(jù),類似于類的私有變量。我們這

個(gè)簡(jiǎn)樸的例子用不到任何變量,所以可以不去動(dòng)它。

Init.vi類似于類的構(gòu)造函數(shù)。在我們這個(gè)簡(jiǎn)樸的例子中也不需要去改變它。

Facade.vi是最重要的VI,XControl的外觀和行為都是在這個(gè)VI中定義

的。Facade,vi的界面就是XContr。1控件的外觀??刂瓶丶袨榈拇a也是放

在這個(gè)VI的程序框圖上。

我們要做的是個(gè)按鈕,所以就在Facade.vi的前面板上放一個(gè)按鈕。假如希望用

戶在使用這個(gè)XControl時(shí)可以調(diào)整它的大小,在我們這個(gè)簡(jiǎn)樸例子中,只要設(shè)

立Facade,vi窗口尺寸屬性中的“在窗口尺寸變化時(shí),按比例調(diào)整控件大小”這個(gè)選

項(xiàng)就可以了。對(duì)于復(fù)雜的XControl控件,要另寫代碼,在窗口尺寸變化后重新計(jì)算

每個(gè)控件的大小和位置。

窗口尺寸屬性設(shè)立

控制按鈕顏色的代碼也需要放在Facade,vi中:把前文提到的按鈕的Mouse

Enter和MouseLeave放在這里即可。具體實(shí)現(xiàn)方法,可以參考文章結(jié)尾給出

的范例程序:氈().zipo

二、有連續(xù)運(yùn)動(dòng)的XControl

Facade.vi不可以連續(xù)運(yùn)營(yíng),只有在有事件發(fā)生時(shí),LabVIEW才會(huì)調(diào)用這個(gè)VI。

解決完這個(gè)事件,F(xiàn)acade.vi就會(huì)停止運(yùn)營(yíng)。不要試圖讓Facade,vi連續(xù)運(yùn)營(yíng),

否則會(huì)導(dǎo)致整個(gè)LabVIEW被掛起。

有時(shí)候,需要控件可以循環(huán)地或者連續(xù)一段時(shí)間地作一個(gè)動(dòng)作。比如說,需要做一

個(gè)不斷閃爍的小燈??刂茻艄忾W爍的代碼就不可以放在Facade.vi中。實(shí)現(xiàn)這種

功能的一個(gè)方法是:

把定期控制小燈顏色的代碼放在一個(gè)可重入VI中,通過小燈控件的引用參考來

定期更改它的顏色屬性。在XControl的Init.vi中把這個(gè)定期VI動(dòng)態(tài)加載并以

異步方式運(yùn)營(yíng);在XCont⑹的Uninit.vi中再把這個(gè)定期VI卸載即可。

Uninit.vi不是一個(gè)必須的XContro1功能定義VI(AbilityVI),新建的

XControl沒有這個(gè)VI。可以在工程瀏覽窗口,鼠標(biāo)右擊這個(gè)XContro1來為

它添加新的功能定義VI。

劭9.zip,它只能在LabVIEW8.5下打開。

XControl是可以在VI的面板上放多個(gè)實(shí)例的,每個(gè)實(shí)例小燈的閃爍頻率也許不

同。我在這個(gè)例子里,每個(gè)XControl實(shí)例都有自己的一個(gè)專用定期VI,由于這些

VI是可重入的。定期的方法我采用的是加延時(shí)。

我做了一下測(cè)試,發(fā)現(xiàn)現(xiàn)在的XControl有個(gè)問題,就是在程序面板上放多個(gè)

XControl實(shí)例之后,定期就變得非常不準(zhǔn)確了,小燈閃爍速度明顯減慢。這也許

是XControl的bug,也許是LabVIEW延時(shí)函數(shù)的問題。解決這個(gè)問題的方法就

是使用一個(gè)定期VI控制所有的實(shí)例,當(dāng)然這樣的實(shí)現(xiàn)方法會(huì)比較麻煩一些。

下載文章中的示例程序:A錯(cuò)誤!未定義書簽。£錯(cuò)誤!未定義書簽。

LabVIEW中的泛型容器

Google網(wǎng)站里有個(gè)Google實(shí)驗(yàn)室,有不少Google的產(chǎn)品最初就是放在這個(gè)實(shí)

驗(yàn)室里的?,F(xiàn)在NI也有NI實(shí)驗(yàn)室了。NI實(shí)驗(yàn)室公布出來的項(xiàng)目一般是NI

工程師運(yùn)用額外時(shí)間做的一些調(diào)查研究。這些項(xiàng)目不是公司的正式產(chǎn)品,但是它們

的設(shè)計(jì)很有創(chuàng)新或者是比較有應(yīng)用潛力。與其讓這些項(xiàng)目被埋沒了,不如先看看用

戶對(duì)這些項(xiàng)目的反映,假如相稱一部分用戶覺得某個(gè)項(xiàng)目非常有幫助,或許它就值

得我們?yōu)槠湓鲩L(zhǎng)投資,把它作成正式產(chǎn)品了。

我這里給大家介紹其中的一個(gè)項(xiàng)目:“LabVIEWGenericContainerMa

p”。由于這個(gè)項(xiàng)目是我設(shè)計(jì)的,所以對(duì)它了解比較多一些。當(dāng)時(shí)?,我們打算提出這

個(gè)項(xiàng)目的時(shí)候,重要有兩個(gè)目的:第一是幫助用戶編寫有復(fù)雜數(shù)據(jù)結(jié)構(gòu)的應(yīng)用程序;

第二是推動(dòng)LabVIEW向通用編程語言方向做改善。

C++的程序員基本都很喜歡STL這個(gè)模板庫。程序中常會(huì)使用數(shù)組、隊(duì)列、字符

串等等數(shù)據(jù)類型和結(jié)構(gòu),假如自己設(shè)計(jì)實(shí)現(xiàn)這些數(shù)據(jù)結(jié)構(gòu)和相關(guān)的操作,是相稱花

費(fèi)精力的。好在STL實(shí)現(xiàn)了這些數(shù)據(jù)結(jié)構(gòu),和它們常用的操作方法。借用

STL提供的功能,編程時(shí)很多細(xì)節(jié)方面不需要再去考慮了,這就讓工作簡(jiǎn)化了許

多。嘗到STL甜頭的程序員,在編寫程序的時(shí)候,已經(jīng)很難離開STL了。

STL中非常重要的一個(gè)部分就是容器。容器用于存放數(shù)據(jù),程序通過調(diào)用容器的

結(jié)構(gòu)函數(shù)保存數(shù)據(jù)到容器或者訪問容器中的數(shù)據(jù)。容器也分為不同的類型,如鏈

表、隊(duì)列等。它們?cè)跀?shù)據(jù)的組織方式上,或存取方式上有所分別,以合用不同的需

求。STL中的容器和方法都是泛型的或者說是數(shù)據(jù)類型無關(guān)的,就是說這些容器

可以保存和操作任何類型的數(shù)據(jù)。

其它一些常用的編程語言,如Java、C#也都有類似的泛型容器以方便程序員使

用。

LabVIEW的重要方針是簡(jiǎn)化工程師們編寫程序的難度,以前用LabVIEW編

寫的程序大多是工業(yè)領(lǐng)域流程控制類型的。這種類型的程序用不到太復(fù)雜的數(shù)據(jù)結(jié)

構(gòu)和算法,因此,LabVIEW中對(duì)我們?cè)谟?jì)算機(jī)課程中學(xué)到的那些經(jīng)典數(shù)據(jù)結(jié)構(gòu)以及

算法的支持并不多。

但是在我自己用LabVIEW多了之后,用它比用C++要順手,任何類型的程序

都喜歡使用LabVIEW來編寫,涉及一些通常用途的程序。這時(shí)候,LabVIEW

缺少對(duì)基本數(shù)據(jù)類型支持的缺陷就格外突出了。于是我和周邊幾個(gè)同事就想到應(yīng)當(dāng)

在這些方面對(duì)LabVIEW做一些補(bǔ)充,做一些比較規(guī)范的泛型容器和算法,一方面

方便自己,也許還可以提供應(yīng)別的用戶。

由于這不是正式項(xiàng)目,我們能投入的資源很有限,不也許一開始就做得很全面。作

為開始,我們選擇了M叩容器和它最常用的幾個(gè)方法。一方面選擇M叩一是由于

它比較常用,二是其它容器中,有些在概念上和LabVIEW中已有的一些函數(shù)比

較接近,假如選則他們,也許會(huì)引起用戶的誤解。

LabVIEW中的Array操作與STL中的vector是非常相似的,功能齊全,不需要

考慮底層操作如內(nèi)存管理等。STL中的deque,queue,stack等,與

LabVEW中的“隊(duì)列”(Queue)操作比較類似。但是LabVIEW中的隊(duì)列存在

的目的不是為了作容器,而是用于在多線程程序中通訊。在“生產(chǎn)者/消費(fèi)者”程序模

式中,經(jīng)常使用隊(duì)列在不同的線程中傳遞數(shù)據(jù)或消息。由于LabVIEW中隊(duì)列操作

重要用于不同線程間的通訊,因此它的函數(shù)并沒有采用LabVIEW的重要傳參方式

-傳數(shù)據(jù),而是采用了傳引用的方式。

我們實(shí)現(xiàn)的這個(gè)Map(這個(gè)按字面翻譯比較別扭,中文也許翻譯成“字典”還比較合

理)泛型容器功能與C++STL中的Map是類似的,它重要用于程序經(jīng)常需要

按某一關(guān)鍵字查詢數(shù)據(jù)的情況。

M叩已經(jīng)涉及了編寫查詢程序時(shí)常用的操作,比如把數(shù)據(jù)放到容器中、查找一個(gè)

數(shù)據(jù)、刪除、清空容器等。

我們的LabVIEWGenericContainerMap內(nèi)部的數(shù)據(jù)是按照平衡二叉樹

的方式組織存儲(chǔ)的,它的查詢復(fù)雜度比一般線性數(shù)據(jù)結(jié)構(gòu)的要低。這樣,在數(shù)據(jù)量

很大的情況下,使用Map的程序效率明顯高于使用數(shù)組的程序。

M叩采用的是符合LabVIEW風(fēng)格的傳數(shù)據(jù)方式,把整個(gè)M叩中的數(shù)據(jù)在不同

函數(shù)間傳遞。

C++是支持泛型編程的。簡(jiǎn)樸地說,泛型編程可以這樣理解,就是程序員可以

實(shí)現(xiàn)一個(gè)方法,這個(gè)方法可以應(yīng)用在任何合法的數(shù)據(jù)類型上。比如,前面提到的

STL,它有個(gè)“比較”方法,你可以用它來比較整數(shù),也可以比較字符串,或者使用

戶定義的類的實(shí)例等等。

支持泛型編程,程序員就可一抽象出與數(shù)據(jù)類型無關(guān)的算法,從而使代碼具有更好

的可重用性。

目前,用戶還不能在LabVIEW上實(shí)現(xiàn)泛型編程。使用PolymorphicVI可

以使一個(gè)方法支持某幾種特定的數(shù)據(jù)類型,但不是任何數(shù)據(jù)類型。真正能做到數(shù)據(jù)

無關(guān)的函數(shù),比如說0g1”函數(shù),都是1^囚正亞自帶的,用戶無法寫出這樣一

個(gè)函數(shù)或VI。

我是非常想推動(dòng)LabVIEW有朝一日也實(shí)現(xiàn)泛型編程的。這

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論