設備可以使用物模型功能,實現屬性上報(上報設備狀態)、事件上報(上報設備異常或錯誤)和服務調用(通過云端調用設備提供的服務)。
前提條件
物聯網平臺已為目標設備完成物模型默認模塊或自定義模塊的物模型功能定義(屬性、事件和服務定義)。具體操作,請參見添加物模型。
如果物模型功能定義為空,會導致設備上報屬性、上報事件和云端調用服務失敗。
背景信息
物聯網平臺物模型功能的更多信息,請參見什么是物模型。
物模型數據格式的更多信息,請參見設備屬性、事件、服務。
使用說明
在物模型的默認模塊中,屬性、事件、服務的identifier不需要加前綴。例如名為lightSwitch的屬性,identifier就是lightSwitch。
在物模型的自定義模塊中,屬性、事件、服務的identifier要加模塊名為前綴。例如myBlock模塊中的lightSwitch屬性,identifier要寫成myBlock:lightSwitch。
調用本文提及的物模型相關接口后,回調中onSuccess
僅代表對應消息從設備發出成功,不代表消息對應的任務執行成功。設備執行業務邏輯請勿依賴onSuccess
。
設備上報屬性
getDeviceThing()
返回的IThing接口介紹參見 IThing ApiReference,具體代碼實現請參見Demo中的ThingSample.java。從1.2.3版本開始,
thingPropertyPost
的回調接口通過alinkId
字段透出當前所發送的上行消息的ID。如果要觀察設備上報的屬性消息是否已到達云端,可以在訂閱
/sys/${productKey}/${deviceName}/thing/event/property/post_reply
消息后,關注IConnectNotifyListener
類(參考Demo的ThingSample.java
)的接口onNotify
。該接口會透出reply
消息的alinkId
,如果上下行消息的alinkId
一致,表示上行消息已經被服務端處理。
默認模塊
// 設備上報
Map<String, ValueWrapper> reportData = new HashMap<>();
// identifier 是云端定義的屬性的唯一標識,valueWrapper是屬性的值
// 以上報整型數據為例,我們構造如下valueWrapper
// ValueWrapper valueWrapper = new ValueWrapper.IntValueWrapper(1);
// reportData.put(identifier, valueWrapper); // 參考示例,更多使用可參考demo
LinkKit.getInstance().getDeviceThing().thingPropertyPost(reportData, new IPublishResourceListener() {
public void onSuccess(String alinkId, Object o) {
// 消息從設備發出成功
// alinkId表示該消息的messageId
}
public void onError(String alinkId, AError aError) {
// 屬性上報失敗
// alinkId表示該消息的messageId
}
});
自定義模塊
以上報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) {
// 屬性上報失敗
}
});
設備上報事件
從1.2.3版本開始,
thingEventPost
的回調接口通過alinkId
字段透出當前所發送的上行消息的ID。如果要觀察設備上報的事件消息是否已到達云端,可以在訂閱
/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post_reply
消息后,關注IConnectNotifyListener
類(參考Demo的ThingSample.java
)的接口onNotify
。該接口會透出reply
消息的alinkId
,如果上下行消息的alinkId
一致,表示上行消息已經被服務端處理。
默認模塊
HashMap<String, ValueWrapper> hashMap = new HashMap<>();
// TODO 用戶根據實際情況設置
// hashMap.put("ErrorCode", new ValueWrapper.IntValueWrapper(0));
OutputParams params = new OutputParams(valueWrapperMap);
LinkKit.getInstance().getDeviceThing().thingEventPost(identity, params, new IPublishResourceListener() {
public void onSuccess(String alinkId, Object o) {
// 消息從設備發出成功
// alinkId表示該消息的messageId
}
public void onError(String alinkId, AError aError) {
// 事件上報失敗
// alinkId表示該消息的messageId
}
});
自定義模塊
以上報myBlock模塊中的OnDetect事件為例:
HashMap<String, ValueWrapper> hashMap = new HashMap<>();
hashMap.put("StoreID", new ValueWrapper.StringValueWrapper("1"));
OutputParams params = new OutputParams(hashMap);
LinkKit.getInstance().getDeviceThing().thingEventPost("myBlock:OnDetect", params, new IPublishResourceListener() {
@Override
public void onSuccess(String resId, Object o) { // 事件上報動作成功
}
@Override
public void onError(String resId, AError aError) { // 事件上報失敗
}
});
云端下發屬性和服務
默認模塊
設備服務獲取Service定義參見 Service API Reference。
//獲取默認模塊(非用戶自定義模塊)的服務列表 LinkKit.getInstance().getDeviceThing().getServices()
獲取默認模塊(非用戶自定義模塊)的事件列表。
LinkKit.getInstance().getDeviceThing().getEvents()
設備服務調用監聽。
云端在添加設備服務時,需設置該服務的調用方式,由Service中的
callType
字段表示:同步服務調用時,
callType="sync"
。異步服務調用時,
callType="async"
。
設備屬性設置和獲取也是通過服務調用監聽方式實現云端服務的下發。
異步服務調用
先注冊服務的處理監聽器,當云端觸發異步服務調用時,下行的請求會到注冊的監聽器中。一個設備會有多種服務,通常需要注冊所有服務的處理監聽器。
onProcess
是設備收到的云端下行的服務調用方法,第一個參數是需要調用服務對應的identifier,用戶可以根據identifier做不同的處理。云端調用設置服務時,設備需要在收到設置指令后,調用設備執行真實操作,操作結束后上報一條屬性狀態變化的通知。說明identifier是在物聯網平臺為產品創建屬性、事件、服務時定義的標識符,用戶可在物聯網平臺控制臺查看產品功能定義中屬性、事件、服務對應的identifier。
public void setServiceHandler() { ALog.d(TAG, "setServiceHandler() called"); List<Service> srviceList = LinkKit.getInstance().getDeviceThing().getServices(); for (int i = 0; srviceList != null && i < srviceList.size(); i++) { Service service = srviceList.get(i); LinkKit.getInstance().getDeviceThing().setServiceHandler(service.getIdentifier(), mCommonHandler); } LinkKit.getInstance().registerOnNotifyListener(connectNotifyListener); } private ITResRequestHandler mCommonHandler = new ITResRequestHandler() { public void onProcess(String identify, Object result, ITResResponseCallback itResResponseCallback) { ALog.d(TAG, "onProcess() called with: s = [" + identify + "], o = [" + result + "], itResResponseCallback = [" + itResResponseCallback + "]"); try { if (SERVICE_SET.equals(identify)) { /** 云端下發屬性,SDK收到后觸發的回調 * * TODO: 用戶需要將下發的屬性值,設置到真實設備里面。 * 若設置成功,需要將isSetPropertySuccess寫為true, * demo將通過itResResponseCallback這個回調,將設備本地更新后的屬性值寫到云平臺, * 云平臺的設備詳情的物模型數據一欄屬性值將會刷新 * 若設置失敗,需要將isSetPropertySuccess寫為false, demo將不更新云平臺中的屬性值 * * 這里假定用戶已經將屬性設置到真實設備里面,將isSetPropertySuccess寫為true */ boolean isSetPropertySuccess = true; if (isSetPropertySuccess) { if (result instanceof InputParams) { Map<String, ValueWrapper> data = (Map<String, ValueWrapper>) ((InputParams) result).getData(); // 如果控制臺下發了屬性OverTiltEnable,可以通過data.get("OverTiltEnable") 來獲取相應的屬性值 ALog.d(TAG, "收到下行數據 " + data); /** * 讀取屬性的值 * * 假設用戶物模型中有OverCurrentEnable這個屬性,并且用戶在控制臺對OverCurrentEnable進行了下發屬性的操作 * 我們下面示例代碼演示如何從中讀取到屬性的值 * * * TODO:用戶需要根據自己的物模型進行適配 */ // ValueWrapper.IntValueWrapper intValue = (ValueWrapper.IntValueWrapper) data.get("OverCurrentEnable"); // if (null != intValue) { // ALog.d(TAG, "收到下行數據 " + intValue.getValue()); // } } /** * 向云端上報數據 * * errorInfo為空,表示接收數據成功,itResResponseCallback.onComplete回調將 * 回復/sys/${productKey}/${deviceName}/thing/service/property/set_reply給云端 * 同時,該回調會再通過/sys/${productKey}/${deviceName}/thing/service/property/post將更新后的屬性上報到云端 * 表示設備端更新該屬性成功 */ 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 { /** * 異步服務下行處理 */ ALog.d(TAG, "用戶根據真實的服務返回服務的值,請參照set示例"); OutputParams outputParams = new OutputParams(); // outputParams.put("op", new ValueWrapper.IntValueWrapper(20)); /** * 設備端接收到服務,并返回響應數據給服務端 */ itResResponseCallback.onComplete(identify, null, outputParams); } } catch (Exception e) { e.printStackTrace(); ALog.d(TAG, "TMP 返回數據格式異常"); } } public void onSuccess(Object o, OutputParams outputParams) { ALog.d(TAG, "onSuccess() called with: o = [" + o + "], outputParams = [" + outputParams + "]"); ALog.d(TAG, "注冊服務成功"); } public void onFail(Object o, ErrorInfo errorInfo) { ALog.d(TAG, "onFail() called with: o = [" + o + "], errorInfo = [" + errorInfo + "]"); ALog.d(TAG, "注冊服務失敗"); } };
同步服務調用(RRPC調用)
先注冊一個下行數據監聽,注冊方法請參見認證與連接中連接狀態與下行消息監聽的
notifyListener
。當云端觸發服務調用時,用戶可以在onNotify收到云端的下行服務調用。
用戶收到云端的下行服務調用后,根據實際服務調用對設備做服務處理,處理之后需要設備回復云端的請求,即發布一個帶有處理結果的請求到云端。
說明當前版本添加了支持使用自定義RRPC,云端的同步服務屬性下行是通過自定義RRPC通道下行,用戶在升級SDK之后要注意這個改動點。
private IConnectNotifyListener connectNotifyListener = new IConnectNotifyListener() { public void onNotify(String connectId, String topic, AMessage aMessage) { ALog.d(TAG, "onNotify() called with: connectId = [" + connectId + "], topic = [" + topic + "], aMessage = [" + printAMessage(aMessage) + "]"); try { if (CONNECT_ID.equals(connectId) && !StringUtils.isEmptyString(topic) && topic.startsWith("/sys/" + productKey + "/" + deviceName + "/rrpc/request")) { ALog.d(TAG, "收到云端系統RRPC下行" + printAMessage(aMessage)); // ALog.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.replace("request", "response"); String resId = topic.substring(topic.indexOf("rrpc/request/") + 13); request.msgId = resId; // TODO 用戶根據實際情況填寫,僅做參考 request.payloadObj = "{\"id\":\"" + resId + "\", \"code\":\"200\"" + ",\"data\":{} }"; // aResponse.data = LinkKit.getInstance().getMqttClient().publish(request, new IConnectSendListener() { public void onResponse(ARequest aRequest, AResponse aResponse) { ALog.d(TAG, "onResponse() called with: aRequest = [" + aRequest + "], aResponse = [" + aResponse + "]"); } public void onFailure(ARequest aRequest, AError aError) { ALog.d(TAG, "onFailure() called with: aRequest = [" + aRequest + "], aError = [" + getError(aError) + "]"); } }); } else if (CONNECT_ID.equals(connectId) && !TextUtils.isEmpty(topic) && topic.startsWith("/ext/rrpc/")) { ALog.d(TAG, "收到云端自定義RRPC下行"); // ALog.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[3]; request.msgId = resId; // TODO 用戶根據實際情況填寫,僅做參考 request.payloadObj = "{\"id\":\"" + resId + "\", \"code\":\"200\"" + ",\"data\":{} }"; // aResponse.data = LinkKit.getInstance().publish(request, new IConnectSendListener() { @Override public void onResponse(ARequest aRequest, AResponse aResponse) { ALog.d(TAG, "onResponse() called with: aRequest = [" + aRequest + "], aResponse = [" + aResponse + "]"); } @Override public void onFailure(ARequest aRequest, AError aError) { ALog.d(TAG, "onFailure() called with: aRequest = [" + aRequest + "], aError = [" + aError + "]"); } }); } } catch (Exception e) { e.printStackTrace(); } } };
自定義模塊
用戶自定義模塊中的服務要先向SDK注冊,才能監聽到相應的回調,以myBlock模塊中的VehDtcService服務為例,需要通過如下方式訂閱:
thing.setServiceHandler("myBlock:VehDtcService", resRequestHandler);
其中resRequestHandler
是物模型報文處理handler
的實例,詳細內容,請參見Java SDK Demo中的TSLActivity.java
。