高嘉韻,倪晉平,徐 飛
(1.西安工業大學 光電工程學院,西安 710000; 2.西安工業大學 計算機學院,西安 710000)
靶場試驗測試作為武器研發、生產和測試的重要一環,主要對武器的各種性能指標進行的檢測,以確定武器是否能夠達到預期的指標要求。立靶密集度測試是槍彈生產檢驗的關鍵參數,雙線陣交會精度靶密集度參數測量系統因較高的測量精度、使用方便以及相對較低的使用成本而被廣泛使用[1-3]。系統包括三個部分,上位機軟件、數據采集處理下位機軟件和精度靶采集系統,其中上位機軟件就是本文要設計實現的。
目前國內外對于該領域的測控軟件通常采用C/S(客戶端/服務器)架構實現的,軟件形成兩層結構,服務器負責處理具有復雜邏輯的業務功能,客戶端實現用戶界面和簡單的數據處理。這種架構軟件實際使用中一般需要專門的客戶端軟件安裝使用安裝,處理出現的問題難度較大,也難以功能擴展,可能會出現開發一個全新的軟件的情況[4]。如丁力等[5]設計C/S架構的控制管理系統有如下缺點:系統擴展性差,業務變更不靈活;兼容性差,開發工具不兼容,獨立客戶端安裝;維護和升級成本高。
結合上述問題,精度靶測試系統上位機軟件采用拓展性和維護性方便、簡單、共享性強和訪問簡單的B/S(瀏覽器/服務器)架構開發[6-7]。將用戶界面等在瀏覽器端實現,為了不僅能簡化系統的維護,還可以降低使用成本,將所有的功能邏輯都集中到了服務器端。
雙線陣交會精度靶測控系統整體上可分為精度靶測控系統軟件上位機部分、數據采集處理下位機部分和精度靶采集硬件系統三部分,如圖1所示。其中將上位機部分習慣稱為控制端,下位機和精度靶硬件采集系統整體稱為測量端,從使用角度來說,一般情況下上位機和測量端之間間隔100米左右,通過網線進行連接,實現遠程指令控制和數據傳輸的使用需求。

圖1 精度靶測控系統整體結構圖
精度靶測控系統功能主要是實現對整個試驗流程進行控制,需要上位機執行相應的控制命令,判斷當前的任務或流程執行情況,以及是否進入下一個控制命令[8];其次是接收下位機傳輸的數據。這里控制方面主要需要實現遠程控制網絡繼電器操作以及任務開始、結束的控制,數據方面主要是對下位接收的數據進行一個直觀顯示和存儲。
精度靶系統對于上位機的要求是能夠及時準確的接收下位的數據,同時對整個試驗流程進行功能控制。從上述情況來看,上位機主要實現功能分為以下幾個:
1) 電源開關控制:上位機軟件啟動后,檢測與網絡繼電器連接狀態,向網絡繼電器發送控制命令,從而實現遠程控制下位機和精度靶硬件部分的電源開關。
2) 狀態檢測:下位機軟件和精度靶硬件采集部分啟動后,下位機軟件獲取和硬件的連接狀態,將連接狀態發送到上位機軟件進行顯示,方便使用者知道下位機和精度靶硬件采集部分的連接情況,便于出現異常狀態問題的快速解決。
3) 試驗參數設置:該功能是精度靶測試試驗前的相關參數初始化設置,包含試驗任務的名稱、組數、每組的次數和試驗操作人員的相關信息設置,以及測試用的彈種數據包含彈徑、彈長和彈速的設置。
4) 測試試驗:該功能包含了多個子功能,開始試驗、數據顯示、試驗密集度參數計算、試驗組數和結束試驗。開始試驗是使用者操作上位機向下位機發送開始試驗的控制命令,下位機接收到指令后進入測試狀態;數據顯示是上位機軟件把從下位機接收到的試驗數據結果進行展示;試驗密集度參數計算是由上位機完成的,在試驗中,上位機接收到試驗數據大于等于三的時候,程序會自動進行參數結果的計算,也可以手動選擇計算;試驗組數是在試驗中進行試驗組別切換;試驗結束同開始試驗,用戶操作上位機向下位機發送試驗結束指令,且該功能不設置前置條件。
5) 歷史數據:在試驗中,上位機會把從下位機接收到的試驗結果和密集度參數計算的結果全部保存到本地數據庫,并且以任務創建時間為名稱區別存儲,通過任務創建時間或名稱檢索對應的歷史試驗數據,還可以選擇所需試驗任務數據導出到Excel。
基于B/S架構的上位機軟件的整體結構如圖2所示,依據整體框圖的結構層次,進一步闡述層與層之間模塊的設計和信息的交互。上位機軟件整體分為上層、中層和下層,表示層位于最上層,是為了與用戶進行交互的層面,用戶在界面觸發相關控制命令的按鈕操作,隨之用戶的請求及數據由表示層接收,接著對數據進行處理,最后顯示處理后的數據。業務邏輯層作為紐帶存在于表示層和數據之間,需要依據業務的具體功能需求,實現相對應的業務功能。該軟件系統數據來自與下位機的通信獲得,所以設計在中間層需要搭建TCP服務器進行數據交互。數據層主要實現對數據庫的增刪改查,用于TCP服務器存儲從下位機獲取來的數據以及向歷史數據查詢功能模塊提供數據支持。

圖2 上位機軟件系統整體框圖
服務器和瀏覽器作為B/S軟件架構的核心。其中瀏覽器作為用戶直接操作的層面,主要包含功能操作、發送請求和獲取數據,最后通過瀏覽器展示出來。服務器承載了大多數的業務分析數據處理,也是真正的實現界面應用層各模塊的業務邏輯,例如在和數據庫部分的交互中,專門設計了數據緩存處理,數據查詢的優化等。更重要的是,在與下位機的交互中,提供可靠的數據傳輸,較好的傳輸性能。
根據軟件系統整體設計數據流圖,如圖3所示,對上位機軟件的功能需求進行分析整理,以技術出發的角度,上位機軟件服務器部分設計實現了應用Netty通信框架、HTTP傳輸方式[9]、數據庫等的搭建。其中,服務器的Netty通信框架的設計與實現應用是核心問題,保證了和下位機之間穩定的數據傳輸。Netty多用于搭建數據通信底層部分,并且是基于TCP通信協議實現,Netty因其非阻塞通信方式,實現了優異的性能而被廣泛應用,也是目前大多數框架的主流或被服務端首選用來搭建軟件底層框架。

圖3 軟件系統整體數據流圖
2.2.1 B/S架構
目前軟件常用的開發模式有兩種,C/S和B/S[10]。C/S模式,范圍比較有限,大部分基于局域網環境使用,因為它的架構主要是由表示層和數據層構成的,所以使用方式必須是安裝獨有客戶端使用,且難以一次開發實現多平臺的使用。C/S這種架構還有著一些其他缺點,舉例來說,在開發者方面,客戶端的升級和維護增加了工作量;對于用戶來說,時常進行迭代也增加了使用成本。因此,需要簡化客戶端的安裝部署工作。
B/S模式下,選擇了瀏覽器成為客戶端,避免進行繁瑣的客戶端安裝工作,使其可以專注于業務功能邏輯的開發實現。B/S是一個由三個主要部分組成的Web應用程序:瀏覽器、服務器、數據庫[11]。承擔用戶交互的主要應用軟件,成為了瀏覽器部分,但是不承擔業務部署和功能性開發,服務器端即可完成所有的業務邏輯。并且通過瀏覽器即可訪問系統,瀏覽器的UI設計更加美觀多樣化,升級方便。
2.2.2 Netty框架
Netty 是一個利用 Java 的高級網絡的能力,隱藏其背后的復雜性而提供一個易于使用的 API 的客戶端/服務器框架[12]。Netty實現了多種通信協議的支持,因其使用簡單,對非阻塞通信方式進行封裝;功能強大,支持多種主流協議,內置集成了解碼功能;穩定,目前已經修復了框架已發現的缺陷。所以,它已經成為大多框架和項目實現的底層,并被許多開發者青睞。Netty框架的底層實現包括了一個非阻塞的IO架構,允許開發人員輕松地編寫相關的應用程序,以達到優化網絡編程的目的,雖然Netty的底層框架邏輯實現非常復雜,但是其API簡單易用,開發者在編程中易于繞開業務層實現。其構成有三個模塊:傳輸服務,支持的協議和核心模塊[13]。
Netty在設計上有以下幾個優點:NIO通信;無鎖化的串行設計理念;高效的并發編程;高性能的序列化框架等[14]。本系統選擇Netty主要是三方面的優勢:并發高、傳輸可靠和封裝好。
1)并發高:對比于BIO(阻塞IO),Netty框架是基于非阻塞IO開發的網絡通信框架。其單線程能處理的連接數比BIO要高出很多。當建立了一個連接之后,接著還有兩個步驟,先是將客戶端發過來的數據全部接收,然后是在服務器處理完請求給客戶端響應。這也是NIO和BIO的一個主要區別。在BIO中,等候客戶端發送數據這個過程是阻塞的,這樣就造成了一個線程只能處理一個請求的情況,而且計算機的處理線程也不是無限的。但是在NIO中,當一個套接字建立好以后,Thread類不會像BIO一樣將其阻塞,而且將請求推到了選擇器,由選擇器去不停地遍歷所有產生的套接字,當有一個套接字建立,選擇器就會通知Thread類,然后交由Thread類處理后再響應給客戶端,這樣整個流程是非阻塞的。
2)傳輸快:Netty的數據發送和接收主要是使用bytebuffer,其使用對外內存直接對套接字進行讀寫操作。如果使用傳統的堆內存對套接字進行讀寫,Java虛擬機會將buffer直接復制一份到內存中后再寫入套接字,多了一次緩沖區的內存復制操作。但是Netty提供了一種組合ByteBuf,可以避免這樣的情況,因為其并沒有將Buffer組合起來,而是保存了它們的引用,本質上是避免了數據的復制,實現了零拷貝。Netty中還使用了FileChannel的transfer to 方法,此方法依賴于操作系統實現零拷貝機制。
3)封裝好:編程過程中,從代碼量來看,Netty有很多自帶處理方法,實現連接、請求和接入等[16]。如ChannelHandler及其實現類,ChannelHandler 接口定義了許多事件處理的方法, Netty開發中需要自定義一個 Handler 類去實現 ChannelHandle接口或其子接口或其實現類,然后我們可以通過重寫這些方法去實現具 體的業務邏輯。channelActive通道就緒事件,channelRead通道讀取數據事件,exceptionCaught通道發生異常的事件。
在軟件整體設計中提到,TCP服務器的設計與實現是上位機系統中最關鍵部分。承擔著與下位機通信,接收、發送、解析、保存數據等功能。其中主要就是可靠接收傳輸消息和及時發送消息指令,同時考慮到功能需求分析中下位機會頻繁發送數據等問題,故選用了Netty通信框架搭建TCP服務器底層,通信架構如圖4所示。在Netty通信框架的應用過程中主要研究了適應于本系統基于Netty的通信實現、傳輸中TCP粘包拆包處理和心跳監測機制的應用。

圖4 通信框架圖
Netty被定義為是一個基于Java 非阻塞I/O(輸入/輸出)的異步事件驅動的網絡應用框架[17-18],提供了高層次的抽象來簡化TCP服務器的編程,具有高性能和高可靠性的特點,且廣泛應用于客戶端與服務器之間長連接、高并發的場景。近幾年,Netty框架在計算機互聯網行業熱門起來,已經成為了Java網絡編程框架的首選。
基本實現思路,通信的讀寫邏輯均是啟動階段實現連接數據的讀寫邏輯,其通過邏輯處理鏈Pipeline來添加邏輯處理器。首先,下位機連接成功回調處理器的channelActive()方法,當下位機和上位機分別接收到對方的消息回調自己的處理器的channelRead()方法;接著是上位機和下位機向對方寫數據的時候調用writeAndFlush()方法,規定上位機和下位機之間交互信息的二進制數據傳輸載體為ByteBuf。
Netty服務器端的實現,是被動的接收發送請求,為了避免出現服務器之間多端口產生沖突的問題,將服務器的端口號固定且綁定為6000。首先,創建ServerBootstrap()實例,設置并且綁定Reactor()線程池:EventLoopGroup,EventLoop就是處理所有注冊到該線程的選擇器上的;設置并綁定服務器端的Channel();TCP鏈路建立時創建ChannelPipeline()方法,添加并設置ChannelHandler(),用來處理網絡事件;綁定監聽端口并啟動服務端;然后進入Netty獨有流程,通過多路復用器輪詢,進行網絡事件通知,最后執行Netty系統和業務通過執行pipline中的方法最終調度調用HandlerChannel()方法,過程如圖5所示。

圖5 基于Netty服務器實現流程圖
實現部分代碼如下:
public void run() throws Exception{
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();//創建服務器端的啟動對象,配置參數
serverBootstrap.group(groupBoss, groupWorker) //兩個處理線程組設置
.option(ChannelOption.SO_BACKLOG, 1024) // 線程隊列并發數的設置
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_KEEPALIVE, true) //保持活動的連接狀態設置
.childHandler((ChannelInit)(ch)->{});
//服務器啟動并綁定端口
bind(serverBootstrap, PORT);
// 對關閉通道進行監聽
cf.channel().closeFuture().sync();
} catch (Exception e) {
log.error(" netty服務啟動異常 " + e.getMessage());
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
在Netty框架的設計與實現應用的過程中,主要實現了基礎的數據通信交互后,因為TCP使用的是一種“數據流”的方式傳輸,還需要設計并且解決基于TCP數據傳輸過程中出現的粘包拆包的問題。
TCP是一種“數據流”的協議,處理數據包以流的方式,所以在接收數據的時候不能完整的得到一條數據稱為TCP粘包拆包。Netty這里提供了消息定長度,將傳輸消息大小固定不夠的空位用空格補齊然后發送;符號分割即用特殊符合進行標識,接收方遇到標識符就知道是下一條數據;發送長度,每次發送數據的時候將這條數據的長度一并發送,這樣對方解析的時候根據長度來判斷數據接收是否完整。本軟件采用了換行符的處理方式,結合Netty提供的多種解碼器來解決這種問題。并且設計了自己的處理方法,順序遍歷ByteBuffer中的刻度字節[19],查找標志位“/n”。
不斷地從TCP緩沖區中讀取數據,每次都需要判斷這是不是一個完整的包讀取完后,假如當下讀到的消息不能夠拼成完整的業務數據包,那就先把該數據保留,再從TCP緩沖區中讀出待得到完整的消息包。數據內容正確需要進行業務處理,推向前臺頁面或者存入數據庫,流程如圖6所示。

圖6 TCP數據處理流程圖
實現部分代碼如下:
class Solution(SocketChannel ch){
ch.pipeline().addLast(new IdleStateHandler(5,0,0));//換行符處理
ch.pipeline().addLast(new LineBaseFrameDecoder(1024));
ch.pipeline().addLast(new StringDecoder());//編碼
ch.pipeline().addLast(new StringEncoder());//解碼
ch.pipeline().addLast(new MyServerHandler());
}
實現了TCP傳輸中粘包拆包問題的解決方法后,還需要通過對Netty框架提供的心跳機制功能接口進行研究,來實現狀態檢測管理功能模塊。
在使用的過程中,會出現網絡不穩定的情況,而在TCP通信中,發生網絡問題就會導致上下位機連接中斷。沒有通信連接,那就無法發現相互掉線,這時候需要通過心跳機制實現狀態監測。類似一端發送一個數據包內容給對面,另一端收到后馬上返回一個響應包,這樣的一個數據交互就是心跳交互[20]。通俗來說,就是定時發送一個自定義的心跳包,讓對方知道己方的存在,來確保連接的有效性。
下位機每間隔1 s向上位機發送一個包含特殊內容數據的消息Ping,上位機收到后馬上返回一個消息Pong,于是雙方通過這樣一對心跳消息來確定TCP的連接是否斷開,如圖7所示。

圖7 心跳機制
在本文中,依據功能需求,基于心跳機制的原理,借助Netty提供的IdleStateHandler類,來實現處理下位機的連接狀態。
public userEventTriggered
(ChannelHandlerContext ctx, Object evt){
if(evt instanceof IdleStateEvent){
IdleStateEvent e =( IdleStateEvent) evt;
switch(e.state()){
case READER_IDLE:
handlerReaderIdle(ctx);
break;
case WRITER_IDLE;
handleWriterIdle(ctx);
break;
case ALL_IDLE:
handlerAllIdle(ctx);
break;
}
}
}
其中需要通過創建通用類負責接收心跳,客戶端發送心跳,在上文中提到過,使用Netty提供的IdleStateHandler類實現心跳的關鍵,根據不同的idle類型發生不同的idle類事件,而該事件的捕捉就是通過上文的userEventTriggered實現的。
心跳機制的實現,下位機就需要按照設定周期頻繁的發送數據包,上位機就必須能夠響應和處理下位機發送來的數據請求。這里,Netty框架提供的NettyServerHandler類又實現了多個重要的方法,設計使用非公平鎖機制提高系統的吞吐量,更好地處理與客戶端的連接、斷開和數據讀取。非公平鎖是指多個線程獲取鎖的順序,并不是按照申請鎖的順序,有可能申請的線程比先申請的線程優先獲得鎖,收發效率較高。
對本系統軟件數據通信進行測試和試用,上位機軟件測試環境為Intel(R) Core(TM) i3-8100,內存8 GB,CPU3.6 GHz,硬盤1 T,操作系統Windows 7 64位;下位機操作系統為Windows7,上下位機之間為千兆網絡連接,測試環境框架如圖8所示。

圖8 通信測試環境框架圖
本系統上位機軟件關鍵部分在于TCP服務器的設計,采用Netty通信框架設計,實現穩定可靠的上下位機數據傳輸,滿足上位機接收處理下位機頻繁的數據發送需求。TCP服務器部分屬于底層開發,所以采用WebSocket技術通過Web頁面進行模擬測試的數據展示。WebSocket是一種新的協議在HTML5下,其還是基于TCP的協議本質上說。實現了全雙工通信在瀏覽器端與服務器之間,模擬Socket協議,可以雙向發送或接受信息,實現數據推送,主動由服務端推送向瀏覽器端;完成一次握手,由瀏覽器和服務器,即可以建立長時間的連接在兩者之間,實現數據傳輸,更加方便我們進行通信模擬測試。如圖9所示,對下位機部分進行了模擬設計實現,充分達到真實的使用情況,實現心跳機制發送狀態數據,以及模擬坐標數據發送。

圖9 下位機模擬程序
模擬下位機實現狀態數據發送,以周期1 s的時間模擬下位機向上位機發送狀態的協議內容和手動發送模擬的測量試驗結果坐標數據。上位機通過瀏覽器頁面展示由下位機發送的心跳狀態數據和坐標試驗數據,TCP服務器模擬接收數據測試結果如圖10所示。

圖10 上位機數據接收測試圖
通過模擬測試,下位機所發送的狀態檢測數據和試驗坐標數據上位機的TCP服務器都能正常接收,且全部顯示到測試頁面,滿足基本的功能需求。
最后對上位機接收數據處理性能進行對比測試。根據功能需求分析所提出的技術指標要求,需要上位機對接收數據的處理不能低于每秒鐘一個請求。創建兩組不同周期數據發送的對比測試,第一組設計1 000個樣本數據,以周期1 s發送數據,測試服務器接收處理請求的性能;第二組同樣設計1 000個樣本數據,發送速率比第一組提高10倍以周期100 ms發送數據,測試服務器接收處理請求的性能。這里采用JMeter開源測試軟件,模擬一個下位機以周期1 s的時間向上位機發送數據判斷上位機TCP服務器的接收情況,如圖11所示。由測試吞吐量結果可知,每秒能夠處理請求的次數約為1,能夠滿足功能需求。

圖11 第一組性能測試結果
第二組性能測試將數據發送速率設為100 ms,對服務器處理性能測試,測試結果如圖12所示。

圖12 第二組性能測試結果
從上述的兩組對比性能測試中,在1 000個樣本數據的情況下,將數據發送速率從1 s提高10倍的測試結果看,第一組的吞吐量測試結果為服務器每秒處理請求約1個,第二組的吞吐量測試結果為服務器每秒處理請求約8個,滿足性能要求以及技術指標。
進行了模擬測試和性能測試后,再對上位機軟件進行系統測試,測試的內容為將上位機軟件置于真實的使用環境,測試上位機軟件是否能達到效果。
把上位機軟件置于試驗測試的界面,如圖13所示。從彈丸發射開始計算時間起始,上位機界面顯示模擬坐標計算為時間結束,來衡量數據通信和軟件系統響應的情況。同時作為對性能測試的一個補充測試。

圖13 實際測試
通過對整個精度靶的系統聯調測試,可以得到在實際使用環境中的結果。從圖中的測試結果可以看到,從彈丸發射開始計時到最后上位機顯示坐標位置整個過程時間發生都在1 s以內,忽略掉計時操作的時間以及系統的響應時間,數據的通信部分能夠滿足相應的技術指標要求,整體上使用能夠滿足數據實時顯示的要求。
本文針對精度靶系統軟件功能的需求分析,參考一些該類型軟件,設計了一個采用B/S架構的CCD精度靶上位機軟件,關鍵設計了上位機服務器整體結構和核心模塊Netty通信架構。通過模擬測試上位機滿足通過網絡穩定接收下位機傳輸數據的功能需求;解決了C/S模式軟件的擴展難度大,維護成本高和用戶后期使用升級復雜等問題。與C/S模式不同,B/S模式軟件的界面是基于瀏覽器進行首次全新開發,和服務器采用HTTP進行交互。本系統軟件目前前端頁面部分還未完善,存在頁面布局和功能缺陷問題。
經分析選擇B/S架構的設計思想[22]和面向對象的編程范式,降低了各模塊代碼之間的耦合性,使得軟件維護的效率提升,同時節省了軟件開發的成本,在一類具有上下位機工作屬性的控制軟件[23]設計方面具有重要意義。