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

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

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

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

圖4 單件模式的模型
4.2 紅色報(bào)錯(cuò)對(duì)話框的設(shè)計(jì)方法
1)設(shè)計(jì)要求:在主控軟件界面上,保證在同一時(shí)刻只有一個(gè)紅色報(bào)錯(cuò)對(duì)話框,能夠顯示各個(gè)外圍設(shè)備的異常信息。
2)實(shí)現(xiàn)方法:
使用單件模式為界面顯示類創(chuàng)建唯一的實(shí)例,在任何一個(gè)訪問點(diǎn)訪問類實(shí)例,調(diào)用界面顯示類的方法,完成界面顯示功能。分解設(shè)計(jì)要求與單件模式特性的對(duì)應(yīng)關(guān)系:

①鎖定資源:紅色對(duì)話框的控制權(quán);
②SS:對(duì)話框的句柄;
③SH:顯示控制器的異常信息;
④SI:對(duì)話框句柄為空,則創(chuàng)建一個(gè)紅色對(duì)話框,否則指向當(dāng)前句柄。在這個(gè)全局訪問點(diǎn),控制器填入顯示信息。
4.3 主機(jī)狀態(tài)的設(shè)計(jì)方法
1)設(shè)計(jì)要求:在主控計(jì)算機(jī)的甲乙上以主機(jī)和副機(jī)狀態(tài)運(yùn)行主控軟件,需要保證只能有一個(gè)主控程序以主機(jī)狀態(tài)運(yùn)行。
2)實(shí)現(xiàn)方法:使用單件模式保證測(cè)發(fā)控流程控制權(quán)只有一個(gè)實(shí)例。
①鎖定資源:測(cè)發(fā)控流程的控制權(quán);
②SS:操作系統(tǒng)級(jí)的命名互斥量;
③SH:構(gòu)造函數(shù)中使用CreateMutex創(chuàng)建同名互斥量;
④SI:若創(chuàng)建成功,則當(dāng)前主控程序合法取得流程控制權(quán),可繼續(xù)運(yùn)行;若創(chuàng)建失敗,則意味著流程控制權(quán)被其他主控程序鎖定,當(dāng)前程序應(yīng)當(dāng)立即退出主機(jī)狀態(tài)。
4.4 設(shè)備控制的設(shè)計(jì)方法
1)設(shè)計(jì)要求:對(duì)外部設(shè)備進(jìn)行發(fā)送命令、接收數(shù)據(jù)等操作時(shí),主控軟件首先要檢查設(shè)備是否連接正常。若未連接則提供重連接口,若已連接則關(guān)閉重連接口,保證連接的唯一性和存在性。
2)實(shí)現(xiàn)方法:
①鎖定資源:特定測(cè)試設(shè)備的控制權(quán);
②SS:用設(shè)備編號(hào)和操作句柄創(chuàng)建設(shè)備映射表;
③SI:在使用該設(shè)備前,檢查相應(yīng)設(shè)備編號(hào)是否存在操作句柄;
④SH:若存在操作句柄,則使用該操作句柄控制設(shè)備;若不存在操作句柄,則對(duì)遠(yuǎn)端設(shè)備進(jìn)行初始化操作,初始化成功后將新創(chuàng)建的操作句柄加入該設(shè)備的映射表項(xiàng)中,并返回新創(chuàng)建的操作句柄。
闡述了某主控軟件的資源訪問沖突問題,采用2種方法解決該問題,并總結(jié)出單件模式解決問題的優(yōu)勢(shì)。從報(bào)錯(cuò)對(duì)話框、主機(jī)狀態(tài)和設(shè)備控制等3個(gè)方面的設(shè)計(jì)方法描述單件模式的實(shí)現(xiàn)方法,將單件模式應(yīng)用在主控重用框架上,可以使代碼結(jié)構(gòu)更加清晰易懂,提高軟件的可維護(hù)性、可復(fù)用性。
[1] 楊喆,馬衛(wèi)華,等.設(shè)計(jì)模式在地面測(cè)發(fā)控軟件中的應(yīng)用[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.可復(fù)用面向?qū)ο筌浖幕A(chǔ)[M].李英軍,譯.北京:機(jī)械工業(yè)出版社,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] 夏克寒,牟建華,等.導(dǎo)彈測(cè)試流程優(yōu)化系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)[J].導(dǎo)彈與航天運(yùn)載技術(shù), 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.