王鑫 許智鴻 胡華昌 楊豐羽
摘要:隨著經(jīng)濟(jì)社會(huì)的高速發(fā)展,“互聯(lián)網(wǎng)+”時(shí)代的到來,基于Online To Offline(020)商業(yè)模式的外賣配送行業(yè)迅速崛起,其發(fā)展前景一片廣闊,在提供方便快捷服務(wù)的同時(shí)也帶動(dòng)了社會(huì)經(jīng)濟(jì)的發(fā)展[1]。但是隨著外賣配送行業(yè)的進(jìn)一步壯大,基于外賣配送的很多問題也日益凸顯,比如外賣偷盜和錯(cuò)拿以及配送過程中帶來的交通安全和社會(huì)違法犯罪隱患等[2]。通過智能外賣自提柜系統(tǒng),配送員將外賣暫存于智能外賣自提柜中,可以有效地解決外賣配送過程中出現(xiàn)的各種問題,安全有保障地將外賣送達(dá)到客戶手中[3]。本文通過對(duì)移動(dòng)端APP、PC端Web信息管理平臺(tái)、設(shè)備端實(shí)體柜以及服務(wù)器四個(gè)部分的整體設(shè)計(jì)與分析來整合搭建一套完整可運(yùn)行的智能自提柜系統(tǒng),并較為詳細(xì)地對(duì)各部分之間交互通信方式的設(shè)計(jì)和實(shí)現(xiàn)原理進(jìn)行了分析。
關(guān)鍵詞:智能外賣自提柜系統(tǒng);Netty框架;Android;Flask MVC框架;stm32單片機(jī);數(shù)據(jù)庫
中圖分類號(hào):TP311 文獻(xiàn)標(biāo)識(shí)碼:A
文章編號(hào):1009-3044(2020)19-0010-04
開放科學(xué)(資源服務(wù))標(biāo)識(shí)碼(OSID):
1 智能外賣自提柜系統(tǒng)工作原理
智能外賣自提柜系統(tǒng)以服務(wù)器為中心,移動(dòng)端和設(shè)備端通過自定義通信協(xié)議與服務(wù)器進(jìn)行通信。系統(tǒng)采用前后端完全分離的設(shè)計(jì)模式,將系統(tǒng)數(shù)據(jù)庫搭建在服務(wù)器端,并對(duì)來自移動(dòng)端和設(shè)備端的請(qǐng)求消息進(jìn)行編解碼和事務(wù)處理。PC端平臺(tái)與服務(wù)器共享系統(tǒng)數(shù)據(jù)庫,實(shí)現(xiàn)系統(tǒng)數(shù)據(jù)的可視化管理,系統(tǒng)的總體設(shè)計(jì)圖如圖1所示。
2 服務(wù)器端開發(fā)
服務(wù)器端選擇完全異步非阻塞的Netty框架作為底層的通信框架,Netty是對(duì)NIO的優(yōu)化和封裝,其API使用更簡(jiǎn)單,開發(fā)門檻更低[4]。同時(shí)服務(wù)器整合了MyBatis框架來管理MySQL數(shù)據(jù)庫。服務(wù)器分為移動(dòng)端服務(wù)器和設(shè)備端服務(wù)器兩部分。其中,Netty服務(wù)器創(chuàng)建啟動(dòng)的時(shí)序圖如圖2所示。
2.1 移動(dòng)端服務(wù)器開發(fā)
2.1.1移動(dòng)端通信傳輸協(xié)議
移動(dòng)端通信協(xié)議由數(shù)據(jù)頭部和消息兩部分組成,其中數(shù)據(jù)頭部的內(nèi)容為消息長(zhǎng)度,消息部分為傳輸?shù)南?duì)象的Json格式字符串的二進(jìn)制。同時(shí),本系統(tǒng)選擇速度較快的阿里巴巴的Json轉(zhuǎn)換工具Fastj son來實(shí)現(xiàn)JavaBean與字符串之間的轉(zhuǎn)換。協(xié)議格式如圖3所示。
2.1.2 移動(dòng)端通信框架的實(shí)現(xiàn)
TCP/IP協(xié)議是以字節(jié)為單位的流式的數(shù)據(jù)傳輸協(xié)議,數(shù)據(jù)之間是連續(xù)的沒有界限的,這就導(dǎo)致一次收到的數(shù)據(jù)幀可能不是完整的,也就是常見的TCP粘包/拆包問題。為了解決這個(gè)問題,Netty提供了很多數(shù)據(jù)編解碼器,來幫助開發(fā)人員更方便地完成數(shù)據(jù)的編解碼任務(wù)。
協(xié)議頭部的數(shù)據(jù)長(zhǎng)度部分是由NetW提供的LengthField-Prepender自定義長(zhǎng)度編碼器添加的,它可以計(jì)算當(dāng)前待發(fā)送消息的二進(jìn)制字節(jié)長(zhǎng)度,將該長(zhǎng)度添加到待發(fā)送數(shù)據(jù)的頭部。同時(shí),在此之前還需要通過一個(gè)自定義編碼器來將傳輸對(duì)象轉(zhuǎn)換為JSON格式字符串,并進(jìn)一步轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)。與自定義長(zhǎng)度編碼器配套使用的是LengthFieldBasedFrameDecoder解碼器,通過該解碼器可以讀取數(shù)據(jù)幀頭部的長(zhǎng)度部分,并會(huì)在此后連續(xù)讀取長(zhǎng)度部分個(gè)字節(jié),將這些字節(jié)作為一個(gè)完整的消息傳遞到下一個(gè)自定義消息解碼器中,進(jìn)一步轉(zhuǎn)換為消息對(duì)象,再交給自定義事務(wù)處理器ServerHandler進(jìn)行處理。
2.1.3移動(dòng)端服務(wù)器事務(wù)處理
Android移動(dòng)端的事務(wù)處理請(qǐng)求被封裝為一個(gè)Interacti-veRequest類,其內(nèi)部的成員變量msgType表示該請(qǐng)求的事務(wù)類型。移動(dòng)端發(fā)送給服務(wù)器的所有請(qǐng)求包括:注冊(cè)請(qǐng)求,登錄請(qǐng)求,訂單查詢請(qǐng)求,空柜查詢請(qǐng)求,存放請(qǐng)求,注銷請(qǐng)求等。當(dāng)服務(wù)器端接收到移動(dòng)端發(fā)來的消息時(shí),首先經(jīng)過解碼器將消息轉(zhuǎn)換為消息對(duì)象,然后在ServiceHandler的ChannelRead0方法中獲取其類型屬性,根據(jù)屬性的值確定其代表的事務(wù)類型,并進(jìn)行相應(yīng)的事務(wù)處理,最終返回給客戶端相應(yīng)的處理結(jié)果。
2.2 設(shè)備端服務(wù)器開發(fā)
2.2.1設(shè)備端通信傳輸協(xié)議
服務(wù)器與設(shè)備實(shí)體柜之間使用自定義通信傳輸協(xié)議進(jìn)行TCP通信,數(shù)據(jù)楨格式如下表l所示。部分指令類型的詳細(xì)設(shè)計(jì)如下表3所示。
2.2.2 設(shè)備端通信框架的實(shí)現(xiàn)
服務(wù)器端通過自定義解碼器DeviceDecoder來解決TCP粘包/拆包問題,當(dāng)解碼器讀到四個(gè)連續(xù)的OxOa時(shí)才認(rèn)為是讀到了一個(gè)數(shù)據(jù)幀的幀頭,并獲取其后兩個(gè)字節(jié)的幀長(zhǎng)度,若Byte-Buf中剩余字節(jié)數(shù)量大于等于幀長(zhǎng)度,則將幀頭后連續(xù)幀長(zhǎng)度個(gè)字節(jié)作為一個(gè)完整的消息進(jìn)行處理。否則,則認(rèn)為發(fā)生了拆包現(xiàn)象,標(biāo)記ByteBuf讀取位置,等待下一次讀取再處理。
本系統(tǒng)規(guī)定除應(yīng)答消息外,不論是服務(wù)器端還是設(shè)備端對(duì)于接收到的其他指令都必須回復(fù)一個(gè)應(yīng)答消息來通知另一端已經(jīng)接收到正確信息。但是考慮到在通信過程中可能會(huì)發(fā)生數(shù)據(jù)丟失或者數(shù)據(jù)出錯(cuò)等情況,所以系統(tǒng)規(guī)定對(duì)于丟失或者出錯(cuò)的數(shù)據(jù)均采取丟棄處理,不予應(yīng)答,并制定了相應(yīng)的指令超時(shí)重傳和指令重復(fù)不處理的機(jī)制。對(duì)于服務(wù)器端來說:
(1)對(duì)于接收的設(shè)備端指令的處理
服務(wù)器端在每個(gè)設(shè)備端對(duì)應(yīng)的服務(wù)器端DeviceHandler中維護(hù)一個(gè)已接收并處理指令集ReceiveMap和一個(gè)已發(fā)送但未應(yīng)答指令集SendMesMap,分別用來存儲(chǔ)已成功接收到的設(shè)備端的指令Frameld和已發(fā)送的但還未收到應(yīng)答的服務(wù)器端指令Frameld,則存在以下情況:
若設(shè)備端指令在傳輸過程中丟失或者校驗(yàn)和檢驗(yàn)出錯(cuò),服務(wù)器端采用丟棄處理,不予應(yīng)答。
若設(shè)備端指令完整接收,且校驗(yàn)和無誤,則立即返回給設(shè)備一個(gè)應(yīng)答消息指令,其Frameld為接收的設(shè)備端消息的編號(hào),表示對(duì)于設(shè)備傳輸?shù)脑摼幪?hào)指令已經(jīng)接收并且無誤。并且將該指令的編號(hào)存儲(chǔ)到相應(yīng)的ReceiveMap中,表示該指令已處理過,防止由于應(yīng)答消息丟失導(dǎo)致的設(shè)備端重傳指令的重復(fù)處理。
若設(shè)備端指令為應(yīng)答指令,則將SendMesMap中相應(yīng)編號(hào)的已發(fā)送服務(wù)器端指令進(jìn)行刪除。
(2)對(duì)于發(fā)送的服務(wù)器端指令的處理
對(duì)于服務(wù)器端發(fā)送給設(shè)備端的指令采用超時(shí)重傳機(jī)制。使用scheduleAtFixedRate0方法在每個(gè)設(shè)備對(duì)應(yīng)的服務(wù)器端線程中開啟一個(gè)定時(shí)器,每隔一段時(shí)間都會(huì)將相應(yīng)SendMesMap中還沒收到應(yīng)答的指令進(jìn)行重傳。
2.2.3 設(shè)備端服務(wù)器事務(wù)處理
由于各類型指令除Content部分外其它部分?jǐn)?shù)據(jù)格式以及整體編解碼方式都一致,所以采用模板方法模式設(shè)計(jì)指令類。服務(wù)器端將父類指令封裝為Devlnstruction抽象類,各類型指令封裝為類Devlnstruction000-006.均繼承自父類指令。其中,父類中給出整體指令編解碼的邏輯骨架,Content部分的編解碼方法以及指令的具體業(yè)務(wù)處理邏輯被設(shè)置為抽象方法,在子類中進(jìn)行方法的掛鉤和具體實(shí)現(xiàn)。
為了更好地管理設(shè)備和用戶,向不同的設(shè)備和用戶主動(dòng)的發(fā)送消息,服務(wù)器維護(hù)了ChanneIDevList和ChanneIUserList列表來管理在線設(shè)備和用戶,并使用Collections.synchronizedList0方法對(duì)List進(jìn)行包裝來獲取一個(gè)線程安全的List。同時(shí)在線列表提供了對(duì)指定用戶或設(shè)備的消息發(fā)送方法,允許服務(wù)器對(duì)指定的設(shè)備和用戶進(jìn)行消息傳輸,實(shí)現(xiàn)了設(shè)備端服務(wù)器和設(shè)備服務(wù)器之間的通信。
3 移動(dòng)端開發(fā)
3.1 Netty通信客戶端的整合
移動(dòng)端選擇廣泛使用的Android平臺(tái)開發(fā)移動(dòng)端手機(jī)APP.Android APP需要通過Netty客戶端與服務(wù)器端進(jìn)行自定義協(xié)議通信,所以需要將移動(dòng)端程序與Netty客戶端框架進(jìn)行整合,其中,Netty客戶端創(chuàng)建啟動(dòng)的時(shí)序圖如下圖4所示。
Application是Android的一個(gè)系統(tǒng)組件,Android系統(tǒng)會(huì)為每個(gè)程序運(yùn)行時(shí)創(chuàng)建一個(gè)Application類的對(duì)象且僅創(chuàng)建一個(gè),因?yàn)樗侨值膯卫模晕覀儗etty客戶端搭建在自定義的Application組件中,并對(duì)外提供與服務(wù)器傳輸消息的方法來實(shí)現(xiàn)通信。在程序啟動(dòng)時(shí),系統(tǒng)就在Application的OnCreate0方法中初始化Netty客戶端連接。
4 PC端開發(fā)
PC端在Python主流開發(fā)語言環(huán)境下,利用Flask工具搭建一個(gè)可高效擴(kuò)展開發(fā)的基于MVC的自提柜信息管理平臺(tái)[6],同時(shí)使用SQLAlchemy創(chuàng)建ORM模型,然后利用ORM模型對(duì)數(shù)據(jù)庫進(jìn)行操作。開發(fā)簡(jiǎn)單快捷,拓展性極強(qiáng),運(yùn)行效率較高。同時(shí)采用了Nginx、uWSGI的部署模式,提高了服務(wù)的并發(fā)能力。
4.1 服務(wù)器架構(gòu)
PC端設(shè)計(jì)了Web服務(wù)器、應(yīng)用服務(wù)器、Web應(yīng)用以及數(shù)據(jù)庫服務(wù)器的四層服務(wù)器架構(gòu),其交互邏輯如下圖5所示。
Nginx作為直接對(duì)外的服務(wù)接口,負(fù)責(zé)接收客戶端發(fā)送過來的HrITP請(qǐng)求。當(dāng)用戶端發(fā)起HTTP請(qǐng)求時(shí),如果是靜態(tài)文件請(qǐng)求,Nginx就根據(jù)Nginx配置的靜態(tài)文件目錄返回請(qǐng)求的靜態(tài)資源;如果是動(dòng)態(tài)請(qǐng)求,Nginx就通過配置文件將請(qǐng)求傳遞給uWSGI。uWSGI根據(jù)請(qǐng)求的URL調(diào)用Flask App對(duì)應(yīng)的函數(shù)。涉及數(shù)據(jù)庫的部分,便通過SQIAlchemy高效訪問MySQL數(shù)據(jù)庫,并返回?cái)?shù)據(jù)庫操作結(jié)果給Flask。
全部處理完后Flask將結(jié)果轉(zhuǎn)發(fā)回uWSGI,uWSGI接收后轉(zhuǎn)發(fā)回Nginx,Nginx最終將結(jié)果返回給客戶端。
4.2 MVC框架搭建
MVC框架(Model View Controller)是一種將應(yīng)用程序的邏輯層和視圖層相分離設(shè)計(jì),并通過控制層連接的開發(fā)模式[7]。其中模型、視圖、控制器三個(gè)核心部件構(gòu)成了整個(gè)Web應(yīng)用程序。在本系統(tǒng)中,通過對(duì)Flask原生的MVC架構(gòu)進(jìn)行改裝[8],框架的工程目錄設(shè)計(jì)清晰明了,各部分功能明確,當(dāng)開發(fā)人員使用MVC框架進(jìn)行二次開發(fā)時(shí),只需要根據(jù)自己開發(fā)任務(wù)的要求,對(duì)各個(gè)目錄功能進(jìn)行擴(kuò)展和善即可高效開發(fā)。
5 設(shè)備端開發(fā)
設(shè)備端硬件平臺(tái)基于stm32單片機(jī),使用WIFI模塊和GPRS模塊實(shí)現(xiàn)通信功能,支持WIFI網(wǎng)絡(luò)和運(yùn)營商網(wǎng)絡(luò),滿足多數(shù)投放環(huán)境的需要。軟件平臺(tái)基于FreeRTOS實(shí)時(shí)操作系統(tǒng),平衡單片機(jī)資源和設(shè)備功能需求,保證設(shè)備交互和通信的實(shí)時(shí)性,設(shè)備投放簡(jiǎn)單,易于維護(hù),具有高可擴(kuò)展性。
5.1硬件設(shè)計(jì)
設(shè)備硬件由通信模塊、設(shè)備交互模塊、控制核心和轉(zhuǎn)接模塊構(gòu)成,其中轉(zhuǎn)接模塊根據(jù)設(shè)備功能需求定制,不同的轉(zhuǎn)接模塊可以組建不同配置的柜體。
(1)通信模塊:采用SIM800+ESP8266模塊,二者都通過AT指令與單片機(jī)通訊,可根據(jù)投放地的網(wǎng)絡(luò)情況選擇不同的連接方式。
(2)設(shè)備交互模塊:包括按鍵和顯示屏,當(dāng)前方案為按鍵輸入取貨密碼,顯示屏提示信息,設(shè)備后續(xù)迭代升級(jí)較為方便,例如按鍵輸入可升級(jí)為掃描二維碼。
(3)控制核心:使用stm32f103RBT6,片內(nèi)有20k字節(jié)RAM,128k字節(jié)flash,滿足系統(tǒng)運(yùn)行和數(shù)據(jù)緩存的需求,支持捧電檢測(cè),當(dāng)外部斷電時(shí),切換至內(nèi)部電源供電,同時(shí)向服務(wù)器報(bào)警,當(dāng)內(nèi)部電源電量過低時(shí),進(jìn)入掉電模式,所有緩存數(shù)據(jù)存人flash中,保證掉電后設(shè)備數(shù)據(jù)不丟失,同時(shí)通知服務(wù)器,等待維護(hù)人員更換電源。
(4)轉(zhuǎn)接模塊:考慮到設(shè)備的多樣化和可擴(kuò)展性,硬件設(shè)計(jì)中,將柜體的控制部分獨(dú)立,做成轉(zhuǎn)接模塊,由于設(shè)備電鎖等開關(guān)型器件較多,10口資源有限,轉(zhuǎn)接模塊中使用74HC595等串人并出的IC節(jié)省輸出10口,轉(zhuǎn)接模塊板載了stm32f0單片機(jī),實(shí)現(xiàn)柜體中數(shù)字傳感器數(shù)據(jù)采樣和處理。
5.2 軟件設(shè)計(jì)
軟件設(shè)計(jì)中的任務(wù)設(shè)計(jì)框圖如圖7所示。由于設(shè)備與服務(wù)器之間吞吐數(shù)據(jù)量小,指令固定,因此使用自定義通信協(xié)議,除了協(xié)議格式的確定.還需加入應(yīng)答機(jī)制、數(shù)據(jù)重發(fā)機(jī)制。每條指令有唯一的Frameld,指令發(fā)送后,接收方會(huì)返回對(duì)應(yīng)Fra-meld的應(yīng)答幀,如指定時(shí)間內(nèi)未收到應(yīng)答,則需重發(fā)。
利用FreeRTOS動(dòng)態(tài)分配任務(wù)棧的特點(diǎn),可以方便得實(shí)現(xiàn)上述需求,本設(shè)計(jì)通過動(dòng)態(tài)創(chuàng)建和刪除數(shù)據(jù)重發(fā)任務(wù)實(shí)現(xiàn)多條指令并行的應(yīng)答等待和數(shù)據(jù)重發(fā)。設(shè)計(jì)中用到數(shù)據(jù)解析任務(wù)和數(shù)據(jù)重發(fā)任務(wù)。
(1)數(shù)據(jù)解析任務(wù):解析接收到的指令,指令分為應(yīng)答幀和非應(yīng)答幀,非應(yīng)答幀包含服務(wù)器的控制指令,應(yīng)答幀包含其父指令的Frameld(將每幀數(shù)據(jù)稱為其對(duì)應(yīng)的應(yīng)答幀的父指令)。數(shù)據(jù)解析任務(wù)如果收到應(yīng)答幀,會(huì)將其中包含的Frameld存人消息隊(duì)列MsgBox_Rpy中。
(2)數(shù)據(jù)重發(fā)任務(wù):每條指令發(fā)送后都會(huì)根據(jù)Frameld創(chuàng)建唯一的數(shù)據(jù)重發(fā)任務(wù),任務(wù)被創(chuàng)建后,執(zhí)行如下操作:
申請(qǐng)內(nèi)存,將該幀數(shù)據(jù)緩存。
每5s檢測(cè)一次消息隊(duì)列MsgBox_Rpy,檢測(cè)完成則重發(fā)數(shù)據(jù),最多重發(fā)3次,3次后刪除本任務(wù)。
如果檢測(cè)到MsgBox_Rpy中有數(shù)據(jù),則判斷其中的數(shù)據(jù)是否和本任務(wù)的Frameld匹配,如果匹配,則刪除本任務(wù)。
參考文獻(xiàn):
[1]張文紅.020模式下的餐飲外賣行業(yè)分析研究[J].農(nóng)村經(jīng)濟(jì)與科技,2019,30(474):124-125.
[2]黃子成,網(wǎng)絡(luò)外賣引發(fā)的社會(huì)治安問題及綜合治理對(duì)策研究[J].現(xiàn)代商貿(mào)工業(yè),2018,39:151-153.
[3]王恩全,李鑫雨,劉雪薇,等.互聯(lián)網(wǎng)時(shí)代保溫自提柜在外賣配送行業(yè)的應(yīng)用與展望[J].中國市場(chǎng),2019(17):161-162.
[3]王恩全,李鑫雨,劉雪薇,等.互聯(lián)網(wǎng)時(shí)代保溫自提柜在外賣配送行業(yè)的應(yīng)用與展望[J].中國市場(chǎng),2019(17):161-162.
[4]魏瑩.基于Netty框架的智能終端與服務(wù)器通信的研究[Dl.西安:西安電子科技大學(xué),2017.
[5]李林鋒.Netty權(quán)威指南[M].北京:電子工業(yè)出版社,2014:11-45.
[6]李超,徐云龍,華中偉,等.一種基于Python Flask的Web服務(wù)器端設(shè)計(jì)[J].信息與電腦,2019(8):87-88.
[7]李守振,張南平,常國鋒.Web應(yīng)用分層與開發(fā)框架設(shè)計(jì)研究[Jl.計(jì)算機(jī)工程,2006,32(22):274-276.
[8]邊蓓蓓,于萍.MVC模式在Web中的應(yīng)用研究[J].信息技術(shù)與信息化,2015(6):82-83.
【通聯(lián)編輯:李雅琪】
收稿日期:2020-03-27
作者簡(jiǎn)介:王鑫(1999-),男,山東泰安人,山東科技大學(xué),本科;許智鴻(1998-),男,山東菏澤人,山東科技大學(xué),本科;胡華昌(2000一),男,湖北黃岡人,山東科技大學(xué),本科;楊豐羽(1999-),男,山東青島人,山東科技大學(xué),本科。