摘 要: 以一個通信仿真系統(tǒng)" title="仿真系統(tǒng)">仿真系統(tǒng)的開發(fā)為例,針對傳統(tǒng)的LabWindows/CVI" title="LabWindows/CVI">LabWindows/CVI調用外部DLL的方法不能應用于HLA系統(tǒng)的問題,,提出創(chuàng)建LabWindows/CVI的DLL和利用外部編譯器兩種解決方案將LabWindows/CVI與HLA仿真系統(tǒng)相結合,。
關鍵詞: LabWindows/CVI 虛擬儀器 半實物分布式仿真系統(tǒng) HLA RTI
半實物仿真又稱為硬件在回路中的仿真(Hardware-in-the-loop Simulation),,是一種將實物接入仿真回路中的仿真試驗,。半實物仿真已經成為航空航天,、武器系統(tǒng)等研究領域不可缺少的重要手段。隨著計算機仿真技術的迅猛發(fā)展,,仿真系統(tǒng)變得越來越復雜,,許多復雜的仿真牽涉到一些不同類型系統(tǒng)的仿真聯合,而這些系統(tǒng)各自的仿真環(huán)境組成了整個仿真環(huán)境,。為了解決不同類型的仿真間的互操作和仿真部件的重用,,美國國防部公布了建模與仿真領域里的高層體系結構HLA(High Level Architecture)。HLA正日益得到重視和廣泛的應用,,基于HLA的分布式系統(tǒng)仿真在半實物仿真中的比重日益增加[1]。而另一方面,,NI公司的LabWindows/CVI集成了GPIB,、VXI、PXI,、RS232/485和插入式(PCI,、USB)數據采集設備等通信的功能,支持DataSocket和TCP/UDP等技術與遠程應用程序通信,。作為虛擬儀器開發(fā)工具在數據采集和界面控制方面具有明顯的優(yōu)勢,。所以,將LabWindows/CVI應用于半實物分布式仿真非常有利于系統(tǒng)的快速開發(fā),。
本文以一個通信對抗仿真系統(tǒng)的開發(fā)為例,,分析了LabWindows/CVI應用于基于HLA的半實物仿真系統(tǒng)所存在的問題,并提出兩種解決方案將LabWindows/CVI與HLA仿真系統(tǒng)完美結合,。
1 半實物仿真系統(tǒng)結構
筆者所開發(fā)的半實物通信對抗仿真系統(tǒng)中的干擾機和通信設備均為硬件實物,,而干擾機和通信設備之間的電磁環(huán)境是使用軟硬件模擬產生的。系統(tǒng)的簡化框圖如圖1所示,。
該仿真系統(tǒng)共有七個HLA成員,,HLA成員之間通過RTI發(fā)送和接收數據。各成員的主要功能如下:
·指揮控制成員負責仿真場景與系統(tǒng)參數的設置,,通過HLA進行時間推進,,并不斷發(fā)送控制參數給各成員進行多種偵察和干擾實驗,。
·干擾器成員、模擬器成員和地面站成員將從HLA系統(tǒng)接收到的仿真命令發(fā)送給硬件實物并根據從硬件采集到的數據進行分析,,將捕獲時間,、誤碼率等信息發(fā)送給HLA中的其他成員。
·環(huán)境模擬成員隨著仿真推進實時計算各設備的位置,、姿態(tài),、速度、距離衰減和多普勒頻移,,并控制連接各設備的微波網絡,,以模擬通信設備之間的鏈路。
·效能評估成員根據從實物采集到的數據計算干擾效能,。
·視景仿真成員顯示各硬件實物在仿真場景中的位置,、姿態(tài)和其他信息。
由于設備的用途,、使用環(huán)境各異,,考察的指標各有側重,設備實物和工控計算機之間的連接類型也各不相同,,見表1,。借助LabWindows/CVI的數據采集和處理能力,可以使每臺工控機變成一臺虛擬儀器,,完成控制數據的發(fā)送,、遙測數據的采集和實時顯示功能。這些工控機程序同時又是HLA的仿真成員,,在HLA的協調下工作,。工控機的雙重使命使研究如何將虛擬儀器技術和分布式仿真平臺HLA結合成為必要。
2 LabWindows/CVI應用于HLA系統(tǒng)的兩種解決方案
LabWindows/CVI提供了多種與其他開發(fā)工具的編程接口,其中最常用的方法是以LabWindows /CVI編寫主程序" title="主程序">主程序,使用其他開發(fā)工具編寫DLL(Dynamic Link Library),,再將DLL加入到LabWindows/CVI系統(tǒng)中使用[2]。這種方法對一般的硬件采集和處理非常簡便,,但是對于本文所討論的HLA系統(tǒng)無法適用。所有HLA應用均通過RTI接口庫調用HLA所提供的功能,,RTI庫一般以C++或Java類庫的形式提供,,而LabWindows/CVI只能調用使用C語言接口的DLL庫文件。曾經有人嘗試過將HLA 的服務封裝成MEX(Matlab規(guī)定的C語言接口的DLL)[3],,但一直沒有進入實用階段,。因為RTI提供100多種仿真服務,要將這些復雜的服務都以C語言接口的形式封裝成DLL,不但費時費力,,而且必然削弱HLA作為面向對象的分布式仿真平臺的功能,。所以,必須尋找其它方法解決這個問題,。在實際開發(fā)中,,筆者先后使用以下兩種方案解決前述問題。
2.1 基于LabWindows/CVI的DLL方法
通常的LabWindows/CVI程序開發(fā)是在其集成環(huán)境中編寫C程序,,最終編譯生成可執(zhí)行文件(.exe),。采用基于LabWindows/CVI的DLL方法與此不同。該方法將LabWindows/CVI程序編譯成為DLL庫,,然后在Visual C++編寫的主程序中加以調用,。軟件的主程序使用Visual C++編寫,可以調用RTI庫加入HLA仿真系統(tǒng),,并且調用LabWindows/CVI編寫的DLL所提供的輸出函數進行界面顯示,、數據采集與控制。當用戶在界面上進行操作或者數據采集完成時,,LabWindows的DLL程序可以借助Visual C++主程序提供的回調" title="回調">回調(callback)函數通知主程序,,并將數據發(fā)送給主程序。軟件各模塊的調用關系可用圖2表示,。
要生成DLL庫,,需要在LabWindows/CVI中選擇菜單“Build | Target Type | Dynamic Link Library”。選擇菜單“Edit | Insert Construct | DllMain”向.c文件中加入DLL所需要的DllMain函數,。
LabWindows/CVI中最主要的工作是編寫DLL輸出函數,。例如下面的CallCVI函數顯示LabWindows的用戶面板TestUI.uir:
int __stdcall CallCVI ()
{
if ((panelHandle = LoadPanelEx (0, 'TestUI.uir',PANEL,__CVIUserHInst)) < 0)
return -1;
DisplayPanel (panelHandle);
RunUserInterface ();
DiscardPanel (panelHandle);
return 0;
}
這里使用LoadPanelEx函數,而不是通常使用的LoadPanel函數,。如果使用LoadPanel函數,在Visual C++中運行時會因為找不到.uir文件而報告錯誤,。在LabWindows中要實現DLL輸出函數,,需要將函數的聲明寫到一個頭文件(.h)中,不要直接將DLL輸出函數的聲明寫到面板對應的.h文件中,。因為修改面板時,,LabWindows/CVI會重新生成.h文件,從而丟失手工添加的定義,。建立一個export.h加入到工程中,,在export.h中加入CallCVI函數的定義;再選擇菜單“Build | Target Settings”,,點擊對話框" title="對話框">對話框中Exports框架的Change按鈕,。在“DLL Export Options”對話框中,設置“Export What”為“Include File Symbols”,并選中export.h,。最后選擇菜單“Build | Create Debuggable Dynamic Link Library”就可以創(chuàng)建.dll庫文件和.lib文件,。將.dll、.lib,、.uir文件和export.h文件拷貝到Visual C++的工程目錄下,,從Visual C++菜單中執(zhí)行“Project | Add To Project | Files”,將.lib和export.h添加到工程中,。在程序中調用DLL中的函數CallCVI()即可顯示LabWindows/CVI的面板,。借助DLL輸出函數的參數,可以實現從Visual C++程序向LabWindows/CVI程序發(fā)送數據,。
反過來,,如果希望將LabWindows/CVI程序中的用戶操作或外部輸入數據傳遞給Visual C++,可以通過由Visual C++提供回調函數來實現,??梢孕薷那懊娴腃allCVI的定義為:
typedef int (CVICALLBACK*VCPROC)(void*callbackData);
static VCPROC pCallbackFunc = 0;
int __stdcall CallCVI (void* pFunc)
{
pCallbackFunc = (VCPROC)pFunc;
……
}
這里使用typedef定義了一個回調函數類型VCPROC。VCPROC實際上是一個函數指針,,函數的參數為void類型,。函數指針VCPROC的返回值和參數可以根據實際應用的情況靈活修改。Visual C++調用CallCVI時,,需要提供一個回調函數的地址作為參數,,如:
CallCVI((void*)HLAProc);
這里的HLAProc 是Visual C++程序中的一個用戶函數,其參數和返回值都要與VCPROC中的定義一致,。CallCVI中,,LabWindows/CVI程序將回調函數的地址保存在全局變量pCallbackFunc中。當LabWindows/CVI中某個事件發(fā)生時,,可以調用pCallbackFunc變量中所保存的函數指針通知Visual C++程序,。下面的例子中,用戶點擊LabWindows/CVI程序面板上的按鈕后,,程序調用回調函數發(fā)送一個字符串給Visual C++,,執(zhí)行加入HLA聯邦的操作:
int CVICALLBACK ClickCallback (int panel, int control, int event, void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_COMMIT:
if(pCallbackFunc)
{ // 加入HLA聯邦
(*pCallbackFunc)((void*)'Join Fed');
}
break;
}
return 0;
}
在Visual C++函數HLAProc函數中,可以實現對RTI的CreateFederation,、JoinFederation等服務的調用,。
LabWindows/CVI 編寫的DLL也可以進行源代碼級調試,但需要做一些額外的設置,。先在Visual C++中編寫好調用DLL的程序并編譯為.exe文件,,然后在LabWindows/CVI中打開工程,選擇菜單“Run | Specify External Process”,,指定Visual C++編寫的.exe文件作為外部運行程序,。在LabWindows/CVI程序中設置斷點后,,選擇菜單“Run | Debug Project”進行程序調試。LabWindows/CVI將先啟動Visual C++編寫的程序,,當執(zhí)行到LabWindows/CVI的DLL內部的代碼時即可進行源代碼級跟蹤調試,。
2.2 基于外部編譯器的方法
另一種解決方案是利用Visual C++作為外部編譯器來實現HLA與LabWindows/CVI程序之間的相互調用。LabWindows/CVI自身編譯器的核心是David R. Hanson開發(fā)的lcc,,這個編譯器以容錯和調試為擅長,,但是優(yōu)化性能較弱[4]。使用外部編譯器還可以生成優(yōu)化的代碼,,從而提高程序運行效率,。
使用這種方法,先借助LabWindows/CVI生成顯示面板和硬件操作所需要的C語言代碼,,然后把代碼和LabWindows/CVI的頭文件和庫文件添加到Visual C++的工程中進行編譯,。由于LabWindows/CVI生成的代碼與Visual C++生成的代碼是在同一個工程中,可以直接相互調用,,不需要像上一種方法那樣設計DLL輸出函數和Visual C++回調函數,。
要使用外部編譯器,最主要的工作是為LabWindows的面板生成對象表,。在LabWindows/CVI環(huán)境中開發(fā)程序時,,LabWindows/CVI會自動為所有面板上所有控件的回調函數建立一個對象表并鏈接到程序中,運行時利用這個對象表將.uir文件中的控件對象和控件的回調函數對應起來,。而使用Visual C++等外部編譯器,,并不能自動建立這樣的對象表,也就不能自動識別控件對象的回調函數,。因此,,必須先在LabWindows/CVI中手工生成對象表,才能在外部編譯器中使用LabWindows/CVI的控件對象,?;静僮鞑襟E是在設計好LabWindows/CVI面板后,從菜單中執(zhí)行“Build | External Compiler Support...”,,在“External Compiler Support”對話框中設置“UIR CallBacks”為“Object File”,,并輸入文件路徑和文件名callback.obj,點擊Create按鈕即可創(chuàng)建callback.obj文件,。然后將.uir文件拷貝到與Visual C++編譯生成的可執(zhí)行文件目錄下(如Debug或Release目錄)。這里需要特別注意的是:.uir所在目錄與前一種方法是不同的,。再將生成的.obj,、.c和.h文件都添加到Visual C++工程中,同時還需要將cvi70/extlib/msvc目錄下的文件cvirt.lib,、cvisupp.lib添加到工程中,。在Visual C++中調用LabWindows/CVI程序時,,需要在#include部分添加對cvirte.h和userint.h的包含。在調用LabWindows/CVI生成的代碼之前,,還需要調用InitCVIRTE函數進行初始化,,如:
if (InitCVIRTE (AfxGetInstanceHandle(), 0, 0) == 0)
return; /* 內存不夠 */
本文所介紹的兩種方法都可以將LabWindows/CVI與HLA系統(tǒng)結合,而且兩種方法各有特點:基于LabWindows/CVI的DLL的方法比較獨立于外部編譯環(huán)境,,對于一些不使用 C++接口的RTI(如使用Java語言接口的pRTI)也是適用的,,比較通用,應用范圍較廣,;而基于外部編譯器的方法可以直接調用Visual C++所提供的各種功能,,從而突破了LabWindows/CVI自身的ANSI C編譯器的種種局限,通信模式簡單,,易于編程,,但這種方法不易于借助LabWindows/CVI的環(huán)境對硬件進行調試。硬件設備開發(fā)階段推薦使用前一種方案,,而如果硬件設備已經定型或采用現成商用硬件,,采用后一種方法更簡便。
參考文獻
1 周 彥,,戴劍偉.HLA仿真程序設計[M].北京:電子工業(yè)出版社,,2002
2 劉君華,白 鵬,湯曉君等.基于LabWindows/CVI的虛擬儀器設計[M].北京:電子工業(yè)出版社,2003
3 Pawletta, S., Drewelow, W., Pawletta, T. HLA-based simulation within an interactive engineering environment[J],,Distributed Simulation and Real-Time Applications, 2000. (DS-RT 2000). Proceedings. Fourth IEEE International Workshop on, 2000;(8)
4 National Instruments Corp. LabWindows/LVI Programmer Reference Manual[M].2003