基于泛化协议SDK,通过桥接服务,您的设备可以接入阿里云物联网平台,与物联网平台通信。本文介绍如何配置泛化协议SDK,实现设备上下线和消息上下行等基础能力。

物联网平台提供泛化协议SDK Demo供您参考,请访问泛化协议SDK Demo GitHub地址查看。

流程图

使用泛化协议SDK,桥接设备与物联网平台的整体流程图如下:



部署开发环境

部署Java SDK开发环境,并添加泛化协议SDK的项目Maven依赖。

<dependency>
  <groupId>com.aliyun.openservices</groupId>
  <artifactId>iot-as-bridge-sdk-core</artifactId>
  <version>2.0.1</version>
</dependency>

初始化

初始化SDK

需要创建一个BridgeBootstrap对象实例,并调用bootstrap方法。泛化协议SDK初始化工作完成后,读取网桥信息,并向云端发起网桥设备上线请求等。

此外,可以在调用bootstrap方法的同时,向泛化协议SDK注册一个DownlinkChannelHandler回调,用于接收云端下行消息。

代码示例:

BridgeBootstrap bridgeBootstrap = new BridgeBootstrap();
bridgeBootstrap.bootstrap(new DownlinkChannelHandler() {
    @Override
    public boolean pushToDevice(Session session, String topic, byte[] payload) {
        //get message from cloud
        String content = new String(bytes);
        log.info("Get DownLink message, session:{}, {}, {}", session, topic, content);
        return true;
    }

    @Override
    public boolean broadcast(String topic, byte[] payload) {
        return false;
    }
});

配置网桥信息

网桥配置默认使用配置文件方式。默认从Java工程默认资源文件路径(一般是src/main/resources/)下的application.conf中读取配置文件,格式支持HOCON(JSON超集)。泛化协议SDK使用typesafe.config解析配置文件。

支持两种网桥配置方法:指定网桥设备和动态注册网桥设备。本文中仅提供指定网桥设备的配置示例;动态注册网桥设备的具体方法,请参见动态创建网桥设备

表 1. 网桥配置参数说明
参数 是否必需 说明
productKey 网桥所属产品的 ProductKey。
deviceName 网桥的DeviceName。
  • 您预先注册了网桥设备,使用指定网桥设备证书信息进行配置,此参数为必需。
  • 若您未预先注册网桥设备,而是根据服务器MAC地址作为设备名称,动态注册网桥设备,则不传入此参数。
deviceSecret 网桥的DeviceSecret。
  • 使用指定网桥设备证书信息进行配置,则需传入此参数。
  • 若您未预先注册网桥设备,而是要动态注册网桥设备,则不传入此参数。
http2Endpoint

HTTP2网关服务地址。 网桥和云端通过HTTP2协议建立长连接通道。 HTTP2网关服务地址的结构为:${productKey}.iot-as-http2.${RegionId}.aliyuncs.com:443

变量${productKey}需替换成您的网桥所属产品的ProductKey。

变量${RegionId}需替换成您的服务所在地域代码。RegionId 的表达方法,请参见通用参考中地域和可用区文档。

如,某用户的网桥设备的productKey为alabcabc123,地域为上海,HTTP2网关服务地址是 alabcabc123.iot-as-http2.cn-shanghai.aliyuncs.com:443

authEndpoint

设备认证服务地址。 设备认证服务地址结构为:https://iot-auth.${RegionId}.aliyuncs.com/auth/bridge

其中,变量${RegionId}需替换成您的服务所在地域代码。RegionId 的表达方法,请参见通用参考中地域和可用区文档。

如,地域为上海,则认证服务地址就是 https://iot-auth.cn-shanghai.aliyuncs.com/auth/bridge

popClientProfile 根据MAC地址动态注册网桥设备时需传入的参数。

具体参数配置,请参见动态创建网桥设备

指定网桥设备证书信息的配置格式如下:

# Server endpoint
http2Endpoint = "https://a1tN7OBmTcd.iot-as-http2.cn-shanghai.aliyuncs.com:443"
authEndpoint = "https://iot-auth.cn-shanghai.aliyuncs.com/auth/bridge"

# Gateway device info, productKey & deviceName & deviceSecret
productKey = ${bridge-ProductKey-in-Iot-Plaform}
deviceName = ${bridge-DeviceName-in-Iot-Plaform}
deviceSecret = ${bridge-DeviceSecret-in-Iot-Plaform}

设备认证并上线

配置设备上线

泛化协议SDK中的设备上线接口:

/**
 * 设备认证
 * @param newSession 设备Session信息,下行回调的时候会将这个Session传递回来
 * @param originalIdentity 设备原始身份标识符
 * @return
 */
public boolean doOnline(Session newSession, String originalIdentity);

设备上线时,需要传Session。下行消息回调时,会把Session回调给网桥。Session中包含设备的原始身份标识符字段,以便网桥判断消息属于哪个设备。

此外,Session中还有一个可选的channel字段,设计上可以用来存放设备的连接信息。比如,您的网桥Server是基于Netty构建的,这里可以存放设备长连接对应的channel对象,消息下行的时候就可以直接从Session中获取channel进行操作。channel的数据类型是Object。泛化协议SDK不会对channel数据做任何处理。您也可以根据使用场景,在channel中存放任何设备相关的信息。

设备上线代码示例:

UplinkChannelHandler uplinkHandler = new UplinkChannelHandler();
//创建Session
Object channel = new Object();
Session session = Session.newInstance(originalIdentity, channel);
//设备上线
boolean success = uplinkHandler.doOnline(session, originalIdentity);
if (success) {
    // 设备上线成功,网桥接受后续设备通信请求
} else {
    // 设备上线失败,网桥可以拒绝后续设备通信请求,如断开连接
}

配置映射设备证书信息

配置设备原始身份标识符和设备证书信息的映射关系。默认使用配置文件方式,默认从Java工程的默认资源文件路径(一般是src/main/resources/)下的devices.conf中读取配置文件,格式支持HOCON(JSON超集)。泛化协议SDK使用typesafe.config解析配置文件。

设备证书信息配置文件内容格式:

${device-originalIdentity} {
  productKey : ${device-ProductKey-in-Iot-Plaform}
  deviceName : ${device-DeviceName-in-Iot-Platform}
  deviceSecret : ${device-DeviceSceret-in-Iot-Platform}
}
参数 是否必需 说明
productKey 设备所属产品的ProductKey。
deviceName 设备的DeviceName。
deviceSecret 设备的DeviceSecret。

设备发送上行数据

泛化协议SDK的设备上报消息接口:

/**
 * 发送设备上行消息,同步调用接口
 * @param originalIdentity 设备原始身份标识符
 * @param protocolMsg 待发送消息(包含Topic、消息体、Qos等信息)
 * @param timeout 超时时间,单位秒
 * @return 超时时间内是否发送成功
 */
boolean doPublish(String originalIdentity, ProtocolMessage protocolMsg, int timeout);
/**
 * 发送设备上行消息,异步调用接口
 * @param originalIdentity 设备原始身份标识符
 * @param protocolMsg 待发送消息(包含Topic、消息体、Qos等信息)
 * @return 调用后立即返回CompletableFuture,调用者可进一步处理该future
 */
CompletableFuture<ProtocolMessage> doPublishAsync(String originalIdentity, 
                                                  ProtocolMessage protocolMsg);

接口调用代码示例:

DeviceIdentity deviceIdentity = 
    ConfigFactory.getDeviceConfigManager().getDeviceIdentity(originalIdentity);
ProtocolMessage protocolMessage = new ProtocolMessage();
protocolMessage.setPayload("Hello world".getBytes());
protocolMessage.setQos(0);
protocolMessage.setTopic(String.format("/%s/%s/update", 
        deviceIdentity.getProductKey(), deviceIdentity.getDeviceName()));
//同步发送
int timeoutSeconds = 3;
boolean success = upLinkHandler.doPublish(originalIdentity, protocolMessage, timeoutSeconds);
//异步发送
upLinkHandler.doPublishAsync(originalIdentity, protocolMessage);

网桥推送下行数据给设备

网桥在调用bootstrap方法时,向泛化协议SDK注册了DownlinkChannelHandler。当有下行消息的时候,泛化协议SDK就会回调DownlinkChannelHandlerpushToDevice方法。可以在pushToDevice中配置网桥处理下行消息。

说明 pushToDevice方法中不要做耗时逻辑,否则会阻塞下行消息接收的线程。如果有耗时或者IO逻辑,比如收到云端下行消息后通过网络长连接发给子设备,请采用异步处理。

代码示例:

private static ExecutorService executorService  = new ThreadPoolExecutor(
    Runtime.getRuntime().availableProcessors(),
    Runtime.getRuntime().availableProcessors() * 2,
    60, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(1000),
    new ThreadFactoryBuilder().setDaemon(true).setNameFormat("bridge-downlink-handle-%d").build(),
    new ThreadPoolExecutor.AbortPolicy());
public static void main(String args[]) {
    //Use application.conf & devices.conf by default
    bridgeBootstrap = new BridgeBootstrap();
    bridgeBootstrap.bootstrap(new DownlinkChannelHandler() {
        @Override
        public boolean pushToDevice(Session session, String topic, byte[] payload) {
            //get message from cloud
            //get downlink message from cloud
            executorService.submit(() -> handleDownLinkMessage(session, topic, payload));
            return true;
        }
        @Override
        public boolean broadcast(String s, byte[] bytes) {
            return false;
        }
    });
}
private static void handleDownLinkMessage(Session session, String topic, byte[] payload) {
    String content = new String(payload);
    log.info("Get DownLink message, session:{}, topic:{}, content:{}", session, topic, content);
    Object channel = session.getChannel();
    String originalIdentity = session.getOriginalIdentity();
    //for example, you can send the message to device via channel, it depends on you specific server implementation
}
参数 说明
Session Session是设备在doOnline的时候传递进来的,可以用来区分下行消息是发给哪个设备的。
topic 下行消息的Topic。
payload

二进制格式的下行消息的消息体数据。

设备下线

设备下线分为两种情况:

  • 网桥与云端之间的连接断开,则所有的设备也会自动从云端离线。
  • 网桥主动向云端上报某个设备下线的消息。

网桥上报设备下线的接口:

/**
 * 向云端上报某个设备下线
 * @param originalIdentity 设备原始标识符
 * @return 是否成功上报
 */
boolean doOffline(String originalIdentity);

调用代码示例:

upLinkHandler.doOffline(originalIdentity);