程西娜 蔡文齋
(中國電子科技集團(tuán)公司第三十九研究所陜西西安 710065)
該題目來源于本承研單位統(tǒng)一標(biāo)準(zhǔn)化天線監(jiān)控程序設(shè)計開發(fā)項目,鑒于以前工程監(jiān)控程序分為各工程組開發(fā)模式之弊端,該研發(fā)室欲開發(fā)出一款通用的天線類應(yīng)用程序框架,在該框架基礎(chǔ)上再加入各自工程特點的業(yè)務(wù)邏輯模塊,最終形成某工程使用的監(jiān)控軟件。經(jīng)有關(guān)專家充分論證后,欲開發(fā)出一種通用的與工程無關(guān)的底層控制類框架平臺,用來完成整個控制工程最關(guān)鍵的功能如通訊、線程調(diào)度和消息傳遞機(jī)制等等,對業(yè)務(wù)邏輯開發(fā)者而言,無需關(guān)心硬件層的操作,只需依據(jù)具體項目要求編制有關(guān)的數(shù)據(jù)處理工作即可,這樣可以提高研制效率。本文主要論述了此框架中相關(guān)模塊的實現(xiàn)方法,諸如多串口通訊模塊、網(wǎng)絡(luò)通訊模塊和主控線程模塊。
整個框架運行在windowsxp3系統(tǒng)上,使用Delphi XE2開發(fā)[1-4]。采用抽象構(gòu)架與應(yīng)用邏輯相結(jié)合的開發(fā)方法。首先設(shè)計一個與控制工程項目無關(guān)的核心框架,完成整個系統(tǒng)硬件級通信調(diào)度和調(diào)試等功能,該框架主要功能模塊為:所有硬件層設(shè)備通信模塊、調(diào)度模塊、主工作線程模塊、動態(tài)調(diào)試支持模塊、存儲線程和日志線程工作模塊??蚣苣K與具體控制工程無關(guān),一旦調(diào)試完成,其函數(shù)級文件即可使用在以后其它任何該類工程中。該框架設(shè)計方法已經(jīng)由導(dǎo)師發(fā)表[5],不再贅述。該文主要討論在新開發(fā)環(huán)境下DELPHIXE2如何實現(xiàn)出工程用的部分模塊。
框架將通訊用數(shù)據(jù)定義為抽象緩沖區(qū)形式,當(dāng)框架代碼段完成設(shè)備讀后,發(fā)一個用戶定義的消息給前臺應(yīng)用邏輯窗口給界面,通知硬件層數(shù)據(jù)已準(zhǔn)備就緒。這樣業(yè)務(wù)邏輯中僅實現(xiàn)讀消息函數(shù)即可完成數(shù)據(jù)處理和顯示等工作,這樣設(shè)計的好處在于軟件結(jié)構(gòu)清晰明了,這兩部分可以獨立開發(fā)。因為這次開發(fā)工具的版本提升,以前的許多實現(xiàn)方法都做了調(diào)整,本文主要討論在新環(huán)境下這些部分功能塊是如何實現(xiàn)的。
多路串口分別使用ComPort組件和MoxaPcomm組件庫實現(xiàn),ComPort組件為VCL組件,可以在DELPHI環(huán)境下無縫使用,MoxaPcomm為臺灣專業(yè)串口廠商提供的串口庫,可在DELPHI環(huán)境下使用DLL技術(shù)加載[6],這里重點介紹多路串口初始化參數(shù)配置的技術(shù)實現(xiàn)。
該應(yīng)用程序框架為了適用于所有這一類項目,設(shè)計時考慮的的是通用性,所以將該部分設(shè)計為具備可以配置的動態(tài)自調(diào)整功能,例如工程1使用12個串口,工程2使用32路串口等等,使用一抽象的串口結(jié)構(gòu)定義串口類,再使用動態(tài)數(shù)組形式定義串口實例,在窗口創(chuàng)建函數(shù)中首先枚舉系統(tǒng)中所有串口數(shù)量,根據(jù)系統(tǒng)中串口數(shù)量設(shè)置動態(tài)數(shù)組長度,再使用程序方法自動生成配置界面,配置界面使用DELPHI的屬性頁技術(shù)展現(xiàn),采用NextSuite組件集的NxInspector組件實現(xiàn),該組件在設(shè)計時定制是較方便的,但當(dāng)串口較多時,每個串口都有10個參數(shù),程序員工作量較大,而且不能夠動態(tài)生成,經(jīng)反復(fù)試驗,使用如下關(guān)鍵代碼實現(xiàn)了程序自動生成:
運行程序Inspector組件中的COM 樹已生成,在COM參數(shù)配置樹中勾選需要打開的串口,配置波特率、數(shù)據(jù)位、終止位、校驗位和超時值等參數(shù),配置完畢后點擊。點擊apply按鈕,對應(yīng)的串口指示燈變亮,表明已經(jīng)打開成功,如圖1所示。
圖1 串口配置樹
Moxa串口庫編程與ComPort編程方法有差異,Moxa串口庫使用動態(tài)鏈接庫方式提供,底層實現(xiàn)與ComPort不同,但多參數(shù)配置部分仍然可以使用屬性頁方式編程,串口的讀寫仍然使用動態(tài)分派機(jī)制。
網(wǎng)絡(luò)部分使用了多頁組件PageControl,分別為UDP、TCP/IP和組播3個頁面[7]。網(wǎng)絡(luò)的讀寫采用了Indy10.0.52組件開發(fā),分為客戶端和服務(wù)器端,通過界面設(shè)置IP地址和端口,數(shù)據(jù)以數(shù)組的形式發(fā)送,delphi 2007以上版本使用Indy10組件,與以前版本差異較大。Indy9組件[8]的讀是采用流方式,如UDP讀的重載函數(shù)為IdUDPServerUDP Read(Sender:TObject;AData:TStream;ABinding:TIdSocket Handle);,讀緩存區(qū)的內(nèi)容需要用AData.Read(ReadBuffer,AData.Size)函數(shù)獲得;而Indy10組件的讀是直接采用緩存區(qū)的方式,如UDP讀的重載函數(shù)為IdUDPServerUDPRead (AThread:TIdUDPListener Thread;AData:TBytes;ABinding:
TIdSocket Handle),讀緩存區(qū)的內(nèi)容直接從AData里獲得。
3.2.1 UDP方法的實現(xiàn)
UDP通訊的特點是點對點的通訊方式,服務(wù)器開放端口,客戶端只需要將數(shù)據(jù)發(fā)至該服務(wù)器開放的端口即可,由于它不屬于連接型協(xié)議,即發(fā)送時不需要接收方確認(rèn),可能會出現(xiàn)丟包現(xiàn)象,但是它具有處理速度快和資源消耗小的優(yōu)點。
采用了組件包中IdUDPServer和IdUDPClient實現(xiàn)了8路服務(wù)器端的讀操作和1路客戶端的寫操作。服務(wù)器端的設(shè)置同樣采用了Inspector組件,以配置樹的形式展現(xiàn),點擊需要開放的服務(wù)器的“Enable”,對應(yīng)的端口被激活;客戶端的設(shè)置需填入遠(yuǎn)程地址和端口號,勾選“激活”后客戶端打開成功,如圖2所示。
圖2 服務(wù)器和客戶端配置
服務(wù)器和客戶端打開成功后,重載服務(wù)器組件IdUDPServer的OnUDPRead函數(shù),該函數(shù)在每讀取到一幀數(shù)據(jù)時自動執(zhí)行,通過TIdSocketHandle類的ABinding變量可以獲得客戶端的IP地址和端口號,在TBytes類型的AData變量中可以獲得讀取的緩存區(qū)的內(nèi)容。再采用內(nèi)存拷貝函數(shù),將AData內(nèi)的字節(jié)放到對應(yīng)的讀服務(wù)器緩存區(qū)。函數(shù)如下:
3.2.2 TCP/IP方法的實現(xiàn)
TCP是面向連接的通信協(xié)議,通過3次握手建立連接,通訊完成時要拆除連接,具有穩(wěn)定性好的特點[9]。采用了組件包中IdTCPServer和IdTCPClient組件實現(xiàn)。
客戶端寫的操作實現(xiàn):從界面獲得遠(yuǎn)端IP和端口號,在服務(wù)器打開的前提下,連接服務(wù)器。此時如果服務(wù)器沒有打開,界面會有提示需先打開服務(wù)器。連接成功后,給客戶端的寫緩存區(qū)里賦值需要發(fā)送的字節(jié),調(diào)用IdTCPClient組件的IO句柄的Write(TCPWriteBuffer)函數(shù),完成客戶端寫的功能。
服務(wù)器端的讀操作實現(xiàn):重載了服務(wù)器組件IdTCPServer的OnConnect函數(shù),OnDisconnect函數(shù)和OnExecute函數(shù)。其中OnConnect函數(shù)和OnDisconnect函數(shù)是在客戶端連接和斷開服務(wù)器時的動作,發(fā)給界面連接或斷開的提示。讀服務(wù)器是采用了OnExecute函數(shù),該函數(shù)在每讀取到一幀數(shù)據(jù)時自動執(zhí)行,用TIdContext類的AContext變量的IO 句柄的ReadBytes(TCPReadBuffer)函數(shù),將讀到的數(shù)據(jù)放到服務(wù)器的讀緩存區(qū)。部分代碼如下:
3.2.3 組播方法的實現(xiàn)
組播協(xié)議與現(xiàn)在廣泛使用的單播協(xié)議不同之處在于,一個主機(jī)用單播協(xié)議向n個主機(jī)發(fā)送相同的數(shù)據(jù)時,發(fā)送主機(jī)需要分別向n個主機(jī)發(fā)送,并發(fā)送n次。而組播協(xié)議只需要發(fā)送一次,其數(shù)據(jù)由網(wǎng)絡(luò)中的路由器和交換機(jī)逐級復(fù)制并發(fā)給各個接收方,是一種一對多的通訊方式,這樣既節(jié)省服務(wù)器資源也節(jié)省網(wǎng)絡(luò)主干的帶寬資源[10]。
組播中的讀是在客戶端實現(xiàn),寫是在服務(wù)器中實現(xiàn),這一點與前2種方法正好相反。采用了組件包的IdIPMCastClient實現(xiàn)了1路客戶端的讀操作和IdIPMCastServer實現(xiàn)了1路服務(wù)器端的寫操作。
服務(wù)器端的寫操作:給服務(wù)器端的寫緩存區(qū)里賦值需要發(fā)送的字節(jié),使用IdIPMCastServer組件的Send函數(shù)即可實現(xiàn)寫操作。
客戶端的讀操作:重載IdIPMCastClient組件的OnIPMCastRead函數(shù),該函數(shù)在每讀到一幀數(shù)據(jù)時自動執(zhí)行,在TBytes類型的AData變量中可以獲得讀取的緩存區(qū)的內(nèi)容。再采用內(nèi)存拷貝函數(shù),將AData內(nèi)的字節(jié)放到對應(yīng)的讀服務(wù)器緩存區(qū)。部分代碼如下:
控制主線程由主控制事件驅(qū)動,線程開始后永遠(yuǎn)等待該主控制事件對象,當(dāng)該事件對象變?yōu)橛行盘枒B(tài)時,復(fù)位該數(shù)據(jù)對象到無信號態(tài),調(diào)主控函數(shù)完成控制操作,再次進(jìn)入線程開始進(jìn)入另一次等待。事件的有信號態(tài)由中斷線程的中斷服務(wù)線程設(shè)置,該部分與硬件接口形式有關(guān)系,一般使用Modam狀態(tài)變化或者TTL電平形式的硬件接入方式。主控線程流程圖如圖3所示。
圖3 主線程框圖
由整體基礎(chǔ)架構(gòu)由承研單位有關(guān)專家負(fù)責(zé),作者編程并調(diào)試出文中介紹的各部分模塊,經(jīng)連接測試,代碼無誤功能齊全。調(diào)試期間網(wǎng)絡(luò)通訊部分樣例太少阻力較大,經(jīng)專家指導(dǎo)及反復(fù)試驗最終使用文中介紹的方法實現(xiàn)了設(shè)計。
[1]譚 燕,趙 磊,李之明.Delphi 高級輔助工具精解[M].北京:中國鐵道出版社,2003:28-55.
[2]李之明,高玉琢.Delphi7 組件經(jīng)典解析[M].北京:中國鐵道出版社,2003:88-105.
[3]CANTU M.Delphi Xe Handbook:A Guide to New Features in Delphi Xe[M].USA:Createspace,2011:88-92.
[4]李 慧.Delphi 程序開發(fā)范例寶典(第三版)[M].北京:人民郵電出版社,2012:285-291.
[5]蔡文齋.Win32 環(huán)境下一種通用控制軟件的實現(xiàn)方法[J].現(xiàn)代電子技術(shù),2005(8):51.
[6]張秀關(guān).單片機(jī)與計算機(jī)串口通信實踐[M].北京:電子工業(yè)出版社,2013:30-37.
[7]胡 鳴.Windows 網(wǎng)絡(luò)編程技術(shù)[M].北京:科學(xué)出版社,2008:67-89.
[8]蔡文齋.應(yīng)用INDY 組件設(shè)計網(wǎng)絡(luò)調(diào)試器[J].現(xiàn)代電子技術(shù),2006(24):94.
[9]尹圣雨.TCP/IP 網(wǎng)絡(luò)編程[M].北京:人民郵電出版社,2014:24-55.
[10]劉 瑩,徐 恪.Internet 組播體系結(jié)構(gòu)[M].北京:科學(xué)出版社,2008:44-58.