全部產品
Search
文件中心

IoT Platform:物模型開發

更新時間:Jun 30, 2024

物模型是阿里雲物聯網平台為產品定義的資料模型。您可以通過Android Link SDK,實現裝置端上報屬性和事件,並接收物聯網平台發送的設定屬性和調用服務的指令。

背景資訊

使用說明

重要

調用下文提及的物模型相關介面後,回調中onSuccess僅代表對應訊息從裝置發出成功,不代表訊息對應的任務執行成功。裝置執行商務邏輯請勿依賴onSuccess

  • Android Link SDK中,通過getDeviceThing(),擷取的物模型對象即為IThing。IThing的詳細說明,請參見 IThing ApiReference

  • Android Link SDK預設未開啟物模型功能,如需使用,請參見一機一密裝置認證方式的範例程式碼Step 5。

  • 調用物模型介面後,如果您需根據物模型指令的執行結果編寫業務的處理邏輯,請參考Demo的InitManager.java類中的函數 IConnectNotifyListener介面onNotify,處理下行訊息。

  • 具體代碼實現請參見Demo中的TSLActivity.java

裝置上報屬性

  • 上報屬性:

    // 裝置上報
    Map<String, ValueWrapper> reportData = new HashMap<>();
    // identifier為物聯網平台定義的屬性標識符,valueWrapper為屬性的值
    // ValueWrapper valueWrapper = new ValueWrapper.BooleanValueWrapper(1);   //以布爾型變數值1為例
    // reportData.put(identifier, valueWrapper); // 參考樣本,更多使用可參考Demo 
    LinkKit.getInstance().getDeviceThing().thingPropertyPost(reportData, new IPublishResourceListener() {
     @Override
    public void onSuccess(String alinkId, Object o) {
            // 訊息從裝置發出成功
            // alinkId表示該訊息的messageId
    }
    public void onError(String alinkId, AError aError) {
            // 屬性上報失敗    
            // alinkId表示該訊息的messageId
    }
    });
     
    說明
    • 從1.7.3.1版本開始,thingPropertyPost的回調介面通過alinkId欄位透出當前所發送的上行訊息的ID。

    • 如果要觀察裝置上報的屬性訊息是否已到達雲端,可以在訂閱 /sys/${productKey}/${deviceName}/thing/event/property/post_reply訊息後,關注IConnectNotifyListener類(參考Demo的InitManager.java)的介面onNotify。該介面會透出reply訊息的alinkId,如果上下行訊息的alinkId一致,表示上行訊息已經被服務端處理。

  • 擷取屬性:

    擷取的是緩衝在裝置本地的屬性資料,不是雲端即時的屬性資料。

    // 通過identifier擷取物模型對應屬性的值(預設模組, 非使用者自訂模組)
    String identifier = "******";
    LinkKit.getInstance().getDeviceThing().getPropertyValue(identifier);
    // 擷取預設模組(非使用者自訂模組)的屬性列表
    LinkKit.getInstance().getDeviceThing().getProperties()

裝置上報事件

HashMap<String, ValueWrapper> hashMap = new HashMap<>();
// TODO 您需根據業務實際情況修改該代碼
// hashMap.put("ErrorCode", new ValueWrapper.IntValueWrapper(0));
OutputParams params = new OutputParams(hashMap);
LinkKit.getInstance().getDeviceThing().thingEventPost(identifier, params, new IPublishResourceListener() {
 @Override
public void onSuccess(String alinkId, Object o) {
        // 訊息從裝置發出成功
        // alinkId表示該訊息的messageId
}
public void onError(String alinkId, AError aError) {
        // 事件上報失敗
        // alinkId表示該訊息的messageId
}
});
說明
  • 從1.2.3版本開始,thingEventPost的回調介面通過alinkId欄位透出當前所發送的上行訊息的ID。

  • 如果要觀察裝置上報的事件訊息是否已到達雲端,可以在訂閱 /sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post_reply訊息後,關注IConnectNotifyListener類(參考Demo的InitManager.java)的介面onNotify。該介面會透出reply訊息的alinkId,如果上下行訊息的alinkId一致,表示上行訊息已經被服務端處理。

擷取預設模組(非使用者自訂模組)的事件列表:

擷取的是裝置本地的事件,不是上報到物聯網平台的即時事件。

LinkKit.getInstance().getDeviceThing().getEvents()

雲端下發屬性和服務

  • 裝置調用服務擷取Service的定義,請參見 Service API Reference

  • 擷取預設模組(非使用者自訂模組)的服務列表

    LinkKit.getInstance().getDeviceThing().getServices()
  • 裝置的服務調用支援同步和非同步兩種方式。裝置屬性的設定和擷取也是通過該服務調用的監聽方式,實現物聯網平台服務的下發。

    非同步呼叫

    設定方法

    callType="async"
    • 裝置註冊服務的處理監聽器,當物聯網平台觸發非同步服務調用時,下行的請求會到註冊的監聽器中。

    • onProcess為裝置收到的雲端下行的服務調用。其中,第一個參數為需調用服務對應的identifier。您可根據identifier做不同的業務處理。

      identifier為服務的標識符,更多資訊,請參見標識符

    • 裝置收到物聯網平台的下行服務調用後,根據指令執行對應操作,操作結束後上報一條屬性狀態變化的通知。

    相關代碼

    List<Service> serviceList = thing.getServices();
            //設定handler處理屬性下發,以及預設模組中的非同步服務. 如果是使用者自訂模組的非同步服務,請參見"模組化服務的註冊"一節 
            for (int i = 0; serviceList != null && i < srviceList.size(); i++) {
                Service service = serviceList.get(i);
                LinkKit.getInstance().getDeviceThing().setServiceHandler(service.getIdentifier(), mCommonHandler);
            }
            
            private ITResRequestHandler mCommonHandler = new ITResRequestHandler()  {
            
            @Override
            public void onProcess(String identify, Object result, ITResResponseCallback itResResponseCallback) {
                AppLog.d(TAG, "onProcess() called with: s = [" + identify + "], o = [" + result + "], itResResponseCallback = [" + itResResponseCallback + "]");
    
                try {
                    if (SERVICE_SET.equals(identify)) {
                        /* 雲端下發屬性到裝置 */
                        // TODO 1:使用者需要按照真實裝置的介面調用,設定裝置的屬性. 使用者根據實際情況判斷屬性是否設定成功.
                        // TODO 2:設定完真實裝置屬性之後,上報設定完成的屬性值. 這裡是測試代碼,做了簡化,直接返回成功
                        boolean isSetPropertySuccess = true;
                        if (isSetPropertySuccess){
                            if (result instanceof InputParams) {
                                //TODO 3:解析服務端下行的屬性資料
                                Map<String, ValueWrapper> data = (Map<String, ValueWrapper>) ((InputParams) result).getData();
                                //   data.get()
                                // 響應雲端 接收資料成功
                                itResResponseCallback.onComplete(identify, null, null);
                            } else {
                                itResResponseCallback.onComplete(identify, null, null);
                            }
                        } else {
                            AError error = new AError();
                            error.setCode(100);
                            error.setMsg("setPropertyFailed.");
                            itResResponseCallback.onComplete(identify, new ErrorInfo(error), null);
                        }
    
                    } else if (SERVICE_GET.equals(identify)){
                        //  不需要使用者處理
                    } else {
                        // 雲端下發服務到裝置. 
                        // TODO: 根據不同的服務做不同的處理,跟具體的服務有關係
                        OutputParams outputParams = new OutputParams();
                        // 參考例子: outputParams.put("op", new ValueWrapper.IntValueWrapper(20));
                        itResResponseCallback.onComplete(identify,null, outputParams);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onSuccess(Object o, OutputParams outputParams) {
                AppLog.d(TAG, "onSuccess() called with: o = [" + o + "], outputParams = [" + outputParams + "]");
            }
    
            @Override
            public void onFail(Object o, ErrorInfo errorInfo) {
                AppLog.d(TAG, "onFail() called with: o = [" + o + "], errorInfo = [" + errorInfo + "]");
            }
        };

    同步調用

    設定方法

    callType="sync"
    1. 先註冊一個下行資料監聽,註冊方法請參見認證與串連串連狀態與下行訊息監聽notifyListener

      當雲端觸發服務調用時,使用者可以在onNotify收到雲端的下行服務調用。

    2. 使用者收到雲端的下行服務調用後,根據實際服務調用對裝置做服務處理,處理之後需要裝置回複雲端的請求,即發布一個帶有處理結果的請求到雲端。

    說明

    新版本的Android Link SDK支援自訂RRPC,如果您的SDK是由低版本升級而來,請注意同步服務屬性下行的通道為RRPC。

    相關代碼

    private static IConnectNotifyListener notifyListener = new IConnectNotifyListener() {
          
            @Override
            public void onNotify(String connectId, String topic, AMessage aMessage) {
                String data = new String((byte[]) aMessage.data);
                // 服務端返回資料樣本  data = {"method":"thing.service.test_service","id":"123374967","params":{"vv":60},"version":"1.0.0"}
                AppLog.d(TAG, "onNotify() called with: connectId = [" + connectId + "], topic = [" + topic + "], aMessage = [" + data + "]");
    
    
                if (ConnectSDK.getInstance().getPersistentConnectId().equals(connectId) && !TextUtils.isEmpty(topic) &&
                        topic.startsWith("/ext/rrpc/")) {
                    // 1.處理/ext/rrpc開頭的同步服務的topic
                    
                    //樣本 topic=/ext/rrpc/1138654706478941696//a1ExY4afKY1/testDevice/user/get
                    //AppLog.d(TAG, "receice Message=" + new String((byte[]) aMessage.data));
                    //回複服務端資料樣本  {"method":"thing.service.test_service","id":"123374967","params":{"vv":60},"version":"1.0.0"}
                    MqttPublishRequest request = new MqttPublishRequest();
                    request.isRPC = false;
                    request.topic = topic;
                    String[] array = topic.split("/");
                    String resId = array[3];
                    request.msgId = resId;
    
                    String alinkdId = null;
                    try{
                        JSONObject jsonObject = JSONObject.parseObject(data);
                        alinkdId = jsonObject.getString("id");
                    }catch (Exception e){
                        AppLog.e(TAG,"parse alinkId failed, exit");
                        return;
                    }
    
                    // 雲端下發了同步服務,需要做回複,否則服務端會顯示調用逾時
                    // TODO 使用者根據實際情況填寫 僅做參考
                    request.payloadObj = "{\"id\":\"" + alinkdId + "\", \"code\":\"200\"" + ",\"data\":{\"aa\":1} }";
                    LinkKit.getInstance().publish(request, new IConnectSendListener() {
                        @Override
                        public void onResponse(ARequest aRequest, AResponse aResponse) {
                            // 響應成功
                        }
    
                        @Override
                        public void onFailure(ARequest aRequest, AError aError) {
                            // 響應失敗
                        }
                    });
                } else if (ConnectSDK.getInstance().getPersistentConnectId().equals(connectId) && !TextUtils.isEmpty(topic) &&
                        topic.startsWith("/sys/" + DemoApplication.productKey + "/" + DemoApplication.deviceName + "/rrpc/request/")) {
                    // 2.處理/sys開頭的同步服務的topic
                    
                    // AppLog.d(TAG, "receice Message=" + new String((byte[]) aMessage.data));
                    // 回複服務端資料樣本  {"method":"thing.service.test_service","id":"123374967","params":{"vv":60},"version":"1.0.0"}
                    MqttPublishRequest request = new MqttPublishRequest();
                    // 支援 0 和 1, 預設0
                    // request.qos = 0;
                    request.isRPC = false;
                    request.topic = topic.replace("request", "response");
                    String[] array = topic.split("/");
                    String resId = array[6];
                    request.msgId = resId;
                    // TODO 使用者根據實際情況填寫 僅做參考
                    request.payloadObj = "{\"id\":\"" + resId + "\", \"code\":\"200\"" + ",\"data\":{} }";
    
                    LinkKit.getInstance().publish(request, new IConnectSendListener() {
                        @Override
                        public void onResponse(ARequest aRequest, AResponse aResponse) {
                        }
    
                        @Override
                        public void onFailure(ARequest aRequest, AError aError) {
                        }
                    });
                } else {
                    // 3.處理其他訊息
                    
                    /**
                     * TODO
                     * 根據訂閱的具體 topic 做業務處理
                     */
                }
              
              //TODO: 實現其他回調
            }

模組化物模型

使用說明

  • 只有iot-device-manager的1.7.5.2及以上版本支援模組化物模型。

  • 在物模型預設的模組中,屬性、事件、服務的identifier不用加首碼。例如名為lightSwitch的屬性,identifier就是lightSwitch

  • 在使用者自訂的模組中,屬性、事件、服務的identifier要加模組名為首碼。例如myBlock模組中的lightSwitch屬性,identifier要寫成myBlock:lightSwitch

模組化物模型屬性的上報

以上報myBlock模組中的lightSwitch屬性為例:

Map<String, ValueWrapper> reportData  = new HashMap<>();
// identifier為物聯網平台定義的屬性的標識符,valueWrapper為屬性的值
String identifier = "myBlock:lightSwitch";
reportData.put(identifier, valueWrapper);  // 參考樣本,更多內容可參考Demo
LinkKit.getInstance().getDeviceThing().thingPropertyPost(reportData, new IPublishResourceListener() {
    @Override
    public void onSuccess(String resID, Object o) {
        // 屬性上報成功
    }

    @Override
    public void onError(String resId, AError aError) {
        // 屬性上報失敗
    }
});

模組化物模型事件的上報

以上報map模組中的OnDetect事件為例:

 HashMap<String, ValueWrapper> hashMap = new HashMap<>();
hashMap.put("StoreID", new ValueWrapper.StringValueWrapper("1"));
OutputParams params = new OutputParams(hashMap);
LinkKit.getInstance().getDeviceThing().thingEventPost("map:OnDetect", params, new IPublishResourceListener() {
    @Override
        public void onSuccess(String resId, Object o) { // 事件上報動作成功
    }

    @Override
        public void onError(String resId, AError aError) { // 事件上報失敗
     }
 });

模組化服務的註冊

使用者自訂模組中的服務要先向SDK註冊,才能監聽到相應的回調,以map模組中的VehDtcService服務為例,需要通過如下方式訂閱:

thing.setServiceHandler("map:VehDtcService", resRequestHandler);

其中resRequestHandler是物模型報文處理handler的執行個體,請參見demo中的TSLActivity.java

向裝置下髮指令

  • 物聯網平台下髮指令:通過監控營運功能對裝置進行控制和資料的下發,具體內容,請參見監控營運的線上調試

  • 服務端下髮指令:通過OpenAPI,調用北向控制API下髮指令。具體內容,請參見雲端API參考