1. 背景

目前有两类情况可能会导致设备或子系统无法连接至 IoTOS:

  • IoTOS 目前支持 MQTT、CoAP、LwM2M、HTTP 这四种协议,且认证方式要符合 IoTOS 的规定,但很多存量设备或者子系统使用了 TCP\UDP\WS 等协议,且认证方式多种多样,甚至连产品标识(对应 IoTOS 里的 PK)也有缺失;
  • IoTOS 作为物联网中台对南向设备只有 Server 的角色,没有 Client 的角色,但很多子系统往往提供的是 Server,因此在 IoTOS 和子系统之间必须有一个程序充当 Client 从子系统拉取数据并传到 IoTOS。

本工程,即软件网关,作为 IoTOS 的配套组件,以开源形式提供,研发人员可以基于此代码进行二次开发解决以上2类问题。

2. 使用须知

2.1 环境要求

  • JDK 1.8及以上版本
  • Maven
  • Git

2.2 适用场景

软件网关可用于解决以下2类无法连接 IoTOS 的设备或子系统的情况:

  • 基于 TCP\UDP\HTTP 私有协议的设备或子系统;
  • 自带上位机的软硬件一体系统,该类系统可能暴露如 HTTP\TCP\UDP\JDBC\ODBC 等各种接口对外提供数据。

2.3 源码地址

https://gitee.com/geekhekr/iotos-soft-gateway

3. 简要设计说明

下图是软件网关的基本工作原理,包含三个主要环节:

  1. 设备接入环节

    软件网关中内置了 Server 能力,默认支持 TCP、UDP、HTTP 协议的接入。使用者需要自行实现上报数据的拆包/组包(当为 TCP 时)功能。

    同时软件网关也内置了 Client 能力,默认支持向子系统发起 TCP、UDP、HTTP 请求,从而实现与子系统的交互。

    开发者需要自行实现交互逻辑,还可以自行扩展实现更多协议的支持。

  2. 数据转码环节

    此环节需实现设备原始数据格式(即设备或子系统认识的数据格式)和 KLink (IoTOS 内置的标准数据格式,采用 JSON 标准)的互相转换,使用者需要自行实现 encode(原始数据转 KLink) 和 decode(KLink 转原始数据)这两个 interface。

  3. 与 IoTOS 交互环节

    软件网关使用 MQTT 协议实现与 IoTOS 的交互,使用者只需配置相关参数即可。

    IoTOS 与软件网关交互的数据中一定包含 PK 和 devID,若存量设备本身不含 PK 等标识信息,开发者则需自行完成映射。

    例如,子设备发送亮度状态值light为90,软件网关发送给 IoTOS 的数据格式如下:

    {
    "action": "devSend",
    "msgID": 1,
    "PK": "3276aa89d25a46b789c7987421396e05", /* 子设备PK */
    "devID": "dev-001" /* 子设备ID */
    "data": {
     "cmd": "report",
     "params": {
         "light":90
         }
    }
    }
    
参数 必填 类型 说明
action string 动作,固定为 devSend
PK string 要发送数据的设备产品PK
devID string 要发送数据的设备ID
data object 上报的指令和参数数据
data.cmd string 标识符
data.params object 参数

4. 使用说明

4.1 使用流程

使用者操作流程如下(黄色部分是与软件网关相关的步骤):

第一步:注册登录 IoTOS

因 IoTOS 以私有化部署为主,绝大部分情况下开发者可以 superadmin(即超级管理员)登录内网里部署的 IoTOS,本文以 IoTOS 体验站点为例。

第二步:创建软件网关产品及设备

进入产品中心-产品开发,点击“创建产品”,建立软件网关,“产品信息”栏目根据实际需求而定,“节点类型”和“联网与数据”栏目配置图如下:

进入产品中心-设备管理,点击“创建设备”,其中设备 ID 后续会在软件网关代码里使用,取名方法根据实际需求而定。

第三步:创建子设备产品及设备

进入产品中心-产品开发,点击“创建产品”,“产品信息”栏目根据实际需求而定,“节点类型”和“联网与数据”栏目配置图如下:

若使用者要求规范设备 ID,建议进入产品中心-设备管理,点击“批量添加”,使用表格模板实现批量导入。

注:此时软件网关的数据转码环节中子设备 ID 和表格应一一对应。

第四步:查看并记录网关以及子设备信息

进入产品中心-设备管理,点击软件网关的右侧“查看”按钮。

获取到软件网关 PK、设备 ID 和 devSecret。

然后以相同的方式获取到子设备的产品 PK、设备 ID。

第五步:获取并配置网关设备信息

进入 IoTOS -产品中心-产品开发,点击上一步创建的软件网关产品,可以获取到 MQTT 接入方式信息,以此为 HOST 值。

进入项目路径src/main/resources,打开配置文件config.properties进行参数配置。

以下配置项为软件网关配置的必填信息

  ## mqtt配置(必填)
  #### 进入产品中心-产品开发-软件网关,"MQTT接入方式"栏目即可查询
  iotos.host=106.75.50.110:1883
  #### 软件网关的产品pk,进入产品中心-设备管理-软件网关,"产品pk"栏目即可查询
  gateway.pk=fc5dbdd26fee4688a6ab35b63a294cc1
  #### 软件网关的设备id,进入产品中心-设备管理-软件网关,"设备id"栏目即可查询
  gateway.devID=gatewaydemo
  #### 软件网关的设备密钥,进入产品中心-设备管理-软件网关,"devSecret"栏目点击"复制"按钮即可查询
  gateway.devSecret=d10d6a46f6b5462b88f0d07207479bd2

第六步:程序运行

进入项目路径并打开入口程序src/main/java/hekr/me/iotos/softgateway/IoTGatewayApplication.java

以下注释部分分别为HttpClient、HttpServer、TcpClient、TcpServer、UdpClient、UdpServer的入口,使用者可根据实际需求自行打开需要的部分。

  public static void main(String[] args) throws Exception {
      // 获取配置文件中的相关参数
      P.use("config.properties");

      // 软件网关初始化,完成软件网关参数读取、登陆操作
      ProxyService.init();
      // 软件网关对云端下发指令或回复指令进行相应处理的processor注册
      ProxyCallbackService.processorManager.register(new CloudSendProcessor());

      // 若要启用http则将下行注释打开
      //    HttpServerInit.init();

      // 使用http client示例,此处example方法调用的接口在HttpServer中,因此若要启动此示例方法务必也将HttpServerInit启动
      //    Thread.sleep(5000);
      //    HttpClient.example();

      // 若要启用TCP client则将下行注释打开
      //     TcpClientStarter.start();
      // 若要启用TCP server则将下行注释打开
      //        TcpServerStarter.start();
      // 若要启用UDP client则将下行注释打开
      //     UdpClientStarter.start();
      // 若要启用UDP server则将下行注释打开
      //     UdpService.init();
    }

第七步:数据信息上报

本项目调用6种上报的方法 addSub、subLogin、subLogout、subTopo、delSub 以及 devSend,在SubKLink类中定义了上述方法,使用者可根据具体情况自行添加或修改方法。

若开发者需要将存量设备中的设备 ID 和规范化的设备 ID 进行映射,则需要自行代码实现。

方法 需要参数 说明
addSub pk, devId 配置的软件网关添加子设备
subLogin pk, devId 子设备上线
subLogout pk, devId 子设备下线
subTopo 查看软件网关下关联的子设备拓扑
delSub pk, devId 配置的软件网关删除子设备
devSend pk, devId 发送指定数据信息

第八步:IoTOS 收到下发命令并发送给设备

ProxyConnectService类中添加了如下代码,用于软件网关订阅 IoTOS 下发的信息。

client.subscribe(DOWN_TOPIC,0);

示例:首先调用 addSub 方法向网关添加设备,再调用 subTopo 方法查看网关下子设备绑定情况,得到 KLink 返回信息,可以看到成功绑定了一台设备。

- 接收消息主题 : down/dev/fc5dbdd26fee4688a6ab35b63a294cc1/gatewaydemo
- 接收消息Qos : 0
- 接收消息内容 : {  
                  "action":"getTopoResp",
                  "msgId":0,
                  "pk":"fc5dbdd26fee4688a6ab35b63a294cc1",
                  "devId":"gatewaydemo",
                  "code":0,
                  "subs":[{
                    "pk":"3276aa89d25a46b789c7987421396e05",
                    "devId":"dynamic"
                    }]
                }

可以看到软件网关下的设备 PK 和设备 ID,为成功添加至软件网关的子设备信息。

4.2 代码结构说明

4.2.1 网关总体代码结构

如下图所示,网关总体的相关代码在项目中的src/main/java/hekr/me/iotos/softgateway路径下:

其中:

common 包含各种常量以及枚举数据,例如 KLink 相关协议都在此包中;

northProxy为软件网关核心代码部分,负责软件网关与 IoTOS 的连接以及数据的收发;

pluginAsClient包含 HTTP、TCP、UDP 三种协议下软件网关作为客户端启用的代码;

pluginAsSever包含 HTTP、TCP、UDP 三种协议下作为服务端启用的代码;

utils为工具类,提供 json 数据处理、hash 加密等相关工具类,以便于开发者进行二次开发。

config.properties为程序配置文件,开发者可在此配置所有与连接相关的参数。

4.2.2 软件网关时序图结构

以下展示了软件网关分别作为客户端(pluginAsClient)和服务端(pluginAsServer)时的时序图。

作为客户端时:

作为服务端时:

4.2.3 软件网关主要组成

如下图所示,软件网关的相关代码在项目中的src/main/java/hekr/me/iotos/softgateway/northProxy路径下:

其中:

ProxyCallbackService负责 MQTT 回调指令的相应操作;

ProxyConnectService负责软件网关初始化连接等相应操作;

ProxyServer负责软件网关发送指令;

processor包主要由 Processor接口和ProcessorManager组成,开发者需要实现Processor类来自行开发具备操作云端下发的各种指令的功能。上图中的CloudSendProcessor类为 cloudSend 指令操作的示例代码。

注:开发者通常无需关注Proxy*的类,只需要关注processor的实现。

4.2.4 httpClient

如下图所示,httpClient的相关代码在项目中的src/main/java/hekr/me/iotos/softgateway/pluginAsClient/http路径下:

HttpUtils实现了常用 HTTP 请求(GET、POST 请求),以及相关 hearder 和 body 的生成方法。开发者可通过新建HttpUtils对象并调用相应的方法,用以对接提供 HTTP Server 能力的子系统。

HttpClient为示例代码,可供开发者参考。

4.2.5 工具类

如下图所示,httpClient的相关代码在项目中的src/main/java/hekr/me/iotos/softgateway/utils路径下,软件网关内置三个工具类以便于开发者进行二次开发。

JsonUtil负责对象与 json 格式之间得转换,对于转换 KLink 格式有较大的用处。

mapUtils负责将对象转成 map 格式,便于在 HTTP 请求中构建 header 和 body。

parseUtil负责进行 hash 算法及其相关数据格式的转换等功能,主要用于登陆注册校验部分。

4.3 二次开发

软件网关中内置了 Server 能力,当为 TCP Server 时需要开发者实现 TCP 层面的拆包和组包,以避免粘包问题。在TcpServerMsgHandler类中,开发者可实现PacketCodec来进行适配,如LinePacketCodec示例是当设备以换行符作为分割标识的具体代码,开发者可进行参考。

/** 拆包部分 */
@Override
public Packet decode(
    ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext)
    throws AioDecodeException {
  return packetCodec.decode(buffer, limit, position, readableLength, channelContext);
}

/** 组包部分 */
@Override
public ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext) {
  return packetCodec.encode(packet, tioConfig, channelContext);
}

软件网关中内置了 Client 能力,示例代码提供了软件网关和子设备简单的交互逻辑,用户可以根据自己的需求自定义开发。

src/main/java/hekr/me/iotos/softgateway/northProxy/processor包中,开发者需要实现Processor接口来对应 IoTOS 下发的指令,完成其具体功能。

软件网关内置了数据协议转换的能力,即将原始数据根据一定的规则转换成 KLink 形式,开发者需要实现DataCodec中的encodedecode两个方法。其中RawDataCodec是一个具体的示例。假如设备采用了如下的十六进制数据格式:

透传消息类型 类型描述 消息格式 备注
0x00 动态注册 0x00+pk_length(2字节)+pk+devId_length(2字节)+devId+productSecret_length(2字节)+productSecret+0x0a productSecret为产品秘钥
0x01 登录请求 0x01+pk_length(2字节)+pk+devId_length(2字节)+devId+devSecret_length(2字节)+devSecret+0x0a devSecret为设备秘钥
0x02 上行数据报文 0x02+pk_length(2字节)+pk+devId_length(2字节)+devId+data_length(2字节)+data+0x0a
0x03 心跳 0x03+0x0a IoTOS 心跳周期为5分钟,设备需在5分钟内发送心跳报文
0x04 查看拓扑 0x04+0x0a 查看当前软件网关的拓扑情况

示例:

  • 登录请求:
pk:3276aa89d25a46b789c7987421396e05
devId:dynamic
devSecret:8cb192d8bbde469b8cd2b100fe02f042
登录请求编码为:01 0020 3332373661613839643235613436623738396337393837343231333936653035 0007 64796e616d6963 0020 3863623139326438626264653436396238636432623130306665303266303432 0a
  • 上行数据:
pk:3276aa89d25a46b789c7987421396e05
devId:dynamic
业务数据:{"cmd":"reportFrame","params":{"STS":0}}
上行数据报文编码为:02 0020 3332373661613839643235613436623738396337393837343231333936653035 0007 64796e616d6963 0028 7b22636d64223a227265706f72744672616d65222c22706172616d73223a7b22535453223a307d7d 0a

5. 产品测试

测试工具下载地址:

windows端TCP/UDP测试工具下载地址

mac端TCP/UDP测试工具下载地址

5.1 TCP Server测试

使用 TCP 模拟器对于 TCP Server 进行测试,设备登录测试(本地测试,端口设置为7000,0a为结束标志):

通过 IoTOS 接收到回复信息:

从 IoTOS 回复的信息可以看出,设备成功登陆。

上行数据测试:

通过 IoTOS 接受到回复信息:

从回复的信息看出设备成功发送了信息。

5.2 TCP Client测试

首先在配置文件中设置服务器IP以及端口:

## tcp 客户端配置
tcp.client.connect.ip=192.168.5.47
tcp.client.connect.port=7000

使用 TCP 模拟器对于 TCP Client 进行测试,设备登录测试

从日志里查看 IoTOS 的回复信息:

desc":"success","sub":{"pk":"3276aa89d25a46b789c7987421396e05","devId":"dynamic"}}
[Proxy Call: dev:fc5dbdd26fee4688a6ab35b63a294cc1:gatewaydemo] INFO hekr.me.iotos.softgateway.ProxyCallbackService - 接收消息主题 : down/dev/fc5dbdd26fee4688a6ab35b63a294cc1/gatewaydemo
[Proxy Call: dev:fc5dbdd26fee4688a6ab35b63a294cc1:gatewaydemo] INFO hekr.me.iotos.softgateway.ProxyCallbackService - 接收消息Qos : 0
[Proxy Call: dev:fc5dbdd26fee4688a6ab35b63a294cc1:gatewaydemo] INFO hekr.me.iotos.softgateway.ProxyCallbackService - 接收消息内容 : {"action":"devLoginResp","msgId":0,"pk":"3276aa89d25a46b789c7987421396e05","devId":"dynamic","code":0,"desc":"success, "}

从回复信息可以看出网关成功绑定设备并且设备成功上线。

上行数据测试

从日志里查看 IoTOS 的回复信息:

[Proxy Call: dev:fc5dbdd26fee4688a6ab35b63a294cc1:gatewaydemo] INFO hekr.me.iotos.softgateway.ProxyCallbackService - 接收消息主题 : down/dev/fc5dbdd26fee4688a6ab35b63a294cc1/gatewaydemo
[Proxy Call: dev:fc5dbdd26fee4688a6ab35b63a294cc1:gatewaydemo] INFO hekr.me.iotos.softgateway.ProxyCallbackService - 接收消息Qos : 0
[Proxy Call: dev:fc5dbdd26fee4688a6ab35b63a294cc1:gatewaydemo] INFO hekr.me.iotos.softgateway.ProxyCallbackService - 接收消息内容 : {"action":"devSendResp","msgId":1,"pk":"3276aa89d25a46b789c7987421396e05","devId":"dynamic","code":0,"desc":"success"}

从回复的信息看出设备成功发送了信息。

5.3 HTTP Server测试

  • config.properties中将http server端口配置为8070,默认ip配置为"localhost"
## http 服务端配置(按需选填)
http.server.port=8070
  • 在主程序的main方法中开启 HTTP Server

  • HttpController中定义接口
/** 此接口用来配合测试HttpClient 此处模拟"http server的设备"将指令进行编码后发送给客户端 */
  @RequestPath(value = "/test")
  public HttpResponse sendCommand(HttpRequest request) throws Exception {
    // 此处new DevSend()来模拟云端下发
    DevSend devSend = new DevSend();
    devSend.setAction(SubKLinkAction.HEARTBEAT);
    // 编码后发送
    Object resp = dataCodec.encode(devSend);
    HttpResponse ret = Resps.bytes(request, (byte[]) resp, "ok");
    return ret;
  }
  • 运行后使用 postman 进行测试访问

从上图中可以看到测试成功,HTTP Server 成功启动。

附录

以设备接入软件网关,软件网关与 IoTOS 进行数据交互为例,以下为 KLink 形式示例:

  • 设备绑定网关:
{
  "action": "addTopo",
  "msgID": 1,
  "PK": "fc5dbdd26fee4688a6ab35b63a294cc1", /*网关设备PK*/
  "devID": "gatewaydemo", /*网关设备ID*/
  "sub": {
    "PK": "3276aa89d25a46b789c7987421396e05",
     /*设备PK*/
    "devID": "dynamic" /*设备ID*/
  }
}
  • 设备上/下线:
{
  "action": "devLogin",  /* 下线:"action": "devLogout" */
  "msgID": 1,
  "PK": "3276aa89d25a46b789c7987421396e05", /*设备PK*/
  "devID": "dynamic" /*设备ID*/
}
  • 展示网关拓扑关系:
{
  "action": "getTopo",
  "msgID": 1,
  "PK": "fc5dbdd26fee4688a6ab35b63a294cc1", /*网关设备PK*/
  "devID": "gatewaydemo" /*网关设备ID*/
}
  • 若周围烟雾浓度达到预警,烟雾传感器发送告警上报:
{
  "action": "devSend",
  "msgID": 1,
  "PK": "3276aa89d25a46b789c7987421396e05", /*设备PK*/
  "devID": "dynamic" /*设备ID*/
  "data": {
    "cmd": "reportDev",
    "params": {}
  }

密码生成规则

参数 说明 构造方式
username 用户名 {hashMethod}:{random}
password 密码 hash(pk+devId+devSecret+random),加密密钥:devSecret。

【说明】

  • 参数间使用“:”隔开。
  • {hashMethod} 支持:HmacMD5、HmacSHA1、HmacSHA256 和 HmacSHA512。
  • password 中加密使用的 hashMethod、random 需跟 username 中一致。

【参数构造实例】

Eg:pk=’pk123$’,devId=’1001$’,devSecret=’secret123$’,使用 HashMD5方式进行加密,随机字符(random)为’20191108’。

clientId = dev:pk123$:1001$

username = HashMD5:20191108

password = c0608e3abe2f058df1d33020f963dbaf

password 是通过 HashMD5 方法加密后得到。

要加密的字符串:pk123$1001$secret123$20191108,加密密钥:secret123$,加密后得到“c0608e3abe2f058df1d33020f963dbaf”。

【在线工具】

https://tool.oschina.net/encrypt?type=2

【加密内容】

开发者在进行加密时,可以直接使用工具包中utils.ParseUtil工具类,其中的HmacSHA1Encrypt(String encryptText, String encryptKey)方法实现了加密,encryptText为加密内容,encryptKey为加密密钥,计算后返回byte[],开发者可以调用此类下的parseByte2HexStr(byte[] buf)方法将其转换成字符串。如下图所示:

results matching ""

    No results matching ""

    results matching ""

      No results matching ""