周 蕓
(船舶重工集團(tuán)公司723所,揚(yáng)州225001)
在雷達(dá)數(shù)據(jù)處理系統(tǒng)中,顯控終端承擔(dān)著雷達(dá)數(shù)據(jù)錄取、目標(biāo)顯示、人機(jī)交互、控制雷達(dá)工作、對(duì)外接口通訊等多項(xiàng)任務(wù),是整個(gè)系統(tǒng)的控制處理中心。隨著現(xiàn)代雷達(dá)技術(shù)的迅速發(fā)展,對(duì)雷達(dá)顯控終端的實(shí)時(shí)性、任務(wù)異步性提出了更高的要求。VxWorks操作系統(tǒng)因其多任務(wù)、強(qiáng)實(shí)時(shí)、可裁減、高可靠性等諸多獨(dú)特優(yōu)勢(shì)而在實(shí)時(shí)顯控設(shè)備這一信息吞吐量大、計(jì)算解算頻繁、實(shí)時(shí)性要求高的平臺(tái)上獲得了開(kāi)發(fā)者和設(shè)備使用者的廣泛認(rèn)可,但其圖形界面顯示相對(duì)薄弱,較普遍的用法是采用風(fēng)河公司提供的WindML媒體庫(kù),其圖形功能使用用戶(hù)圖形庫(kù)(UGL)組件,代碼繁瑣,效率較低,開(kāi)發(fā)高質(zhì)量圖形界面相當(dāng)困難[1]。
Zinc是運(yùn)行在VxWorks上的圖形用戶(hù)接口工具(GUI),它類(lèi)似在Windows下使用VB可視化編程一樣,用戶(hù)可以實(shí)現(xiàn)“拖-放”式的“所見(jiàn)即所得”的圖形界面設(shè)計(jì),把設(shè)計(jì)者從大量編碼的圖形界面開(kāi)發(fā)中解放出來(lái),最大地簡(jiǎn)化了界面開(kāi)發(fā)過(guò)程,而顯控軟件復(fù)雜的多任務(wù)實(shí)時(shí)并發(fā)處理則由VxWorks操作系統(tǒng)得到保證。Zinc的開(kāi)發(fā)及運(yùn)行環(huán)境如圖1所示。
圖1 Zinc開(kāi)發(fā)及運(yùn)行環(huán)境示意圖
Zinc的應(yīng)用程序是在基于事件驅(qū)動(dòng)(Eventdriven)的機(jī)制下運(yùn)行的。輸入設(shè)備和應(yīng)用程序之間是通過(guò)事件相互作用的。Zinc的事件有3種來(lái)源:操作系統(tǒng)本身、外部設(shè)備和用戶(hù)應(yīng)用程序。例如鍵盤(pán)輸入就是一個(gè)典型的外部設(shè)備輸入事件。在Zinc應(yīng)用程序運(yùn)行過(guò)程中,事件管理器以輪詢(xún)的方式接收類(lèi)似鍵盤(pán)、鼠標(biāo)等外部設(shè)備以中斷驅(qū)動(dòng)方式發(fā)出的數(shù)據(jù)信息,然后把這些信息對(duì)應(yīng)的事件匯集起來(lái),放進(jìn)內(nèi)部的事件隊(duì)列等待下一步的處理。
在Zinc應(yīng)用中,ZafApplication::Control()函數(shù)是主要的控制環(huán),它通過(guò)調(diào)用ZafWindowManager::Get()例程從事件管理器中得到事件,并且在應(yīng)用終止之前不斷地把它們送到窗口管理器中。當(dāng)事件傳遞到窗口管理器時(shí),窗口管理器就會(huì)決定事件的最終目的地和正確路由,并正確地分派它們到相應(yīng)的作用對(duì)象。首先,Event()方法調(diào)用最終的繼承類(lèi),這個(gè)類(lèi)可能是Zinc庫(kù)中的類(lèi),或者是用戶(hù)自定義的繼承類(lèi),如果這個(gè)最終繼承類(lèi)并不處理事件,這個(gè)事件就會(huì)傳遞到其基類(lèi)的Event()方法,這個(gè)處理可能一直進(jìn)行到找到最終的基類(lèi)ZafWindowObject,此基類(lèi)收到事件,要么處理它,要么把它交給操作系統(tǒng)對(duì)象進(jìn)行處理,如圖2所示。
圖2 Zinc的運(yùn)行機(jī)制
某雷達(dá)顯控終端界面是用Zinc設(shè)計(jì)實(shí)現(xiàn)的,終端界面上的元素復(fù)雜,但設(shè)計(jì)方法步驟相同,下面通過(guò)簡(jiǎn)單的示例來(lái)介紹Zinc的開(kāi)發(fā)過(guò)程。這個(gè)例子創(chuàng)建1個(gè)串口接收窗口,在這個(gè)窗口中可以設(shè)置接收端口、波特率、控制位等,并且在對(duì)話(huà)框中顯示接收到的數(shù)據(jù),如圖3所示。
圖3 Zinc創(chuàng)建的串口接收窗口
設(shè)計(jì)步驟如下:
(1)步驟1:利用Zinc Designer設(shè)計(jì)窗口,生成.znc文件。
(2)步驟2:編輯視窗對(duì)象,設(shè)置對(duì)象屬性。
在編輯窗口上,有1排Zinc對(duì)象工具欄,用戶(hù)首先須選擇一個(gè)窗口并放置,再在窗口上放置按鈕或菜單等其他對(duì)象。以“設(shè)置串口”按鈕為例,按下這個(gè)按扭后,將發(fā)送1個(gè)用戶(hù)應(yīng)用程序事件(自定義EVENT_CUSTOM_SET_PORT),用戶(hù)可選擇串口位置、波特率等信息來(lái)配置串口。設(shè)置這個(gè)按鈕屬性時(shí),在“Send message”項(xiàng)的下拉菜單中選擇“True”,在“Send message value”項(xiàng)中輸入“10 005”(這是1個(gè)用戶(hù)應(yīng)用程序事件,其值應(yīng)大于10 000),在“Send message text”項(xiàng)中輸入“EVENT_CUSTOM_SET_PORT”。當(dāng)應(yīng)用程序運(yùn)行后,用戶(hù)點(diǎn)擊這個(gè)按扭時(shí)就會(huì)向內(nèi)部事件隊(duì)列發(fā)送1個(gè)EVENT_CUSTOM_SET_PORT事件,經(jīng)過(guò)窗口管理器的路由后,按照“設(shè)置串口”的Event()方法來(lái)實(shí)現(xiàn)串口設(shè)置。
(3)步驟3:生成源代碼。
視窗對(duì)象編輯完成并保存example.znc文件后,選擇“Generate Code”,它的任務(wù)是生成example.cpp文件、example.hpp文件和example.inc文件。用戶(hù)在example.cpp文件中編寫(xiě)代碼。example.inc是1個(gè)數(shù)據(jù)結(jié)構(gòu)文件,存放了用戶(hù)的數(shù)據(jù)列表、對(duì)象列表、用戶(hù)函數(shù)列表等結(jié)構(gòu)信息。
(4)步驟4:運(yùn)行應(yīng)用程序。
在Tornado下生成一個(gè)Download型工程,加載上面生成的3個(gè)文件,然后加入1個(gè)通用程序的入口文件v_app.cpp,編譯后下載到目標(biāo)機(jī)。程序在開(kāi)始運(yùn)行時(shí),會(huì)尋找example.znc資源文件,事先必須把它存放在VxWorks的同一目錄下。
根據(jù)某工程對(duì)顯控設(shè)備的具體要求,本應(yīng)用軟件采用模塊化設(shè)計(jì)思想,劃分為以下幾個(gè)功能模塊:任務(wù)管理模塊、初始化模塊、人機(jī)交互模塊、圖形顯示模塊、通訊模塊、數(shù)據(jù)處理維護(hù)模塊,而每個(gè)功能模塊由一個(gè)或多個(gè)任務(wù)組成。人機(jī)界面布局上分為雙頁(yè)顯示,主頁(yè)面是雷達(dá)的PPI顯示,雷達(dá)工作模式及參數(shù)信息顯示,次頁(yè)面顯示雷達(dá)各分機(jī)的工作參數(shù)和自檢信息。設(shè)計(jì)框圖如圖4。
圖4 顯控軟件設(shè)計(jì)架構(gòu)
設(shè)計(jì)中的難點(diǎn)1是非GUI任務(wù)與GUI任務(wù)之間的通訊
對(duì)于顯控軟件來(lái)說(shuō),雙屏顯示任務(wù)稱(chēng)為GUI任務(wù),與圖形繪制相關(guān)的操作都在這個(gè)任務(wù)中完成,而所有其他的任務(wù)稱(chēng)為非GUI任務(wù)。非GUI任務(wù)需要在不同的時(shí)期和GUI任務(wù)通信,而非GUI任務(wù)與GUI任務(wù)之間的通信方式將對(duì)整個(gè)軟件系統(tǒng)的實(shí)時(shí)性、可靠性和穩(wěn)定性產(chǎn)生影響。
Zinc主要提供了如下幾種通信機(jī)制供程序員選擇使用:Zinc入口點(diǎn)、共享內(nèi)存、OS消息隊(duì)列。
(1)Zinc入口點(diǎn)對(duì)Zinc來(lái)說(shuō),主要的通信入口函數(shù)是ZafEventManager::Put(),通過(guò)它可以把事件放到事件隊(duì)列上,此方法屬于異步方法。
(2)共享內(nèi)存在VxWorks中,共享內(nèi)存是容易實(shí)現(xiàn)的。為了安全地共享內(nèi)存,最好給共享的內(nèi)存分配1個(gè)信號(hào)量,這樣就能防止共享內(nèi)存被同時(shí)多次修改。共享內(nèi)存的使用并不要求線(xiàn)程安全,但是對(duì)Zinc卻是要求的,否則當(dāng)1個(gè)窗口對(duì)象的成員指向共享內(nèi)存時(shí),1個(gè)異常就會(huì)發(fā)生。
(3)OS消息隊(duì)列可以在Zinc中創(chuàng)建OS消息隊(duì)列,利用消息隊(duì)列實(shí)現(xiàn)從GUI任務(wù)到非GUI任務(wù)的通信,或者是從非GUI任務(wù)到GUI任務(wù)的通信,但不允許同時(shí)進(jìn)行2個(gè)方向上的通信。
在顯控軟件中使用的是Zinc入口點(diǎn)方式。網(wǎng)絡(luò)接收任務(wù)(非GUI任務(wù))接收來(lái)自雷達(dá)內(nèi)各分系統(tǒng)的資源數(shù)據(jù),利用ZafEvenManager::Put()函數(shù)將其存放到事件隊(duì)列中,GUI任務(wù)利用ZafApplication::Control()循環(huán)讀取事件隊(duì)列,進(jìn)而窗口管理器決定事件的最終目的地和合適的路由并將其發(fā)送。
非GUI任務(wù)與GUI任務(wù)之間通信的實(shí)現(xiàn)主要包括如下幾個(gè)方面:
(a)用戶(hù)事件的定義
Zinc中的事件共分為7類(lèi),其中用戶(hù)事件的取值范圍為10 000~32 767。在顯控軟件系統(tǒng)中定義“刷新數(shù)據(jù)”用戶(hù)事件為:
const ZafEventType EVENT_CUSTOM_SETTEXT=10011;
其中ZafEventType為Zinc事件類(lèi)型。
(b)數(shù)據(jù)打包并發(fā)送用戶(hù)事件
char*text= “hello”;//要刷新的文本“hello”
ZafEventStruct event(EVENT_CUSTOM_SETTEXT); //創(chuàng)建“刷新文本”事件
event.route=pMainWin;//事件路由的目標(biāo)窗口
event.SetVoidData(text);//數(shù)據(jù)打包到事件中
zafApplication->EventManager()->Put(event);//發(fā)送事件
(c)用戶(hù)事件的處理
ZafEventType MainWin::Event(const ZafE-ventStruct &event)
{
char*tmpText;
//判斷是否是用戶(hù)事件EVENT_CUSTOM_SETTEXT
if(event.type== EVENT_CUSTOM_SETTEXT)
{
tmpText= (char*)event.VoidData();
ProcessText(tmpText);//事件處理函數(shù)
}
else
{
/*處理其他事件*/
}
}
這樣類(lèi)似操作就能使界面的控件“活”起來(lái)。
設(shè)計(jì)中的難點(diǎn)2是多頁(yè)顯示技術(shù)。在人機(jī)界面上有時(shí)需要多頁(yè)可切換的頁(yè)面以增加顯示信息容量,用Zinc設(shè)計(jì)顯控界面時(shí),在界面框架布局上設(shè)計(jì)合理就可以實(shí)現(xiàn)這項(xiàng)技術(shù)。
將應(yīng)用程序?qū)?yīng)的根窗口設(shè)計(jì)為主窗口(R),而顯控界面的每個(gè)頁(yè)面設(shè)計(jì)為子窗口(M,F(xiàn)),在子窗口上又可以添加各種類(lèi)型的子子窗口(M1,M2,M3,…,F(xiàn)1,F(xiàn)2,F(xiàn)3,…),如圖5所示。
圖5 多窗口層次關(guān)系
這樣在用Zinc Designer進(jìn)行界面設(shè)計(jì)時(shí)初始化將子窗口M的屬性設(shè)置為顯示,子窗口F設(shè)置為隱藏,子子窗口 M1,M2,…,F(xiàn)1,F(xiàn)2,…的可視屬性和其父窗口相同,這樣在接收到外部的換頁(yè)指令后,可以通過(guò)子窗口(M或F)的指針來(lái)調(diào)用SetVisible函數(shù)(參數(shù)true或false),可實(shí)現(xiàn)頁(yè)面的顯示或隱藏。
在Zinc中可以利用Zinc Designer來(lái)設(shè)計(jì)圖形用戶(hù)界面,實(shí)際上也可以直接寫(xiě)代碼來(lái)繪制圖形。就修改而言,窗口上的控件如果較多且較規(guī)則,用Zinc Designer設(shè)計(jì)時(shí)修改很方便。但要繪制一些不規(guī)則的復(fù)雜圖形(如PPI顯示)時(shí)當(dāng)然要通過(guò)寫(xiě)代碼來(lái)實(shí)現(xiàn)。實(shí)際應(yīng)用中,最好將2種方法結(jié)合起來(lái)使用。
設(shè)計(jì)界面的步驟主要有:
(1)用Zinc Designer設(shè)計(jì)好界面,并產(chǎn)生代碼。
(2)對(duì)界面上的對(duì)象根據(jù)需要定義相應(yīng)的類(lèi),并對(duì)其中的成員函數(shù)編寫(xiě)代碼。
另外,在Tornado安裝目錄下的\target\src\zinc\demos子目錄中有Zinc提供的2種用法的例子,只要對(duì)其深入研究,定會(huì)開(kāi)發(fā)出高質(zhì)量的程序。
總之,Zinc是基于VxWorks強(qiáng)大的GUI開(kāi)發(fā)工具。Zinc的類(lèi)庫(kù)及編程方法與MFC十分類(lèi)似,有過(guò)MFC編程經(jīng)驗(yàn)的人一定體會(huì)過(guò)MFC為Windows編程帶來(lái)的方便,而Zinc堪稱(chēng)VxWorks上的MFC[2]。利用Zinc已經(jīng)成功實(shí)現(xiàn)了在某型雷達(dá)終端上的圖形界面設(shè)計(jì),系統(tǒng)運(yùn)行穩(wěn)定且性能良好。
[1]孔祥營(yíng),柏桂枝.嵌入式實(shí)時(shí)操作系統(tǒng)VxWorks及其開(kāi)發(fā)環(huán)境Tornado[M].北京:中國(guó)電力出版社,2001.
[2]侯俊杰.深入淺出MFC[M].武漢:華中科技大學(xué)出版社,2007.