閆雪麗 薛 靜 王 洋 楊 彬
北京航天自動控制研究所,北京100854
地面測發控系統是流程的測試、發射及控制的核心,一般由主控軟件、數據處理軟件及顯示軟件等多個應用軟件組成,實現發射控制流程數據的接收、判讀、顯示、處理、存儲和發送,完成啟動飛行控制軟件前的發射準備工作。
近年來,航天任務呈現高密度發射狀態,對測發控系統功能要求越來越高,其研制周期也在縮短,提高軟件研制效率,可復用性、可維護性等非功能性要求被提上日程。設計模式是對面向對象軟件設計經驗的總結,是更加方便快捷地復用成功的設計思想。工程實踐表明將設計模式應用在航天軟件設計中,可以降低軟件設計復雜程度,提高軟件可靠性[1]。
設計模式概念由Christopher Alexander提出,核心是提供一個相關問題的解決方案,使人們避免不必要的重復勞動。這個思想也可以應用在面向對象程序設計領域,設計模式是解決某類特定的面向對象軟件問題的方法,也是對軟件設計人員經驗的總結。開發人員利用設計模式可以更加簡單方便地復用成功的設計和體系結構[2]。將已證實的技術表達成設計模式也會使新系統開發者更加容易理解其設計思路,使軟件系統易復用、易維護。
單件模式(Singleton)是一種對象創建型設計模式,它的意圖是保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。為保證實例唯一性,需要從2個點出發:
1)創建實例時,有創建檢查,保證實例唯一。
定義一個靜態成員變量_instance,初始化為0,用于記錄是否創建實例。如果其值為0則用唯一實例初始化它,否則返回該變量值。
2)創建方式唯一,保證只有一個創建接口。
創建實例的唯一接口為public類型的靜態成員函數。構造函數聲明為protected類型,直接實例化將得到一個編譯錯誤信息。這就保證了僅有一個實例可以被創建。
單件模式的C++實現方法見圖1。

圖1 單件模式的C++實現方法
1)主控軟件的功能
主控軟件是地面測發控系統實現流程控制與數據判讀自動化的核心,以主機狀態和副機狀態運行于主控計算機甲機和乙機上,在需要時,可以實現主副機切換。同時,主控軟件啟動后在數據庫中記錄測試項信息、發送/接收數據、出錯信息及用戶操作等數據,便于后期判讀和排故[3]。
2)出現問題的操作及現象
執行測試流程過程中,用戶在主控軟件上操作,使主機切換為副機,再切換為主機,點擊“啟動測試”按鈕執行流程。在執行幾個(每次數量不同)測試步序時,主控界面上有提示框“存儲測試數據失敗”,主控軟件異常終止。
3)產生問題的原因
經分析和排查發現,存入數據庫緩沖區的各項數據,分別以結構體方式定義,每個結構體的成員變量數量和類型各有不同。寫文件線程首先判斷緩沖區內容的數據類型bType,按照對應的結構體成員變量的數量和類型存儲到數據庫文件中,并清除緩沖區內容。
在“啟動測試”按鈕的響應函數中,創建一個寫文件線程和一個mdb存儲文件。在主機切換為副機時沒有關閉這個線程,再切換為主機后,由于用戶操作需要又一次點擊“啟動測試”按鈕,其響應函數再次創建一個寫文件線程和一個mdb存儲文件。為方便描述,這2個線程分別稱為線程1和2。
當線程1和2都獲得了bType,線程1在存儲數據前被線程2中斷。線程2讀取數據后刪除該數據。回到線程1的中斷點,線程1繼續執行。若線程1要取的數據類型與緩沖區現在存放的一致,則程序會繼續執行,只是線程1少存了一組數據。若線程1要取的數據類型與緩沖區現在存放的不一致,那么線程1讀數時,造成內存訪問越界,程序被異常終止(這也是復現問題過程中,造成軟件異常退出時,執行測試步序數量不同的原因)。主控軟件從開始測試到異常終止的過程見圖2。
3.1 打補丁法
該問題出現源于讀取和修改數據庫緩沖區的類是可以創建多個寫文件線程的,解決方法是保證只有一個寫文件線程訪問數據庫的緩沖區。最直接的解決方法是及時關閉靈活創建的線程,在主機切換為副機后,銷毀寫文件線程、關閉存儲文件并斷開與數據庫的連接。在副機切換為主機后,再重新創建寫文件線程、創建存儲文件,建立與數據庫的連接。

圖2 主控軟件問題出現的過程圖
該種方法雖然簡單直接,但屬于發現問題后打補丁的解決辦法。設計初期,需要設計人員準確且全面地分解用戶操作和軟件運行剖面才能做此設計,因此對軟件設計人員的要求高,且代碼重用性差。
3.2 單件模式方法
3.2.1 設計思想
程序設計中應避免創建線程的隨意性,我們期望在主控軟件運行時,從始至終有且只有一個寫文件線程來訪問數據庫。當主控軟件以主機狀態運行時,該線程處于運行狀態;當主控軟件以副機狀態運行時,該線程處于掛起狀態。該線程的創建,應置于程序啟動后就會立即執行且只能執行一次的函數中。
設計時,考慮代碼結構和后期維護,將寫文件線程的維護和數據庫的操作分離開來,我們需要新建一個數據庫操作類CAdoCommand變量,用于對數據庫進行訪問和操作。當主機切換為副機時,只需要關閉主機軟件與數據庫的連接,關閉存儲文件;當切換為主機時,建立與數據庫的連接,新建存儲文件。
3.2.2 實現方法
利用單件模式的一個類只有一個實例的特性實現:
1)用將數據記錄類CDataRecoder的靜態成員函數Instance來定義這個類操作,定義一個靜態成員變量_instance,它是指向類的唯一實例指針,其構造函數聲明為protected,這就保證了僅有一個實例可以被創建。在CDataRecoder構造函數中創建寫文件線程,試圖直接實例化CDataRecoder對象將在編譯時得到一個報錯信息;
2)主控軟件啟動后就只有一個主窗口運行,將創建CDataRecoder對象綁定在主窗口構造函數CCentralConsoleDlg中,從而保證只有一個CDataRecoder實例;
3)在CDataRecoder類中增加成員變量CAdoCommand m_adoCmd,用于數據庫的連接、斷開操作。
針對主控軟件問題的單件模式具體實現方法見圖3,其中紅色部分是修改部分。
3.2.3 單件模式的優勢
單件模式有2個特性:唯一實例和一個全局訪問點。單件模式應用在主控軟件上,有以下幾點優勢:

圖3 單件模式的實現方法
1)從一個類只有一個實例的角度看:
①提高軟件可靠性
多線程訪問資源沖突等問題在主控軟件運行中時有發生,此類問題難發現、難排查且難測試,只有在實際運行到觸發點時才能表現出來。采用單件模式設計主控軟件,可從設計早期就避免建立多個相同線程、同一資源被多處使用等情況,可以提高軟件可靠性;
②代碼結構清晰
使用單件模式來保證類只能創建唯一實例,不需要考慮在不同輸入條件時軟件運行剖面,無須多處增加代碼保證唯一實例。相比方法1在主機切換為副機后增加代碼關閉線程,單件模式使代碼結構更加清晰。結構清晰的代碼框架或代碼,更容易實現重用;
③封裝性好
在構造函數中創建寫文件線程,可以保證只有一個線程創建,避免創建線程的隨意性,封裝性好;
④易管理易維護
該線程在切換主副機時一直存在,無需重復關閉和創建,便于管理。采用單件模式,可以降低開發人員的分析設計工作量和難度,降低了對設計人員的要求,更容易維護。
2)從提供一個全局訪問點的角度看:
①數據共享
若一個模塊中定義了數據庫接口實例,而其它模塊也需要訪問該數據庫的數據,則又需要重新定義數據庫接口實例,這樣在內存中就保存了多份同樣的數據,降低了內存利用效率。若使用單件模式,只保留一個數據庫接口實例,提供一個全局訪問點,各個模塊就可以共享數據,減少資源開銷,提高軟件運行效率;
②維護名空間
單件模式提供一個全局訪問點,是對全局變量的一種改進,避免了存儲唯一實例的全局變量污染名空間,便于管理和后期維護。
4.1 重用框架的改進方案
近年來,地面測發控系統軟件產品復雜度不斷增加,為提高軟件產品的可復用性、可維護性和可靠性,降低對設計人員的要求,地面主控軟件復用框架的研制也被提上日程[4]。從資源管理角度看,主控軟件的某些資源由使用對象進行抽象,但并非能夠創建任意數量的實例,比如界面上彈出的紅色報錯對話框。按照任務要求,同一時間只能出現一個紅色報錯對話框,用來顯示各個設備異常的報錯信息。雖然該對話框實例是由各個設備控制器進行抽象的,但不能創建任意數量的報錯對話框。此時,可以采用單件設計模式Singleton。它的本質是為了確保應用程序在使用環境中僅有一個實例占據資源,避免產生多實例的資源競爭問題。此處的“資源”不只是狹義的內存數據區、消息隊列緩沖區的數據,還包括依據任務要求主控軟件只能出現的一個實例,比如對話框、運行狀態等。
單件模式是最常用的創建型設計模式,它的應用可以讓設計人員更加方便地復用成功的設計和體系結構。單件模式的實現通常由單件狀態標記SS、單件獲取接口SI和單件資源句柄SH構成。初始化時置SS為空,程序運行過程中調用SI獲取SH,并通過SH使用單件資源。在SI工作時,首先檢查SS的值是否為空,如果為空則創建單件資源并綁定這些資源到SH上,置SS的值為滿并返回SH;若SS不為空則標志著單件資源已創建,應立即返回SH。單件模式的模型如圖4所示。

圖4 單件模式的模型
4.2 紅色報錯對話框的設計方法
1)設計要求:在主控軟件界面上,保證在同一時刻只有一個紅色報錯對話框,能夠顯示各個外圍設備的異常信息。
2)實現方法:
使用單件模式為界面顯示類創建唯一的實例,在任何一個訪問點訪問類實例,調用界面顯示類的方法,完成界面顯示功能。分解設計要求與單件模式特性的對應關系:

①鎖定資源:紅色對話框的控制權;
②SS:對話框的句柄;
③SH:顯示控制器的異常信息;
④SI:對話框句柄為空,則創建一個紅色對話框,否則指向當前句柄。在這個全局訪問點,控制器填入顯示信息。
4.3 主機狀態的設計方法
1)設計要求:在主控計算機的甲乙上以主機和副機狀態運行主控軟件,需要保證只能有一個主控程序以主機狀態運行。
2)實現方法:使用單件模式保證測發控流程控制權只有一個實例。
①鎖定資源:測發控流程的控制權;
②SS:操作系統級的命名互斥量;
③SH:構造函數中使用CreateMutex創建同名互斥量;
④SI:若創建成功,則當前主控程序合法取得流程控制權,可繼續運行;若創建失敗,則意味著流程控制權被其他主控程序鎖定,當前程序應當立即退出主機狀態。
4.4 設備控制的設計方法
1)設計要求:對外部設備進行發送命令、接收數據等操作時,主控軟件首先要檢查設備是否連接正常。若未連接則提供重連接口,若已連接則關閉重連接口,保證連接的唯一性和存在性。
2)實現方法:
①鎖定資源:特定測試設備的控制權;
②SS:用設備編號和操作句柄創建設備映射表;
③SI:在使用該設備前,檢查相應設備編號是否存在操作句柄;
④SH:若存在操作句柄,則使用該操作句柄控制設備;若不存在操作句柄,則對遠端設備進行初始化操作,初始化成功后將新創建的操作句柄加入該設備的映射表項中,并返回新創建的操作句柄。
闡述了某主控軟件的資源訪問沖突問題,采用2種方法解決該問題,并總結出單件模式解決問題的優勢。從報錯對話框、主機狀態和設備控制等3個方面的設計方法描述單件模式的實現方法,將單件模式應用在主控重用框架上,可以使代碼結構更加清晰易懂,提高軟件的可維護性、可復用性。
[1] 楊喆,馬衛華,等.設計模式在地面測發控軟件中的應用[J].航天控制,2014,32(2):91-95.(Yang Zhe, Ma Weihua, et al. Design Pattern Used in Software Reuse of Test Launch and Control System[J]. Aerospace Control, 2014,32(2):91-95.)
[2] Gamma E,Helm R,Johnson R.可復用面向對象軟件的基礎[M].李英軍,譯.北京:機械工業出版社,2000.(Gamma E,Helm R,Johnson R. Design Patterns:Elements of Reusable Object-Oriented Software[M].Li Yingjun,Translate.Beijing:China Machine Press,2000.)
[3] 夏克寒,牟建華,等.導彈測試流程優化系統設計與實現[J].導彈與航天運載技術, 2012, 318(2):43-46. (Xia Kehan, Mou Jianhua,et al. Design and Implementation of Missile Test Process Optimizing System[J]. Missiles and Space Vehicles, 2012,318(2):43-46).
[4] Fayad M, Schmidt D, Johnson R. Building Application Frameworks: Object-Oriented Foundations of Framework Design[M]. New York: John Wiley&Sons, 1999.