Commit 4901b676 by fyl

Initial commit

parents
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CheckStyle-IDEA">
<option name="configuration">
<map>
<entry key="checkstyle-version" value="8.7" />
<entry key="copy-libs" value="true" />
<entry key="last-active-plugin-version" value="5.16.3" />
<entry key="location-0" value="BUNDLED:(bundled):Sun Checks" />
<entry key="location-1" value="BUNDLED:(bundled):Google Checks" />
<entry key="scan-before-checkin" value="false" />
<entry key="scanscope" value="JavaOnly" />
<entry key="suppress-errors" value="false" />
</map>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" default="true" />
<component name="SvnConfiguration">
<configuration>C:\Users\Administrator\AppData\Roaming\Subversion</configuration>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Trunk-suizly.iml" filepath="$PROJECT_DIR$/.idea/Trunk-suizly.iml" />
</modules>
</component>
</project>
\ No newline at end of file
# Created by .ignore support plugin (hsz.mobi)
### Example user template template
### Example user template
# IntelliJ project files
.idea
*.iml
out
gen
/.project
/.settings/
FROM hub.senthink.com:8000/senthink/base/java
MAINTAINER LuDash <dash@senthink.com>
WORKDIR /usr/src
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone
RUN mkdir -p /opt/certs jwt/certs conf uploads/firms oom_dump
COPY Slot-web/src/main/resources/keystore.jceks ./jwt/certs/keystore.jceks
COPY logback.xml ./conf/logback.xml
COPY Slot-deploy/target/slot-deploy-fat.jar .
EXPOSE 7100
EXPOSE 7200
EXPOSE 8300
EXPOSE 8400
CMD ["java", "-jar", "/usr/src/slot-deploy-fat.jar"]
\ No newline at end of file
FROM registry.lierda.com/senthink/openjdk:8-jre
MAINTAINER LuDash <dash@senthink.com>
WORKDIR /usr/src
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone
RUN mkdir -p conf uploads/firms jwt/certs
COPY logback.xml ./conf/logback.xml
COPY Slot-fota/target/slot-fota-fat.jar .
COPY Slot-web/src/main/resources/keystore.jceks ./jwt/certs/keystore.jceks
EXPOSE 7200
EXPOSE 8300
CMD ["java", "-jar", "/usr/src/slot-fota-fat.jar"]
\ No newline at end of file
FROM hub.senthink.com:8000/senthink/base/java
MAINTAINER LuDash <dash@senthink.com>
WORKDIR /usr/src
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone
RUN mkdir -p /opt/certs conf
COPY logback.xml ./conf/logback.xml
COPY Slot/target/slot-fat.jar .
EXPOSE 7100
EXPOSE 8400
CMD ["java", "-jar", "/usr/src/slot-fat.jar"]
\ No newline at end of file
FROM hub.senthink.com:8000/senthink/base/java
MAINTAINER LuDash <dash@senthink.com>
WORKDIR /usr/src
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone
RUN mkdir -p conf jwt/certs uploads/firms
COPY logback-fota.xml ./conf/logback.xml
COPY Slot-web/src/main/resources/keystore.jceks ./jwt/certs/keystore.jceks
COPY Slot-fota/target/slot-fota-fat.jar .
EXPOSE 7200
EXPOSE 8300
CMD ["java", "-jar", "/usr/src/slot-fota-fat.jar"]
\ No newline at end of file
FROM hub.senthink.com:8000/senthink/base/java
MAINTAINER LuDash <dash@senthink.com>
WORKDIR /usr/src
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone
RUN mkdir -p conf jwt/certs uploads/firms
COPY Slot-web/src/main/resources/keystore.jceks ./jwt/certs/keystore.jceks
COPY logback.xml ./conf/logback.xml
COPY Slot-web/target/slot-web-fat.jar .
EXPOSE 8300
CMD ["java", "-jar", "/usr/src/slot-web-fat.jar"]
\ No newline at end of file
# IoT平台2.0数据库相关介绍
本文档详细介绍了IoT智能设备接入平台2.0的数据库设计
## 数据库的数据模型设计
### 厂商信息集合 -- openers
openers集合用于存储智能设备硬件厂商用户的基础信息,例如厂商ID、厂商登录信息等。数据库的数据模型设计如下描述:
```
{
"_id": "4CA90DA1", //数据库文档ID,这里用厂商唯一标识ID(openId)作为文档ID
"name": "杭州贤芯科技", //硬件厂商的名称
"openId": "4CA90DA1", //厂商唯一标识ID
"email": "Dash@senthink.com", //厂商用户的邮箱地址:可用于登录或找回密码
"password": "#senthink04%!1", //厂商用户登录密码
"creatAt": "2017-05-25 16:37:14", //厂商用户注册时间
"updateAt": "2017-09-19 19:44:14" //厂商用户信息更新时间
}
```
### 厂商用户密码的验证码集合 -- openers.password.captcha
openers.password.captcha集合用于存储厂商用户找回密码时所使用的验证码。
```
{
"_id": "4CA90DA1", //数据库文档ID,这里用厂商唯一标识ID(openId)作为文档ID
"captcha": "569013", //验证码
"expiredAt": 1505205786150, //验证码过期时间
"updateAt": "2017-09-19 19:44:14" //验证码生成时间
}
```
### 产品信息集合 -- products
```
{
"_id": "4CA90DA0-92AD88F1", //数据库文档ID,这里用厂商唯一标识ID(openId)加产品唯一标识ID作为文档ID
"openId": "4CA90DA1", //产品所属的厂商ID
"name": "贝昂空气净化器产品", //产品名称
"productId": "92AD88F1", //产品唯一标识ID
"productKey": "5CDD2F49E3C5EDF371C61F4AF9BB05FD", //产品的安全密钥(注意保密:用于验证接入设备身份)
"comments": "贝昂空气净化器产品(国外版)", //产品备注信息
"createAt": "2017-05-25 17:17:56", //产品创建时间
"updateAt": "2017-05-25 17:17:56" //产品信息更新时间
}
```
### 产品回收站集合 -- products.recovery
products.recovery集合用于存储用户删除的产品信息,只有当用户删除产品信息并删除了回收站里对应的产品信息,产品信息才会真正被删除且无法恢复。
```
{
"_id": "4CA90DA0-92AD88F1", //数据库文档ID,这里用厂商唯一标识ID(openId)加产品唯一标识ID作为文档ID
"openId": "4CA90DA1", //产品所属的厂商ID
"name": "贝昂空气净化器产品", //产品名称
"productId": "92AD88F1", //产品唯一标识ID
"productKey": "5CDD2F49E3C5EDF371C61F4AF9BB05FD", //产品的安全密钥(注意保密:用于验证接入设备身份)
"createAt": "2017-05-25 17:17:56", //产品创建时间
"updateAt": "2017-05-25 17:17:56", //产品信息更新时间
"deleteAt": "2017-05-25 17:17:56" //产品删除时间
}
```
### 智能设备信息集合 -- nodes
```
{
"_id": "4CA90DA0-92AD88F1-356566071258669", //数据库文档ID,这里用厂商唯一标识ID(openId)、产品唯一标识ID(productId)以及设备唯一标识ID(nodeEui)作为文档ID
"openId": "4CA90DA1", //智能设备所属厂商的ID
"productId": "92AD88F1", //智能设备所属产品的ID
"nodeEui": "356566071258669", //智能设备的唯一标识ID
"otaPlan" : "599e8c4b2366c50674396126", //智能设备使用的OTA升级计划
"connected": false, //智能设备连接连接状态:true-在线;false-离线
"checkSession": true, //智能设备是否设置了定期更新sessionKey:true-定期更新sessionKey;false-不更新sessionKey
"sessionSalt": "998D62187A", //生成sessionKey时使用到的随机盐
"sessionKey": "E2FC73F21B28EA10997D7167887BA0FF", //智能设备的sessionKey:用于对应用数据进行加密解密
"sessionExpiredAt": 1503735739592, //sessionKey过期时间点:如果设置了不更新sessionKey,则此字段不会使用到
"sessionRequestAt": "2017-08-24 16:22:19", //智能设备发起更新sessionKey的时间记录
"registerAt": "2017-08-24 16:22:19", //智能设备注册时间
"lastJoinAt": "2017-08-24 16:22:19", //智能设备最近一次入网时间
"lastHeartbeatAt": "2017-08-24 16:22:19", //智能设备最近一次心跳时间
"lastDisconnectAt": "2017-08-24 16:22:19" //智能设备最近一次离线时间
}
```
### 应用信息集合 -- apps
```
{
"_id": "59719da24d4796455450b82b", //数据库文档ID(这里用appId作为数据库文档ID)
"openId": "4CA90DA1", //应用所属的厂商ID
"productId": "92AD88F1", //应用所属的产品ID
"name": "贤芯科技电动车产品(ZigBee)", //应用名称
"appId": "59719da24d4796455450b82b", //应用唯一ID
"appKey": "F55BE0B8248AB6B206808DA88A161218", //应用的安全密钥(注意保密)
"type": "AMQP", //应用服务器与IoT平台的通信方式:Http、Mqtt或者Amqp
"createAt": "2017-08-16 15:16:51", //应用创建时间
"updateAt": "2017-08-16 15:16:51" //应用信息更新时间
}
```
### OTA升级-固件信息集合 -- ota.firmwares
ota.firmwares集合用于存放智能设备的OTA固件信息。数据库的数据模型设计如下描述:
```
{
"_id" : "59a6898f5908010001b507f7", //数据库文档ID
"openId": "AEC47048", //固件所属的厂商ID
"productId": "C1EB08AC", //固件所属的产品ID
"hardVer": "v1.0.1", //固件所适配的智能设备的硬件版本号
"softVer": "v1.1.3", //固件的版本号
"softSize": 59620, //固件大小,单位字节
"name": "测试固件_v1.1.3", //固件名称
"md5": "9ad6452cd35058f62919d8a63e3561f4", //固件的MD5校验值
"path": "/usr/src/uploads/firmwares/4daba773-b8b8-46af-87d5-9a7ec7cb5fee", //固件在磁盘中的存储位置
"comments": "1. 修复网络连接问题\r\n2. 解决数据连包问题", //固件的备注说明
"createAt": "2017-08-30 17:46:55", //固件添加时间
"updateAt": "2017-08-30 17:46:55" //固件更新时间
}
```
### OTA升级计划信息集合 -- ota.plans
OTA升级计划由用户创建,用于指定OTA升级固件、升级时间段限制等。数据库的数据模型设计如下描述:
```
{
"_id": "599e8c4b2366c50674396126", //数据库文档ID
"openId": "AEC47048", //OTA计划所属的厂商ID
"productId": "C1EB08AC", //OTA计划所属的产品ID
"firmwareId": "59a6898f5908010001b507f7", //OTA计划所使用的固件的ID
"enable": true, //OTA计划是否启用:true-启用;false-禁用
"global": true, //OTA计划是否设置成全局OTA计划:true-设置成全局OTA计划;false-设置成普通OTA计划
"comments": "该OTA升级计划仅用于鲁大师的电动车产品", //OTA升级计划的备注信息
"specifyTime": true, //OTA计划是否设置了设备的OTA升级时间段:true-限制使用该计划的所有设备的OTA升级时间段;false-不限制OTA深根升级时间段
"startTime": "00:00", //OTA升级时间段的起始时间点
"endTime": "22:00", //OTA升级时间段的结束时间点
"createAt": "2017-08-24 16:20:27", //OTA计划创建时间
"updateAt": "2017-08-24 16:20:27" //OTA计划信息更新时间
}
```
### OTA升级-智能设备当前固件版本号记录集合 -- ota.versions
ota.versions集合记录了智能设备最近一次上报的固件版本号信息。数据库的数据模型设计如下描述:
```
{
"_id" : "4CA90DA0-92AD88F1-356566071258669", //数据库文档ID
"openId" : "4CA90DA0", //智能设备所属厂商ID
"productId" : "92AD88F1", //智能设备所属产品ID
"nodeEui" : "356566071258669", //智能设备唯一标识ID
"hardVer" : "v1.0.0", //智能设备的硬件版本号
"softVer" : "v1.1.3", //智能设备当前的固件版本号
"updateAt" : "2017-08-31 13:24:40" //智能设备最近一次上报固件版本号的时间
}
```
### OTA升级-智能设备下载ID信息记录集合 -- ota.downids
ota.downids记录了智能设备的OTA下载固件的过程信息。数据库的数据模型设计如下描述:
```
{
"_id": "4CA90DA0-92AD88F1-356566071258669-A3012040", //数据库文档ID
"openId": "AEC47048", //厂商ID
"productId": "C1EB08AC", //产品ID
"nodeEui": "10000001", //智能设备唯一标识ID
"hardVer": "v1.0.1", //智能设备的硬件版本号
"softVer": "v1.0.0", //智能设备当前的固件版本号
"targetSoftVer": "v1.1.3", //智能设备将要升级的目标固件版本号
"type": "TCP", //智能设备下载固件的方式:TCP或HTTP
"expiredAt": 1504088804023, //本次下载ID的过期时间(过期后,设备将不能使用该下载ID来下载固件文件)
"status": 1, //智能设备的OTA升级状态:
"createAt": "2017-08-30 17:56:44" //OTA下载ID创建时间
}
```
### OTA升级-OTA升级服务器信息集合 -- ota.servers
ota.servers存储了OTA升级服务器信息。数据库的数据模型设计如下描述:
```
{
"_id" : "59a68bccbb3a13358658ea9f", //数据库文档ID
"host" : "10.200.0.206", //OTA升级服务器地址
"port" : 7200, //OTA升级服务器端口
"type" : "TCP" //OTA升级服务器类型:TCP、HTTP
}
```
## 数据库索引
为了提高数据库的读写性能,需要对数据库的每个集合建立必要的索引信息。
\ No newline at end of file
# 贤芯科技IoT智能设备接入平台2.0
## 项目代码: Trunk
## Trunk子模块介绍
### Slot模块(可独立部署)
Slot模块基于"贤芯科技IoT智能设备接入协议v2.0"为智能设备提供接入和安全认证服务。
智能设备(Node)通过贤芯IoT智能设备接入协议接入后,Slot模块会对设备的身份进行安全认证。只有通过认证的设备才能在平台上保持连接以及数据通信。
Slot模块会将智能设备上行的应用数据通过EventBus通道转发给Slot-router模块,由Slot-router模块去推送给客户的应用服务器。
### Slot-router模块(作为Slot模块的一部分服务与Slot模块一起进行打包部署)
Slot-router服务模块提供了消息路由服务。
1. Slot-router服务模块通过EventBus通道接收到Slot模块转发来的上行应用数据后,会根据预先设置的推送规则来选择HTTP、MQTT或者AMQP协议推送到客户的应用服务器;
2. Slot-router服务模块接收应用服务器的下行数据,并通过路由规则下发给指定的智能设备。
### Slot-fota模块(可独立部署)
Slot-fota模块为智能设备提供OTA的TCP断点续传下载固件文件功能。智能设备在Slot服务模块中查询固件更新后,如果有可用固定更新固件,Slot服务会返回Slot-fota服务的地址和端口。智能设备会使用TCP Socket连接到Slot-fota服务,进行TCP断点续传下载固件文件。
### Slot-web模块(可独立部署)
Slot-web模块提供平台管理后台的RESTful API服务。例如创建产品、查看设备状态等API。
### Slot-common模块(公共资源包,不需要部署)
Slot-common模块为Trunk项目的其他子模块提供公共的资源包,如工具类、数据模型等。
### Slot-deploy模块
Slot-deploy模块提供Trunk模块中各个子模块的组合打包部署服务。
\ No newline at end of file
# Created by .ignore support plugin (hsz.mobi)
### Example user template template
### Example user template
# IntelliJ project files
.idea
*.iml
out
gen
/target/
/.classpath
/.project
/.settings/
# Slot-common公共的工具服务模块
Slot-common为Trunk的其他子模块提供通用的工具类服务。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>slot-common</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>Slot-common</name>
<parent>
<groupId>com.senthink</groupId>
<artifactId>Trunk</artifactId>
<version>1.0.0</version>
</parent>
<properties>
<!-- Joda-time -->
<joda-time.version>2.9.9</joda-time.version>
<commons-lang3.version>3.4</commons-lang3.version>
<snakeyaml.version>1.18</snakeyaml.version>
<bcprov-jdk16.version>1.46</bcprov-jdk16.version>
<guice.version>4.1.0</guice.version>
<!-- Logback -->
<slf4j-api.version>1.7.13</slf4j-api.version>
<ch.qos.logback.version>1.1.7</ch.qos.logback.version>
<commons-codec.version>1.7</commons-codec.version>
<protobuf.version>3.3.1</protobuf.version>
<rabbitmq.version>5.0.0</rabbitmq.version>
<paho.version>1.1.1</paho.version>
</properties>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-auth-jwt</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-mongo-client</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-redis-client</artifactId>
</dependency>
<!-- google guice -->
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>${guice.version}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<!-- joda-time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${joda-time.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>${bcprov-jdk16.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${ch.qos.logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${ch.qos.logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>${ch.qos.logback.version}</version>
</dependency>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>${rabbitmq.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>${paho.version}</version>
</dependency>
</dependencies>
<build>
<finalName>slot-common</finalName>
<!--打包时忽略src/main/resources目录下的所有资源文件-->
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<!--<exclude>config.json</exclude>-->
</excludes>
</resource>
</resources>
<plugins>
<!-- We specify the Maven compiler plugin as we need to set it to Java 1.8 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<!--<Main-Class>${main.class}</Main-Class>-->
</manifestEntries>
</transformer>
</transformers>
<artifactSet/>
<outputFile>${project.build.directory}/${project.build.finalName}-fat.jar</outputFile>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package com.senthink.slot.common.cache;
import io.vertx.core.net.NetSocket;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 智能设备的Socket连接信息缓存类
* @Author LuDash
* @Date 2017-5-22 20:59
*/
public class ConnectionCache {
private final static int INIT_CAPACITY = 8192;
/**
* pool1:智能设备的Socket连接信息本地缓存数据库(缓存在内存中)
* <p/>
* 缓存内容:[Key, Value] => [DocumentId, Socket]
* <br/>
* DocumentId:智能设备的数据库文档ID
* <br/>
* Socket:智能设备的TCP Socket链接实例
*/
private final static ConcurrentHashMap<String, NetSocket> pool1 = new ConcurrentHashMap<>(INIT_CAPACITY);
/**
* pool2:智能设备的Socket连接信息本地缓存数据库(缓存在内存中)
* <p/>
* 缓存内容:[Key, Value] => [WriteHandlerID, NodeCache]
* <br/>
* WriteHandlerID:智能设备的TCP Socket链接通道ID
* <br/>
* NodeCache:智能设备的信息缓存实例
*/
private final static ConcurrentHashMap<String, NodeCache> pool2 = new ConcurrentHashMap<>(INIT_CAPACITY);
/**
* 智能设备成功入网后,将智能设备的TCP Socket连接信息缓存到本地内存中
* @param nodeCache 智能设备缓存类的实例
* @param socket 智能设备的Socket链接实例
*/
public static void add(NodeCache nodeCache, NetSocket socket) {
NetSocket s = pool1.put(nodeCache.getDocumentId(), socket);
if (s != null) {
pool2.remove(s.writeHandlerID());
}
pool2.put(socket.writeHandlerID(), nodeCache);
}
/**
* 智能设备掉线 => 删除该设备的所有缓存信息
* @param writeHandlerId 智能设备的TCP Socket通道唯一ID
* @return 智能设备的信息缓存实例(NodeCache)
*/
public static NodeCache remove(String writeHandlerId) {
NodeCache nodeCache = pool2.remove(writeHandlerId);
if (nodeCache != null) {
pool1.remove(nodeCache.getDocumentId());
}
return nodeCache;
}
/**
* 从缓存中获取智能设备的缓存信息
* @param writeHandlerId 智能设备的TCP Socket通道唯一ID
* @return 智能设备的缓存信息类的实例(NodeCache)
*/
public static NodeCache getNodeCache(String writeHandlerId) {
return pool2.get(writeHandlerId);
}
/**
* 从缓存中获取智能设备的缓存信息
* @param documentId
* @return
*/
public static NodeCache getNodeCacheWithDocumentId(String documentId) {
NetSocket socket = getSocket(documentId);
return getNodeCache(socket.writeHandlerID());
}
/**
* 获取缓存中的智能设备的Socket实例
* @param documentId 智能设备的数据库文档ID(DocumentId)
* @return 返回智能设备的TCP Socket实例
*/
public static NetSocket getSocket(String documentId) {
return pool1.get(documentId);
}
/**
* 检查智能设备是否在线
* @param documentId 智能设备的数据库文档ID(DocumentId)
* @return true-设备在线;false-设备离线
*/
public static boolean isConnected(String documentId) {
if (!pool1.containsKey(documentId)) {
return false;
}
NetSocket socket = pool1.get(documentId);
NodeCache nodeCache = getNodeCache(socket.writeHandlerID());
if (nodeCache == null) {
return false;
}
if (nodeCache.getUpdateLocalAt() < System.currentTimeMillis()) {
// 缓存已过期,设备离线 => 删除缓存
remove(socket.writeHandlerID());
return false;
}
return true;
}
public static String getConnectionCount() {
return "pool1: " + pool1.size() + ", pool2: " + pool2.size();
}
public static Set<Map.Entry<String, NodeCache>> getEntrySet() {
return pool2.entrySet();
}
}
package com.senthink.slot.common.cache;
/**
* 智能设备的信息缓存类
* @Author LuDash
* @Date 2017-12-18 10:54
*/
public class NodeCache {
private String documentId; // 智能设备的数据库文档ID
private String sessionKey; // 智能设备的应用数据安全密钥(SessionKey)
private long updateRedisAt = 1L; // updateRedisAt决定了什么时候更新Redis,默认值1
private long updateLocalAt = 60000L; // updateLocalAt为智能设备Socket空闲超时时间点。单位毫秒,默认值60秒
public String getDocumentId() {
return documentId;
}
public NodeCache setDocumentId(String documentId) {
this.documentId = documentId;
return this;
}
public String getSessionKey() {
return sessionKey;
}
public NodeCache setSessionKey(String sessionKey) {
this.sessionKey = sessionKey;
return this;
}
public long getUpdateRedisAt() {
return updateRedisAt;
}
public NodeCache setUpdateRedisAt(long updateRedisAt) {
this.updateRedisAt = updateRedisAt;
return this;
}
public long getUpdateLocalAt() {
return updateLocalAt;
}
public NodeCache setUpdateLocalAt(long updateLocalAt) {
this.updateLocalAt = updateLocalAt;
return this;
}
}
package com.senthink.slot.common.cache;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 产品信息缓存类
* 目的:ProductKey不会更改,所以缓存起来避免设备认证时,每次都查数据库导致数据库查询压力过大
* @Author LuDash
* @Date 2017-12-7 14:17
*/
public class ProductCache {
/**
* pool: 产品的密钥(ProductKey)缓存池
* <p/>
* Key => DocumentId 产品的数据库文档ID; Value => ProductKey-ExpiredAt 产品的密钥-缓存过期时间 (缓存过期后会删除缓存,重新从数据库中加载)
*/
private final static Map<String, String> pool = new ConcurrentHashMap<>();
/**
* CACHE_TIMEOUT: 缓存过期时间,单位分钟。默认30分钟
*/
private final static int CACHE_TIMEOUT = 30;
/**
* 将ProductKey缓存起来
* <p/>
* 特别注意:缓存默认保留30分钟,失效后会重新查询数据库并重新加载到缓存中。
* @param documentId 产品的数据库文档ID
* @param productKey 产品密钥(ProductKey)
*/
public static void add(String documentId, String productKey) {
long expiredAt = System.currentTimeMillis() + CACHE_TIMEOUT*60*1000;
String value = productKey + "-" + expiredAt;
pool.put(documentId, value);
}
/**
* 是否已经缓存了产品密钥(ProductKey)
* @param documentId 产品的数据库文档ID
* @return
*/
public static boolean containsKey(String documentId) {
return pool.containsKey(documentId);
}
/**
* 获取产品密钥
* @param documentId 产品的数据库文档ID
* @return 返回产品密钥(ProductKey-ExpiredAt)
*/
public static String getProductKey(String documentId) {
return pool.get(documentId);
}
/**
* 删除缓存的产品密钥(ProductKey)
* @param documentId 产品的数据库文档ID
*/
public static void remove(String documentId) {
pool.remove(documentId);
}
public static ArrayList<String> getProductCacheCount() {
ArrayList<String> list = new ArrayList<>();
pool.forEach((key, value) -> {
list.add(key + " => " + value);
});
return list;
}
}
package com.senthink.slot.common.constants;
/**
* 所有服务启动所必需的系统环境变量定义
* @Author LuDash
* @Date 2017-6-30 10:37
*/
public interface Environment {
String CONFIG_FROM_ENV = "CONFIG_FROM_ENV";
String WORKER_POOL_SIZE = "WORKER_POOL_SIZE";
String CLUSTER_ENABLE = "CLUSTER_ENABLE";
String MQTT_BROKER = "MQTT_BROKER";
String MQTT_USERNAME = "MQTT_USERNAME";
String MQTT_PASSWORD = "MQTT_PASSWORD";
String MQTT_MAX_INFLIGHT = "MQTT_MAX_INFLIGHT";
String MQTT_SSL_CERT_TYPE = "MQTT_SSL_CERT_TYPE";
String MQTT_SSL_TRUSTSTORE_JKS_FILE = "MQTT_SSL_TRUSTSTORE_JKS_FILE";
String MQTT_SSL_TRUSTSTORE_JKS_FILE_PASS = "MQTT_SSL_TRUSTSTORE_JKS_FILE_PASS";
String MQTT_SSL_KEYSTORE_JKS_FILE = "MQTT_SSL_KEYSTORE_JKS_FILE";
String MQTT_SSL_KEYSTORE_JKS_FILE_PASS = "MQTT_SSL_KEYSTORE_JKS_FILE_PASS";
String MQTT_SSL_CA_PEM_CERT_FILE = "MQTT_SSL_CA_PEM_CERT_FILE";
String MQTT_SSL_CLIENT_PEM_CERT_FILE = "MQTT_SSL_CLIENT_PEM_CERT_FILE";
String MQTT_SSL_CLIENT_PEM_KEY_FILE = "MQTT_SSL_CLIENT_PEM_KEY_FILE";
String MQTT_SSL_CLIENT_PEM_KEY_FILE_PASS = "MQTT_SSL_CLIENT_PEM_KEY_FILE_PASS";
String REDIS_ADDRESS = "REDIS_ADDRESS";
String REDIS_PASSWORD = "REDIS_PASSWORD";
String REDIS_SELECT_INDEX = "REDIS_SELECT_INDEX";
/**
* DEFAULT_WORKER_POOL_SIZE: 允许使用的默认工作者线程数量
*/
int DEFAULT_WORKER_POOL_SIZE = 20;
/**
* DEFAULT_CLUSTER_ENABLE: 默认关闭Slot服务的集群模式
*/
boolean DEFAULT_CLUSTER_ENABLE = false;
/**
* DEFAULT_REDIS_ENABLE: 默认关闭集群模式下的Redis功能。如果需要必须显示设置为true
*/
boolean DEFAULT_REDIS_ENABLE = false;
/**
* DEFAULT_MQTT_BROKER: Slot服务器开启集群模式时,默认连接的MQTT服务器地址
*/
String DEFAULT_MQTT_BROKER = "tcp://localhost:1883";
}
package com.senthink.slot.common.constants;
/**
* MongoDB启动时所需的环境变量名定义类
* @Author LuDash
* @Date 2017/7/1 7:12
*/
public interface MongoEnvironment extends Environment {
String DB_CLUSTER_ENABLE = "DB_CLUSTER_ENABLE";
String DB_CLUSTER_URI = "DB_CLUSTER_URI";
String DB_CLUSTER_REPLICA_SET = "DB_CLUSTER_REPLICA_SET";
String DB_HOST = "DB_HOST";
String DB_PORT = "DB_PORT";
String DB_NAME = "DB_NAME";
String DB_AUTH_SOURCE = "DB_AUTH_SOURCE";
String DB_AUTH_MECHANISM = "DB_AUTH_MECHANISM";
String DB_USERNAME = "DB_USERNAME";
String DB_PASSWORD = "DB_PASSWORD";
String DB_MAX_POOL_SIZE = "DB_MAX_POOL_SIZE";
String DB_MIN_POOL_SIZE = "DB_MIN_POOL_SIZE";
String DB_MAX_WAIT_QUEUE_SIZE = "DB_MAX_WAIT_QUEUE_SIZE";
String DB_MAX_IDLE_TIME_MS = "DB_MAX_IDLE_TIME_MS";
String DB_MAX_LIFE_TIME_MS = "DB_MAX_LIFE_TIME_MS";
String DB_WAIT_QUEUE_TIMEOUT_MS = "DB_WAIT_QUEUE_TIMEOUT_MS";
String DB_MAINTENANCE_INIT_DELAY_MS = "DB_MAINTENANCE_INIT_DELAY_MS";
String DB_GSSAPI_SERVICE_NAME = "DB_GSSAPI_SERVICE_NAME";
/**
* DEFAULT_DB_MIN_POOL_SIZE: MongoDB客户端连接池容量最小值,默认值为10
* 用户没有设置连接池容量则使用默认值
*/
int DEFAULT_DB_MIN_POOL_SIZE = 10;
/**
* DEFAULT_DB_MAX_IDLE_TIME_MS: 连接池中连接空闲时间最大值(如果连接空闲超过此值,连接将被回收),单位ms,默认值为600000
*/
int DEFAULT_DB_MAX_IDLE_TIME_MS = 600*1000;
}
package com.senthink.slot.common.constants;
/**
* Slot服务模块启动时所需的环境变量名定义类
* @Author LuDash
* @Date 2017-6-29 16:26
*/
public interface SlotEnvironment extends MongoEnvironment {
String SLOT_TCP_PORT = "SLOT_TCP_PORT";
String SLOT_TCP_SSL_PORT = "SLOT_TCP_SSL_PORT";
String SLOT_SOCKET_IDLE = "SLOT_SOCKET_IDLE";
String SLOT_SSL_ENABLE = "SLOT_SSL_ENABLE";
String SLOT_SSL_REQUIRE_CLIENT_AUTH = "SLOT_SSL_REQUIRE_CLIENT_AUTH";
String SLOT_SSL_CA_CERT_FILE = "SLOT_SSL_CA_CERT_FILE";
String SLOT_SSL_SERVER_CERT_FILE = "SLOT_SSL_SERVER_CERT_FILE";
String SLOT_SSL_SERVER_KEY_FILE = "SLOT_SSL_SERVER_KEY_FILE";
String SLOT_DEPLOY_INSTANCES = "SLOT_DEPLOY_INSTANCES";
String ROUTER_INSTANCES = "ROUTER_INSTANCES";
String HTTP_ROUTER_ENABLE = "HTTP_ROUTER_ENABLE";
String HTTP_ROUTER_MAX_POOL_SIZE = "HTTP_ROUTER_MAX_POOL_SIZE";
String HTTP_ROUTER_MAX_WAIT_QUEUE_SIZE = "HTTP_ROUTER_MAX_WAIT_QUEUE_SIZE";
String HTTP_ROUTER_CONN_TIMEOUT = "HTTP_ROUTER_CONN_TIMEOUT";
String HTTP_ROUTER_REQUEST_TIMEOUT = "HTTP_ROUTER_REQUEST_TIMEOUT";
String HTTP_ROUTER_IDLE_TIMEOUT = "HTTP_ROUTER_IDLE_TIMEOUT";
String HTTP_ROUTER_DOWNLINK_PORT = "HTTP_ROUTER_DOWNLINK_PORT";
String MQTT_ROUTER_ENABLE = "MQTT_ROUTER_ENABLE";
String MQTT_ROUTER_BROKER = "MQTT_ROUTER_BROKER";
String MQTT_ROUTER_USERNAME = "MQTT_ROUTER_USERNAME";
String MQTT_ROUTER_PASSWORD = "MQTT_ROUTER_PASSWORD";
String MQTT_ROUTER_SSL_CERT_TYPE = "MQTT_ROUTER_SSL_CERT_TYPE";
String MQTT_ROUTER_SSL_TRUSTSTORE_JKS_FILE = "MQTT_ROUTER_SSL_TRUSTSTORE_JKS_FILE";
String MQTT_ROUTER_SSL_TRUSTSTORE_JKS_FILE_PASS = "MQTT_ROUTER_SSL_TRUSTSTORE_JKS_FILE_PASS";
String MQTT_ROUTER_SSL_KEYSTORE_JKS_FILE = "MQTT_ROUTER_SSL_KEYSTORE_JKS_FILE";
String MQTT_ROUTER_SSL_KEYSTORE_JKS_FILE_PASS = "MQTT_ROUTER_SSL_KEYSTORE_JKS_FILE_PASS";
String MQTT_ROUTER_SSL_CA_PEM_CERT_FILE = "MQTT_ROUTER_SSL_CA_PEM_CERT_FILE";
String MQTT_ROUTER_SSL_CLIENT_PEM_CERT_FILE = "MQTT_ROUTER_SSL_CLIENT_PEM_CERT_FILE";
String MQTT_ROUTER_SSL_CLIENT_PEM_KEY_FILE = "MQTT_ROUTER_SSL_CLIENT_PEM_KEY_FILE";
String MQTT_ROUTER_SSL_CLIENT_PEM_KEY_FILE_PASS = "MQTT_ROUTER_SSL_CLIENT_PEM_KEY_FILE_PASS";
String RABBITMQ_ROUTER_ENABLE = "RABBITMQ_ROUTER_ENABLE";
String RABBITMQ_BROKER = "RABBITMQ_BROKER";
String RABBITMQ_USERNAME = "RABBITMQ_USERNAME";
String RABBITMQ_PASSWORD = "RABBITMQ_PASSWORD";
String RABBITMQ_DOWNLINK_EXCHANGE = "RABBITMQ_DOWNLINK_EXCHANGE";
String RABBITMQ_DOWNLINK_QUEUE = "RABBITMQ_DOWNLINK_QUEUE";
String RABBITMQ_DOWNLINK_CONSUMERS = "RABBITMQ_DOWNLINK_CONSUMERS";
String RABBITMQ_ROUTER_SSL_CERT_TYPE = "RABBITMQ_ROUTER_SSL_CERT_TYPE";
String RABBITMQ_ROUTER_SSL_TRUSTSTORE_JKS_FILE = "RABBITMQ_ROUTER_SSL_TRUSTSTORE_JKS_FILE";
String RABBITMQ_ROUTER_SSL_TRUSTSTORE_JKS_FILE_PASS = "RABBITMQ_ROUTER_SSL_TRUSTSTORE_JKS_FILE_PASS";
String RABBITMQ_ROUTER_SSL_KEYSTORE_JKS_FILE = "RABBITMQ_ROUTER_SSL_KEYSTORE_JKS_FILE";
String RABBITMQ_ROUTER_SSL_KEYSTORE_JKS_FILE_PASS = "RABBITMQ_ROUTER_SSL_KEYSTORE_JKS_FILE_PASS";
String RABBITMQ_ROUTER_SSL_CA_PEM_CERT_FILE = "RABBITMQ_ROUTER_SSL_CA_PEM_CERT_FILE";
String RABBITMQ_ROUTER_SSL_CLIENT_PEM_CERT_FILE = "RABBITMQ_ROUTER_SSL_CLIENT_PEM_CERT_FILE";
String RABBITMQ_ROUTER_SSL_CLIENT_PEM_KEY_FILE = "RABBITMQ_ROUTER_SSL_CLIENT_PEM_KEY_FILE";
String RABBITMQ_ROUTER_SSL_CLIENT_PEM_KEY_FILE_PASS = "RABBITMQ_ROUTER_SSL_CLIENT_PEM_KEY_FILE_PASS";
/**
* DEFAULT_TCP_PORT: 默认监听的TCP端口
*/
int DEFAULT_TCP_PORT = 7100;
/**
* DEFAULT_TCP_SSL_PORT: 默认监听的TCP SSL/TLS端口
*/
int DEFAULT_TCP_SSL_PORT = 7101;
/**
* DEFAULT_SOCKET_IDLE: 默认的Socket超时时间,单位秒 => 5分钟
*/
int DEFAULT_SOCKET_IDLE = 300;
/**
* DEFAULT_SLOT_SSL_ENABLE: 默认不启用SSL/TLS
*/
boolean DEFAULT_SLOT_SSL_ENABLE = false;
/**
* DEFAULT_SLOT_SSL_REQUIRE_CLIENT_AUTH: 默认使用SSL/TLS双向认证
*/
boolean DEFAULT_SLOT_SSL_REQUIRE_CLIENT_AUTH = true;
/**
* DEFAULT_SSL_CA_CERT_PATH: 开启SSL/TLS模式时,默认的CA根证书文件的路径
*/
String DEFAULT_SSL_CA_CERT_PATH = "/opt/certs/ca.cert.pem";
/**
* DEFAULT_SSL_SERVER_CERT_PATH: 开启SSL/TLS模式时,默认的服务器证书文件的路径
*/
String DEFAULT_SSL_SERVER_CERT_PATH = "/opt/certs/server.cert.pem";
/**
* DEFAULT_SSL_SERVER_KEY_PATH: 开启SSL/TLS模式时,默认的服务器证书密钥文件的路径
*/
String DEFAULT_SSL_SERVER_KEY_PATH = "/opt/certs/server.key.pem";
/**
* DEFAULT_DEPLOY_INSTANCES: 默认部署Vertx verticle实例数量
*/
int DEFAULT_DEPLOY_INSTANCES = 4;
/**
* SlotRouter
*/
/**
* DEFAULT_ROUTER_HTTP_DOWNLINK_PORT: Http下行路由服务默认监听的Http端口
*/
int DEFAULT_ROUTER_HTTP_DOWNLINK_PORT = 8400;
/**
* DEFAULT_HTTP_ROUTER_MAX_POOL_SIZE: Http路由消息给应用服务器时,建立最大的Http连接池大小
*/
int DEFAULT_HTTP_ROUTER_MAX_POOL_SIZE = 50;
/**
* DEFAULT_HTTP_ROUTER_MAX_WAIT_QUEUE_SIZE: Http路由消息给应用服务器时,Http推送队列允许的最大消息数量
*/
int DEFAULT_HTTP_ROUTER_MAX_WAIT_QUEUE_SIZE = 5000;
/**
* DEFAULT_HTTP_ROUTER_CONN_TIMEOUT: Http路由消息给应用服务器时,Http建立链接超时时间 => 单位毫秒(ms)
*/
int DEFAULT_HTTP_ROUTER_CONN_TIMEOUT = 10000;
/**
* DEFAULT_HTTP_ROUTER_REQUEST_TIMEOUT: Http路由消息给应用服务器时,Http的POST请求超时时间 => 单位毫秒(ms)
*/
int DEFAULT_HTTP_ROUTER_REQUEST_TIMEOUT = 8000;
/**
* DEFAULT_HTTP_ROUTER_IDLE_TIMEOUT: Http路由消息给应用服务器时,Http连接设置了KeepAlive属性,如果超过设定时间没有任何数据交互,该链接将会关闭(回收链接资源)
* 单位秒(s)
*/
int DEFAULT_HTTP_ROUTER_IDLE_TIMEOUT = 300;
/**
* DEFAULT_ROUTER_INSTANCES: 推送客户端实例的个数,默认2
*/
int DEFAULT_ROUTER_INSTANCES = 2;
}
package com.senthink.slot.common.constants;
/**
* SlotFota服务模块启动时所需的环境变量名定义类
* @Author LuDash
* @Date 2017-6-29 16:26
*/
public interface SlotFotaEnvironment extends MongoEnvironment {
String SLOT_FOTA_ENABLE = "SLOT_FOTA_ENABLE";
String SLOT_FOTA_TCP_PORT = "SLOT_FOTA_TCP_PORT";
String SLOT_FOTA_SOCKET_IDLE = "SLOT_FOTA_SOCKET_IDLE";
/**
* DEFAULT_TCP_PORT: 默认监听的TCP端口
*/
int DEFAULT_TCP_PORT = 7200;
/**
* DEFAULT_SOCKET_IDLE: 默认的Socket超时时间 => 180秒
*/
int DEFAULT_SOCKET_IDLE = 180;
}
package com.senthink.slot.common.constants;
/**
* SlotWeb服务模块启动时所需的环境变量名定义类
* @Author LuDash
* @Date 2017-6-29 16:26
*/
public interface SlotWebEnvironment extends MongoEnvironment {
String SLOT_WEB_ENABLE = "SLOT_WEB_ENABLE";
String SLOT_WEB_PORT = "SLOT_WEB_PORT";
String SLOT_WEB_REQUEST_BODY_LIMIT = "SLOT_WEB_REQUEST_BODY_LIMIT";
String SLOT_WEB_TOKEN_TIMEOUT = "SLOT_WEB_TOKEN_TIMEOUT";
String SLOT_WEB_JWT_KEYSTORE_FILE = "SLOT_WEB_JWT_KEYSTORE_FILE";
String SLOT_WEB_JWT_KEYSTORE_PWD = "SLOT_WEB_JWT_KEYSTORE_PWD";
int DEFAULT_SLOT_WEB_PORT = 8300; // SlotWeb服务默认监听的Http端口
int DEFAULT_REQUEST_BODY_LIMIT = 20; // 限制请求body体的最大值,单位MB。默认值为20MB
long DEFAULT_TOKEN_TIMEOUT = 6 * 60L; // token过期时间,单位分钟 -- 默认6小时过期
String UPLOADS_PATH = "/usr/src/uploads/firms"; // 智能设备固件文件存储地址
String DEFAULT_JWT_KEYSTORE_FILE = "/usr/src/jwt/certs/keystore.jceks"; // 默认的Jwt的KeyStore文件路径
String DEFAULT_JWT_KEYSTORE_PWD = "senthink123#"; // 默认的Jwt的KeyStore的密码
}
package com.senthink.slot.common.message;
/**
* Trunk各个子服务模块间的Eventbus地址定义
* @Author LuDash
* @Date 2017-5-22 21:27
*/
public interface EBAddress {
/**
* SLOT: Slot模块监听的EventBus地址
*/
String SLOT = "slot.server.senthink.com";
/**
* SLOT_WEB: Slot-web模块监听的EventBus地址
*/
String SLOT_WEB = "slot.web.senthink.com";
/**
* SLOT_ROUTER: 上行消息路由服务监听的EventBus地址
*/
String SLOT_ROUTER = "slot.router.senthink.com";
/**
* SLOT_FOTA: OTA升级服务(TCP断点续传服务)监听的EventBus地址
*/
String SLOT_FOTA = "slot.fota.senthink.com";
}
package com.senthink.slot.common.message;
/**
* MQTT消息主题定义类
* @Author LuDash
* @Date 2017-11-8 20:52
*/
public interface MqttTopicFilter {
/**
* DEBUG_DOWN_MESSAGE: 在线调试设备的MQTT下行消息主题
*/
String DEBUG_DOWN_MESSAGE = "debug/downMessage";
/**
* DEBUG_UP_MESSAGE: 在线调试设备的MQTT上行消息主题
*/
String DEBUG_UP_MESSAGE = "debug/upMessage";
}
package com.senthink.slot.common.message.decoder;
import com.senthink.slot.common.message.decoder.impl.MessageDecoderImpl;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
/**
* 协议数据帧的粘包、拆包处理器
* @Author LuDash
* @Date 2017-5-22 10:15
*/
public interface MessageDecoder extends Handler<Buffer> {
/**
* 创建解码器
* @param output 解码器解码结果输出
* @return
*/
static MessageDecoder newDecoder(Handler<Buffer> output) {
return MessageDecoderImpl.newDecoder(output);
}
/**
* 解码器的处理器
* @param buffer buffer
*/
void handle(Buffer buffer);
/**
* 异常处理器
* @param handler handler
*/
void exceptionHandler(Handler<Throwable> handler);
}
package com.senthink.slot.common.message.decoder.impl;
import com.senthink.slot.common.message.decoder.MessageDecoder;
import com.senthink.slot.common.message.protocol.*;
import com.senthink.slot.common.message.protocol.enums.CommandEnum;
import com.senthink.slot.common.util.EncodeUtils;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import static com.senthink.slot.common.message.protocol.Frame.FrameFixedLen;
import static com.senthink.slot.common.message.protocol.Frame.PreambleIndex;
/**
* 协议数据帧粘包、拆包处理器的实现类
* @Author LuDash
* @Date 2017-5-22 10:16
*/
public class MessageDecoderImpl implements MessageDecoder {
private Buffer buffer;
private Handler<Buffer> output;
private Handler<Throwable> exceptionHandler;
public MessageDecoderImpl(Handler<Buffer> output) {
this.output = output;
}
public static MessageDecoderImpl newDecoder(Handler<Buffer> output) {
return new MessageDecoderImpl(output);
}
@Override
public void handle(Buffer buffer) {
if (this.buffer == null) {
this.buffer = buffer.copy();
} else {
this.buffer.appendBuffer(buffer);
}
decode();
}
@Override
public void exceptionHandler(Handler<Throwable> handler) {
exceptionHandler = handler;
}
/**
* 开始解码
*/
private void decode() {
if (buffer.length() < FrameFixedLen) {
return;
}
if (!Frame.isPreambleOK(buffer.getByte(PreambleIndex), buffer.getByte(PreambleIndex+1))) {
// exceptionHandler.handle(new Exception("Preamble not ok => " + EncodeUtils.encodeHex(buffer.getBytes())));
buffer = null;
return;
}
CommandEnum commandEnum = Frame.getCommand(buffer.getByte(Frame.HeaderCtrlIndex));
if (commandEnum == null) {
exceptionHandler.handle(new Exception("Command not ok => " + EncodeUtils.encodeHex(buffer.getBytes())));
buffer = null;
return;
}
switch (commandEnum) {
case AUTH_REQUEST:
decodeAuthRequestFrame();
break;
case JOIN_REQUEST:
decodeJoinRequestFrame();
break;
case SESSION_REQUEST:
decodeSessionRequestFrame();
break;
case UPLINK:
decodeUplinkFrame();
break;
case HEARTBEAT:
decodeHeartbeatFrame();
break;
case FIRMWARE_VERSION_REPORT:
decodeFirmwareVersionReportFrame();
break;
case OTA_NOTIFY:
decodeOTANotifyResponseFrame();
break;
case OTA_TCP_DOWNLOAD_REQUEST:
decodeOTATcpDownloadRequestFrame();
break;
case OTA_RESULT_NOTIFY:
decodeOTAResultNotifyFrame();
break;
default:
break;
}
}
/**
* 处理智能设备认证平台身份的数据帧
*/
private void decodeAuthRequestFrame() {
int len = AuthRequestFrame.AuthRequestFrameLen;
if (buffer.length() >= len) {
doDecode(len);
}
}
/**
* 处理智能设备入网请求数据帧
*/
private void decodeJoinRequestFrame() {
if (buffer.length() <= JoinRequestFrame.JoinRequestFrameFixedLen) {
return;
}
int nodeEuiLen = buffer.getUnsignedByte(JoinRequestFrame.NodeEuiLenIndex);
int len = JoinRequestFrame.JoinRequestFrameFixedLen + nodeEuiLen;
if (buffer.length() >= len) {
doDecode(len);
}
}
/**
* 处理智能设备请求会话Key的数据帧
*/
private void decodeSessionRequestFrame() {
int len = SessionRequestFrame.SessionRequestFrameLen;
if (buffer.length() >= len) {
doDecode(len);
}
}
/**
* 处理智能设备的应用数据上行数据帧
*/
private void decodeUplinkFrame() {
if (buffer.length() <= UplinkFrame.PayloadIndex) {
return;
}
int dataLen = buffer.getUnsignedShort(UplinkFrame.DataLenIndex);
int len = UplinkFrame.UplinkFrameFixedLen + dataLen;
if (buffer.length() >= len) {
doDecode(len);
}
}
/**
* 处理智能设备的心跳数据帧
*/
private void decodeHeartbeatFrame() {
int len = HeartbeatFrame.HeartbeatFrameLen;
if (buffer.length() >= len) {
doDecode(len);
}
}
/**
* 处理智能设备上报固件版本号数据帧
*/
private void decodeFirmwareVersionReportFrame() {
if (buffer.length() <= FirmwareVersionReportFrame.HardVerIndex) {
return;
}
int hardVerLen = buffer.getUnsignedByte(FirmwareVersionReportFrame.HardVerLenIndex);
if (buffer.length() <= (FirmwareVersionReportFrame.HardVerIndex+hardVerLen+3)) {
return;
}
int softVerLenIndex = FirmwareVersionReportFrame.HardVerIndex + hardVerLen;
int softVerLen = buffer.getUnsignedByte(softVerLenIndex);
int len = FirmwareVersionReportFrame.ReportFrameFixedLen + hardVerLen + softVerLen;
if (buffer.length() >= len) {
doDecode(len);
}
}
/**
* 处理平台主动推送固件更新消息后,智能设备回复的确认数据帧
*/
private void decodeOTANotifyResponseFrame() {
int len = OTANotifyResponseFrame.OTANotifyResponseFrameLen;
if (buffer.length() >= len) {
doDecode(len);
}
}
/**
* 处理智能设备TCP断点续传下载固件的数据帧
*/
private void decodeOTATcpDownloadRequestFrame() {
int len = OTADownloadRequestFrame.OTADownloadRequestFrameLen;
if (buffer.length() >= len) {
doDecode(len);
}
}
/**
* 处理智能设备OTA升级结果的通知数据帧
*/
private void decodeOTAResultNotifyFrame() {
int len = OTAResultNotifyFrame.OTAResultNotifyFrameLen;
if (buffer.length() >= len) {
doDecode(len);
}
}
/**
* 开始Decode
* @param len
*/
private void doDecode(int len) {
try {
int remain = buffer.length() - len;
if (remain > 0) {
Buffer out = buffer.getBuffer(PreambleIndex, len);
buffer = buffer.getBuffer(len, buffer.length());
output.handle(out);
if (buffer.length() == 0) {
buffer = null;
} else {
decode();
}
} else if (remain == 0) {
output.handle(buffer);
buffer = null;
}
} catch (Exception e) {
exceptionHandler.handle(new Exception("Protocol bytes decoded error => " + EncodeUtils.encodeHex(buffer.getBytes())));
buffer = null;
}
}
}
package com.senthink.slot.common.message.enums;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* IoT平台与应用服务器之间的数据交换格式定义
* @Author LuDash
* @Date 2017-8-30 11:37
*/
public enum MessageFormatEnum {
Json("Json", "Json message"),
ProtoBuf("ProtoBuf", "Google ProtoBuf message");
private String format;
private String info;
private final static Map<String, MessageFormatEnum> pool = new ConcurrentHashMap<>();
private MessageFormatEnum(String value, String info) {
setFormat(value);
setInfo(info);
}
static {
for (MessageFormatEnum formatEnum : MessageFormatEnum.values()) {
pool.put(formatEnum.format, formatEnum);
}
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public static MessageFormatEnum indexByValue(String format) {
return pool.get(format);
}
public static boolean contains(String format) {
return pool.containsKey(format);
}
}
package com.senthink.slot.common.message.enums;
/**
* MQTT发布消息的消息质量定义类
* @Author LuDash
* @Date 2017-10-31 16:50
*/
public enum MqttQosEnum {
/**
* AT_MOST_ONCE: 最多一次
*/
AT_MOST_ONCE(0),
/**
* AT_LEAST_ONCE: 至少一次
*/
AT_LEAST_ONCE(1),
/**
* EXACTLY_ONCE: 只有一次
*/
EXACTLY_ONCE(2);
private int qos;
private MqttQosEnum(int qos) {
setQos(qos);
}
public int getQos() {
return qos;
}
public void setQos(int qos) {
this.qos = qos;
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.util.EncodeUtils;
/**
* 智能设备请求认证平台身份的数据帧(Command为0x01)
* @Author LuDash
* @Date 2017-5-22 13:36
*/
public class AuthRequestFrame extends Frame {
/**
* OpenIdIndex: OpenID字段开始位置的下标
*/
public final static int OpenIdIndex = NonceIndex + NonceLen;
/**
* OpenIdLen: OpenID字段所占字节数
*/
public final static int OpenIdLen = 4;
/**
* ProductIdIndex: ProductID字段开始位置的下标
*/
public final static int ProductIdIndex = OpenIdIndex + OpenIdLen;
/**
* ProductIdLen: ProductID字段所占字节数
*/
public final static int ProductIdLen = 4;
/**
* SignIndex: Sign字段开始位置的下标
*/
public final static int SignIndex = ProductIdIndex + ProductIdLen;
/**
* SignLen: Sign字段所占字节数
*/
public final static int SignLen = 4;
/**
* AuthRequestFrameLen: 智能设备请求认证平台的数据帧长度(固定不变)
*/
public final static int AuthRequestFrameLen = FrameFixedLen + OpenIdLen + ProductIdLen + SignLen;
/**
* 获取请求认证平台身份数据帧中的OpenID
* @param bytes
* @return
*/
public static String getOpenId(byte[] bytes) {
byte[] openId = new byte[OpenIdLen];
System.arraycopy(bytes, OpenIdIndex, openId, 0, OpenIdLen);
return EncodeUtils.encodeHex(openId).toUpperCase();
}
/**
* 获取请求认证平台身份数据帧中的ProductID
* @param bytes
* @return
*/
public static String getProductId(byte[] bytes) {
byte[] productId = new byte[ProductIdLen];
System.arraycopy(bytes, ProductIdIndex, productId, 0, ProductIdLen);
return EncodeUtils.encodeHex(productId).toUpperCase();
}
/**
* 获取请求认证平台身份数据帧中的Sign
* @param bytes
* @return
*/
public static byte[] getSign(byte[] bytes) {
byte[] sign = new byte[SignLen];
System.arraycopy(bytes, SignIndex, sign, 0, SignLen);
return sign;
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.util.RandomUtils;
/**
* 平台回复智能设备的认证请求数据帧(Command为0x01)
* @Author LuDash
* @Date 2017-5-22 13:45
*/
public class AuthResponseFrame extends Frame {
/**
* SignIndex: Sign字段开始位置的下标
*/
public final static int SignIndex = NonceIndex + NonceLen;
/**
* SignLen: Sign字段所占字节数
*/
public final static int SignLen = 4;
/**
* AuthResponseFrameLen: 平台回复智能设备请求认证平台的数据帧长度(固定不变)
*/
public final static int AuthResponseFrameLen = FrameFixedLen + SignLen;
/**
* 生成认证响应数据帧
* @param bytes
* @param sign
* @return
*/
public static byte[] getAuthResponseFrame(byte[] bytes, byte[] sign) {
byte[] rt = new byte[AuthResponseFrameLen];
System.arraycopy(bytes, 0, rt, 0, FrameFixedLen-MICLen);
System.arraycopy(sign, 0, rt, SignIndex, SignLen);
rt[NonceIndex] = (byte) RandomUtils.getRandomInt(256);
setMic(rt);
return rt;
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.message.protocol.enums.CommandEnum;
import com.senthink.slot.common.message.protocol.enums.ModeEnum;
import com.senthink.slot.common.util.HexUtils;
import com.senthink.slot.common.util.RandomUtils;
/**
* 应用数据下行数据帧(Command为0x07)
* @Author LuDash
* @Date 2017-5-22 15:07
*/
public class DownlinkFrame extends Frame {
/**
* DataLenIndex: DataLen字段开始位置的下标
*/
public final static int DataLenIndex = NonceIndex + NonceLen;
/**
* DataLenLen: DataLen字段所占字节数
*/
public final static int DataLenLen = 2;
/**
* PayloadIndex: Payload字段开始位置的下标
*/
public final static int PayloadIndex = DataLenIndex + DataLenLen;
/**
* DownlinkFrameFixedLen: 下行数据固定部分的长度(总长度 = DownlinkFrameFixedLen + Payload的实际长度)
*/
public final static int DownlinkFrameFixedLen = FrameFixedLen + DataLenLen;
/**
* 生成下行数据帧
* @param payload
* @param modeEnum
* @return
*/
public static byte[] getDownlinkFrame(byte[] payload, ModeEnum modeEnum) {
byte[] rt = new byte[DownlinkFrameFixedLen + payload.length];
rt[PreambleIndex] = Preamble[PreambleIndex];
rt[PreambleIndex+1] = Preamble[PreambleIndex+1];
rt[HeaderFlagIndex] = (byte) ((Frame.Protocol_Version << 1) | modeEnum.getValue());
rt[HeaderCtrlIndex] = (byte) (CommandEnum.DOWNLINK.getValue() & (byte) 0x0f);
rt[NonceIndex] = (byte) RandomUtils.getRandomInt(256);
System.arraycopy(HexUtils.convertInt2Hex(payload.length, DataLenLen), 0, rt, DataLenIndex, DataLenLen);
System.arraycopy(payload, 0, rt, PayloadIndex, payload.length);
setMic(rt);
return rt;
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.message.protocol.enums.CommandEnum;
import com.senthink.slot.common.message.protocol.enums.ModeEnum;
import com.senthink.slot.common.util.RandomUtils;
/**
* 平台向智能设备查询固件版本号的数据帧定义类
* @Author LuDash
* @Date 2018-1-16 19:44
*/
public class FirmwareVersionQueryFrame extends Frame {
private final static int FirwareVersionQueryFrameLen = FrameFixedLen; // 平台向智能设备查询固件版本号的数据帧长度,固定长度
/**
* 生成向智能设备查询固件版本号的数据帧
* @return
*/
public static byte[] generateFirmwareVersionQueryFrame() {
byte[] rt = new byte[FirwareVersionQueryFrameLen];
rt[PreambleIndex] = Preamble[PreambleIndex];
rt[PreambleIndex + 1] = Preamble[PreambleIndex + 1];
rt[HeaderFlagIndex] = (byte) ((Protocol_Version << 1) | ModeEnum.NoEncrypt.getValue());
rt[HeaderCtrlIndex] = (byte) (CommandEnum.FIRMWARE_VERSION_QUERY.getValue() & (byte) 0x0f);
rt[NonceIndex] = (byte) RandomUtils.getRandomInt(256);
setMic(rt);
return rt;
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.message.protocol.enums.OTATypeEnum;
/**
* 智能设备上报固件版本号的数据帧(Command为8)
* @Author LuDash
* @Date 2017-7-6 10:08
*/
public class FirmwareVersionReportFrame extends Frame {
public final static int HardVerLenIndex = NonceIndex + NonceLen; // HardVerLen字段开始位置的下标
public final static int HardVerLenLen = 1; // HardVerLen字段所占字节数
public final static int HardVerIndex = HardVerLenIndex + HardVerLenLen; // HardVer字段开始位置的下标
public final static int SoftVerLenLen = 1; // SoftVerLen字段所占字节数
public final static int TypeLen = 1; // Type字段所占字节数
/**
* ReportFrameFixedLen: 上报固件版本号的数据帧固定部分的长度
* 总长度 = FrameFixedLen + HardVer的实际长度 + SoftVer的实际长度
*/
public final static int ReportFrameFixedLen = FrameFixedLen + HardVerLenLen + SoftVerLenLen + TypeLen;
/**
* 获取数据帧中上报的设备硬件版本号HardVer
* @param bytes
* @return
*/
public static String getHardVer(byte[] bytes) {
int hardVerLen = bytes[HardVerLenIndex];
byte[] hardVer = new byte[hardVerLen];
System.arraycopy(bytes, HardVerIndex, hardVer, 0, hardVer.length);
return new String(hardVer);
}
/**
* 获取数据帧中上报的设备固件版本号SoftVer
* @param bytes
* @return
*/
public static String getSoftVer(byte[] bytes) {
int hardVerLen = bytes[HardVerLenIndex];
int softVerLenIndex = HardVerIndex + hardVerLen;
int softVerLen = bytes[softVerLenIndex];
int softVerIndex = softVerLenIndex + SoftVerLenLen;
byte[] softVer = new byte[softVerLen];
System.arraycopy(bytes, softVerIndex, softVer, 0, softVer.length);
return new String(softVer);
}
/**
* 获取数据帧中上报的设备固件升级方式Type
* Type: 1-TCP断点续传方式;2-HTTP方式
* @param bytes
* @return
*/
public static OTATypeEnum getOTAType(byte[] bytes) {
int type = bytes[bytes.length-MICLen-TypeLen];
return OTATypeEnum.getOTATypeEnum(type);
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.util.HexUtils;
import com.senthink.slot.common.message.protocol.enums.CommandEnum;
import com.senthink.slot.common.message.protocol.enums.ModeEnum;
import java.util.zip.CRC32;
/**
* 数据帧格式定义类
* @Author LuDash
* @Date 2017-5-22 11:13
*/
public class Frame {
/**
* Protocol_Version: 智能设备接入协议版本号(目前是v2.0)
*/
public final static int Protocol_Version = 2;
/**
* Preamble: Preamble字段的固定值 FFAA
*/
public final static byte[] Preamble = new byte[] {(byte) 0xFF, (byte) 0xAA};
/**
* PreambleIndex: Preamble字段起始位置的下标
*/
public final static int PreambleIndex = 0;
/**
* PreambleLen: Preamble字段所占字节数
*/
public final static int PreambleLen = 2;
/**
* HeaderFlagIndex: HeaderFlag字段起始位置的下标
*/
public final static int HeaderFlagIndex = PreambleIndex + PreambleLen;
/**
* HeaderFlagLen: HeaderFlag字段所占字节数
*/
public final static int HeaderFlagLen = 1;
/**
* HeaderCtrlIndex: HeaderCtrl字段开始位置的下标
*/
public final static int HeaderCtrlIndex = HeaderFlagIndex + HeaderFlagLen;
/**
* HeaderCtrlLen: HeaderCtrl字段所占字节数
*/
public final static int HeaderCtrlLen = 1;
/**
* NonceIndex: Nonce字段开始位置的下标
*/
public final static int NonceIndex = HeaderCtrlIndex + HeaderCtrlLen;
/**
* NonceLen: Nonce字段所占字节数
*/
public final static int NonceLen = 1;
/**
* MICLen: MIC字段所占字节数
*/
public final static int MICLen = 1;
/**
* HeaderFixedLen: 协议帧Header域的固定部分长度
*/
public final static int HeaderFixedLen = 5;
/**
* FrameFixedLen: 协议数据帧固定部分的字节数(Header除去HeaderOpts部分的长度 + MIC的长度)
*/
public final static int FrameFixedLen = 6;
/**
* 检查协议数据帧的头部Preamble的值是否正确
* @param preamble1
* @param preamble2
* @return
*/
public static boolean isPreambleOK(byte preamble1, byte preamble2) {
return (preamble1 == Preamble[PreambleIndex]) && (preamble2 == Preamble[PreambleIndex+1]);
}
/**
* 检验消息完整性校验码(MIC)是否正确
* @param bytes
* @return
*/
public static boolean isMicOK(byte[] bytes) {
byte[] b = new byte[bytes.length-1];
System.arraycopy(bytes, 0, b, 0, b.length);
byte[] c = crc32(b);
return c[0] == bytes[bytes.length-1];
}
/**
* 计算并设置数据帧的MIC
* @param bytes
* @return
*/
public static void setMic(byte[] bytes) {
byte[] b = new byte[bytes.length-1];
System.arraycopy(bytes, 0, b, 0, b.length);
byte[] c = crc32(b);
bytes[bytes.length-1] = c[0];
}
/**
* 对输入的hex数据计算CRC32的值
* @param bytes
* @return
*/
public static byte[] crc32(byte[] bytes) {
CRC32 c = new CRC32();
c.update(bytes);
long crc32 = c.getValue();
return HexUtils.convertInt2Hex(crc32, 4);
}
/**
* 获取接入协议的协议标识号
* @param bytes
* @return
*/
public static int getVersion(byte[] bytes) {
return (bytes[HeaderFlagIndex] >> 1) & 0xff;
}
/**
* 获取数据是否加密标识:0-不加密;1-加密
* @param bytes
* @return
*/
public static ModeEnum getMode(byte[] bytes) {
return ModeEnum.indexByValue(bytes[HeaderFlagIndex] & 0x01);
}
/**
* 获取数据帧的CommandFlag <br />
* 每条交互指令的CommandFlag含义都不一样,可用于对每条交互指令进行灵活扩展。
* 例如在应用数据上行的指令中,CommandFlag可以提供给应用将应用数据进行分类,使平台可以分类地将应用数据进行推送;这样一来,应用服务器也可以对消息进行分类地消费,提升效率。
* @param bytes 原始数据帧
* @return
*/
public static int getCommandFlag(byte[] bytes) {
return (bytes[Frame.HeaderCtrlIndex] >> 4) & 0x0f;
}
/**
* 获取数据帧的Command
* @param headerCtrl
* @return
*/
public static CommandEnum getCommand(byte headerCtrl) {
Integer command = headerCtrl & ((byte) 0x0f);
return CommandEnum.indexByValue(command);
}
/**
* 获取数据帧中的Nonce
* @param bytes
* @return
*/
public static int getNonce(byte[] bytes) {
return bytes[NonceIndex] & 0xff;
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.message.protocol.enums.CommandEnum;
import com.senthink.slot.common.message.protocol.enums.ModeEnum;
import com.senthink.slot.common.util.RandomUtils;
/**
* 智能设备的心跳数据帧(Command为0x05)
* @Author LuDash
* @Date 2017-5-22 14:22
*/
public class HeartbeatFrame extends Frame {
/**
* HeartbeatFrameLen: 智能设备心跳数据帧长度(固定不变)
*/
public final static int HeartbeatFrameLen = FrameFixedLen;
/**
* 生成心跳数据帧
* @return
*/
public static byte[] getHeartBeatFrame() {
byte[] rt = new byte[HeartbeatFrameLen];
rt[PreambleIndex] = Preamble[PreambleIndex];
rt[PreambleIndex+1] = Preamble[PreambleIndex+1];
rt[HeaderFlagIndex] = (byte) ((Frame.Protocol_Version << 1) | ModeEnum.NoEncrypt.getValue());
rt[HeaderCtrlIndex] = (byte) (CommandEnum.HEARTBEAT.getValue() & (byte) 0x0f);
rt[NonceIndex] = (byte) RandomUtils.getRandomInt(256);
setMic(rt);
return rt;
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.util.AescmacUtils;
import com.senthink.slot.common.util.EncodeUtils;
/**
* 智能设备向Slot服务器请求入网的数据帧(Command为0x02)
* @Author LuDash
* @Date 2017-5-22 13:48
*/
public class JoinRequestFrame extends Frame {
/**
* OpenIdIndex: OpenID字段开始位置的下标
*/
public final static int OpenIdIndex = NonceIndex + NonceLen;
/**
* OpenIdLen: OpenID字段所占字节数
*/
public final static int OpenIdLen = 4;
/**
* ProductIdIndex: ProductID字段开始位置的下标
*/
public final static int ProductIdIndex = OpenIdIndex + OpenIdLen;
/**
* ProductIdLen: ProductID字段所占字节数
*/
public final static int ProductIdLen = 4;
/**
* NodeEuiLenIndex: Len字段开始位置的下标
*/
public final static int NodeEuiLenIndex = ProductIdIndex + ProductIdLen;
/**
* NodeEuiLenLen: Len字段所占字节数
*/
public final static int NodeEuiLenLen = 1;
/**
* NodeEuiIndex: DevEUI字段开始位置的下标
*/
public final static int NodeEuiIndex = NodeEuiLenIndex + NodeEuiLenLen;
/**
* SignLen: Sign字段所占字节数
*/
public final static int SignLen = 4;
/**
* JoinRequestFrameFixedLen: 请求入网数据帧固定部分的长度(总长度 = JoinRequestFrameFixedLen + NodeEui实际长度)
*/
public final static int JoinRequestFrameFixedLen = FrameFixedLen + OpenIdLen + ProductIdLen + NodeEuiLenLen + SignLen;
/**
* 获取请求入网数据帧中的OpenId
* @param bytes
* @return
*/
public static String getOpenId(byte[] bytes) {
byte[] openId = new byte[OpenIdLen];
System.arraycopy(bytes, OpenIdIndex, openId, 0, OpenIdLen);
return EncodeUtils.encodeHex(openId).toUpperCase();
}
/**
* 获取请求入网数据帧中的ProductId
* @param bytes
* @return
*/
public static String getProductId(byte[] bytes) {
byte[] productId = new byte[ProductIdLen];
System.arraycopy(bytes, ProductIdIndex, productId, 0, ProductIdLen);
return EncodeUtils.encodeHex(productId).toUpperCase();
}
/**
* 获取请求入网数据帧中的NodeEui
* @param bytes
* @return
*/
public static String getNodeEui(byte[] bytes) {
int nodeEuiLen = bytes[NodeEuiLenIndex];
byte[] nodeEui = new byte[nodeEuiLen];
System.arraycopy(bytes, NodeEuiIndex, nodeEui, 0, nodeEui.length);
return new String(nodeEui);
}
/**
* 获取请求入网数据帧中的Sign
* @param bytes
* @return
*/
public static String getSign(byte[] bytes) {
byte[] sign = new byte[SignLen];
System.arraycopy(bytes, bytes.length-MICLen-SignLen, sign, 0, SignLen);
return EncodeUtils.encodeHex(sign);
}
/**
* 验证智能设备入网时上传的Sign字段是否合法(合法才允许入网)
* @param sign
* @param bytes
* @param productKey
* @return
*/
public static boolean isSignOK(String sign, byte[] bytes, String productKey) {
byte[] openId = EncodeUtils.decodeHex(getOpenId(bytes));
byte[] productId = EncodeUtils.decodeHex(getProductId(bytes));
byte[] nodeEui = getNodeEui(bytes).getBytes();
byte nonce = bytes[NonceIndex];
byte[] b = new byte[OpenIdLen+ProductIdLen+ nodeEui.length+NonceLen];
System.arraycopy(openId, 0, b, 0, OpenIdLen);
System.arraycopy(productId, 0, b, OpenIdLen, ProductIdLen);
System.arraycopy(nodeEui, 0, b, OpenIdLen+ProductIdLen, nodeEui.length);
b[b.length-1] = nonce;
AescmacUtils.AescmacContext context = new AescmacUtils.AescmacContext().initWithKey(productKey);
byte[] s = AescmacUtils.aesCmac(b, context);
byte[] tmp = new byte[SignLen];
System.arraycopy(s, 0, tmp, 0, SignLen);
return EncodeUtils.encodeHex(tmp).equalsIgnoreCase(sign);
}
}
package com.senthink.slot.common.message.protocol;
/**
* 平台回复智能设备入网请求的数据帧(Command为0x02)
* @Author LuDash
* @Date 2017-5-22 13:57
*/
public class JoinResponseFrame extends Frame {
/**
* SessionIdIndex: SessionId字段开始位置的下标
*/
public final static int SessionIdIndex = NonceIndex + NonceLen;
/**
* SessionIdLen: SessionId字段所占字节数
*/
public final static int SessionIdLen = 4;
/**
* JoinResponseFrameLen: 回复数据帧的长度(固定不变)
*/
public final static int JoinResponseFrameLen = FrameFixedLen + SessionIdLen;
/**
* 生成智能设备入网请求的响应数据帧
* @param bytes 智能设备请求入网的原始数据帧
* @param nonce 平台生成的Nonce
* @param sessionId 平台生成的SessionID
* @return
*/
public static byte[] generateJoinResponseFrame(byte[] bytes, int nonce, byte[] sessionId) {
byte[] rt = new byte[JoinResponseFrameLen];
System.arraycopy(bytes, 0, rt, 0, NonceIndex);
rt[NonceIndex] = (byte) nonce;
System.arraycopy(sessionId, 0, rt, SessionIdIndex, SessionIdLen);
setMic(rt);
return rt;
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.util.EncodeUtils;
import com.senthink.slot.common.util.HexUtils;
/**
* 智能设备使用TCP断点续传方式下载固件文件的请求数据帧(Command为0x0A)
* @Author LuDash
* @Date 2017-7-11 15:07
*/
public class OTADownloadRequestFrame extends Frame {
/**
* DownIdIndex: DownId字段起始位置的下标
*/
public final static int DownIdIndex = NonceIndex + NonceLen;
/**
* DownIdLen: DownId字段所占字节数
*/
public final static int DownIdLen = 4;
/**
* StartIndex: Start字段起始位置的下标
*/
public final static int StartIndex = DownIdIndex + DownIdLen;
/**
* StartLen: Start字段所占字节数
*/
public final static int StartLen = 4;
/**
* EndIndex: End字段起始位置的下标
*/
public final static int EndIndex = StartIndex + StartLen;
/**
* EndLen: End字段所占字节数
*/
public final static int EndLen = 4;
/**
* OTADownloadRequestFrameLen: TCP断点续传请求数据帧的长度(固定不变)
*/
public final static int OTADownloadRequestFrameLen = FrameFixedLen + DownIdLen + StartLen + EndLen;
/**
* 获取请求数据帧中的DownId
* @param bytes
* @return
*/
public static String getDownId(byte[] bytes) {
byte[] downId = new byte[DownIdLen];
System.arraycopy(bytes, DownIdIndex, downId, 0, DownIdLen);
return EncodeUtils.encodeHex(downId).toUpperCase();
}
/**
* 获取请求数据帧中的Start
* @param bytes
* @return
*/
public static int getStart(byte[] bytes) {
byte[] start = new byte[StartLen];
System.arraycopy(bytes, StartIndex, start, 0, StartLen);
return HexUtils.convertHex2UnsignedInt(start);
}
/**
* 获取请求数据帧中的End
* @param bytes
* @return
*/
public static int getEnd(byte[] bytes) {
byte[] end = new byte[EndLen];
System.arraycopy(bytes, EndIndex, end, 0, EndLen);
return HexUtils.convertHex2UnsignedInt(end);
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.util.DigestsUtils;
import com.senthink.slot.common.util.HexUtils;
/**
* 平台回复智能设备使用TCP断点续传方式下载固件文件的请求数据帧(Command为0x0A)
* @Author LuDash
* @Date 2017-7-14 9:42
*/
public class OTADownloadResponseFrame extends Frame {
/**
* MD5Index: MD5字段起始位置的下标
*/
public final static int MD5Index = NonceIndex + NonceLen;
/**
* MD5Len: MD5字段所占字节数
*/
public final static int MD5Len = 16;
/**
* StartIndex: Start字段起始位置的下标
*/
public final static int StartIndex = MD5Index + MD5Len;
/**
* StartLen: Start字段所占字节数
*/
public final static int StartLen = 4;
/**
* EndIndex: End字段起始位置的下标
*/
public final static int EndIndex = StartIndex + StartLen;
/**
* EndLen: End字段所占字节数
*/
public final static int EndLen = 4;
/**
* DataLenIndex: DataLen字段起始位置的下标
*/
public final static int DataLenIndex = EndIndex + EndLen;
/**
* DataLenLen: DataLen字段所占字节数
*/
public final static int DataLenLen = 2;
/**
* PayloadIndex: Payload字段起始位置的下标
*/
public final static int PayloadIndex = DataLenIndex + DataLenLen;
/**
* OTADownloadResponseFrameFixedLen: 回复数据帧固定部分的长度
* 总长度 = OTADownloadResponseFrameFixedLen + Payload的长度(实际固件数据的长度)
*/
public final static int OTADownloadResponseFrameFixedLen = FrameFixedLen + MD5Len + StartLen + EndLen + DataLenLen;
/**
* 生成TCP断点续传请求的响应数据
* @param bytes
* @param payload
* @param start
* @return
*/
public static byte[] getOTADownloadResponseFrame(byte[] bytes, byte[] payload, int start) {
byte[] rt = new byte[OTADownloadResponseFrameFixedLen + payload.length];
System.arraycopy(bytes, 0, rt, 0, HeaderFixedLen);
byte[] md5 = DigestsUtils.md5(payload);
System.arraycopy(md5, 0, rt, MD5Index, MD5Len);
System.arraycopy(HexUtils.convertInt2Hex(start, StartLen), 0, rt, StartIndex, StartLen);
int end = start + payload.length;
System.arraycopy(HexUtils.convertInt2Hex(end, EndLen), 0, rt, EndIndex, EndLen);
System.arraycopy(HexUtils.convertInt2Hex(payload.length, DataLenLen), 0, rt, DataLenIndex, DataLenLen);
System.arraycopy(payload, 0, rt, PayloadIndex, payload.length);
setMic(rt);
return rt;
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.message.protocol.enums.CommandEnum;
import com.senthink.slot.common.message.protocol.enums.ModeEnum;
import com.senthink.slot.common.message.protocol.enums.OTATypeEnum;
import com.senthink.slot.common.mongodb.model.OTAServer;
import com.senthink.slot.common.util.EncodeUtils;
import com.senthink.slot.common.util.HexUtils;
import com.senthink.slot.common.util.RandomUtils;
/**
* 平台主动推送固件更新消息的数据帧
* @Author LuDash
* @Date 2017-7-7 9:31
*/
public class OTANotifyFrame extends Frame {
public final static int TypeIndex = NonceIndex + NonceLen; // Type字段起始位置的下标
public final static int TypeLen = 1; // Type字段所占字节数
public final static int HostLenIndex = TypeIndex + TypeLen; // HostLen字段起始位置的下标
public final static int HostLenLen = 1; // HostLen字段所占字节数
public final static int HostIndex = HostLenIndex + HostLenLen; // Host字段起始位置的下标
public final static int UrlLenIndex = TypeIndex + TypeLen; // UrlLen字段起始位置的下标
public final static int UrlLenLen = 1; // UrlLen字段所占字节数
public final static int UrlIndex = UrlLenIndex + UrlLenLen; // Url字段起始位置的下标
public final static int PortLen = 2; // TCP推送的数据帧中Port字段所占字节数
public final static int SoftVerLenLen = 1; // SoftSizeLen字段所占字节数
public final static int SoftSizeLen = 4; // SoftSize字段所占字节数
public final static int MD5Len = 16; // MD5字段所占字节数
public final static int DownIdLen = 4; // DownID字段所占字节数
public final static int TcpNotifyFrameFixedLen = 35; // TCP推送的数据帧固定数据部分的长度
public final static int HttpNotifyFrameFixedLen = 33; // HTTP推送的数据帧固定数据部分的长度
/**
* OTANotifyFrameFixedLen: 平台主动推送固件升级数据帧的长度(固定不变)
*/
public final static int OTANotifyFrameLen = FrameFixedLen;
/**
* 生成主动推送固件更新消息的数据帧
* @param nonce
* @return
*/
public static byte[] getOTANotifyFrame(int nonce) {
byte[] rt = new byte[OTANotifyFrameLen];
rt[PreambleIndex] = Frame.Preamble[PreambleIndex];
rt[PreambleIndex + 1] = Frame.Preamble[PreambleIndex + 1];
rt[HeaderFlagIndex] = (byte) ((Frame.Protocol_Version << 1) | ModeEnum.NoEncrypt.getValue());
rt[HeaderCtrlIndex] = (byte) (CommandEnum.OTA_NOTIFY.getValue() & (byte) 0x0f);
rt[NonceIndex] = (byte) nonce;
setMic(rt);
return rt;
}
/**
* 生成TCP方式的固件升级推送消息
* @param otaServer OTA升级服务器
* @param softVer 推送的固件版本号
* @param softSize 推送的固件的大小,单位字节
* @param md5 推送的固件的MD5值
* @param downId 推送的固件的下载ID
* @return
*/
public static byte[] generateTcpOTANotifyFrame(OTAServer otaServer, String softVer, long softSize, String md5, String downId) {
byte[] hostBytes = otaServer.getHost().getBytes();
byte[] portBytes = HexUtils.convertInt2Hex(otaServer.getPort(), PortLen);
byte[] softVerBytes = softVer.getBytes();
byte[] softSizeBytes = HexUtils.convertInt2Hex(softSize, SoftSizeLen);
byte[] md5Bytes = EncodeUtils.decodeHex(md5);
byte[] downIdBytes = EncodeUtils.decodeHex(downId);
byte[] rt = new byte[TcpNotifyFrameFixedLen + hostBytes.length + softVerBytes.length];
rt[PreambleIndex] = Preamble[PreambleIndex];
rt[PreambleIndex + 1] = Preamble[PreambleIndex + 1];
rt[HeaderFlagIndex] = (byte) ((Protocol_Version << 1) | ModeEnum.NoEncrypt.getValue());
rt[HeaderCtrlIndex] = (byte) (CommandEnum.OTA_NOTIFY.getValue() & (byte) 0x0f);
rt[NonceIndex] = (byte) RandomUtils.getRandomInt(256);
rt[TypeIndex] = (byte) OTATypeEnum.TCP.getType().intValue();
rt[HostLenIndex] = (byte) hostBytes.length;
System.arraycopy(hostBytes, 0, rt, HostIndex, hostBytes.length);
int portIndex = HostIndex + hostBytes.length;
System.arraycopy(portBytes, 0, rt, portIndex, PortLen);
int softVerLenIndex = portIndex + PortLen;
rt[softVerLenIndex] = (byte) softVerBytes.length;
int softVerIndex = softVerLenIndex + SoftVerLenLen;
System.arraycopy(softVerBytes, 0, rt, softVerIndex, softVerBytes.length);
int softSizeIndex = softVerIndex + softVerBytes.length;
System.arraycopy(softSizeBytes, 0, rt, softSizeIndex, SoftSizeLen);
int md5Index = softSizeIndex + SoftSizeLen;
System.arraycopy(md5Bytes, 0, rt, md5Index, MD5Len);
int downIdIndex = md5Index + MD5Len;
System.arraycopy(downIdBytes, 0, rt, downIdIndex, DownIdLen);
setMic(rt);
return rt;
}
/**
* 生成TCP方式的固件升级推送消息
* @param documentId 智能设备的数据库文档ID
* @param otaServer OTA升级服务器
* @param softVer 推送的固件版本号
* @param softSize 推送的固件的大小,单位字节
* @param md5 推送的固件的MD5值
* @param downId 推送的固件的下载ID
* @return
*/
public static byte[] generateHttpOTANotifyFrame(String documentId, OTAServer otaServer, String softVer, long softSize, String md5, String downId) {
String httpScheme;
if ("HTTPS".equalsIgnoreCase(otaServer.getType())) {
httpScheme = "https://";
} else {
httpScheme = "http://";
}
String[] tmp = documentId.split("-");
String openId = tmp[0];
String productId = tmp[1];
String nodeEui = tmp[2];
String url = httpScheme + otaServer.getHost() + ":" + otaServer.getPort() +
"/ota/v0.1/firm/down/" +
openId + "/" +
productId + "/" +
nodeEui;
byte[] urlBytes = url.getBytes();
byte[] softVerBytes = softVer.getBytes();
byte[] softSizeBytes = HexUtils.convertInt2Hex(softSize, SoftSizeLen);
byte[] md5Bytes = EncodeUtils.decodeHex(md5);
byte[] downIdBytes = EncodeUtils.decodeHex(downId);
byte[] rt = new byte[HttpNotifyFrameFixedLen + urlBytes.length + softVerBytes.length];
rt[PreambleIndex] = Preamble[PreambleIndex];
rt[PreambleIndex + 1] = Preamble[PreambleIndex + 1];
rt[HeaderFlagIndex] = (byte) ((Protocol_Version << 1) | ModeEnum.NoEncrypt.getValue());
rt[HeaderCtrlIndex] = (byte) (CommandEnum.OTA_NOTIFY.getValue() & (byte) 0x0f);
rt[NonceIndex] = (byte) RandomUtils.getRandomInt(256);
rt[TypeIndex] = (byte) OTATypeEnum.HTTP.getType().intValue();
rt[UrlLenIndex] = (byte) urlBytes.length;
System.arraycopy(urlBytes, 0, rt, UrlIndex, urlBytes.length);
int softVerLenIndex = UrlIndex + urlBytes.length;
rt[softVerLenIndex] = (byte) softVerBytes.length;
int softVerIndex = softVerLenIndex + SoftVerLenLen;
System.arraycopy(softVerBytes, 0, rt, softVerIndex, softVerBytes.length);
int softSizeIndex = softVerIndex + softVerBytes.length;
System.arraycopy(softSizeBytes, 0, rt, softSizeIndex, SoftSizeLen);
int md5Index = softSizeIndex + SoftSizeLen;
System.arraycopy(md5Bytes, 0, rt, md5Index, MD5Len);
int downIdIndex = md5Index + MD5Len;
System.arraycopy(downIdBytes, 0, rt, downIdIndex, DownIdLen);
setMic(rt);
return rt;
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.util.EncodeUtils;
/**
* 平台主动推送固件更新消息后,设备回复的消息
* @Author LuDash
* @Date 2017-11-14 10:19
*/
public class OTANotifyResponseFrame extends Frame {
private final static int DownIdIndex = NonceIndex + NonceLen;
private final static int DownIdLen = 4;
/**
* OTANotifyResponseFrameFixedLen: 平台主动推送固件更新消息后,设备回复的消息的长度(固定不变)
*/
public final static int OTANotifyResponseFrameLen = FrameFixedLen + DownIdLen;
/**
* 获取响应数据中的DownID
* @param bytes
* @return
*/
public static String getDownId(byte[] bytes) {
byte[] downId = new byte[DownIdLen];
System.arraycopy(bytes, DownIdIndex, downId, 0, DownIdLen);
return EncodeUtils.encodeHex(downId).toUpperCase();
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.message.protocol.enums.CommandEnum;
import com.senthink.slot.common.message.protocol.enums.ModeEnum;
import com.senthink.slot.common.util.EncodeUtils;
import com.senthink.slot.common.util.HexUtils;
/**
* 智能设备OTA升级结果通知的数据帧
* @Author LuDash
* @Date 2017-7-31 14:23
*/
public class OTAResultNotifyFrame extends Frame {
/**
* DownIdIndex: DownID字段起始位置的下标
*/
public final static int DownIdIndex = NonceIndex + NonceLen;
/**
* DownIdLen: DownID字段所占字节数
*/
public final static int DownIdLen = 4;
/**
* ResultIndex: Result字段起始位置的下标
*/
public final static int ResultIndex = DownIdIndex + DownIdLen;
/**
* ResultLen: Result字段所占字节数
*/
public final static int ResultLen = 1;
/**
* OTAResultNotifyFrameLen: 智能设备OTA升级成功通知的数据帧的长度(固定不变)
*/
public final static int OTAResultNotifyFrameLen = FrameFixedLen + DownIdLen + ResultLen;
/**
* 获取数据帧中的DownId
* @param bytes
* @return
*/
public static String getDownId(byte[] bytes) {
byte[] downId = new byte[DownIdLen];
System.arraycopy(bytes, DownIdIndex, downId, 0, DownIdLen);
return EncodeUtils.encodeHex(downId).toUpperCase();
}
/**
* 获取数据帧中的Result
* @param bytes
* @return
*/
public static Integer getResult(byte[] bytes) {
return HexUtils.convertByteToUnsignedInt(bytes[ResultIndex]);
}
/**
* 生成响应设备的数据帧
* @param nonce 智能设备上报数据帧的nonce
* @return
*/
public static byte[] generateResponseFrame(int nonce) {
byte[] rt = new byte[FrameFixedLen];
rt[PreambleIndex] = Preamble[PreambleIndex];
rt[PreambleIndex + 1] = Preamble[PreambleIndex + 1];
rt[HeaderFlagIndex] = (byte) ((Protocol_Version << 1) | ModeEnum.NoEncrypt.getValue());
rt[HeaderCtrlIndex] = (byte) (CommandEnum.OTA_RESULT_NOTIFY.getValue() & (byte) 0x0f);
rt[NonceIndex] = (byte) nonce;
setMic(rt);
return rt;
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.message.protocol.enums.CommandEnum;
import com.senthink.slot.common.message.protocol.enums.ModeEnum;
/**
* 平台主动向智能设备推送终止本次OTA升级的消息数据帧
* @Author LuDash
* @Date 2017-7-11 16:06
*/
public class OTATerminateNotifyFrame extends Frame {
/**
* OTATerminateNotifyFrameLen: 平台主动向智能设备推送终止本次OTA升级的消息数据帧的长度(固定不变)
*/
public final static int OTATerminateNotifyFrameLen = FrameFixedLen;
/**
* 生成主动推送终止本次OTA升级消息的数据帧
* @param nonce
* @return
*/
public static byte[] getOTATerminateNotifyFrame(int nonce) {
byte[] rt = new byte[OTATerminateNotifyFrameLen];
rt[PreambleIndex] = Preamble[PreambleIndex];
rt[PreambleIndex + 1] = Preamble[PreambleIndex + 1];
rt[HeaderFlagIndex] = (byte) ((Protocol_Version << 1) | ModeEnum.NoEncrypt.getValue());
rt[HeaderCtrlIndex] = (byte) (CommandEnum.OTA_TERMINATE_NOTIFY.getValue() & (byte) 0x0f);
rt[NonceIndex] = (byte) nonce;
setMic(rt);
return rt;
}
}
package com.senthink.slot.common.message.protocol;
import com.senthink.slot.common.util.RandomUtils;
import com.senthink.slot.common.message.protocol.enums.CommandEnum;
import com.senthink.slot.common.message.protocol.enums.ModeEnum;
/**
* 平台通知智能设备会话Key过期的数据帧(Command为0x06)
* @Author LuDash
* @Date 2017-5-22 14:58
*/
public class SessionExpiredNotifyFrame extends Frame {
/**
* SessionExpiredNotifyFrameLen: 平台通知智能设备会话Key过期的数据帧的长度(固定不变)
*/
public final static int SessionExpiredNotifyFrameLen = FrameFixedLen;
/**
* 生成平台通知智能设备会话Key过期的数据帧
* @return
*/
public static byte[] getSessionExpiredNotifyFrame() {
byte[] rt = new byte[SessionExpiredNotifyFrameLen];
rt[PreambleIndex] = Frame.Preamble[PreambleIndex];
rt[PreambleIndex + 1] = Frame.Preamble[PreambleIndex + 1];
rt[HeaderFlagIndex] = (byte) ((Frame.Protocol_Version << 1) | ModeEnum.NoEncrypt.getValue());
rt[HeaderCtrlIndex] = (byte) (CommandEnum.SESSION_EXPIRED_NOTIFY.getValue() & (byte) 0x0f);
rt[NonceIndex] = (byte) RandomUtils.getRandomInt(256);
setMic(rt);
return rt;
}
}
package com.senthink.slot.common.message.protocol;
/**
* 智能设备请求更新会话Key的数据帧(Command为0x03)
* @Author LuDash
* @Date 2017-5-22 14:07
*/
public class SessionRequestFrame extends Frame {
/**
* SessionRequestFrameLen: 请求数据帧的长度(固定不变)
*/
public final static int SessionRequestFrameLen = FrameFixedLen;
}
package com.senthink.slot.common.message.protocol;
/**
* 平台回复智能设备请求更新会话Key的数据帧(Command为0x03)
* @Author LuDash
* @Date 2017-5-22 14:10
*/
public class SessionResponseFrame extends Frame {
/**
* SessionIdIndex: SessionID字段开始位置的下标
*/
public final static int SessionIdIndex = NonceIndex + NonceLen;
/**
* SessionIdLen: SessionID字段所占字节数
*/
public final static int SessionIdLen = 4;
/**
* SessionResponseFrameFixedLen: 回复数据帧的长度(固定不变)
*/
public final static int SessionResponseFrameLen = FrameFixedLen + SessionIdLen;
/**
* 生成智能设备请求会话Key的响应数据帧
* @param bytes
* @param nonce
* @param sessionID
* @return
*/
public static byte[] getSessionResponseFrame(byte[] bytes, int nonce, byte[] sessionID) {
byte[] rt = new byte[SessionResponseFrameLen];
System.arraycopy(bytes, 0, rt, 0, NonceIndex);
rt[NonceIndex] = (byte) nonce;
System.arraycopy(sessionID, 0, rt, SessionIdIndex, SessionIdLen);
setMic(rt);
return rt;
}
}
package com.senthink.slot.common.message.protocol;
/**
* 智能设备应用数据上行数据帧(Command为0x04)
* @Author LuDash
* @Date 2017-5-22 14:12
*/
public class UplinkFrame extends Frame {
/**
* DataLenIndex: DataLen字段开始位置的下标
*/
public final static int DataLenIndex = NonceIndex + NonceLen;
/**
* DataLenLen: DataLen字段所占字节数
*/
public final static int DataLenLen = 2;
/**
* PayloadIndex: Payload字段开始位置的下标
*/
public final static int PayloadIndex = DataLenIndex + DataLenLen;
/**
* UplinkFrameFixedLen: 上行数据固定部分的长度(总长度 = UplinkFrameFixedLen + Payload的实际长度)
*/
public final static int UplinkFrameFixedLen = FrameFixedLen + DataLenLen;
/**
* 获取上行数据帧中的Payload(应用数据)
* @param bytes
* @return
*/
public static byte[] getPayload(byte[] bytes) {
int len = (((bytes[DataLenIndex] & 0x00ff) << 8) & 0xff00) + (bytes[DataLenIndex+1] & 0x00ff);
byte[] payload = new byte[len];
System.arraycopy(bytes, PayloadIndex, payload, 0, payload.length);
return payload;
}
}
package com.senthink.slot.common.message.protocol.enums;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 协议数据帧指令Command的定义
* @Author dash@senthink.com
* @Date 2017-5-22 11:28
*/
public enum CommandEnum {
/************************************
* 以下Command为智能设备主动发起的指令
************************************/
AUTH_REQUEST(1, "AUTH_REQUEST"), // 智能设备请求认证平台的身份
JOIN_REQUEST(2, "JOIN_REQUEST"), // 智能设备请求入网
SESSION_REQUEST(3, "SESSION_REQUEST"), // 智能设备请求更新SessionKey
UPLINK(4, "UPLINK"), // 应用数据上行
HEARTBEAT(5, "HEARTBEAT"), // 智能设备的心跳
FIRMWARE_VERSION_REPORT(8, "FIRMWARE_VERSION_REPORT"), // 智能设备上报固件版本号
OTA_TCP_DOWNLOAD_REQUEST(10, "OTA_TCP_DOWNLOAD_REQUEST"), // 智能设备使用TCP断点续传方式请求下载固件数据
OTA_RESULT_NOTIFY(12, "OTA_RESULT_NOTIFY"), // 智能设备OTA升级结果通知
/***********************************
* 以下Command为IoT平台主动发起的指令
***********************************/
SESSION_EXPIRED_NOTIFY(6, "SESSION_EXPIRED_NOTIFY"), // 智能设备SessionKey过期的通知
DOWNLINK(7, "DOWNLINK"), // 应用数据下行
OTA_NOTIFY(9, "OTA_NOTIFY"), // 平台推送固件更新消息
OTA_TERMINATE_NOTIFY(11, "OTA_TERMINATE_NOTIFY"), // 平台终止本次OTA升级的消息
FIRMWARE_VERSION_QUERY(13, "FIRMWARE_VERSION_QUERY"); // 平台查询智能设备的固件版本号
private CommandEnum(Integer value, String info) {
setValue(value);
setInfo(info);
}
private Integer value;
private String info;
private final static Map<Integer, CommandEnum> pool = new ConcurrentHashMap<>();
static {
for (CommandEnum commandEnum : CommandEnum.values()) {
pool.put(commandEnum.getValue(), commandEnum);
}
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public static CommandEnum indexByValue(Integer value) {
return pool.get(value);
}
}
package com.senthink.slot.common.message.protocol.enums;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 智能设备的应用数据是否加密的类定义
* @Author dash@senthink.com
* @Date 2017-5-22 19:24
*/
public enum ModeEnum {
/**
* NoEncrypt: 上行和下行应用数据不加密进行传输
*/
NoEncrypt(0, "NoEncrypt"),
/**
* Encrypt: 上行和下行应用数据加密进行传输
*/
Encrypt(1, "Encrypt");
private ModeEnum(Integer value, String info) {
setValue(value);
setInfo(info);
}
private Integer value;
private String info;
private final static Map<Integer, ModeEnum> pool = new ConcurrentHashMap<>();
static {
for (ModeEnum modeEnum : ModeEnum.values()) {
pool.put(modeEnum.getValue(), modeEnum);
}
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public static ModeEnum indexByValue(Integer value) {
return pool.get(value);
}
}
package com.senthink.slot.common.message.protocol.enums;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 智能设备上报的OTA升级方式定义
* 目前仅支持两种固件下载方式:1. TCP断点续传方式;2. HTTP方式
* @Author LuDash
* @Date 2017-7-6 10:55
*/
public enum OTATypeEnum {
/**
* TCP: 智能设备通过TCP断点续传方式下载固件文件
*/
TCP(1, "TCP"),
/**
* HTTP: 智能设备通过HTTP方式下载固件文件
*/
HTTP(2, "HTTP");
private Integer type;
private String info;
private final static Map<Integer, OTATypeEnum> pool = new ConcurrentHashMap<>();
private OTATypeEnum(Integer type, String info) {
setType(type);
setInfo(info);
}
static {
for (OTATypeEnum otaTypeEnum : OTATypeEnum.values()) {
pool.put(otaTypeEnum.getType(), otaTypeEnum);
}
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public static OTATypeEnum getOTATypeEnum(Integer type) {
if (type == null) {
return null;
}
return pool.get(type);
}
}
package com.senthink.slot.common.module;
import com.google.inject.AbstractModule;
import com.google.inject.name.Names;
import io.vertx.core.json.JsonObject;
import java.util.Properties;
/**
* 集群模式的依赖注入配置类
* @Author LuDash
* @Date 2017/9/23 12:25
*/
public class ClusterModule extends AbstractModule {
private final JsonObject config;
public ClusterModule(JsonObject config) {
this.config = config;
}
@Override
protected void configure() {
Names.bindProperties(binder(), bindToProperties(this.config));
}
/**
* 将字段属性注入到到配置管理中
* @param config
* @return
*/
private Properties bindToProperties(JsonObject config) {
JsonObject cluster = config.getJsonObject("cluster");
boolean clusterEnable = cluster.getBoolean("enable");
boolean redisEnable = cluster.getJsonObject("redis").getBoolean("enable");
Properties properties = new Properties();
properties.setProperty("clusterEnable", clusterEnable?"true":"false");
properties.setProperty("redisEnable", redisEnable?"true":"false");
return properties;
}
}
package com.senthink.slot.common.module;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.senthink.slot.common.mongodb.repository.*;
import com.senthink.slot.common.mongodb.repository.impl.*;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.mongo.MongoClient;
/**
* MongoDB依赖注入配置类
* @Author LuDash
* @Date 2017-6-28 16:18
*/
public class MongoModule extends AbstractModule {
private final Vertx vertx;
private final JsonObject config;
public MongoModule(Vertx vertx, JsonObject config) {
this.vertx = vertx;
this.config = config;
}
@Override
protected void configure() {
bind(NodeRepository.class).to(NodeRepositoryImpl.class).in(Scopes.SINGLETON);
bind(ProductRepository.class).to(ProductRepositoryImpl.class).in(Scopes.SINGLETON);
bind(ProductRecoveryRepository.class).to(ProductRecoveryRepositoryImpl.class).in(Scopes.SINGLETON);
bind(OpenerRepository.class).to(OpenerRepositoryImpl.class).in(Scopes.SINGLETON);
bind(PasswordCaptchaRepository.class).to(PasswordCaptchaRepositoryImpl.class).in(Scopes.SINGLETON);
bind(AppRepository.class).to(AppRepositoryImpl.class).in(Scopes.SINGLETON);
bind(FirmwareRepository.class).to(FirmwareRepositoryImpl.class).in(Scopes.SINGLETON);
bind(OTAServerRepository.class).to(OTAServerRepositoryImpl.class).in(Scopes.SINGLETON);
bind(DownIdRepository.class).to(DownIdRepositoryImpl.class).in(Scopes.SINGLETON);
bind(OTAVersionRepository.class).to(OTAVersionRepositoryImpl.class).in(Scopes.SINGLETON);
bind(OTAPlanRepository.class).to(OTAPlanRepositoryImpl.class).in(Scopes.SINGLETON);
}
@Provides
public MongoClient mongoClient() {
boolean mongoCluster = config.getJsonObject("mongodb").getBoolean("clusterEnable");
JsonObject mongo;
if (mongoCluster) {
mongo = config.getJsonObject("mongodb").getJsonObject("cluster");
} else {
mongo = config.getJsonObject("mongodb").getJsonObject("noneCluster");
}
return MongoClient.createShared(vertx, mongo);
}
}
package com.senthink.slot.common.module;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.redis.RedisClient;
import io.vertx.redis.RedisOptions;
/**
* Redis依赖注入配置类
* @Author LuDash
* @Date 2017/9/23 11:22
*/
public class RedisModule extends AbstractModule {
private final Vertx vertx;
private final JsonObject config;
public RedisModule(Vertx vertx, JsonObject config) {
this.vertx = vertx;
this.config = config;
}
@Override
protected void configure() {}
@Provides
public RedisClient redisClient() {
boolean cluster = config.getJsonObject("cluster").getBoolean("enable");
if (cluster) {
JsonObject redis = config.getJsonObject("cluster").getJsonObject("redis");
boolean redisEnable = redis.getBoolean("enable");
if (redisEnable) {
String address = redis.getString("address"); //Redis地址:IP(域名) + 端口
String host = address.split(":")[0];
int port = Integer.valueOf(address.split(":")[1]);
RedisOptions options = new RedisOptions()
.setHost(host)
.setPort(port)
.setSelect(redis.getInteger("select"));
if (redis.getString("password") != null) {
options.setAuth(redis.getString("password"));
}
return RedisClient.create(vertx, options);
} else {
return RedisClient.create(vertx);
}
} else {
return RedisClient.create(vertx);
}
}
}
package com.senthink.slot.common.mongodb;
/**
* MongoDB数据库集合名称定义
* @Author LuDash
* @Date 2017-5-25 16:30
*/
public interface Collections {
/**
* OPENERS: 智能设备所属厂商的数据库集合名称
*/
String OPENERS = "openers";
/**
* OPENERS_PASSWORD_CAPTCHA: 智能设备厂商用户找回密码的验证码数据库集合名称
*/
String OPENERS_PASSWORD_CAPTCHA = "openers.password.captcha";
/**
* PRODUCTS: 智能设备所属产品的数据库集合名称
*/
String PRODUCTS = "products";
/**
* PRODUCTS_RECOVERY: 已被删除的产品的数据库集合名称
*/
String PRODUCTS_RECOVERY = "products.recovery";
/**
* NODES: 智能设备的数据库集合名称
*/
String NODES = "nodes";
/**
* APPS: 应用的数据库集合名称
*/
String APPS = "apps";
/**
* FIRMWARES: 智能设备的固件信息的数据库集合名称
*/
String FIRMWARES = "ota.firmwares";
/**
* OTA_SERVERS: OTA升级服务器信息的数据库集合名称
*/
String OTA_SERVERS = "ota.servers";
/**
* OTA_DOWNID: OTA升级的DownID记录信息的数据库集合名称
*/
String OTA_DOWNID = "ota.downids";
/**
* OTA_VERSIONS: OTA查询固件更新时,设备上报的固件版本号信息的数据库集合名称
*/
String OTA_VERSIONS = "ota.versions";
/**
* OTA_PLANS: OTA升级计划信息的数据库集合名称
*/
String OTA_PLANS = "ota.plans";
}
package com.senthink.slot.common.mongodb;
/**
* MongoDB的Operator定义类
* @Author LuDash
* @Date 2017-8-1 14:08
*/
public interface MongoOperator {
/**
* $set: The $set operator replaces the value of a field with the specified value.
*/
String SET = "$set";
/**
* $unset: The $unset operator deletes a particular field.
*/
String UNSET = "$unset";
/**
* REGEX: The $regex operator support regex expression
*/
String REGEX = "$regex";
/**
* OPTIONS: The $options operator
*/
String OPTIONS = "$options";
}
package com.senthink.slot.common.mongodb;
/**
* MongoDB的RunCommand定义类
* @Author LuDash
* @Date 2017-8-1 14:06
*/
public interface MongoRunCommand {
/***
* update:
* The update command modifies documents in a collection.
* A single update command can contain multiple update statements.
* The update methods provided by the MongoDB drivers use this command internally.
*/
String Update = "update";
}
package com.senthink.slot.common.mongodb.enums;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 应用服务器通信类型定义
* @Author LuDash
* @Date 2017-8-16 15:36
*/
public enum AppTypeEnum {
HTTP("HTTP", "HTTP"),
MQTT("MQTT", "MQTT"),
RabbitMQ("RabbitMQ", "RabbitMQ");
private String type;
private String info;
private final static Map<String, AppTypeEnum> pool = new ConcurrentHashMap<>();
private AppTypeEnum(String type, String info) {
setType(type);
setInfo(info);
}
static {
for (AppTypeEnum typeEnum : AppTypeEnum.values()) {
pool.put(typeEnum.type, typeEnum);
}
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public static AppTypeEnum getAppTypeEnum(String type) {
return pool.get(type);
}
}
package com.senthink.slot.common.mongodb.enums;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* OTA推送事件状态定义类
* @Author LuDash
* @Date 2017-11-14 9:29
*/
public enum OTANotifyEnum {
/**
* CREATED: OTA推送事件--已创建推送(未推送成功)
* 注意:只有状态为CREATED时,平台才会生成OTA主动推送消息并推送给设备
*/
CREATED("CREATED"),
/**
* SUCCESS: OTA推送事件--已成功推送
*/
SUCCESS("SUCCESS"),
/**
* CANCELED: OTA推送事件--已取消推送
*/
CANCELED("CANCELED");
private String status;
private final static Map<String, OTANotifyEnum> pool = new ConcurrentHashMap<>();
private OTANotifyEnum(String status) {
setStatus(status);
}
static {
for (OTANotifyEnum otaNotifyEnum : OTANotifyEnum.values()) {
pool.put(otaNotifyEnum.getStatus(), otaNotifyEnum);
}
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public static OTANotifyEnum getOTAEventEnum(String status) {
return pool.get(status);
}
}
package com.senthink.slot.common.mongodb.enums;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* OTA升级服务器类型定义类
* @Author LuDash
* @Date 2017-11-14 17:29
*/
public enum OTAServerTypeEnum {
TCP("TCP"),
HTTP("HTTP"),
HTTPS("HTTPS");
private String type;
private final static Map<String, OTAServerTypeEnum> pool = new ConcurrentHashMap<>();
private OTAServerTypeEnum(String type) {
setType(type);
}
static {
for (OTAServerTypeEnum otaServerTypeEnum : OTAServerTypeEnum.values()) {
pool.put(otaServerTypeEnum.getType(), otaServerTypeEnum);
}
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public static OTAServerTypeEnum getOTAServerTypeEnum(String type) {
return pool.get(type.toUpperCase());
}
}
package com.senthink.slot.common.mongodb.enums;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 智能设备OTA升级过程监控状态定义类
* @Author LuDash
* @Date 2017-11-7 18:20
*/
public enum OTAStatusEnum {
OTA_CREATED(0, "未开始下载"),
OTA_DOWNLOADING(1, "正在下载固件"),
OTA_DOWNLOAD_COMPLETE(2, "固件下载完毕"),
OTA_DOWNLOAD_FAILED(3, "升级失败:网络异常导致固件下载失败"),
OTA_SUCCESS(4, "升级成功"),
OTA_FIRM_APPLY_FAILED(5, "升级失败:固件下载成功,但是应用新版本固件失败"),
OTA_SERVER_TERMINATE(6, "升级失败:服务器终止OTA升级");
private Integer result;
private String info;
private final static Map<Integer, OTAStatusEnum> pool = new ConcurrentHashMap<>();
private OTAStatusEnum(Integer result, String info) {
setResult(result);
setInfo(info);
}
static {
for (OTAStatusEnum otaStatusEnum : OTAStatusEnum.values()) {
pool.put(otaStatusEnum.getResult(), otaStatusEnum);
}
}
public Integer getResult() {
return result;
}
public void setResult(Integer result) {
this.result = result;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public static boolean containsKey(Integer result) {
return pool.containsKey(result);
}
public static OTAStatusEnum getOTAResultEnum(Integer result) {
return pool.get(result);
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.mongodb.enums.AppTypeEnum;
import com.senthink.slot.common.mongodb.model.constants.AppConstants;
import io.vertx.core.json.JsonObject;
/**
* 应用服务器实体类
* @Author LuDash
* @Date 2017-8-16 15:24
*/
public class App implements AppConstants {
private JsonObject object;
public App() {
object = new JsonObject();
}
public App(JsonObject object) {
this.object = object;
}
@Override
public String getDocumentId() {
return object.getString(DocumentId);
}
@Override
public App setDocumentId(String id) {
object.put(DocumentId, id);
return this;
}
@Override
public JsonObject toJson() {
return object;
}
public String getOpenId() {
return object.getString(OpenId);
}
public App setOpenId(String openId) {
object.put(OpenId, openId.toUpperCase());
return this;
}
public String getProductId() {
return object.getString(ProductId);
}
public App setProductId(String productId) {
object.put(ProductId, productId.toUpperCase());
return this;
}
public String getName() {
return object.getString(Name);
}
public App setName(String name) {
object.put(Name, name);
return this;
}
public String getAppId() {
return object.getString(AppId);
}
public App setAppId(String appId) {
object.put(AppId, appId);
return this;
}
public String getAppKey() {
return object.getString(AppKey);
}
public App setAppKey(String appKey) {
object.put(AppKey, appKey);
return this;
}
public Boolean getEnable() {
return object.getBoolean(Enable);
}
public App setEnable(Boolean enable) {
object.put(Enable, enable);
return this;
}
public AppTypeEnum getAppType() {
String type = object.getString(Type);
return AppTypeEnum.getAppTypeEnum(type);
}
public App setAppType(AppTypeEnum appType) {
object.put(Type, appType.getType());
return this;
}
public String getMessageFormat() {
return object.getString(MessageFormat);
}
public App setMessageFormat(String format) {
object.put(MessageFormat, format);
return this;
}
public String getAppDataNotifyUrl() {
return object.getString(AppDataNotifyUrl);
}
public App setAppDataNotifyUrl(String appDataNotifyUrl) {
object.put(AppDataNotifyUrl, appDataNotifyUrl);
return this;
}
public String getHeartbeatNotifyUrl() {
return object.getString(HeartbeatNotifyUrl);
}
public App setHeartbeatNotifyUrl(String heartbeatNotifyUrl) {
object.put(HeartbeatNotifyUrl, heartbeatNotifyUrl);
return this;
}
public String getConnectionNotyfiUrl() {
return object.getString(ConnectNotifyUrl);
}
public App setConnectionNotifyUrl(String connectionNotifyUrl) {
object.put(ConnectNotifyUrl, connectionNotifyUrl);
return this;
}
public String getOtaResultNotifyUrl() {
return object.getString(OtaResultNotifyUrl);
}
public App setOtaResultNotifyUrl(String otaResultNotifyUrl) {
object.put(OtaResultNotifyUrl, otaResultNotifyUrl);
return this;
}
public String getExchange() {
return object.getString(Exchange);
}
public App setExchange(String exchange) {
object.put(Exchange, exchange);
return this;
}
public String getHeartbeatExchange() {
return object.getString(HeartbeatExchange);
}
public App setHeartbeatExchange(String heartbeatExchange) {
object.put(HeartbeatExchange, heartbeatExchange);
return this;
}
public String getConnectionExchange() {
return object.getString(ConnectionExchange);
}
public App setConnectionExchange(String connectionExchange) {
object.put(ConnectionExchange, connectionExchange);
return this;
}
public String getOtaResultExchange() {
return object.getString(OtaResultExchange);
}
public App setOtaResultExchange(String otaResultExchange) {
object.put(OtaResultExchange, otaResultExchange);
return this;
}
public String getPublishTopic() {
return object.getString(PublishTopic);
}
public App setPublishTopic(String publishTopic) {
object.put(PublishTopic, publishTopic);
return this;
}
public String getHeartBeatPublishTopic() {
return object.getString(HeartBeatPublishTopic);
}
public App setHeartBeatPublishTopic(String heartBeatPublishTopic) {
object.put(HeartBeatPublishTopic, heartBeatPublishTopic);
return this;
}
public String getConnectPublishTopic() {
return object.getString(ConnectPublishTopic);
}
public App setConnectPublishTopic(String connectPublishTopic) {
object.put(ConnectPublishTopic, connectPublishTopic);
return this;
}
public String getOtaResultPublishTopic() {
return object.getString(OtaResultPublishTopic);
}
public App setOtaResultPublishTopic(String otaResultPublishTopic) {
object.put(OtaResultPublishTopic, otaResultPublishTopic);
return this;
}
public String getCreateAt() {
return object.getString(CreateAt);
}
public App setCreateAt(String createAt) {
object.put(CreateAt, createAt);
return this;
}
public String getUpdateAt() {
return object.getString(UpdateAt);
}
public App setUpdateAt(String updateAt) {
object.put(UpdateAt, updateAt);
return this;
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.mongodb.model.constants.DebugEventConstants;
import io.vertx.core.json.JsonObject;
/**
* 智能设备在线调试事件实体类定义
* @Author LuDash
* @Date 2017-11-7 11:48
*/
public class DebugEvent implements DebugEventConstants {
private JsonObject object;
public DebugEvent() {
object = new JsonObject();
}
public DebugEvent(JsonObject object) {
this.object = object;
}
public Boolean getEnable() {
return object.getBoolean(Enable);
}
public DebugEvent setEnable(Boolean enable) {
object.put(Enable, enable);
return this;
}
public Long getExpiredAt() {
return object.getLong(ExpiredAt);
}
public DebugEvent setExpiredAt(Long expiredAt) {
object.put(ExpiredAt, expiredAt);
return this;
}
public JsonObject toJson() {
return object;
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.mongodb.enums.OTAStatusEnum;
import com.senthink.slot.common.mongodb.model.constants.DownIdConstants;
import io.vertx.core.json.JsonObject;
/**
* OTA升级的DownID信息实体类定义
* @Author LuDash
* @Date 2017-7-7 12:29
*/
public class DownId implements DownIdConstants {
private JsonObject object;
public DownId() {
object = new JsonObject();
}
public DownId(JsonObject object) {
this.object = object;
}
@Override
public String getDocumentId() {
return object.getString(DocumentId);
}
@Override
public DownId setDocumentId(String id) {
object.put(DocumentId, id);
return this;
}
@Override
public JsonObject toJson() {
return object;
}
public String getOpenId() {
return object.getString(OpenId);
}
public DownId setOpenId(String openId) {
object.put(OpenId, openId.toUpperCase());
return this;
}
public String getProductId() {
return object.getString(ProductId);
}
public DownId setProductId(String productId) {
object.put(ProductId, productId.toUpperCase());
return this;
}
public String getNodeEui() {
return object.getString(NodeEui);
}
public DownId setNodeEui(String nodeEui) {
object.put(NodeEui, nodeEui);
return this;
}
public String getHardVer() {
return object.getString(HardVer);
}
public DownId setHardVer(String hardVer) {
object.put(HardVer, hardVer);
return this;
}
public String getSoftVer() {
return object.getString(SoftVer);
}
public DownId setSoftVer(String softVer) {
object.put(SoftVer, softVer);
return this;
}
public String getOTAType() {
return object.getString(OTAType);
}
public DownId setOTAType(String otaType) {
object.put(OTAType, otaType);
return this;
}
public Long getExpiredAt() {
return object.getLong(ExpiredAt);
}
public DownId setExpiredAt(Long expiredAt) {
object.put(ExpiredAt, expiredAt);
return this;
}
public int getStatus() {
return object.getInteger(Status);
}
public DownId setStatus(OTAStatusEnum statusEnum) {
object.put(Status, statusEnum.getResult());
return this;
}
public String getStatusInfo() {
return object.getString(StatusInfo);
}
public DownId setStatusInfo(String info) {
object.put(StatusInfo, info);
return this;
}
public Long getTakesTime() {
return object.getLong(TakesTime);
}
public DownId setTakesTime(Long takesTime) {
object.put(TakesTime, takesTime);
return this;
}
public String getTakesTimeInfo() {
return object.getString(TakesTimeInfo);
}
public DownId setTakesTimeInfo(String takesTimeInfo) {
object.put(TakesTimeInfo, takesTimeInfo);
return this;
}
public String getCreateAt() {
return object.getString(CreateAt);
}
public DownId setCreateAt(String createAt) {
object.put(CreateAt, createAt);
return this;
}
public String getUpdateAt() {
return object.getString(UpdateAt);
}
public DownId setUpdateAt(String updateAt) {
object.put(UpdateAt, updateAt);
return this;
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.mongodb.model.constants.FirmwareConstants;
import io.vertx.core.json.JsonObject;
/**
* 智能设备的固件文件实体类定义
* @Author LuDash
* @Date 2017-7-6 13:58
*/
public class Firmware implements FirmwareConstants {
private JsonObject object;
public Firmware() {
object = new JsonObject();
}
public Firmware(JsonObject object) {
this.object = object;
}
@Override
public String getDocumentId() {
return object.getString(DocumentId);
}
@Override
public Firmware setDocumentId(String id) {
object.put(DocumentId, id);
return this;
}
@Override
public JsonObject toJson() {
return object;
}
public String getName() {
return object.getString(Name);
}
public Firmware setName(String name) {
object.put(Name, name);
return this;
}
public String getOriginalFileName() {
return object.getString(OriginalFileName);
}
public Firmware setOriginalFileName(String originalFileName) {
object.put(OriginalFileName, originalFileName);
return this;
}
public String getOpenId() {
return object.getString(OpenId);
}
public Firmware setOpenId(String openId) {
object.put(OpenId, openId.toUpperCase());
return this;
}
public String getProductId() {
return object.getString(ProductId);
}
public Firmware setProductId(String productId) {
object.put(ProductId, productId.toUpperCase());
return this;
}
public String getHardVer() {
return object.getString(HardVer);
}
public Firmware setHardVer(String hardVer) {
object.put(HardVer, hardVer);
return this;
}
public String getSoftVer() {
return object.getString(SoftVer);
}
public Firmware setSoftVer(String softVer) {
object.put(SoftVer, softVer);
return this;
}
public long getSoftSize() {
return object.getLong(SoftSize);
}
public Firmware setSoftSize(long softSize) {
object.put(SoftSize, softSize);
return this;
}
public String getMD5() {
return object.getString(MD5);
}
public Firmware setMD5(String md5) {
object.put(MD5, md5);
return this;
}
public String getPath() {
return object.getString(Path);
}
public Firmware setPath(String path) {
object.put(Path, path);
return this;
}
public String getCommnets() {
return object.getString(Comments);
}
public Firmware setComments(String comments) {
object.put(Comments, comments);
return this;
}
public String getCreateAt() {
return object.getString(CreateAt);
}
public Firmware setCreateAt(String createAt) {
object.put(CreateAt, createAt);
return this;
}
public String getUpdateAt() {
return object.getString(UpdateAt);
}
public Firmware setUpdateAt(String updateAt) {
object.put(UpdateAt, updateAt);
return this;
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.message.protocol.enums.OTATypeEnum;
import com.senthink.slot.common.mongodb.model.constants.NodeConstants;
import io.vertx.core.json.JsonObject;
/**
* 智能设备的实体类
* @Author LuDash
* @Date 2017-5-22 21:25
*/
public class Node implements NodeConstants {
private JsonObject object;
public Node() {
object = new JsonObject();
}
public Node(JsonObject object) {
this.object = object;
}
@Override
public String getDocumentId() {
return object.getString(DocumentId);
}
@Override
public Node setDocumentId(String id) {
object.put(DocumentId, id);
return this;
}
@Override
public JsonObject toJson() {
return object;
}
public String getOpenId() {
return object.getString(OpenId);
}
public Node setOpenId(String openId) {
object.put(OpenId, openId.toUpperCase());
return this;
}
public String getProductId() {
return object.getString(ProductId);
}
public Node setProductId(String productId) {
object.put(ProductId, productId.toUpperCase());
return this;
}
public String getNodeEui() {
return object.getString(NodeEui);
}
public Node setNodeEui(String nodeEui) {
object.put(NodeEui, nodeEui);
return this;
}
public String getOTAPlan() {
return object.getString(OTAPlan);
}
public Node setOTAPlan(String otaPlan) {
object.put(OTAPlan, otaPlan);
return this;
}
public boolean getConnected() {
return object.getBoolean(Connected);
}
public Node setConnected(boolean connected) {
object.put(Connected, connected);
return this;
}
public boolean getCheckSession() {
return object.getBoolean(CheckSession);
}
public Node setCheckSession(boolean checkSession) {
object.put(CheckSession, checkSession);
return this;
}
public String getSessionSalt() {
return object.getString(SessionSalt);
}
public Node setSessionSalt(String sessionSalt) {
object.put(SessionSalt, sessionSalt.toUpperCase());
return this;
}
public String getSessionKey() {
return object.getString(SessionKey);
}
public Node setSessionKey(String sessionKey) {
object.put(SessionKey, sessionKey.toUpperCase());
return this;
}
public Long getSessionExpiredAt() {
return object.getLong(SessionExpiredAt);
}
public Node setSessionExpiredAt(Long sessionExpiredAt) {
object.put(SessionExpiredAt, sessionExpiredAt);
return this;
}
public String getSessionRequestAt() {
return object.getString(SessionRequestAt);
}
public Node setSessionRequestAt(String sessionRequestAt) {
object.put(SessionRequestAt, sessionRequestAt);
return this;
}
public String getRegisterAt() {
return object.getString(RegisterAt);
}
public Node setRegisterAt(String registerAt) {
object.put(RegisterAt, registerAt);
return this;
}
public String getLastJoinAt() {
return object.getString(LastJoinAt);
}
public Node setLastJoinAt(String lastJoinAt) {
object.put(LastJoinAt, lastJoinAt);
return this;
}
public String getLastDisconnectAt() {
return object.getString(LastDisconnectAt);
}
public Node setLastDisconnectAt(String lastDisconnectAt) {
object.put(LastDisconnectAt, lastDisconnectAt);
return this;
}
public String getOTAType() {
return object.getString(OTAType);
}
public Node setOTAType(OTATypeEnum typeEnum) {
object.put(OTAType, typeEnum.getInfo());
return this;
}
public OTANotify getOTANotify() {
JsonObject otaNotify = object.getJsonObject(OTANotify);
return otaNotify!=null?new OTANotify(otaNotify):null;
}
public Node setOTANotify(OTANotify otaNotify) {
object.put(OTANotify, otaNotify.toJson());
return this;
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.mongodb.enums.OTANotifyEnum;
import com.senthink.slot.common.mongodb.model.constants.OTANotifyConstants;
import io.vertx.core.json.JsonObject;
/**
* 智能设备OTA推送事件实体类定义
* @Author LuDash
* @Date 2017-11-13 16:38
*/
public class OTANotify implements OTANotifyConstants {
private JsonObject object;
public OTANotify() {
this.object = new JsonObject();
}
public OTANotify(JsonObject object) {
this.object = object;
}
public String getHardVer() {
return object.getString(HardVer);
}
public OTANotify setHardVer(String hardVer) {
object.put(HardVer, hardVer);
return this;
}
public String getSoftVer() {
return object.getString(SoftVer);
}
public OTANotify setSoftVer(String softVer) {
object.put(SoftVer, softVer);
return this;
}
public long getSoftSize() {
return object.getInteger(SoftSize);
}
public OTANotify setSoftSize(long softSize) {
object.put(SoftSize, softSize);
return this;
}
public String getSoftMd5() {
return object.getString(SoftMd5);
}
public OTANotify setSoftMd5(String md5) {
object.put(SoftMd5, md5);
return this;
}
public OTANotifyEnum getStatus() {
return OTANotifyEnum.getOTAEventEnum(object.getString(Status));
}
public OTANotify setStatus(OTANotifyEnum status) {
object.put(Status, status.getStatus());
return this;
}
public String getDownId() {
return object.getString(DownId);
}
public OTANotify setDownId(String downId) {
object.put(DownId, downId);
return this;
}
public long getStartTime() {
return object.getLong(StartTime);
}
public OTANotify setStartTime(long startTime) {
object.put(StartTime, startTime);
return this;
}
public String getCreateAt() {
return object.getString(CreateAt);
}
public OTANotify setCreateAt(String createAt) {
object.put(CreateAt, createAt);
return this;
}
public String getUpdateAt() {
return object.getString(UpdateAt);
}
public OTANotify setUpdateAt(String updateAt) {
object.put(UpdateAt, updateAt);
return this;
}
public JsonObject toJson() {
return object;
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.mongodb.model.constants.OTAPlanConstants;
import com.senthink.slot.common.util.JodaTimeUtils;
import io.vertx.core.json.JsonObject;
import java.util.Date;
/**
* 智能设备OTA升级计划的实体类
* @Author LuDash
* @Date 2017-7-31 16:09
*/
public class OTAPlan implements OTAPlanConstants {
private JsonObject object;
public OTAPlan() {
object = new JsonObject();
}
public OTAPlan(JsonObject object) {
this.object = object;
}
@Override
public String getDocumentId() {
return object.getString(DocumentId);
}
@Override
public OTAPlan setDocumentId(String id) {
object.put(DocumentId, id);
return this;
}
@Override
public JsonObject toJson() {
return object;
}
public String getOpenId() {
return object.getString(OpenId);
}
public OTAPlan setOpenId(String openId) {
object.put(OpenId, openId.toUpperCase());
return this;
}
public String getProductId() {
return object.getString(ProductId);
}
public OTAPlan setProductId(String productId) {
object.put(ProductId, productId.toUpperCase());
return this;
}
public String getFirmwareId() {
return object.getString(FirmwareId);
}
public OTAPlan setFirmwareId(String firmwareId) {
object.put(FirmwareId, firmwareId);
return this;
}
public boolean getGlobal() {
return object.getBoolean(Global);
}
public OTAPlan setGlobal(boolean global) {
object.put(Global, global);
return this;
}
public boolean getEnable() {
return object.getBoolean(Enable);
}
public OTAPlan setEnable(boolean enable) {
object.put(Enable, enable);
return this;
}
public boolean getSpecifyTime() {
return object.getBoolean(SpecifyTime);
}
public OTAPlan setSpecifyTime(boolean specifyTime) {
object.put(SpecifyTime, specifyTime);
return this;
}
public String getStartTime() {
return object.getString(StartTime);
}
public OTAPlan setStartTime(String startTime) {
object.put(StartTime, startTime);
return this;
}
public String getEndTime() {
return object.getString(EndTime);
}
public OTAPlan setEndTime(String endTime) {
object.put(EndTime, endTime);
return this;
}
public String getComments() {
return object.getString(Comments);
}
public OTAPlan setComments(String comments) {
object.put(Comments, comments);
return this;
}
public String getCreateAt() {
return object.getString(CreateAt);
}
public OTAPlan setCreateAt(Date createAt) {
object.put(CreateAt, JodaTimeUtils.formatDateTime(createAt));
return this;
}
public String getUpdateAt() {
return object.getString(UpdateAt);
}
public OTAPlan setUpdateAt(Date updateAt) {
object.put(UpdateAt, JodaTimeUtils.formatDateTime(updateAt));
return this;
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.mongodb.model.constants.OTAServerConstants;
import io.vertx.core.json.JsonObject;
/**
* OTA升级服务器实体类定义
* @Author LuDash
* @Date 2017-7-6 17:02
*/
public class OTAServer implements OTAServerConstants {
private JsonObject object;
public OTAServer() {
object = new JsonObject();
}
public OTAServer(JsonObject object) {
this.object = object;
}
@Override
public String getDocumentId() {
return object.getString(DocumentId);
}
@Override
public OTAServer setDocumentId(String id) {
object.put(DocumentId, id);
return this;
}
@Override
public JsonObject toJson() {
return object;
}
public String getOpenId() {
return object.getString(OpenId);
}
public OTAServer setOpenId(String openId) {
object.put(OpenId, openId);
return this;
}
public String getName() {
return object.getString(Name);
}
public OTAServer setName(String name) {
object.put(Name, name);
return this;
}
public String getHost() {
return object.getString(Host);
}
public OTAServer setHost(String host) {
object.put(Host, host);
return this;
}
public int getPort() {
return object.getInteger(Port);
}
public OTAServer setPort(int port) {
object.put(Port, port);
return this;
}
public long getConnections() {
return object.getLong(Connections);
}
public OTAServer setConnections(long connections) {
object.put(Connections, connections);
return this;
}
public String getType() {
return object.getString(Type);
}
public OTAServer setType(String type) {
object.put(Type, type);
return this;
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.message.protocol.enums.OTATypeEnum;
import com.senthink.slot.common.mongodb.model.constants.OTAVersionConstants;
import io.vertx.core.json.JsonObject;
/**
* 智能硬件固件版本号信息实体类定义
* @Author LuDash
* @Date 2017-7-13 15:14
*/
public class OTAVersion implements OTAVersionConstants {
private JsonObject object;
public OTAVersion() {
object = new JsonObject();
}
public OTAVersion(JsonObject object) {
this.object = object;
}
@Override
public String getDocumentId() {
return object.getString(DocumentId);
}
@Override
public OTAVersion setDocumentId(String id) {
object.put(DocumentId, id);
return this;
}
@Override
public JsonObject toJson() {
return object;
}
public String getOpenId() {
return object.getString(OpenId);
}
public OTAVersion setOpenId(String openId) {
object.put(OpenId, openId.toUpperCase());
return this;
}
public String getProductId() {
return object.getString(ProductId);
}
public OTAVersion setProductId(String productId) {
object.put(ProductId, productId.toUpperCase());
return this;
}
public String getNodeEui() {
return object.getString(NodeEui);
}
public OTAVersion setNodeEui(String nodeEui) {
object.put(NodeEui, nodeEui);
return this;
}
public String getHardVer() {
return object.getString(HardVer);
}
public OTAVersion setHardVer(String hardVer) {
object.put(HardVer, hardVer);
return this;
}
public String getSoftVer() {
return object.getString(SoftVer);
}
public OTAVersion setSoftVer(String softVer) {
object.put(SoftVer, softVer);
return this;
}
public String getOTAType() {
return object.getString(OTAType);
}
public OTAVersion setOTAType(OTATypeEnum typeEnum) {
object.put(OTAType, typeEnum.getInfo());
return this;
}
public String getUpdateAt() {
return object.getString(UpdateAt);
}
public OTAVersion setUpdateAt(String updateAt) {
object.put(UpdateAt, updateAt);
return this;
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.mongodb.model.constants.OpenerConstants;
import com.senthink.slot.common.util.JodaTimeUtils;
import io.vertx.core.json.JsonObject;
import java.util.Date;
/**
* 智能设备所属厂商的实体类定义
* @Author LuDash
* @Date 2017-5-23 10:20
*/
public class Opener implements OpenerConstants {
private JsonObject object;
public Opener() {
object = new JsonObject();
}
public Opener(JsonObject object) {
this.object = object;
}
@Override
public String getDocumentId() {
return object.getString(DocumentId);
}
@Override
public Opener setDocumentId(String id) {
object.put(DocumentId, id);
return this;
}
@Override
public JsonObject toJson() {
return object;
}
public String getName() {
return object.getString(Name);
}
public Opener setName(String name) {
object.put(Name, name);
return this;
}
public String getOpenId() {
return object.getString(OpenId);
}
public Opener setOpenId(String openId) {
object.put(OpenId, openId.toUpperCase());
return this;
}
public String getEmail() {
return object.getString(Email);
}
public Opener setEmail(String email) {
object.put(Email, email);
return this;
}
public String getPassword() {
return object.getString(Password);
}
public Opener setPassword(String password) {
object.put(Password, password);
return this;
}
public String getCreateAt() {
return object.getString(CreateAt);
}
public Opener setCreateAt(Date createAt) {
object.put(CreateAt, JodaTimeUtils.formatDateTime(createAt));
return this;
}
public String getUpdateAt() {
return object.getString(UpdateAt);
}
public Opener setUpdateAt(Date updateAt) {
object.put(UpdateAt, JodaTimeUtils.formatDateTime(updateAt));
return this;
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.mongodb.model.constants.PasswordCaptchaConstants;
import com.senthink.slot.common.util.JodaTimeUtils;
import io.vertx.core.json.JsonObject;
import java.util.Date;
/**
* 智能设备厂商用户找回密码的验证码的数据库实体类定义
* @Author LuDash
* @Date 2017-10-24 17:29
*/
public class PasswordCaptcha implements PasswordCaptchaConstants {
private JsonObject object;
public PasswordCaptcha() {
object = new JsonObject();
}
public PasswordCaptcha(JsonObject object) {
this.object = object;
}
@Override
public String getDocumentId() {
return object.getString(DocumentId);
}
@Override
public PasswordCaptcha setDocumentId(String id) {
object.put(DocumentId, id);
return this;
}
@Override
public JsonObject toJson() {
return object;
}
public String getOpenId() {
return object.getString(OpenId);
}
public PasswordCaptcha setOpenId(String openId) {
object.put(OpenId, openId);
return this;
}
public String getCaptcha() {
return object.getString(Captcha);
}
public PasswordCaptcha setCaptcha(String captcha) {
object.put(Captcha, captcha);
return this;
}
public Long getExpiredAt() {
return object.getLong(ExpiredAt);
}
public PasswordCaptcha setExpiredAt(Long expiredAt) {
object.put(ExpiredAt, expiredAt);
return this;
}
public String getUpdateAt() {
return object.getString(UpdateAt);
}
public PasswordCaptcha setUpdateAt(Date updateAt) {
object.put(UpdateAt, JodaTimeUtils.formatDateTime(updateAt));
return this;
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.mongodb.model.constants.ProductConstants;
import com.senthink.slot.common.util.JodaTimeUtils;
import io.vertx.core.json.JsonObject;
import java.util.Date;
/**
* 智能设备所属产品的数据库实体类定义
* @Author LuDash
* @Date 2017-5-23 10:19
*/
public class Product implements ProductConstants {
private JsonObject object;
public Product() {
object = new JsonObject();
}
public Product(JsonObject object) {
this.object = object;
}
@Override
public String getDocumentId() {
return object.getString(DocumentId);
}
@Override
public Product setDocumentId(String id) {
object.put(DocumentId, id);
return this;
}
@Override
public JsonObject toJson() {
return object;
}
public String getProductName() {
return object.getString(ProductName);
}
public Product setProductName(String name) {
object.put(ProductName, name);
return this;
}
public String getOpenId() {
return object.getString(OpenId);
}
public Product setOpenId(String openId) {
object.put(OpenId, openId.toUpperCase());
return this;
}
public String getProductId() {
return object.getString(ProductId);
}
public Product setProductId(String productId) {
object.put(ProductId, productId.toUpperCase());
return this;
}
public String getProductKey() {
return object.getString(ProductKey);
}
public Product setProductKey(String productKey) {
object.put(ProductKey, productKey.toUpperCase());
return this;
}
public String getComments() {
return object.getString(Comments);
}
public Product setComments(String comments) {
object.put(Comments, comments);
return this;
}
public String getCreateAt() {
return object.getString(CreateAt);
}
public Product setCreateAt(Date createAt) {
object.put(CreateAt, JodaTimeUtils.formatDateTime(createAt));
return this;
}
public String getUpdateAt() {
return object.getString(UpdateAt);
}
public Product setUpdateAt(Date updateAt) {
object.put(UpdateAt, JodaTimeUtils.formatDateTime(updateAt));
return this;
}
}
package com.senthink.slot.common.mongodb.model;
import com.senthink.slot.common.mongodb.model.constants.ProductRecoveryConstants;
import com.senthink.slot.common.util.JodaTimeUtils;
import io.vertx.core.json.JsonObject;
import java.util.Date;
/**
* 已删除的产品数据库数据库实体类定义
* @Author LuDash
* @Date 2017-10-24 10:47
*/
public class ProductRecovery implements ProductRecoveryConstants {
private JsonObject object;
public ProductRecovery() {
object = new JsonObject();
}
public ProductRecovery(JsonObject object) {
this.object = object;
}
@Override
public String getDocumentId() {
return object.getString(DocumentId);
}
@Override
public ProductRecovery setDocumentId(String id) {
object.put(DocumentId, id);
return this;
}
@Override
public JsonObject toJson() {
return object;
}
public String getProductName() {
return object.getString(ProductName);
}
public ProductRecovery setProductName(String name) {
object.put(ProductName, name);
return this;
}
public String getOpenId() {
return object.getString(OpenId);
}
public ProductRecovery setOpenId(String openId) {
object.put(OpenId, openId.toUpperCase());
return this;
}
public String getProductId() {
return object.getString(ProductId);
}
public ProductRecovery setProductId(String productId) {
object.put(ProductId, productId.toUpperCase());
return this;
}
public String getProductKey() {
return object.getString(ProductKey);
}
public ProductRecovery setProductKey(String productKey) {
object.put(ProductKey, productKey.toUpperCase());
return this;
}
public String getComments() {
return object.getString(Comments);
}
public ProductRecovery setComments(String comments) {
object.put(Comments, comments);
return this;
}
public String getCreateAt() {
return object.getString(CreateAt);
}
public ProductRecovery setCreateAt(Date createAt) {
object.put(CreateAt, JodaTimeUtils.formatDateTime(createAt));
return this;
}
public String getUpdateAt() {
return object.getString(UpdateAt);
}
public ProductRecovery setUpdateAt(Date updateAt) {
object.put(UpdateAt, JodaTimeUtils.formatDateTime(updateAt));
return this;
}
public String getDeleteAt() {
return object.getString(DeleteAt);
}
public ProductRecovery setDeleteAt(Date deleteAt) {
object.put(DeleteAt, JodaTimeUtils.formatDateTime(deleteAt));
return this;
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment