Compare commits

...

8 Commits
v3.1 ... v3

Author SHA1 Message Date
PengyuDeng ff7e796a09
doc: 完善TCP文档内容 (#16)
* doc: 完善TCP文档内容

* doc: 增加设备上报示例

* doc: 增加请求设备上线示例

* doc: 文档增加byteBuf构建示例

* doc: 使用databuffer构建报文
2024-01-24 19:22:33 +08:00
PengyuDeng 7b9a4cf128
doc: 修复TCP协议解析注释错误 (#15) 2024-01-05 11:27:33 +08:00
老周 0bf5111e31
Update pom.xml 2023-08-03 16:40:12 +08:00
老周 ec45b7c16d
Merge pull request #14 from 445990772/v3
fix(单元测试): 单元测试类生成报文无法上线,优化输出报文显示
2023-08-02 09:17:44 +08:00
李洁 fe8ef8a1c4 fix(单元测试): 单元测试类生成报文无法上线,优化输出报文显示 2023-08-01 18:04:01 +08:00
445990772 d006a81847
Merge pull request #1 from 445990772/fix-bug
fix(单元测试): 单元测试类生成报文无法上线,优化输出报文显示
2023-08-01 17:50:40 +08:00
李洁 a01497f0db fix(单元测试): 单元测试类生成报文无法上线,优化输出报文显示 2023-08-01 17:45:19 +08:00
ht-lijie 9f09f83d95 build(官方协议): 更新官方协议jar包 2023-04-04 17:30:41 +08:00
4 changed files with 171 additions and 83 deletions

View File

@ -1,89 +1,167 @@
## 头信息
| 字节序 | 类型 | 字段 |
|:----:|---------|:-----------:|
| 0 | INT8 | 消息类型 |
| 1-8 | INT64 | UTC时间戳 |
| 9-11 | INT16 | 消息序号 |
| 12-n | STRING | 设备ID |
| ... | MESSAGE | 消息类型对应的编码规则 |
| 字节数 | 类型 | 字段 | 备注 |
| :----: | ------- | :--------------------: | ------------------- |
| n | 自定 | 消息长度 | 平台配置项 |
| 1 | INT8 | 消息类型 | 见消息类型定义 |
| 8 | INT64 | UTC时间戳 | |
| 2 | INT16 | 消息序号 | |
| 2 | INT16 | 设备ID长度 | |
| n | STRING | 设备ID | 根据设备ID长度得出 |
| n | MESSAGE | 消息类型对应的编码规则 | |
| 2 | INT16 | secureKeyLength | TCP认真配置密钥长度 |
| n | STRING | secureKey | 密钥 |
## 数据类型
所有数据类型均采用`大端`编码
| Byte | Type | 编码规则 |
|:----:|:-------:|---------------------------------------------|
| 0x00 | NULL | 0x01 |
| 0x01 | BOOLEAN | 1字节 0x00为false 其他为true |
| 0x02 | INT8 | 1字节 (byte) |
| 0x03 | INT16 | 2字节整型 (short) |
| 0x04 | INT32 | 4字节整型 (int) |
| 0x05 | INT64 | 8字节整型 (long) |
| 0x06 | UINT8 | 1字节无符号整型 |
| 0x07 | UINT16 | 2字节无符号整型 |
| 0x08 | UINT32 | 4字节无符号整型 |
| 0x09 | FLOAT | 4字节 IEEE 754浮点数 |
| 0x0a | DOUBLE | 8字节 IEEE 754浮点数 |
| 0x0b | STRING | 前`2字节无符号整型`表示字符串长度,接下来长度的字节为字符串内容,UTF8编码 |
| 0x0c | BINARY | 前`2字节无符号整型`表示数据长度,接下来长度的字节为数据内容 |
| 0x0d | ARRAY | 前`2字节无符号整型`表述数组长度,接下来根据后续报文类型来解析元素 |
| 0x0e | OBJECT | 前`2字节无符号整型`表述对象字段长度,接下来根据后续报文类型来解析key value |
| Byte | Type | 编码规则 | 备注 |
| :--: | :-----: | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 0x00 | NULL | | |
| 0x01 | BOOLEAN | 1字节 0x00为false 其他为true | |
| 0x02 | INT8 | 1字节 (byte) | |
| 0x03 | INT16 | 2字节整型 (short) | |
| 0x04 | INT32 | 4字节整型 (int) | |
| 0x05 | INT64 | 8字节整型 (long) | |
| 0x06 | UINT8 | 1字节无符号整型 | |
| 0x07 | UINT16 | 2字节无符号整型 | |
| 0x08 | UINT32 | 4字节无符号整型 | |
| 0x09 | FLOAT | 4字节 IEEE 754浮点数 | |
| 0x0a | DOUBLE | 8字节 IEEE 754浮点数 | |
| 0x0b | STRING | 前`2字节无符号整型`表示字符串长度,接下来长度的字节为字符串内容,UTF8编码 | 2+N2个字节UnsignedShort表示N的长度 |
| 0x0c | BINARY | 前`2字节无符号整型`表示数据长度,接下来长度的字节为数据内容 | 2+N2个字节UnsignedShort表示N的长度 |
| 0x0d | ARRAY | 前`2字节无符号整型`表述数组长度,接下来根据后续报文类型来解析元素 | 2+N2个字节UnsignedShort表示ARRAY的长度N=很多*(1+X)1个字节UnsignedShort表示X的数据类型X表示长度见上几行。 |
| 0x0e | OBJECT | 前`2字节无符号整型`表述对象字段长度,接下来根据后续报文类型来解析key value | 2+N2个字节UnsignedShort表示OBJECT的长度,N是STRING+数据类型的组合了(见上几行)。 |
## 消息类型定义
| Byte | Type | 说明 |
|:----:|:-------------------|--------|
| 0x00 | keepalive | 心跳 |
| 0x01 | online | 首次连接 |
| 0x02 | ack | 应答 |
| 0x03 | reportProperty | 上报属性 |
| 0x04 | readProperty | 读取属性 |
| 0x05 | readPropertyReply | 读取属性回复 |
| 0x06 | writeProperty | 修改属性 |
| 0x07 | writePropertyReply | 修改属性回复 |
| 0x08 | function | 功能调用 |
| 0x09 | functionReply | 功能调用回复 |
| Byte | Type | 说明 | Message示例 |
| :--: | :----------------- | ------------ | ------------------------------------------------------------ |
| 0x00 | keepalive | 心跳 | |
| 0x01 | online | 首次连接 | [STRING:密钥信息 ] |
| 0x02 | ack | 应答 | [应答码 ]<br />应答码: 0x00:ok , 0x01: 未认证, 0x02: 不支持. |
| 0x03 | reportProperty | 上报属性 | [属性数据:OBJECT类型 ] |
| 0x04 | readProperty | 读取属性 | [属性列表:ARRAY类型 ] |
| 0x05 | readPropertyReply | 读取属性回复 | 读取成功:[`0x01`,属性数据:OBJECT类型 ]<br />读取失败:[`0x00`,错误码:动态类型,错误消息:动态类型 ] |
| 0x06 | writeProperty | 修改属性 | [属性列表:OBJECT类型 ] |
| 0x07 | writePropertyReply | 修改属性回复 | 修改成功:[`0x01`,属性数据:OBJECT类型 ]<br />修改失败:[`0x00`,错误码:动态类型,错误消息:动态类型 ] |
| 0x08 | function | 功能调用 | [功能ID:STRING类型,功能参数:OBJECT类型 ] |
| 0x09 | functionReply | 功能调用回复 | 调用成功:[`0x01`,属性数据:OBJECT类型 ]<br />调用失败:[`0x00`,错误码:动态类型,错误消息:动态类型 ] |
### 0x00 keepalive 心跳
[ 0x00 ]
### 0x01 online 首次连接
[ 0x01,STRING:密钥信息 ]
### 0x02 ack 应答
[ 0x02,应答码 ]
应答码: 0x00:ok , 0x01: 未认证, 0x02: 不支持.
### 0x03 reportProperty 上报属性
[ 0x03,属性数据:OBJECT类型 ]
### 0x04 readProperty 读取属性
[ 0x04,属性列表:ARRAY类型 ]
### 0x05 readPropertyReply 读取属性回复
读取成功:
[ 0x05,`0x01`,属性数据:OBJECT类型 ]
读取失败:
[ 0x05,`0x00`,错误码:动态类型,错误消息:动态类型 ]
### 备注
`动态读取`表示类型不确定,根据对应的`数据类型`来定义类型.
如: 无错误信息
[ 0x05,0x00,`0x00`,`0x00` ]
[ 0x00,`0x00`,`0x00` ]
`INT8(0x02)`类型错误码:`0x04`
[ 0x05,0x00,`0x02,0x04`,0x00 ]
[0x00,`0x02,0x04`,0x00 ]
TODO 更多消息类型
## 示例
### 设备上线
```
000000270100000186c51a890f0001001331363531383533343133303332383934343634000561646d696e
```
```java
String hexForOnline =
"00000027" +//消息长度
"01" + //请求上线
"00000186c51a890f" +//时间戳
"0001" +//消息序号
"0013" +//设备ID长度
"31363531383533343133303332383934343634" +//设备ID
"0005" +//secureKey长度
"61646d696e";//平台配置的secureKey
//构建方式一使用byteBuf构建上线消息
String deviceId = "1651853413032894464";
String secureKey = "admin";
ByteBuf data = Unpooled
.buffer()
.writeByte(0x01)
.writeLong(System.currentTimeMillis())
.writeShort(1)
.writeShort(deviceId.getBytes().length)
.writeBytes(deviceId.getBytes())
.writeShort(secureKey.getBytes().length)
.writeBytes(secureKey.getBytes());
ByteBuf onlineBuf = Unpooled.buffer()
.writeInt(data.readableBytes())
.writeBytes(data);
//构建方式二使用jetlinks-core构建消息
DeviceOnlineMessage message = new DeviceOnlineMessage();
message.setDeviceId("1651853413032894464");
message.addHeader(BinaryDeviceOnlineMessage.loginToken, "admin");
message.setMessageId("1");
message.setTimestamp(1678344096015L);
ByteBuf byteBuf = BinaryMessageType.write(message, Unpooled.buffer());
ByteBuf buf = Unpooled
.buffer()
.writeInt(byteBuf.readableBytes())
.writeBytes(byteBuf);
```
### 设备数据上报
```
0000006C0300000186C567FA7900020013313635313835333431333033323839343436340001000474656d700B000433362e35000561646d696e
```
```java
String hexForReport =
"0000006C" +//消息长度
"03" +//上报消息
"00000186C567FA79" + //时间戳
"0002" +//消息序号
"0013" +//设备ID长度
"31363531383533343133303332383934343634" +//设备ID
"0001" + //OBJECT对象数量
"0004" +//key的长度
"74656d70" + //值
"0B" + //value的类型
"0004" +//Value的长度
"33362e35" + //值
"0005" + //secureKey长度
"61646d696e";//平台配置的secureKey
//构建方式一
String deviceId = "1651853413032894464";
String secureKey = "admin";
String key = "temp";
String value = "36.5";
ByteBuf data = Unpooled
.buffer()
.writeByte(0x03)
.writeLong(System.currentTimeMillis())
.writeShort(2)
.writeShort(deviceId.getBytes().length)
.writeBytes(deviceId.getBytes())
.writeShort(1)
.writeShort(key.getBytes().length)
.writeBytes(key.getBytes())
.writeByte(0x0B)
.writeShort(value.getBytes().length)
.writeBytes(value.getBytes())
.writeShort(secureKey.getBytes().length)
.writeBytes(secureKey.getBytes());
ByteBuf reportBuf = Unpooled.buffer()
.writeInt(data.readableBytes())
.writeBytes(data);
//构建方式二
ReportPropertyMessage message = new ReportPropertyMessage();
message.setDeviceId("1651853413032894464");
message.addHeader(BinaryDeviceOnlineMessage.loginToken, "admin");
message.setMessageId("2");
message.setProperties(Collections.singletonMap("temp", 32.88));
ByteBuf data = BinaryMessageType.write(message, Unpooled.buffer());
ByteBuf buf = Unpooled.buffer()
.writeInt(data.readableBytes())
.writeBytes(data);
```

View File

@ -271,12 +271,12 @@
<repository>
<id>releases</id>
<name>Nexus Release Repository</name>
<url>http://nexus.hsweb.me/content/repositories/releases/</url>
<url>https://nexus.jetlinks.cn/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<name>Nexus Snapshot Repository</name>
<url>http://nexus.hsweb.me/content/repositories/snapshots/</url>
<url>https://nexus.jetlinks.cn/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
@ -285,7 +285,7 @@
<repository>
<id>hsweb-nexus</id>
<name>Nexus Release Repository</name>
<url>https://nexus.hsweb.me/content/groups/public/</url>
<url>https://nexus.jetlinks.cn/content/groups/public/</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>

View File

@ -152,11 +152,11 @@ public enum BinaryMessageType {
if (type.forTcp == null) {
return null;
}
// 1-4字节 时间戳
// 1-8字节 时间戳
long timestamp = data.readLong();
// 5-6字节 消息序号
// 9-11字节 消息序号
int msgId = data.readUnsignedShort();
// 7... 字节 设备ID
// 12... 字节 设备ID
String deviceId = (String) DataType.STRING.read(data);
if (deviceId == null) {
deviceId = deviceIdMaybe;

View File

@ -10,7 +10,6 @@ import org.jetlinks.core.message.function.FunctionInvokeMessageReply;
import org.jetlinks.core.message.property.*;
import org.junit.Assert;
import org.junit.Test;
import reactor.test.StepVerifier;
import java.util.Collections;
@ -27,11 +26,14 @@ public class BinaryMessageTypeTest {
ByteBuf byteBuf = BinaryMessageType.write(message, Unpooled.buffer());
System.out.println(ByteBufUtil.prettyHexDump(byteBuf));
System.out.println(ByteBufUtil.prettyHexDump(Unpooled
ByteBuf buf = Unpooled
.buffer()
.writeInt(byteBuf.readableBytes())
.writeBytes(byteBuf)));
.writeBytes(byteBuf);
System.out.println(ByteBufUtil.prettyHexDump(buf));
//登录报文
System.out.println(ByteBufUtil.hexDump(buf));
}
@Test
@ -97,11 +99,19 @@ public class BinaryMessageTypeTest {
ByteBuf data = BinaryMessageType.write(message, Unpooled.buffer());
System.out.println(ByteBufUtil.prettyHexDump(data));
DeviceMessage read = BinaryMessageType.read(data);
// System.out.println(ByteBufUtil.prettyHexDump(data));
ByteBuf buf = Unpooled.buffer()
.writeInt(data.readableBytes())
.writeBytes(data);
System.out.println(ByteBufUtil.prettyHexDump(buf));
System.out.println(ByteBufUtil.hexDump(buf));
//将长度字节读取后直接解析报文正文
buf.readInt();
DeviceMessage read = BinaryMessageType.read(buf);
if (null != read.getHeaders()) {
read.getHeaders().forEach(message::addHeader);
}
System.out.println(read);
Assert.assertEquals(read.toString(), message.toString());
}