你玩過輕量系統軟匯流排應用嗎?

OpenHarmony開發者社群發表於2022-07-14

你玩過輕量系統軟匯流排應用嗎?

專案概述

可能有些同學已經接觸過了標準系統上的軟匯流排應用開發,但是你玩過輕量系統上的軟匯流排應用嗎?現在它來了。我們利用OpenAtom OpenHarmony 3.1 Release(以下簡稱“OpenHarmony”)版本的輕量系統軟匯流排能力,將智慧燃氣檢測裝置和智慧窗戶通風裝置組成一個輕量級分散式網路,實現裝置之間的相互控制。

原理圖如下:

               

當家中的燃氣告警時,無需任何操作,直接控制窗戶通風系統中的電機工作。

開發說明

從上面的視訊中可以看到,相關案例裝置的應用介面都可以分成外設互動頁面和軟匯流排操作頁面兩大塊。在下面的系統框圖中可以看到相關頁面和其依賴的相關輕量系統能力。例如外設互動介面通過自定義JSI介面與裝置硬體打交道,軟匯流排操作介面則通過輕量系統的裝置管理能力、軟匯流排能力來實現裝置間的發現、認證、傳輸等軟匯流排操作。

在下面的內容中我們將以智慧燃氣告警器裝置為例,將相關開發過程分成JS應用端開發、自定義JSI實現、開發板端程式碼三部分來說明:

               

JS應用開發

燃氣告警器JS應用是基於3.1 release版本,並結合方舟開發框架(ArkUI)、分散式組網等特性,使用JS語言開發的一款分散式安全廚房應用。為了體現了 OpenHarmony輕量級分散式特性,不僅需要考慮頁面該如何設計、應用怎樣同外設互動,還需要考慮兩個輕量級裝置間如何進行裝置認證,裝置間如何進行通訊等問題。

所以在相關應用中設計了操作頁面,報警頁面以及裝置認證頁面。其中首頁是設定燃氣濃度閾值,以及顯示當前燃氣濃度;報警頁面是當首頁檢測到當前燃氣濃度達到或高於我們設定的閾值時,會從首頁跳轉到報警介面;裝置認證頁面則是對兩臺裝置進行分散式組網。

程式碼結構如下圖:

               

外設互動頁面開發

               

相關介面如上圖所示,我們將首頁頁面分成三個部分:頂部標籤、燃氣濃度顯示、設定告警閾值。接下來是具體的解析內容:

1)頂部標籤解析

頂部標籤中除了相關文字介面,主要是頂部兩個按鈕,分別用來顯示當前Wi-Fi連線狀態以及跳轉到裝置認證頁面。


 GetKey() {


       com.get({ // 獲取wifi狀態
           key: 'storage_key',
           success: (data)=> {
               let res = JSON.parse(data)
               if (res.wifi) {
                   if(res.wifi == 'connected') {
                       this.isWifi = true;
                   } else {
                       this.isWifi = false;
                   }
               }
           .....
           },
       });
   },
changePage(operation) {
       router.replace({
           uri:"pages/dm/dm" // 跳轉到裝置認證頁面
       });
   }

2)燃氣濃度顯示

主要是獲取當前燃氣濃度,並實時重新整理到相關介面上。


 onInit() {


        setTimeout(()=>{
           setInterval(()=>this.GetKey(),500) // 每500ms 獲取一次
       },3000);
   },


GetKey() {
       com.get({
           key: 'storage_key',
           success: (data)=> {
               let res = JSON.parse(data)


               if (res.CurrentGasCONC) {
                   this.currentValue = res.CurrentGasCONC;
                   this.progressPercent = ((this.currentValue ) /300) * 100
                   if(this.currentValue >this.PresetValue && !this.isChange){
                       this.isChange = true;
                       router.replace({
                           uri:"pages/warn/warn"  // 燃氣數值超標後自動跳轉告警頁面
                       });
                   }
               }
           },
           ......
       });
   },

3)設定報警閾值

首頁介面中對於報警閾值的處理,主要包含減小預設閾值和增大預設閾值,都是通過呼叫相關SetKey操作完成的。


reduceProgress(){  //減小預設閾值


       if (this.PresetValue <= 0) {
           this.PresetValue = 0
       }else{
           this.PresetValue = parseInt(this.PresetValue) - 20
       }
       this.isChange = false;
       this.setProgress = ((this.PresetValue ) /300) * 100
       this.SetKey( 'GasThreshold', this.PresetValue );
   },
   
addProgress(){  //增大預設閾值
       if (this.PresetValue >= 300) {
           this.PresetValue = 300
       }else{
           this.PresetValue = parseInt(this.PresetValue) + 20
       }
       this.isChange = false;
       this.setProgress = ((this.PresetValue ) /300) * 100
       this.SetKey( 'GasThreshold', this.PresetValue );
   },
   
SetKey(key1, value1) {
       com.set({
           key: key1 + '',
           value: value1 + '',
           // success or failed 狀態列印
       });
   },

裝置認證頁面開發

               

相關介面如上圖所示,我們將裝置認證步驟分成四個步驟:發現裝置、發起認證、允許認證、輸入PIN碼。接下來是具體的解析內容:

1)發現裝置解析

裝置認證因裝置狀態不同顯示對應的UI,上圖顯示的UI對應裝置狀態”status = start“。


startDevice(){


       this.subscribeId = Math.floor(Math.random() * 10000 + 1000)
       var info = {
           "subscribeId": this.subscribeId, // 特定隨機的
           "mode": 0xAA, // 設定主動發現模式,除此之外還有被動模式DISCOVER_MODE_PASSIVE
           "medium": 0, // 自動選擇發現介質,目前用的是coap
           "freq": 2,  // 傳送發現訊息的頻率,目前用的是HIGH 還有LOW/MID/SUPER_HIGH
           "isSameAccount": false, // 取消同一賬號下才能發現的限制
           "isWakeRemote": false,  // 目前輕量系統沒有睡眠模式,所以不用睡眠喚醒功能
           "capability": 0 // 目前使用DDMP
       devicemanager.startDeviceDiscovery(info);   // 開始裝置發現
   },

2)發起認證解析


AuthenticateDevice(){ // 發起認證


       let extraInfo = {
           targetPkgName: 'test',
           appName: "Newname",
           appDescription: "testAPP",
           business: '0',
           displayOwner: 0
       };
       let AuthParam = {
           authType: 1, // 以PIN 碼方式進行認證校驗
           appIcon:null,
           appThumbnail:null,
           extraInfo: extraInfo
       };
       let _this = this;
       devicemanager.authenticateDevice(this.statusInfo, AuthParam, {
        // 省略了相關success 和fail 回撥處理,完整程式碼見參考連結
       },  

3)允許認證解析 

當裝置狀態”status = join-pin“,允許相關認證動作並且顯示相關PIN碼。


    joinAuthOk() {


       this.joinPin() //切換顯示PIN碼介面
       this.initStatue()  //獲取PIN碼並顯示
       devicemanager.setUserOperation(0)
   },
   initStatue() {
       this.log('initStatue')
       const data = devicemanager.getAuthenticationParam()
       // 引數值轉換為 JSON 字串寫入data
       this.log('getAuthenticationParam:' + JSON.stringify(data))
       // Authentication type, 1 for pin code.
       // ode ==1,pin碼
       if (data && data.authType == 1) {
       // 完整程式碼見參考連結
       }
   },

4)輸入PIN碼解析

當裝置狀態"status = main-pin",進到相關PIN碼輸入、校驗介面。


  mainInputPin(s) { // 輸入六位數字


        if (this.pinNumb == 6) return
       if (this.pinNumb < 6) {
           this.pin[this.pinNumb] = s
           ++this.pinNumb
       }
       if (this.pinNumb == 6) {
           console.log("verifyAuthInfo ok")
           this.verifyAuthInfo(this.pin.join(''))  // PIN碼校驗
       }
   },

自定義JSI原理和實現

JSI是OpenHarmony輕量和小型系統的一種JS API實現機制,適合封裝IO、CPU密集型、OS底層等能力給到JS應用呼叫,通過JSI可以實現JS與C/C++程式碼互相訪問。與OpenHarmony 輕量級系統中涉及到的 audio、device、sensor 等需要與硬體打交道的JSI 模組類似,CommunicationKit 和DeviceManager模組同樣首先要加入到相關的配置檔案中。ace_lite_engine 通過JSI::SetModuleAPI將JS應用中使用的關鍵字對映成C++函式。具體操作如下:

foundation/ace/ace_engine_lite/frameworks/module_manager/ohos_module_config.h中的OHOS_MODULES新增如下欄位:


{"CommunicationKit", InitNativeApiCommunicationKit},


{"devicemanager", InitDeviceManagerModule},

載入自定義模組

如上所示,在JS應用外設控制介面中資料讀取和命令下發時,引入了CommunicationKit模組,現在我們就看一下相關具體內容:

InitNativeApiCommunicationKit函式相關內容如下:


vendor/team_x/common/communicationkit/native_utils/src/nativeapi_communication_kit.cpp


void InitNativeApiCommunicationKit(JSIValue exports) {
 JSI::SetModuleAPI(exports, "get", NativeapiCommunicationKit::Get); // 與JS應用中的關鍵字一致
 JSI::SetModuleAPI(exports, "set", NativeapiCommunicationKit::Set);
}

相關C++實現NativeapiCommunicationKit::Get 和Set 的方式類似,我們參考輕量系統原始碼中其他模組的實現,使用ExecuteAsyncWork函式。根據給定的引數建立一個非同步工作,並將其分派給主應用程式任務處理程式。其獲取燃氣濃度邏輯在ExecuteGet實現,設定輕量系統燃氣告警閾值由ExecuteSet實現。函式args引數用來接收JS端傳過來的引數(JSIValue陣列),argsNum表示該陣列長度。


ExecuteAsyncWork(thisVal, args, argsNum, ExecuteGet, false);


ExecuteAsyncWork(thisVal, args, argsNum, ExecuteSet, false);

載入裝置管理模組

與自定義模組類似,JS應用使用軟匯流排介面時,需要在應用執行前載入裝置管理模組。裝置管理模組也是OpenHarmony系統中的重要組成部分,我們在標準系統中是通過NAPI的方式來載入相關模組,而在我們輕量系統中,裝置管理模組是通過JSI的方式來載入的。首先看到InitDeviceManagerModule函式相關內容如下:


foundation/distributedhardware/devicemanager/interfaces/kits/js_mini/src/native_devicemanager_js.cpp


void InitDeviceManagerModule(JSIValue exports) {
 JSI::SetModuleAPI(exports, "createDeviceManager", DeviceManagerModule::CreateDeviceManager);
 ......
 JSI::SetModuleAPI(exports, "startDeviceDiscovery", DeviceManagerModule::StartDeviceDiscoverSync);
 JSI::SetModuleAPI(exports, "stopDeviceDiscovery", DeviceManagerModule::StopDeviceDiscoverSync);
 JSI::SetModuleAPI(exports, "authenticateDevice", DeviceManagerModule::AuthenticateDevice);
 JSI::SetModuleAPI(exports, "verifyAuthInfo", DeviceManagerModule::VerifyAuthInfo);
 JSI::SetModuleAPI(exports, "setUserOperation", DeviceManagerModule::SetUserOperationSync);
 JSI::SetModuleAPI(exports, "getAuthenticationParam", DeviceManagerModule::GetAuthenticationParamSync);
 ......
}

開發板端程式碼開發說明

如下圖所示,我們從輕量系統軟匯流排裝置的系統啟動流程出發,來分析軟匯流排應用執行的相關要點。第一步:首先初始化軟匯流排server;第二步:註冊與軟匯流排相關的服務,例如PRC、裝置管理DeviceManager服務;第三步:JS engine載入DeviceManager介面宣告;第四步:具體的RPC通訊操作流程。

               

初始化軟匯流排服務

輕量系統裝置啟動的啟動過程中,會註冊相關初始化軟匯流排服務執行緒。該執行緒中的主要內容就是呼叫InitSoftBusServer函式。該函式會初始化與軟匯流排相關的配置、發現、認證等相關操作。

               

軟匯流排相關服務註冊

服務是OpenHarmony系統中的一個重要概念,不同的功能模組,不同執行緒/程式之間的呼叫介面,都統一抽象成了服務。利用服務機制,作業系統、驅動框架等提供的能力都能被包裝成服務提供給到應用呼叫。下面給大傢俱體介紹軟匯流排相關的服務註冊細節和要點。

               


SAMGR:作為中介者,管理Provider提供的能力,同時幫助Consumer發現Provider的能力。

Provider:服務的提供者,為系統提供能力(對外介面)。

PRC服務註冊

RPC:(Remote Procedure Call)用於跨裝置跨程式間的通訊,在輕量系統軟匯流排應用中,我們利用RPC能力實現了安全廚房專案中兩個裝置的關聯控制,RPC服務註冊的具體內容如下:

1.建立相關的靜態服務物件;


static MiniService g_miniService = {


       .GetName = GetName, // 相關服務名為mini_sa_rpc
       .Initialize = Initialize,
       .MessageHandle = MessageHandle,
       .GetTaskConfig = GetTaskConfig,                                                        
       SERVER_IPROXY_IMPL_BEGIN,
       .Invoke = FeatureInvoke,  // 對外提供Invoke方法,供RPC相關client端程式呼叫
       IPROXY_END,
};

2.註冊相關的服務和預設物件;


SAMGR_GetInstance()->RegisterService((Service *)&g_miniService); 


SAMGR_GetInstance()->RegisterDefaultFeatureApi(MINI_SERVICE, GET_IUNKNOWN(g_miniService));

裝置管理服務註冊

如果沒註冊裝置管理服務,那麼相關軟匯流排能力也就無從談起。在輕量系統啟動時,執行相關裝置管理服務初始化動作,具體內容如下:

1)建立相關的靜態服務物件;


static DeviceManagerSamgrService service = {


       .GetName = GetName, // 相關服務名為dev_mgr_svc
       .Initialize = Initialize,
       .MessageHandle = MessageHandle,
       .GetTaskConfig = GetTaskConfig,
   };

2)註冊相關的服務和預設物件;


SAMGR_GetInstance()->RegisterService((Service *)&service)) ;


SAMGR_GetInstance()->RegisterDefaultFeatureApi(DEVICE_MANAGER_SERVICE_NAME, GET_IUNKNOWN(service));

3)執行相關初始化動作。DeviceManager服務的初始化函式將完成相關狀態回撥註冊和傳輸通道初始化,然後執行相關Publish動作,做好接收發現訊息的準備。

               

裝置間RPC通訊

在標準系統應用開發中,我們可以通過分散式資料庫和啟動遠端Ability的方式實現裝置之間的通訊,而在輕量系統中則通過RPC的方法來實現。在前面的內容中我們講了服務的概念,下面是擴充的相關原理圖:

               

Consumer:服務的消費者,呼叫服務提供的功能(對外介面)。

在完成軟匯流排組網後,如果檢測到燃氣度數超標後,按如下步驟即可實現對智慧通風裝置的控制:

1.獲取軟匯流排網路中的相關節點資訊;


   GetAllNodeDeviceInfo("com.ohos.devicemanagerui", &nodeInfo, &infoNum);


2.獲取遠端節點發布的mini_sa_rpc服務中對應的IUnknown方法;


   IUnknown *miniDefApi = SAMGR_GetInstance()->GetRemoteDefaultFeatureApi(nodeInfo[0]->networkId, "mini_sa_rpc");


3.查詢服務所釋出的相關能力,獲取指向具體API介面的指標miniInterface;


   miniDefApi->QueryInterface(miniDefApi, 0, (void **) &miniInterface);


4.呼叫相關mini_sa_rpc對外提供的Invoke能力;


   miniInterface->Invoke(miniInterface, 1, &reply, NULL, NULL);


操作體驗

1. 提前準備好安全廚房場景中的智慧窗戶通風裝置和智慧燃氣告警裝置,並完成相關的編譯和應用安裝動作;

2. 提前準備好正常工作的無線路由裝置(請保證預設熱點名稱:test_wifi 密碼:12345678;是否能連線網際網路均可)

3. 將燃氣檢測裝置和窗戶通風裝置上電,確認兩個裝置應用啟動正常和操作正常;

4. 按如下步驟將通風裝置、燃氣檢測裝置組成一個軟匯流排網路:

● 分別點選兩個裝置應用介面右上角的軟匯流排配置圖示,進入軟匯流排配置介面;

● 點選智慧燃氣檢測裝置應用發現圖示,間隔3S後點選發起認證圖示;

● 點選智慧通風裝置軟匯流排配置介面下的允許認證圖示,正常情況下會顯示一個6位數的PIN碼;

● 點選智慧燃氣檢測裝置應用輸入PIN碼按鈕,進入數字鍵盤輸入PIN碼;

● 分別點選兩個應用軟匯流排配置圖示左上角的返回按鍵,進入裝置控制介面。

5. 設定燃氣檢測裝置的閾值低於實際讀取的燃氣數值,燃氣檢測應用進入警報介面的同時會控制電機工作,自動通風換氣,保證家居的安全。待到實際燃氣數值低於設定的閾值時,則關閉電機。

參考連結

本專案中涉及到的參考資料和相關文件路徑如下:歐智通BES2600WM開發板快速上手學習路徑:

輕量系統應用開發軟匯流排視訊課程:

裝置管理模組文件:

https://gitee.com/openharmony/device_manager/blob/master/README_zh.md

智慧燃氣檢測系統樣例:

智慧窗戶通風系統樣例:

總結

從本文中可以看到與標準系統一樣應用都是呼叫裝置管理模組提供的相關介面來實現的軟匯流排發現、認證等功能,但是不同的地方在於標準系統使用了預製的DeviceManager_UI.hap來顯示PIN碼、輸入PIN碼。而輕量系統軟匯流排應用中,相關PIN碼顯示、PIN碼輸入需要自己呼叫相關介面。

與標準系統軟匯流排應用相比,目前輕量系統軟匯流排應用只實現了輕量系統裝置之前資料流轉功能,輕量系統分散式拉起、分散式資料庫等功能待後續更新迭代。下一步還將研究如何利用軟匯流排來連線輕量系統和標準系統,敬請大家期待。

豐富多樣的OpenHarmony開發樣例離不開廣大合作伙伴和開發者的貢獻,如果你也想把自己開發的樣例分享出來,歡迎提交到OpenHarmony知識體系SIG倉庫。

共建開發樣例參考

               


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70011554/viewspace-2905701/,如需轉載,請註明出處,否則將追究法律責任。

相關文章