天天看點

阿裡雲物聯網平台網關子裝置接入JAVA Sample

概述

子裝置不直接連接配接物聯網平台,而是通過網關接入物聯網平台。首先,需在物聯網平台上建立網關和子裝置;然後,開發網關裝置端SDK,實作網關直連物聯網平台;再由網關向物聯網平台上報網關與子裝置的拓撲關系;通過網關上報子裝置證書(一機一密方式)或者子裝置動态注冊的認證方式,物聯網平台校驗子裝置的身份和該子裝置與網關的拓撲關系。所有校驗通過,才會建立子裝置邏輯通道,并綁定至網關實體通道上,實作子裝置通過網關,與物聯網平台建立連接配接,并進行通信。本文主要如何示範使用JAVA SDK實作相關過程。

關系圖

阿裡雲物聯網平台網關子裝置接入JAVA Sample

操作步驟

1、建立網關和子裝置,參考

連結

2、子裝置産品的物模型定義:

阿裡雲物聯網平台網關子裝置接入JAVA Sample

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、運作結果

阿裡雲物聯網平台網關子裝置接入JAVA Sample

更多參考

裝置安全認證 網關接入物聯網平台 子裝置接入物聯網平台