馬志剛,劉文怡
(1.中北大學儀器科學與動態測試教育部重點實驗室,電子測試技術重點實驗室,山西 太原 030051;2.山西農業大學信息科學與工程學院,山西 太谷 030801)
在各類工業控制、工程測試領域中經常需要采集、傳輸、存儲、處理大量數據。例如,在航天測試領域的各類電壓、緩變、速變、振動、沖擊、溫度、液位、高速圖像等數據,它們有的具有很高的采樣率和較長的采集時間。因此,需要處理的數據量非常大。同時,人們很難直接通過原始數據理解問題的實質;如果將這些數據轉化為生動形象的數據曲線或圖像,將有助于分析和解決問題。
數據曲線是對連續變化物理量參數最直觀的處理和描述方法。數據曲線一般分為實時曲線和歷史曲線。實時曲線可以反映監控參數在當前或最近時刻的測量值和變化趨勢,是測量系統當前工作狀態的直觀描述,實時性要求非常高。歷史曲線是將存儲在數據文件中的歷史數據用曲線形式顯示,以反映系統在過去一段時間內的運行情況,在對系統進行故障診斷時具有重要作用[1]。本文僅就歷史曲線繪制問題做探討。
在計算機編程應用中,圖形程序設計曾經是一項比較復雜的工作。例如用匯編語言、C語言等繪圖就很繁瑣,在真正繪圖前需要做大量準備工作。20世紀末,隨著計算機應用技術的迅速發展,出現大量軟件開發工具、套件、第三方控件[2]可用于繪圖軟件的研制,例如LabWindows、Visual Basic(VB)、Visual C++(VC)等。使用VB繪制圖形比較簡單,在數據量較小的場合應用此方法比較方便、有效。當數據量較大時,使用VB繪圖會出現困難,因為繪制曲線時使用“Line”方法的次數太多,且VB本身的執行效率較低。因此,VB適合于數據量較小且對實時性要求不高的應用環境。采用VB調用API繪圖函數來繪制曲線能在一定程度上提高繪圖效率,但由于受VB本身性能所限,效果依然不理想。此外,在繪圖時經常會遇到屏幕閃爍的問題,這會嚴重影響視覺效果。很對研究者均對閃爍問題及其解決方法作了研究[3-7],但鮮有將該問題結合“提高程序執行效率問題”來討論。原因在于:上述文獻大都僅針對小容量數據處理,并不十分注重效率問題;而對于大容量數據[8],效率問題就顯得比較突出了。
程序執行效率除了與數據的存儲結構、程序的控制結構、所處理的數據量等因素有關外,開發工具的選擇也很重要。在常用的編程語言和程序開發工具中,C++語言及其開發環境(VC++、C++ Builder等)被普遍認為具有較高的執行效率[9],再結合調用微軟基礎類庫 (microsoft foundation classes, MFC)中的應用程序接口(application program interface, API)相關函數,應該會在很大程度上改善程序執行效率。本文討論的大容量數據大都超過了2 GB,一般都在4 GB左右,數據量較大。為保證軟件在繪圖時有較高的執行效率,綜合各方面因素,采用VC++6.0設計繪圖軟件。
繪制曲線之前,必須先將各通道數據從原始數據文件中提取出來,這就涉及到文件操作。此外,還應充分考察數據文件中數據的組織方式。從文件中提取數據并繪制曲線的一般過程如圖1所示。

圖1 繪制曲線的一般過程
文件是存儲在計算機外部介質上數據信息的集合,分為文本文件和二進制文件。本文討論的測試數據文件屬于二進制文件。Win32 API提供大量用于文件操作的函數,主要包括:CreateFile、ReadFile、WriteFile、FindFirstFile等;其中,CreateFile函數用于打開或創建文件;通過WriteFile函數可以向指定文件中寫入數據;ReadFile函數可以從指定文件中讀取數據。調用FindFirstFile函數,可獲得文件的詳細信息,其原型為:

函數執行后,變量lpFindFileData返回一個WIN32_FIND_DATA型結構體,它包含的文件信息較多,其中:nFileSizeHigh和nFileSizeLow分別表示文件大小(以字節為單位)的高32位和低32位。可見,調用該函數可以獲得不超過16 EB(16 MTB)文件的大小。利用FindFirstFile函數,本文設計了獲取大容量(超過 4 GB)數據文件大小的函數:Get4GFileSize,該函數的入口參數為包含完整路徑的文件名;返回值是文件的大小,類型為ULONGLONG型(64位無符號整型),相關代碼如下:



不同種類的測試數據均有其特定的數據組織方式,一般將這種數據組織方式稱為“編幀”,編幀得到的一幀數據稱為“幀結構”,測試數據就是由一系列“幀結構”組成的。要從數據文件中提取有效數據,必須明確數據的存儲和編幀格式。表1給出了某類測試數據的幀結構。

表1 某類測試數據的幀結構
表1所示測試數據的幀結構分為4部分:①幀頭(0X''DC92'')表示一幀數據的開始;②數據內容是數據幀中的有效數據部分,一幀數據中的“數據內容”可長可短,其長度設置一般需考慮采樣路數、采樣率、采集時序等因素,該幀結構中包括n路模擬量數據;③幀計數相當于數據幀的編號,以遞增方式設計,用于判斷相鄰幀是否連續,如不連續,則說明存在數據幀丟失的情況;④幀尾(0X''EB90'')表示該幀結束。
以表1所示幀結構數據為例,提取通道數據是指將每幀數據中的n路通道數據分別存入各自的通道數據文件,以備后續繪制曲線使用。在提取通道數據的同時,還應判斷原始數據是否完整,理論上講應該考察所有數據幀的長度是否正確、幀計數是否連續。考慮到原始數據容量較大,無法將所有數據一次性讀入內存進行處理。本文采取“分塊讀取”的處理方式,具體過程為:
(1) 讀出一塊數據,進行數據處理;并將該塊剩余的未處理部分存至臨時空間;
(2) 讀出下一塊數據,將上一塊剩余部分與該塊數據銜接后,再進行數據處理;同樣將剩余數據緩存;
(3) 依此類推,直至全部數據被處理完畢。其中,“數據處理”是指找到該塊數據中所有的有效數據幀,并提取各通道數據寫入各自文件。
提取數據時,“一塊數據”的大小并無嚴格限定,應根據計算機硬件配置,尤其是內存的情況進行選擇。經測試,當塊大小在256 KB~64 MB范圍內取值時,程序執行效率較高,但該值不宜超過64 MB。
VC是一種基于Windows用戶界面的、面向對象程序設計開發工具,由于其功能強大,幾乎無所不能。VC的編程方式有兩種:一種是直接調用API函數;另一種是使用MFC類庫。前者是傳統的Windows編程方式,還帶有面向過程的特征。第二種方式下,MFC把相關的API函數封裝到各種類中,是面向對象與Windows編程的有機結合,真正體現了VC面向對象的編程特點。
MFC中提供了大量封裝好的類,其中包含豐富的繪圖工具和方法。MFC提供的繪圖工具類封裝了繪圖中用到的畫筆、畫刷、位圖等。包括:CGdiObject(圖形設備接口對象類)、CPen(畫筆類)、CBrush(畫刷類)、CBitmap(位圖類)等。應用程序中需要哪種繪圖工具,就用對應的繪圖工具類生成相應的對象,然后調用SelectObject函數將其選入設備描述表中,使其生效。
曲線繪制的數據來源于各個通道數據文件。本文要處理的數據文件容量較大,數據曲線不可能一次性顯示,只能以分塊、分屏方式顯示。以一個8 GB的通道數據文件為例,假定每路數據均按照16位采樣,則每個數據點將占兩個字節,所以總的數據點數為4 G個;如果每屏一次顯示2048個數據點,則需分成2048塊。
顯示曲線時,給定要顯示數據在文件中的起始位置和需一次顯示的數據點數后,就可以通過移動文件指針定位到文件的相應位置并讀出一塊數據存入數組。根據上述功能要求,編寫了函數GetDataFromFile。其入口參數FileName為包含完整路徑的文件名;StartPos用于設置文件指針的位置;GetLen表示要讀取的數據量;Dat指定函數執行成功后存放數據的數組首地址。函數的返回值為實際讀取到的數據量。

在VC中使用MFC繪制曲線,一般需要以下幾個步驟:①確定繪圖區域;②創建設備描述表;③準備繪圖工具,包括:畫刷、畫筆、字體、顏色等;④調用API函數繪制曲線;⑤釋放設備描述表等資源。其一般流程如圖2所示。

圖2 VC中使用MFC繪制曲線的一般流程
本文設計的繪圖函數包含兩個入口參數:①數據內容;②一次顯示的數據點數。該函數的核心代碼如下:

以上代碼僅給出了繪圖函數的核心部分,一些輔助代碼并未列出。其中:(X1, Y1)和(X2, Y2)分別是繪圖區域左上角和右下角的坐標,二者共同確定了繪圖區域范圍;變量dW表示PlotCnt個數據點將繪圖區域水平均分后相鄰兩點的距離,它用于定位數據點在繪圖區域中的橫坐標;變量dH表示將繪圖區域垂直平分為65536(每個數據點占2個字節)份后的單位高度,它用于定位數據點在繪圖區域中的縱坐標。
繪制曲線的思路為:先調用MoveTo函數將畫筆定位到第一個數據點處(PlotDat[0]);再調用LineTo函數將剩余的(PlotCnt-1)個數據點依次用直線相連。另外,繪圖區的縱坐標走向是自上而下遞增的,這與常用的平面直角坐標系縱坐標自下而上遞增不同。因此,數據點的縱坐標需經過換算(Y2 - PlotDat[i]* dH)才能轉化為平面直角坐標系下的形式。
圖形程序設計是Windows應用程序的重要組成部分。有過開發圖形界面軟件經驗的程序設計人員都有這樣的體會:①顯示的數據量較大或伴隨有復雜計算時,CPU占用率就會很高,繪圖的效率極低,甚至會導致程序崩潰;②在曲線或圖形繪制過程中,如果過于頻繁地刷新或切換,顯示屏幕將出現閃爍現象,影響視覺效果。
程序執行效率與數據的存儲結構、程序的控制結構、所處理的數據量、采用的算法等因素有關。實際上,本文在編寫繪圖函數時已經在算法、數據結構、程序結構等方面做了優化設計,但是實際效果依然不佳。表現在:需繪制的數據點數越多,程序運行越慢,甚至有卡機情況出現。例如:繪圖時每次顯示的數據點數超過512 K(524288)時,就會出現比較明顯的畫面停頓。
經過分析,繪圖時間大都花費在LineTo操作上,點數越多,調用LineTo的次數就越多,不斷累積的時間將非常可觀。為縮短繪圖時間、提高程序執行效率,可以調用MFC中的Polyline函數,該函數可以將一組點一次性顯示在繪圖區域,其原型為:
BOOL Polyline(LPPOINT lpPoints, int nCount);
參數lpPoints用來指出CPoint對象數組的首地址,而CPoint對象數組中存放的正是需要依次連接形成曲線的數據點,lpPoints.x和lpPoints.y為每個數據點的橫、縱坐標;nCount參數用來指定數據點數,即lpPoints數組中的元素個數。這樣一來,CurvePlot函數可以改為如下代碼:

表2是兩類方法的繪圖時間與數據點數的關系比較。經過比較,采用Polyline方法繪圖比LineTo方法在時間上平均縮短了93.566%,從很大程度上提高了程序執行效率。
在進行歷史曲線回放時,經常會發現程序所繪制的曲線在顯示屏幕閃爍,特別是一次顯示的數據量很大時,該現象尤為嚴重。實際上,顯示閃爍是由VC繪圖機制引起的,這一情況在VB或其他編程工具中同樣存在,只是在多數情況下閃爍并不明顯而不易被人眼發覺。而在圖形被頻繁切換或有較大反差時,如動畫、游戲等,往往會出現較為明顯的閃爍。

表2 兩類方法繪圖時間與顯示數據點數的關系比較
在VC中,界面每次被刷新時,系統都將自動利用背景色填充繪圖區域;填充完畢后,系統才會重新調用繪圖函數對繪圖區域進行重繪。因此每次重繪時,就相當于在兩次繪圖間插入一幅背景色圖,而背景色可能與繪圖顏色反差很大。所以繪圖頻率越快,反差也就越明顯,于是就出現了閃爍現象。
實際應用中有許多消除圖形閃爍的方法,其中的雙緩沖法是使用較為普遍的一種。雙緩沖法的基本思想是:在內存中有兩塊緩存,除了顯示區域設備描述表(前端緩沖區)外,還有一塊需要手動建立的與設備描述表兼容的內存設備描述表(后備緩沖區)。繪圖時,先將圖形繪制在后備緩沖區中,然后將后備緩沖區中的圖像拷貝到前端緩沖區中進行顯示[3,7]。其過程可概括為以下幾步:
(1) 創建與顯示區域設備描述表兼容的內存設備描述表;
(2) 創建與內存設備描述表相兼容的臨時位圖并將該位圖選入內存設備描述表中;
(3) 將圖形繪制在內存設備描述表中;
(4) 將內存設備描述表中的圖形拷貝到顯示區域設備描述表中;
(5) 釋放設備描述表句柄、臨時位圖等資源。
拷貝圖形可直接調用BitBlt函數,其原型為:


經過充分測試,采用雙緩沖法可以有效消除繪圖時出現的顯示區域閃爍現象。與此同時,采用該方法能進一步提高程序執行效率。表3列出了采用雙緩沖法與非雙緩沖法的繪圖時間與顯示數據點數的關系比較。從表中統計數據可以看到,采用雙緩沖法后,繪圖時間平均縮短了10%左右。由此可見,采用雙緩沖法既可消除繪圖閃爍,還有助于進一步縮短繪圖時間。這對大容量數據處理具有重要意義。

表3 雙緩沖法和非雙緩沖法的繪圖時間與顯示數據點數的關系比較
本文使用VC++ 6.0設計了“航天測試系統大容量數據曲線繪制軟件”,其界面效果如圖3所示。圖中曲線對應的是一個通道數據文件,大小為3.26 GB(3510750168 B),當前顯示的點數為32768。該通道數據的采樣率為270 kHz,曲線橫坐標為根據數據點和采樣率換算的時間(s);縱坐標是采集的模擬量對應的電壓值(0~5 V)。

圖3 曲線繪制軟件界面
數據是考核設備工作狀態和系統性能的重要指標,而曲線則是測試數據最直觀的表現方式。本文的主要工作在于:基于MFC對圖形編程的支持,利用VC++ 6.0設計了一種大容量數據曲線繪制軟件,并著力解決繪圖軟件的執行效率和顯示閃爍問題。為解決第一個問題,本文直接采用Polyline函數繪圖,該方法比傳統的LineTo函數繪圖法在時間上縮短了九成以上。對于第二個問題,采用雙緩沖方法,不但解決了圖形顯示閃爍的問題,還進一步提高了軟件的執行效率。本文選用VC++ 6.0作為軟件開發工具,它僅能較好地支持用戶在Windows 7以下版本操作系統中進行編程。如果選擇高版本操作系統進行軟件設計,VC++ 6.0下的程序可以很方便地被移植到諸如VC++ 2008、VC++2010或更高版本中。
除了歷史曲線繪制,還有一類應用較廣的實時曲線繪制問題[10-11]。相比之下,實時曲線繪制對時間的要求更高,它要求在特定時間內(例如:25 ms或100 ms)將最近接收的數據以曲線的形式體現在顯示屏上,因此留給曲線繪制的時間非常短。而在這段極其有限的時間內還要完成數據接收、分析、存盤等操作。軟件設計一旦在時間上控制不好,實時系統就可能出現丟數、無法存盤、顯示時滯等問題[9]。隨著計算機或所采用的嵌入式系統性能逐步提升,這從一定程度上節省了部分時間;而與之對應的是對測試系統提出的更高要求:單位時間內要處理成倍增加的數據、實時性要求不斷提升等。因此,必須從程序或算法本身入手,降低其時間和空間復雜度、不斷優化算法。
曲線繪制在各領域均有廣泛應用。本文設計的算法和軟件在多項航天測試項目中得到了成功應用,并取得了較為理想的效果。雖然不同的測試領域對數據曲線的顯示要求各有差異,但本文設計的軟件具備一定的通用性,可經過適應性修改應用到其他測試領域。
[1]趙衛強, 魯墨武.基于VB6.0的監控系統軟件中數據存儲及數據曲線實現方法的研究[J].沈陽航空工業學院學報, 2003, 20(1): 31-34.
[2]王瑾琦, 王紅亮.某遙測系統數據記錄設備軟件設計[J].傳感器與微系統, 2011, 30(9): 128-130.
[3]焦景欣, 代 亮, 胡含凱, 司昕璐.采用雙緩存的視景仿真圖像顯示效果改善方法[J].探測與控制學報,2010, 32(5): 92-95.
[4]葉林瓚, 雷小永, 戴樹嶺.飛行仿真系統數據可視化設計與實現[J].計算機仿真, 2012, 29(3): 141-144, 197.
[5]王 艷, 張禮君, 韓 嘯.數字超聲輪軸探傷儀軟件系統的設計與實現[J].自動化與儀表, 2011, 26(7): 21-25.
[6]郭振華, 江亞群, 楊帥雄, 梁勇超, 黃 純.故障錄波器后臺分析軟件關鍵問題研究[J].電力系統保護與控制, 2011, 39(19): 73-78.
[7]唐海全, 邵才瑞, 李洪強.隨鉆測井曲線無閃爍繪制技術[J].測井技術, 2010, 34(5): 479-482.
[8]李 河, 王祝文, 李舟波, 王朝輝.大數據量地球物理測井繪圖關鍵技術研究[J].地球物理學進展, 2005,20(1): 71-77.
[9]劉文怡, 甄國涌.高速測控系統中實時數據處理軟件技術研究[J].華北工學院學報, 2004, 25(3): 191-193.
[10]吳建飛, 程明霄.嵌入式Linux圖形系統實時采樣曲線繪制的實現[J].計算機工程, 2007, 33(19): 259-261.
[11]王 澍, 林 輝, 張海濤.應用C++ Builder 6.0的串口數據實時曲線繪制方法研究[J].電光與控制, 2008,15(7): 93-96.