【OC平臺】編解碼插件離線開發教程【二】
本文是編解碼插件離線教程的第二篇,也是最后一篇,主要從零開始詳細講解下工程的建立、代碼的編寫過程。
三、??? 插件編寫
1、源文件說明
從華為資源中心下載編解碼插件Demo(-:https://developer.obs.cn-north-4.myhuaweicloud.com/manage/tool/CodecDemo/CodecDemo.zip),并解壓到本地。文件結構如下圖所示:
源文件在src文件夾下;編譯生成的插件包在target文件夾下。src 文件夾包含 main 、test 兩個子文件夾,main下存放源碼,test下是單元測試代碼。
官網下載的Demo中,源碼的路徑是:src\main\java\com\Huawei\NBIoTDevice\WaterMeter ;
單元測試代碼的路徑是:src\test\java\com\Huawei\NBIoTDevice\WaterMeter。
插件源碼文件有5個:
a)??? ProtocolAdapterImpl.java 可以理解為是插件的入口文件,對外提供調用接口。該文件只需要修改兩個字符串的定義即可:
//?廠商名稱 private?static?final?String?MANU_FACTURERID?=?"Huawei"; //?設備型號 private?static?final?String?MODEL?=?"NBIoTDevice";
修改為profile當中定義的廠商ID和設備型號。
b)??? CmdProcess.java 實現下行命令的編碼工作,將從收到的服務器報文中提取出命令字段對應的內容,并將其轉換成字節流。
需要實現的函數是:??public byte[] toByte()。
c)??? ReportProcess.java 實現將收到的二進制碼流按照格式解碼出對應profile中的屬性值,并生成JSON格式。
需要實現的函數是:
public ReportProcess(byte[] binaryData),根據二進制碼流的格式,從中取出對應字節,轉換成profile中對應屬性的值。
public ObjectNode toJsonNode(),將解碼出來的屬性值封裝成JSON格式。
d)?ByteBufUtils.java 和 Utilty.java 文件封裝了一些公共方法,不用做修改。也不會使用到。
2、修改文件路徑(包名)
插件包名的要求是:com.廠商名稱.設備型號.設備類型。因此下載下來的代碼,要根據自己的設備修改下文件路徑。即將Huawei文件夾重命名為profile中定義的廠商名稱,NBIoTDevice文件夾重命名為profile中定義的設備型號,WaterMeter文件夾重命名為profile中定義的設備類型。注意:src\main 和src\test 下都要修改。在本例中,需要修改為:
src\main\java\com\ThirdParty\MyModel\MyTyp, src\test\java\com\ThirdParty\MyModel\MyType
3、修改pom.xml
打開pom.xml文件,修改第7行“artifactId”和第88行“Bundle-SymbolicName”的值為:設備類型-廠商ID-設備型號。在本例中,需要修改為:MyType-ThirdParty-MyModel。注:根據自己的實際情況該寫即可。
4、導入工程
打開eclipse,點擊file->import,在彈出窗口中選擇maven工程,如下圖所示:
之后在彈出的窗口中,點擊Browse,選擇工程路徑(pom.xml文件所在路徑)。工程導入后如下圖所示:
從上圖中可以看到首次導入工程后是有錯誤的。這是因為我們在第2節中將文件路徑修改了,與代碼里面的包路徑不一致引起的。解決方法為:依次打開源文件,將第一行的
package?com.Huawei.NBIoTDevice.WaterMeter;
修改為:
package?com.ThirdParty.MyModel.MyType;
接著,打開OSGI_INF目錄下的CodeProvideHandler.xml 文件:
打開后,文件內容如下圖所示:
將Name 、 Class* 內的路徑也修改為對應的包路徑:
5、代碼實現
第1節中說明了各個源文件要修改的地方,本節中具體講解實現的方法。
1)?修改ProtocolAdatpterImpl.java文件
在文件中找到如下兩行:
//?廠商名稱???????? ????????private?static?final?String?MANU_FACTURERID?=?"Huawei"; ????????//?設備型號 ????????private?static?final?String?MODEL?=?"NBIoTDevice";
將MANU_FACTURERID 和 MODEL定義修改為profile中定義的廠商ID和設備型號,本例中需要修改為:
//?廠商名稱 ????private?static?final?String?MANU_FACTURERID?=?"ThirdParty"; ????//?設備型號 ????private?static?final?String?MODEL?=?"MyModel";
2)?解碼實現
解碼,是將NB模組上報的二進制碼流按格式解析出對應字段的過程。解碼的代碼在ReportProcess.java 文件中。
第一個函數:public ReportProcess(byte[] binaryData) 入參 byte[] binaryData就是NB模組上報的二進制碼流。解碼得到數據存儲在成員變量當中。本例中的代碼實現如下:
public?ReportProcess(byte[]?binaryData)?{ /*?設備上報數據格式為:前兩個字節表示batteryLevel,大端; ?*?第三個字節表示后續字節長度; ?*?第四個字節到最后表示不定長負載數據,其長度由第三個字節的值表示 ?*?因此,數據長度的合法值是:?binaryData[2]?+?3 ?*?BatteryLevel和upData是定義的成員變量.用來存儲解碼得到的數據。 ?*/ ????int?tempVal; ????//長度校驗,不合法則直接退出 ????if(binaryData.length?3) ????{ ????return; ????} ????tempVal?=?binaryData[2]?&?0xFF; ????if(binaryData.length?
NB上報二進制數據的格式為:前兩個字節表示batteryLevel,大端,整型;第三個字節表示后邊還有多少字節;第四個字節往后表示不定長字段upData。因此,解碼的思路便是:
a)??? 首先判斷數據長度是否合法,至少應為3個字節,對應第37行代碼;數據長度應不小于第3個字節的值加上3,對應第42行代碼。該部分代碼屬于保護性代碼。
b)??? 將前兩個字節拼成一個16位的整型數據,表示batteryLevel
c)??? 根據第三個字節的值,創建一個Byte數組,將第四個字節往后的內容拷貝至該數組內,得到upData。System.arraycopy 是JDK提供的數組拷貝函數:?第一個參數是源數組,第二個參數是偏移,表示從源數組的第幾個字節開始拷貝,第三個參數是目的數組,第四個參數是目的數組的偏移,第5個參數表示拷貝的長度。
第二個函數:public ObjectNode toJsonNode() 返回一個ObjectNode對象(JSON)。該函數的功能,是將解碼后得到的數據,按照規定格式填入一個JSON對象中。本例中,生成的JSON對象的內容格式如下圖所示:
JSON對象的內容格式要求是:"msgType":? "deviceReq",? 表示設備上報數據,固定不動;“data”:數組對象,數組中的每個元素分別對應profile中的一個服務;“serviceID”的值是profile中定義的服務名稱;“serviceData”的值是該服務下所有的屬性值。(本例中,profile定義了兩個服務,Battery服務中有一個BatteryLevel屬性;Transmission服務中有一個upData屬性)。由圖13的“upData”的值可以看出,數組類型的值,需要將二進制流轉成base64編碼的格式。
該函數的代碼實現如下所示:
public?ObjectNode?toJsonNode()?{ ????????try?{ ????????????//組裝body體 ????????????ObjectMapper?mapper?=?new?ObjectMapper(); ????????????ObjectNode?root?=?mapper.createObjectNode(); ????????????//?root.put("identifier",?this.identifier); ????????????root.put("msgType",?"deviceReq"); ????????????ArrayNode?arrynode?=?mapper.createArrayNode(); ????????????//serviceId=Battery?數據組裝 ????????????ObjectNode?serviceNode?=?mapper.createObjectNode(); ????????????ObjectNode?serviceDataNode?=?mapper.createObjectNode(); ????????????serviceDataNode.put("BatteryLevel",?this.BatteryLevel); ????????????serviceNode.put("serviceId",?"Battery"); ????????????serviceNode.set("serviceData",?serviceDataNode); ????????????arrynode.add(serviceNode); ????????????//serviceId=Transmission?數據組裝 ????????????serviceNode?=?mapper.createObjectNode(); ????????????serviceDataNode?=?mapper.createObjectNode(); ????????????serviceDataNode.put("upData",?this.upData); ????????????serviceNode.put("serviceId",?"Transmission"); ????????????serviceNode.set("serviceData",?serviceDataNode); ????????????arrynode.add(serviceNode); ????????????root.set("data",?arrynode); ????????????return?root; ????????}?catch?(Exception?e)?{ ????????????e.printStackTrace(); ????????????return?null; ????????} ????}
該函數代碼比較簡單,主要是用到了 ObjectMapper 這個類,該類提供了JAVA中操作JSON數據的方法,可對照上圖中上報數據格式,仔細理解該部分代碼。
3)??編碼實現
編碼,是將IoT平臺收到的服務器下行數據(服務器下行數據是http或者https協議),從中提取出下行字段,并將其拼成二進制碼流。編碼部分的代碼在 CmdProcess.java 文件中。需要實現的函數是:
public byte[] toByte()
本例中,該函數的實現代碼如下圖所示:
public?byte[]?toByte()?{ ????????try?{ ????????????if?(this.msgType.equals("cloudReq"))?{ ????????????/* ?????????????*?msgType?==?cloudReq?表示應用服務器下發的控制命令 ?????????????*?本例只有一條控制命令:CLOUDREQ(profile中定義的下行命令名稱) ?????????????*?如果有其他控制命令,增加判斷即可。 ?????????????*?命令有兩個參數:cmdType,一個字節,downData,不定長數組 ?????????????*?下行的二進制數據數據格式是:?cmdType?+?downData????? ????????????*/ ????????????????if?(this.cmd.equals("CLOUDREQ"))?{ ????????????????????byte?cmdType?=?(byte)paras.get("cmdType").asInt(); ????????????????????byte[]?downData?=?paras.get("downData").binaryValue(); ???????????????????? ????????????????????byte[]?byteRead?=?new?byte[downData.length+1]; ????????????????????byteRead[0]?=?cmdType; ????????????????????System.arraycopy(downData,0,byteRead,1,downData.length); ????????????????????return?byteRead; ????????????????} ????????????} ????????????/* ????????????????????平臺收到設備的上報數據,根據需要編碼ACK,對設備進行響應,如果此處返回null,表示不需要對設備響應。 ????????????*?*/ ????????????else?if?(this.msgType.equals("cloudRsp"))?{ ????????????????byte[]?ack?=?new?byte[4]; ????????????????ByteBufUtils?buf?=?new?ByteBufUtils(ack); ????????????????buf.writeByte((byte)?0xAA); ????????????????buf.writeByte((byte)?0xAA); ????????????????buf.writeByte((byte)?this.errcode); ????????????????buf.writeByte((byte)?this.hasMore); ????????????????return?ack; ????????????} ????????????return?null; ????????}?catch?(Exception?e)?{ ????????????//?TODO:?handle?exception ????????????e.printStackTrace(); ????????????return?null; ????????} ????}
說明: 從服務器下行命令的JSON數據格式是:
該格式由平臺定義,其中:
"msgType": "cloudReq", 固定值,表示服務器下行命令;
"serviceId",profile中對應的服務,本例中是"Transmission",
"cmd",profile中定義的下行命令,本例中是"CLOUDREQ",
"paras",profile中定義的下行命令的各個字段,本例中是cmdType和downData兩個字段;圖16中,cmdType的值是2,downData是一個不定長數組,base64編碼格式。
因此,編碼的思路是:
a)??? 判斷下行命令是否是profile中定義的。
b)??? 獲取cmdType字段的值,該值占一個字節。
c)??? 獲取downData的值,該值是base64編碼形式,需轉成二進制碼流
d) 將兩個字段的值拼接成一個二進制數組,并返回。
6、生成Jar包
經過前面的工作后,代碼就已經準備好了,接下來是生成JAR包。在DOS窗口中進入pom.xml文件所在路徑,執行 mvn package 命令,最后彈出如下圖所示的結果,則表明生成Jar包成功。如果有錯誤,則根據提示再去修改代碼,然后重新執行 mvn package。
在工程目錄的target文件夾下,存放生成的JAR包“MyType-ThirdParty-MyModel-1.0.0.jar”。JAR包的命名規則是:
設備類型-廠商ID-設備型號-版本號.jar
四、??? 插件打包
1、??????? 新建package文件,包含一個“preload”子文件夾,將上一章中生成的JAR包拷貝至preload文件夾下。
2、??????? 在package文件夾中新建“package-info.json”文件(文本格式)。打開該文件,以UTF-8無BOM格式編輯,將以下大括號內容拷入該文件中并保存。
{ ???????"specVersion":?"1.0", ???????"fileName":?"package.zip", ???????"version":?"1.0.0", ???????"deviceType":?"MyType", ???????"manufacturerName":?"ThirdParty", ???????"model":?"MyModel", ???????"description":?"CIG?codec?plugin?auto-generated?by?sps.", ???????"platform":?"linux", ???????"packageType":?"CIGPlugin", ???????"date":?"Tue?Nov?27?07:55:49?GMT?2018", ???????"ignoreList":?[], ???????"bundles":?[{ ??????????????"bundleName":?"MyType-ThirdParty-MyModel", ??????????????"bundleVersion":?"1.0.0", ??????????????"priority":?5, ??????????????"fileName":?"MyType-ThirdParty-MyModel-1.0.0.jar", ??????????????"bundleDesc":?"", ??????????????"versionDesc":?"" ???????}] }
注: 在移植到別的項目中的時候,該文件需要修改的地方有:
"deviceType",需根據實際的profile填寫設備類型
"manufacturerName",需根據實際的profile填寫廠商名稱
"model",需根據實際的profile填寫設備型號
"bundleName",根據實際的profile填寫,設備型號-廠商ID-設備類型
"fileName",jar包的名稱
3、??????? 選中"package"文件夾中的全部文件,打包成zip格式。(“package.zip”,該壓縮包內不能包含“package”目錄)
說明:本章內容可參考 “華為IoT平臺NB-IoT設備集成開發指南.pdf” 6.5.4.2.3章節的“制作插件包”部分的內容。
package.zip 文件即為制作好的編解碼插件包。可在平臺上傳該文件部署即可。
后續的工作還有插件質檢、簽名,相對就簡單很多,也不是必須的過程。本文就不贅述了。
附件中給出插件demo和參考文檔
附件: testPlugSourceCode.zip 199.33KB 下載次數:7次
附件: 華為IoT平臺NB-IoT設備集成開發指南.pdf 5.91M 下載次數:14次
IoT 編解碼插件
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。