概述
子裝置不直接連接配接物聯網平台,而是通過網關接入物聯網平台。首先,需在物聯網平台上建立網關和子裝置;然後,開發網關裝置端SDK,實作網關直連物聯網平台;再由網關向物聯網平台上報網關與子裝置的拓撲關系;通過網關上報子裝置證書(一機一密方式)或者子裝置動态注冊的認證方式,物聯網平台校驗子裝置的身份和該子裝置與網關的拓撲關系。所有校驗通過,才會建立子裝置邏輯通道,并綁定至網關實體通道上,實作子裝置通過網關,與物聯網平台建立連接配接,并進行通信。本文主要如何示範使用JAVA SDK實作相關過程。
關系圖
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLzMDZ2ETYjFzMiRDM2gDZ3EWN0MzYmJ2NllzN3YDMxcDNyQmNjFWO48CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
操作步驟
1、建立網關和子裝置,參考
連結。
2、子裝置産品的物模型定義:
2、pom.xml
<repositories>
<repository>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.aliyun.alink.linksdk</groupId>
<artifactId>iot-linkkit-java</artifactId>
<version>1.2.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.40</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>iot-java-sdk</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
3、Code Sample
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.aliyun.alink.dm.api.BaseInfo;
import com.aliyun.alink.dm.api.DeviceInfo;
import com.aliyun.alink.dm.api.InitResult;
import com.aliyun.alink.dm.api.SignUtils;
import com.aliyun.alink.dm.model.ResponseModel;
import com.aliyun.alink.linkkit.api.ILinkKitConnectListener;
import com.aliyun.alink.linkkit.api.IoTMqttClientConfig;
import com.aliyun.alink.linkkit.api.LinkKit;
import com.aliyun.alink.linkkit.api.LinkKitInitParams;
import com.aliyun.alink.linksdk.channel.gateway.api.subdevice.ISubDeviceActionListener;
import com.aliyun.alink.linksdk.channel.gateway.api.subdevice.ISubDeviceChannel;
import com.aliyun.alink.linksdk.channel.gateway.api.subdevice.ISubDeviceConnectListener;
import com.aliyun.alink.linksdk.cmp.connect.channel.MqttPublishRequest;
import com.aliyun.alink.linksdk.cmp.connect.channel.MqttSubscribeRequest;
import com.aliyun.alink.linksdk.cmp.core.base.AMessage;
import com.aliyun.alink.linksdk.cmp.core.base.ARequest;
import com.aliyun.alink.linksdk.cmp.core.base.AResponse;
import com.aliyun.alink.linksdk.cmp.core.base.ConnectState;
import com.aliyun.alink.linksdk.cmp.core.listener.IConnectNotifyListener;
import com.aliyun.alink.linksdk.cmp.core.listener.IConnectSendListener;
import com.aliyun.alink.linksdk.cmp.core.listener.IConnectSubscribeListener;
import com.aliyun.alink.linksdk.tools.AError;
import com.aliyun.alink.linksdk.tools.ALog;
import java.util.*;
import static com.aliyun.alink.linksdk.tools.ALog.LEVEL_DEBUG;
// 子裝置測試
public class SubDeviceDemo {
// 初始化參數
private static String regionId = "cn-shanghai";
private static final String TAG = "TOPO";
//網關裝置三元組資訊
private static String GWproductKey = "********";
private static String GWdeviceName = "********";
private static String GWdeviceSecret = "********";
public static void main(String[] args) {
LinkKitInitParams params = new LinkKitInitParams();
/**
* 設定 Mqtt 初始化參數
*/
IoTMqttClientConfig config = new IoTMqttClientConfig();
config.productKey = GWproductKey;
config.deviceName = GWdeviceName;
config.deviceSecret = GWdeviceSecret;
config.channelHost = GWproductKey + ".iot-as-mqtt." + regionId + ".aliyuncs.com:1883";
/**
* 是否接受離線消息
* 對應 mqtt 的 cleanSession 字段
*/
config.receiveOfflineMsg = false;
params.mqttClientConfig = config;
ALog.setLevel(LEVEL_DEBUG); // 設定日志列印級别
ALog.i(TAG, "mqtt connetcion info=" + params);
/**
* 設定初始化,傳入裝置證書資訊
*/
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.productKey = GWproductKey;
deviceInfo.deviceName = GWdeviceName;
deviceInfo.deviceSecret = GWdeviceSecret;
params.deviceInfo = deviceInfo;
/**建立連結**/
LinkKit.getInstance().init(params, new ILinkKitConnectListener() {
public void onError(AError aError) {
ALog.e(TAG, "Init Error error=" + aError);
}
public void onInitDone(InitResult initResult) {
ALog.i(TAG, "onInitDone result=" + initResult);
//擷取網關下topo關系,查詢網關與子裝置是否已經存在topo關系
//如果已經存在,則直接上線子裝置,不存在則需要添加網關子裝置
LinkKit.getInstance().getGateway().gatewayGetSubDevices(new IConnectSendListener() {
@Override
public void onResponse(ARequest request, AResponse aResponse) {
ALog.i(TAG, "擷取網關的topo關系成功 : " + JSONObject.toJSONString(aResponse));
// 1、擷取子裝置清單結果
try {
ResponseModel<List<DeviceInfo>> response = JSONObject.parseObject(aResponse.data.toString(), new TypeReference<ResponseModel<List<DeviceInfo>>>() {
}.getType());
// TODO 根據實際應用場景處理
Integer subDeviceCounts = response.data.size();
System.out.println("topo下子裝置的數目:" + subDeviceCounts);// topo網管下子裝置的數目
// // 2、如果topo下面的子裝置數目為0,則通過動态注冊擷取子裝置Secret資訊
// if (subDeviceCounts == 0)
// {
// /**
// * 子裝置動态注冊擷取裝置deviceSecret,如果裝置已知三元組則忽略此步
// * 預注冊裝置時,可以使用裝置的MAC位址或SN序列号等作為DeviceName
// */
// List<BaseInfo> subDevices = new ArrayList<>();
// BaseInfo baseInfo1 = new BaseInfo();
// // 未激活的子裝置的資訊
// baseInfo1.productKey = "********";
// baseInfo1.deviceName = "********";
// subDevices.add(baseInfo1);
//
// LinkKit.getInstance().getGateway().gatewaySubDevicRegister(subDevices, new IConnectSendListener() {
//
// @Override
// public void onResponse(ARequest request, AResponse response) {
// ALog.i(TAG, "子裝置動态注冊成功 : " + JSONObject.toJSONString(response));
//
// // 通過動态注冊擷取的子裝置資訊,供後續添加子裝置到網關裝置使用
// //{\"iotId\":\"********\",\"deviceSecret\":\"********\",\"productKey\":\"********\",\"deviceName\":\"********\"}
// }
// @Override
// public void onFailure(ARequest request, AError error) {
// ALog.i(TAG, "子裝置注冊失敗 : " + JSONObject.toJSONString(error));
// }
// });
// }
// 3、使用子裝置動态注冊擷取完子裝置三元組資訊後,添加到網關裝置
List<BaseInfo> subDevices = new ArrayList<>();
BaseInfo baseInfo2 = new BaseInfo();
// 未激活的子裝置三元組資訊,可以通過上面的注釋代碼擷取
baseInfo2.productKey = "********";
baseInfo2.deviceName = "********";
String deviceSecret = "********";
subDevices.add(baseInfo2);
LinkKit.getInstance().getGateway().gatewayAddSubDevice(baseInfo2, new ISubDeviceConnectListener() {
@Override
public String getSignMethod() {
// 使用的簽名方法
return "hmacsha1";
}
@Override
public String getSignValue() {
// 擷取簽名,使用者使用 deviceSecret 獲得簽名結果
Map<String, String> signMap = new HashMap<>();
signMap.put("productKey", baseInfo2.productKey);
signMap.put("deviceName", baseInfo2.deviceName);
// signMap.put("timestamp", String.valueOf(System.currentTimeMillis()));
signMap.put("clientId", getClientId());
return SignUtils.hmacSign(signMap, deviceSecret);
}
@Override
public String getClientId() {
// clientId 可為任意值
return "id";
}
@Override
public Map<String, Object> getSignExtraData() {
return null;
}
@Override
public void onConnectResult(boolean isSuccess, ISubDeviceChannel iSubDeviceChannel, AError aError) {
// 添加結果
if (isSuccess) {
// 子裝置添加成功,接下來可以做子裝置上線的邏輯
ALog.i(TAG, "topo關系添加成功 : " + JSONObject.toJSONString(iSubDeviceChannel));
// 子裝置添加成功後,代理子裝置上線
LinkKit.getInstance().getGateway().gatewaySubDeviceLogin(baseInfo2, new ISubDeviceActionListener() {
@Override
public void onSuccess() {
System.out.println("代理子裝置上線成功!");
// 基本資訊設定
System.out.println("使用網關的通道執行子裝置的資料上下行");
String topic = "/******/******/user/datapubandsub"; // 子裝置自定義Topic
String sysSubTopic = "/sys/******/******/thing/event/property/post";// 子裝置系統屬性設定Topic
String payLoad = "{\"id\":\"123\",\"method\":\"thing.event.property.post\",\"params\":{\"RoomHumidity\":19},\"version\":\"1.0\"}"; //注意此處和第二步子裝置物模型定義相關
// 釋出系統消息測試
MqttPublishRequest request1 = new MqttPublishRequest();
// // topic 使用者根據實際場景填寫
request1.topic = sysSubTopic;
request1.payloadObj = payLoad;
LinkKit.getInstance().publish(request1, new IConnectSendListener(){
@Override
public void onResponse(ARequest aRequest, AResponse aResponse) {
System.out.println("發送成功");
}
@Override
public void onFailure(ARequest aRequest, AError aError) {
System.out.println("發送失敗");
}
});
// 訂閱
MqttSubscribeRequest request = new MqttSubscribeRequest();
// topic 使用者根據實際場景填寫
request.topic = topic;
request.isSubscribe = true;
// 直接做Topic的訂閱,注意自定義Topic需要使用這種方式,否則會出現訂閱失敗的情況,SDK有Bug,系統Topic沒有問題
LinkKit.getInstance().subscribe(request, new IConnectSubscribeListener(){
@Override
public void onSuccess() {
System.out.println("直接訂閱成功");
}
@Override
public void onFailure(AError aError) {
System.out.println("直接訂閱失敗" + aError.getMsg());
}
});
// 注冊下行監聽
LinkKit.getInstance().registerOnNotifyListener(new IConnectNotifyListener() {
@Override
public void onNotify(String s, String s1, AMessage aMessage) {
System.out.println("下行消息Topic:" + s);
System.out.println("下行消息:" + new String((byte[])aMessage.getData()));
}
@Override
public boolean shouldHandle(String s, String s1) {
return false;
}
@Override
public void onConnectStateChange(String s, ConnectState connectState) {
System.out.println("連接配接狀态發生變化 :" + s + connectState);
}
});
System.out.println("---取消訂閱---");
// 取消訂閱 參考訂閱方法 request.isSubscribe = false; 即可
}
@Override
public void onFailed(AError aError) {
ALog.d(TAG, "onFailed() called with: aError = [" + aError + "]");
System.out.println(aError.getMsg());
}
});
} else {
ALog.i(TAG, "topo關系添加失敗 : " + JSONObject.toJSONString(aError));
}
}
@Override
public void onDataPush(String s, AMessage aMessage) {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onFailure(ARequest request, AError error) {
ALog.i(TAG, "擷取網關的topo關系失敗 : " + JSONObject.toJSONString(error));
}
});
}
});
}
}
說明
1、首先初始化網關裝置,建立連接配接;
2、檢查目前網關裝置拓撲關系,擷取現有子裝置資訊,示例示範網關裝置尚未添加子裝置;
3、動态注冊擷取網關子裝置DeviceSecret,本部分注釋,第一次運作需要取消注釋;
4、添加子裝置三元組資訊到網關裝置;
5、網關代理子裝置進行Topic的訂閱、消息的釋出及下行監聽注冊。
注意
替換網關裝置三元組資訊、子裝置三元組資訊、子裝置Topic資訊。
4、運作結果