Commit f70f578ac59fd6b357245499adac6b6e3557e304

Authored by YunaiV
1 parent bd8052f5

fix:【IoT 物联网】code review tcp 实现

yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/IotTcpBinaryDeviceMessageCodec.java
... ... @@ -10,11 +10,12 @@ import lombok.Data;
10 10 import lombok.extern.slf4j.Slf4j;
11 11 import org.springframework.stereotype.Component;
12 12  
  13 +// TODO @haohao:设备地址(变长) 是不是非必要哈?因为认证后,不需要每次都带呀。
13 14 /**
14 15 * TCP 二进制格式 {@link IotDeviceMessage} 编解码器
15 16 *
16 17 * 使用自定义二进制协议格式:
17   - * 包头(4字节) | 地址长度(2字节) | 设备地址(变长) | 功能码(2字节) | 消息序号(2字节) | 包体数据(变长)
  18 + * 包头(4 字节) | 地址长度(2 字节) | 设备地址(变长) | 功能码(2 字节) | 消息序号(2 字节) | 包体数据(变长)
18 19 *
19 20 * @author 芋道源码
20 21 */
... ... @@ -27,6 +28,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
27 28 */
28 29 public static final String TYPE = "TCP_BINARY";
29 30  
  31 + // TODO @haohao:这个注释不太对。
30 32 // ==================== 常量定义 ====================
31 33  
32 34 @Override
... ... @@ -67,10 +69,11 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
67 69 TcpDataPackage dataPackage = decodeTcpDataPackage(Buffer.buffer(bytes));
68 70  
69 71 // 2. 根据功能码确定方法
  72 + // TODO @haohao:会不会有事件上报哈。
70 73 String method = (dataPackage.getCode() == TcpDataPackage.CODE_HEARTBEAT) ?
71 74 MessageMethod.STATE_ONLINE : MessageMethod.PROPERTY_POST;
72 75  
73   - // 3. 解析负载数据和请求ID
  76 + // 3. 解析负载数据和请求 ID
74 77 PayloadInfo payloadInfo = parsePayloadInfo(dataPackage.getPayload());
75 78  
76 79 // 4. 构建 IoT 设备消息(设置完整的必要参数)
... ... @@ -78,13 +81,16 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
78 81 payloadInfo.getRequestId(), method, payloadInfo.getParams());
79 82  
80 83 // 5. 设置设备相关信息
  84 + // TODO @haohao:serverId 不是这里解析的哈。
81 85 Long deviceId = parseDeviceId(dataPackage.getAddr());
82 86 message.setDeviceId(deviceId);
83 87  
84   - // 6. 设置TCP协议相关信息
  88 + // 6. 设置 TCP 协议相关信息
  89 + // TODO @haohao:serverId 不是这里解析的哈。
85 90 message.setServerId(generateServerId(dataPackage));
86 91  
87   - // 7. 设置租户ID(TODO: 后续可以从设备信息中获取)
  92 + // 7. 设置租户 ID(TODO: 后续可以从设备信息中获取)
  93 + // TODO @haohao:租户 id 不是这里解析的哈。
88 94 // message.setTenantId(getTenantIdByDeviceId(deviceId));
89 95  
90 96 if (log.isDebugEnabled()) {
... ... @@ -104,6 +110,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
104 110 return TYPE;
105 111 }
106 112  
  113 + // TODO @haohao:这种简单解析,中间不用空格哈。
107 114 /**
108 115 * 构建完整负载
109 116 */
... ... @@ -130,12 +137,10 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
130 137 return payload.toString();
131 138 }
132 139  
133   -
134   -
135 140 // ==================== 编解码方法 ====================
136 141  
137 142 /**
138   - * 解析负载信息(包含requestId和params)
  143 + * 解析负载信息(包含 requestId 和 params)
139 144 */
140 145 private PayloadInfo parsePayloadInfo(String payload) {
141 146 if (StrUtil.isEmpty(payload)) {
... ... @@ -143,6 +148,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
143 148 }
144 149  
145 150 try {
  151 + // TODO @haohao:使用 jsonUtils
146 152 JSONObject jsonObject = JSONUtil.parseObj(payload);
147 153 String requestId = jsonObject.getStr(PayloadField.REQUEST_ID);
148 154 if (StrUtil.isEmpty(requestId)) {
... ... @@ -185,7 +191,7 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
185 191 * @return 服务ID
186 192 */
187 193 private String generateServerId(TcpDataPackage dataPackage) {
188   - // 使用协议类型 + 设备地址 + 消息序号生成唯一的服务ID
  194 + // 使用协议类型 + 设备地址 + 消息序号生成唯一的服务 ID
189 195 return String.format("tcp_%s_%d", dataPackage.getAddr(), dataPackage.getMid());
190 196 }
191 197  
... ... @@ -300,23 +306,28 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
300 306 * 消息方法常量
301 307 */
302 308 public static class MessageMethod {
  309 +
303 310 public static final String PROPERTY_POST = "thing.property.post"; // 数据上报
304 311 public static final String STATE_ONLINE = "thing.state.online"; // 心跳
  312 +
305 313 }
306 314  
307 315 /**
308 316 * 负载字段名
309 317 */
310 318 private static class PayloadField {
  319 +
311 320 public static final String METHOD = "method";
312 321 public static final String PARAMS = "params";
313 322 public static final String TIMESTAMP = "timestamp";
314 323 public static final String REQUEST_ID = "requestId";
315 324 public static final String MESSAGE_ID = "msgId";
  325 +
316 326 }
317 327  
318 328 // ==================== TCP 数据包编解码方法 ====================
319 329  
  330 + // TODO @haohao:lombok 简化
320 331 /**
321 332 * 负载信息类
322 333 */
... ... @@ -361,11 +372,13 @@ public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {
361 372  
362 373 // ==================== 自定义异常 ====================
363 374  
  375 + // TODO @haohao:可以搞个全局的;
364 376 /**
365 377 * TCP 编解码异常
366 378 */
367 379 public static class TcpCodecException extends RuntimeException {
368 380  
  381 + // TODO @haohao:非必要构造方法,可以去掉哈。
369 382 public TcpCodecException(String message) {
370 383 super(message);
371 384 }
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/IotTcpCodecManager.java
... ... @@ -8,11 +8,11 @@ import org.springframework.stereotype.Component;
8 8  
9 9 /**
10 10 * TCP编解码器管理器(简化版)
11   - *
  11 + *
12 12 * 核心功能:
13 13 * - 自动协议检测(二进制 vs JSON)
14 14 * - 统一编解码接口
15   - * - 默认使用JSON协议
  15 + * - 默认使用 JSON 协议
16 16 *
17 17 * @author 芋道源码
18 18 */
... ... @@ -22,6 +22,8 @@ public class IotTcpCodecManager implements IotDeviceMessageCodec {
22 22  
23 23 public static final String TYPE = "TCP";
24 24  
  25 + // TODO @haohao:@Resource
  26 +
25 27 @Autowired
26 28 private IotTcpBinaryDeviceMessageCodec binaryCodec;
27 29  
... ... @@ -40,21 +42,22 @@ public class IotTcpCodecManager implements IotDeviceMessageCodec {
40 42  
41 43 @Override
42 44 public byte[] encode(IotDeviceMessage message) {
43   - // 默认使用JSON协议编码
  45 + // 默认使用 JSON 协议编码
44 46 return jsonCodec.encode(message);
45 47 }
46 48  
  49 + // TODO @haohao:要不还是不自动检测,用户手动配置哈。简化一些。。。
47 50 @Override
48 51 public IotDeviceMessage decode(byte[] bytes) {
49 52 // 自动检测协议类型并解码
50 53 if (isJsonFormat(bytes)) {
51 54 if (log.isDebugEnabled()) {
52   - log.debug("[decode][检测到JSON协议] 数据长度: {}字节", bytes.length);
  55 + log.debug("[decode][检测到 JSON 协议,数据长度: {} 字节]", bytes.length);
53 56 }
54 57 return jsonCodec.decode(bytes);
55 58 } else {
56 59 if (log.isDebugEnabled()) {
57   - log.debug("[decode][检测到二进制协议] 数据长度: {}字节", bytes.length);
  60 + log.debug("[decode][检测到二进制协议,数据长度: {} 字节]", bytes.length);
58 61 }
59 62 return binaryCodec.decode(bytes);
60 63 }
... ... @@ -63,7 +66,7 @@ public class IotTcpCodecManager implements IotDeviceMessageCodec {
63 66 // ==================== 便捷方法 ====================
64 67  
65 68 /**
66   - * 使用JSON协议编码
  69 + * 使用 JSON 协议编码
67 70 */
68 71 public byte[] encodeJson(IotDeviceMessage message) {
69 72 return jsonCodec.encode(message);
... ... @@ -95,42 +98,46 @@ public class IotTcpCodecManager implements IotDeviceMessageCodec {
95 98  
96 99 /**
97 100 * 检测是否为JSON格式
98   - *
  101 + *
99 102 * 检测规则:
100 103 * 1. 数据以 '{' 开头
101 104 * 2. 包含 "method" 或 "id" 字段
102 105 */
103 106 private boolean isJsonFormat(byte[] bytes) {
  107 + // TODO @haohao:ArrayUtil.isEmpty(bytes) 可以简化下
104 108 if (bytes == null || bytes.length == 0) {
105 109 return useJsonByDefault;
106 110 }
107 111  
108 112 try {
109   - // 检测JSON格式:以 '{' 开头
  113 + // 检测 JSON 格式:以 '{' 开头
110 114 if (bytes[0] == '{') {
111   - // 进一步验证是否为有效JSON
  115 + // TODO @haohao:不一定按照顺序写,这个可能要看下。
  116 + // 进一步验证是否为有效 JSON
112 117 String jsonStr = new String(bytes, 0, Math.min(bytes.length, 100));
113 118 return jsonStr.contains("\"method\"") || jsonStr.contains("\"id\"");
114 119 }
115 120  
116 121 // 检测二进制格式:长度 >= 8 且符合二进制协议结构
117 122 if (bytes.length >= 8) {
118   - // 读取包头(前4字节表示后续数据长度)
  123 + // 读取包头(前 4 字节表示后续数据长度)
119 124 int expectedLength = ((bytes[0] & 0xFF) << 24) |
120 125 ((bytes[1] & 0xFF) << 16) |
121 126 ((bytes[2] & 0xFF) << 8) |
122 127 (bytes[3] & 0xFF);
123   -
  128 +
124 129 // 验证长度是否合理
  130 + // TODO @haohao:expectedLength > 0 多余的貌似;
125 131 if (expectedLength == bytes.length - 4 && expectedLength > 0 && expectedLength < 1024 * 1024) {
126 132 return false; // 二进制格式
127 133 }
128 134 }
129 135 } catch (Exception e) {
130   - log.warn("[isJsonFormat][协议检测异常] 使用默认协议: {}", getDefaultProtocol(), e);
  136 + log.warn("[isJsonFormat][协议检测异常,使用默认协议: {}]", getDefaultProtocol(), e);
131 137 }
132 138  
133 139 // 默认使用当前设置的协议类型
134 140 return useJsonByDefault;
135 141 }
  142 +
136 143 }
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/IotTcpJsonDeviceMessageCodec.java
... ... @@ -12,16 +12,16 @@ import org.springframework.stereotype.Component;
12 12 import java.nio.charset.StandardCharsets;
13 13  
14 14 /**
15   - * TCP JSON格式 {@link IotDeviceMessage} 编解码器
16   - *
17   - * 采用纯JSON格式传输,参考EMQX和HTTP模块的数据格式
18   - *
  15 + * TCP JSON 格式 {@link IotDeviceMessage} 编解码器
  16 + *
  17 + * 采用纯 JSON 格式传输,参考 EMQX 和 HTTP 模块的数据格式
  18 + *
19 19 * JSON消息格式:
20 20 * {
21   - * "id": "消息ID",
  21 + * "id": "消息 ID",
22 22 * "method": "消息方法",
23   - * "deviceId": "设备ID",
24   - * "productKey": "产品Key",
  23 + * "deviceId": "设备 ID",
  24 + * "productKey": "产品 Key",
25 25 * "deviceName": "设备名称",
26 26 * "params": {...},
27 27 * "timestamp": 时间戳
... ... @@ -35,6 +35,7 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec {
35 35  
36 36 public static final String TYPE = "TCP_JSON";
37 37  
  38 + // TODO @haohao:变量不太对;
38 39 // ==================== 常量定义 ====================
39 40  
40 41 @Override
... ... @@ -77,14 +78,15 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec {
77 78 }
78 79  
79 80 try {
80   - // 转换为JSON字符串
  81 + // 转换为 JSON 字符串
81 82 String jsonString = new String(bytes, StandardCharsets.UTF_8);
82 83  
83 84 if (log.isDebugEnabled()) {
84 85 log.debug("[decode][开始解码] JSON长度: {}字节, 内容: {}", bytes.length, jsonString);
85 86 }
86 87  
87   - // 解析JSON消息
  88 + // 解析 JSON 消息
  89 + // TODO @haohao:JsonUtils
88 90 JSONObject jsonMessage = JSONUtil.parseObj(jsonString);
89 91  
90 92 // 构建IoT设备消息
... ... @@ -129,7 +131,7 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec {
129 131 }
130 132  
131 133 /**
132   - * 构建JSON消息
  134 + * 构建 JSON 消息
133 135 */
134 136 private JSONObject buildJsonMessage(IotDeviceMessage message) {
135 137 JSONObject jsonMessage = new JSONObject();
... ... @@ -189,7 +191,7 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec {
189 191 message.setMsg(msg);
190 192 }
191 193  
192   - // 设置服务ID(基于JSON格式)
  194 + // 设置服务 ID(基于 JSON 格式)
193 195 message.setServerId(generateServerId(jsonMessage));
194 196  
195 197 return message;
... ... @@ -216,22 +218,26 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec {
216 218 StrUtil.isNotEmpty(id) ? id.substring(0, Math.min(8, id.length())) : "noId");
217 219 }
218 220  
  221 + // TODO @haohao:注释格式不对;
219 222 /**
220 223 * 消息方法常量
221 224 */
222 225 public static class MessageMethod {
  226 +
223 227 public static final String PROPERTY_POST = "thing.property.post"; // 数据上报
224 228 public static final String STATE_ONLINE = "thing.state.online"; // 心跳
225 229 public static final String EVENT_POST = "thing.event.post"; // 事件上报
226 230 public static final String PROPERTY_SET = "thing.property.set"; // 属性设置
227 231 public static final String PROPERTY_GET = "thing.property.get"; // 属性获取
228 232 public static final String SERVICE_INVOKE = "thing.service.invoke"; // 服务调用
  233 +
229 234 }
230 235  
231 236 /**
232 237 * JSON字段名(参考EMQX和HTTP模块格式)
233 238 */
234 239 private static class JsonField {
  240 +
235 241 public static final String ID = "id";
236 242 public static final String METHOD = "method";
237 243 public static final String DEVICE_ID = "deviceId";
... ... @@ -241,5 +247,6 @@ public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec {
241 247 public static final String TIMESTAMP = "timestamp";
242 248 public static final String CODE = "code";
243 249 public static final String MESSAGE = "message";
  250 +
244 251 }
245 252 }
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpUpstreamProtocol.java
... ... @@ -30,6 +30,7 @@ public class IotTcpUpstreamProtocol {
30 30  
31 31 private final IotDeviceMessageService messageService;
32 32  
  33 + // TODO @haohao:不用的变量,可以删除;
33 34 private final IotDeviceCommonApi deviceApi;
34 35  
35 36 private final IotTcpCodecManager codecManager;
... ... @@ -58,6 +59,7 @@ public class IotTcpUpstreamProtocol {
58 59  
59 60 @PostConstruct
60 61 public void start() {
  62 + // TODO @haohao:类似下面 62 到 75 是处理 options 的,因为中间写了注释,其实可以不用空行;然后 77 到 91 可以中间空喊去掉,更紧凑一点;
61 63 // 创建服务器选项
62 64 NetServerOptions options = new NetServerOptions()
63 65 .setPort(tcpProperties.getPort())
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpDownstreamHandler.java
1 1 package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router;
2 2  
3 3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
4   -import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpDeviceMessageCodec;
5 4 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
6 5 import lombok.extern.slf4j.Slf4j;
7 6  
... ... @@ -24,11 +23,12 @@ public class IotTcpDownstreamHandler {
24 23  
25 24 private final IotDeviceMessageService messageService;
26 25  
27   - private final IotTcpDeviceMessageCodec codec;
  26 + // TODO @haohao:代码没提交全,有报错。
  27 +// private final IotTcpDeviceMessageCodec codec;
28 28  
29 29 public IotTcpDownstreamHandler(IotDeviceMessageService messageService) {
30 30 this.messageService = messageService;
31   - this.codec = new IotTcpDeviceMessageCodec();
  31 +// this.codec = new IotTcpDeviceMessageCodec();
32 32 }
33 33  
34 34 /**
... ... @@ -42,7 +42,8 @@ public class IotTcpDownstreamHandler {
42 42 message.getDeviceId(), message.getMethod(), message.getId());
43 43  
44 44 // 编码消息用于日志记录和验证
45   - byte[] encodedMessage = codec.encode(message);
  45 + byte[] encodedMessage = null;
  46 +// codec.encode(message);
46 47 log.debug("[handle][消息编码成功] 设备ID: {}, 编码后长度: {} 字节",
47 48 message.getDeviceId(), encodedMessage.length);
48 49  
... ...
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/TcpBinaryDataPacketExamples.java
... ... @@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
6 6 import java.util.HashMap;
7 7 import java.util.Map;
8 8  
  9 +// TODO @haohao:这种写成单测,会好点
9 10 /**
10 11 * TCP二进制格式数据包示例
11 12 *
... ... @@ -21,13 +22,13 @@ public class TcpBinaryDataPacketExamples {
21 22  
22 23 public static void main(String[] args) {
23 24 IotTcpBinaryDeviceMessageCodec codec = new IotTcpBinaryDeviceMessageCodec();
24   -
  25 +
25 26 // 1. 数据上报包示例
26 27 demonstrateDataReport(codec);
27   -
  28 +
28 29 // 2. 心跳包示例
29 30 demonstrateHeartbeat(codec);
30   -
  31 +
31 32 // 3. 复杂数据上报示例
32 33 demonstrateComplexDataReport(codec);
33 34 }
... ... @@ -37,23 +38,23 @@ public class TcpBinaryDataPacketExamples {
37 38 */
38 39 private static void demonstrateDataReport(IotTcpBinaryDeviceMessageCodec codec) {
39 40 log.info("=== 二进制格式数据上报包示例 ===");
40   -
  41 +
41 42 // 创建传感器数据
42 43 Map<String, Object> sensorData = new HashMap<>();
43 44 sensorData.put("temperature", 25.5);
44 45 sensorData.put("humidity", 60.2);
45 46 sensorData.put("pressure", 1013.25);
46 47 sensorData.put("battery", 85);
47   -
  48 +
48 49 // 创建设备消息
49 50 IotDeviceMessage message = IotDeviceMessage.requestOf("thing.property.post", sensorData);
50 51 message.setDeviceId(123456L);
51   -
  52 +
52 53 // 编码
53 54 byte[] packet = codec.encode(message);
54 55 log.info("编码后数据包长度: {} 字节", packet.length);
55 56 log.info("编码后数据包(HEX): {}", bytesToHex(packet));
56   -
  57 +
57 58 // 解码验证
58 59 IotDeviceMessage decoded = codec.decode(packet);
59 60 log.info("解码后消息ID: {}", decoded.getId());
... ... @@ -63,7 +64,7 @@ public class TcpBinaryDataPacketExamples {
63 64 log.info("解码后服务ID: {}", decoded.getServerId());
64 65 log.info("解码后上报时间: {}", decoded.getReportTime());
65 66 log.info("解码后参数: {}", decoded.getParams());
66   -
  67 +
67 68 System.out.println();
68 69 }
69 70  
... ... @@ -72,16 +73,16 @@ public class TcpBinaryDataPacketExamples {
72 73 */
73 74 private static void demonstrateHeartbeat(IotTcpBinaryDeviceMessageCodec codec) {
74 75 log.info("=== 二进制格式心跳包示例 ===");
75   -
  76 +
76 77 // 创建心跳消息
77 78 IotDeviceMessage heartbeat = IotDeviceMessage.requestOf("thing.state.online", null);
78 79 heartbeat.setDeviceId(123456L);
79   -
  80 +
80 81 // 编码
81 82 byte[] packet = codec.encode(heartbeat);
82 83 log.info("心跳包长度: {} 字节", packet.length);
83 84 log.info("心跳包(HEX): {}", bytesToHex(packet));
84   -
  85 +
85 86 // 解码验证
86 87 IotDeviceMessage decoded = codec.decode(packet);
87 88 log.info("解码后消息ID: {}", decoded.getId());
... ... @@ -90,7 +91,7 @@ public class TcpBinaryDataPacketExamples {
90 91 log.info("解码后设备ID: {}", decoded.getDeviceId());
91 92 log.info("解码后服务ID: {}", decoded.getServerId());
92 93 log.info("解码后参数: {}", decoded.getParams());
93   -
  94 +
94 95 System.out.println();
95 96 }
96 97  
... ... @@ -99,40 +100,40 @@ public class TcpBinaryDataPacketExamples {
99 100 */
100 101 private static void demonstrateComplexDataReport(IotTcpBinaryDeviceMessageCodec codec) {
101 102 log.info("=== 二进制格式复杂数据上报示例 ===");
102   -
  103 +
103 104 // 创建复杂设备数据
104 105 Map<String, Object> deviceData = new HashMap<>();
105   -
  106 +
106 107 // 环境数据
107 108 Map<String, Object> environment = new HashMap<>();
108 109 environment.put("temperature", 23.8);
109 110 environment.put("humidity", 55.0);
110 111 environment.put("co2", 420);
111 112 deviceData.put("environment", environment);
112   -
  113 +
113 114 // GPS数据
114 115 Map<String, Object> location = new HashMap<>();
115 116 location.put("latitude", 39.9042);
116 117 location.put("longitude", 116.4074);
117 118 location.put("altitude", 43.5);
118 119 deviceData.put("location", location);
119   -
  120 +
120 121 // 设备状态
121 122 Map<String, Object> status = new HashMap<>();
122 123 status.put("battery", 78);
123 124 status.put("signal", -65);
124 125 status.put("online", true);
125 126 deviceData.put("status", status);
126   -
  127 +
127 128 // 创建设备消息
128 129 IotDeviceMessage message = IotDeviceMessage.requestOf("thing.property.post", deviceData);
129 130 message.setDeviceId(789012L);
130   -
  131 +
131 132 // 编码
132 133 byte[] packet = codec.encode(message);
133 134 log.info("复杂数据包长度: {} 字节", packet.length);
134 135 log.info("复杂数据包(HEX): {}", bytesToHex(packet));
135   -
  136 +
136 137 // 解码验证
137 138 IotDeviceMessage decoded = codec.decode(packet);
138 139 log.info("解码后消息ID: {}", decoded.getId());
... ... @@ -141,7 +142,7 @@ public class TcpBinaryDataPacketExamples {
141 142 log.info("解码后设备ID: {}", decoded.getDeviceId());
142 143 log.info("解码后服务ID: {}", decoded.getServerId());
143 144 log.info("解码后参数: {}", decoded.getParams());
144   -
  145 +
145 146 System.out.println();
146 147 }
147 148  
... ... @@ -164,9 +165,9 @@ public class TcpBinaryDataPacketExamples {
164 165 log.error("数据包长度不足");
165 166 return;
166 167 }
167   -
  168 +
168 169 int index = 0;
169   -
  170 +
170 171 // 解析包头(4字节) - 后续数据长度
171 172 int totalLength = ((packet[index] & 0xFF) << 24) |
172 173 ((packet[index + 1] & 0xFF) << 16) |
... ... @@ -174,27 +175,27 @@ public class TcpBinaryDataPacketExamples {
174 175 (packet[index + 3] & 0xFF);
175 176 index += 4;
176 177 log.info("包头 - 后续数据长度: {} 字节", totalLength);
177   -
  178 +
178 179 // 解析设备地址长度(2字节)
179 180 int addrLength = ((packet[index] & 0xFF) << 8) | (packet[index + 1] & 0xFF);
180 181 index += 2;
181 182 log.info("设备地址长度: {} 字节", addrLength);
182   -
  183 +
183 184 // 解析设备地址
184 185 String deviceAddr = new String(packet, index, addrLength);
185 186 index += addrLength;
186 187 log.info("设备地址: {}", deviceAddr);
187   -
  188 +
188 189 // 解析功能码(2字节)
189 190 int functionCode = ((packet[index] & 0xFF) << 8) | (packet[index + 1] & 0xFF);
190 191 index += 2;
191 192 log.info("功能码: {} ({})", functionCode, getFunctionCodeName(functionCode));
192   -
  193 +
193 194 // 解析消息序号(2字节)
194 195 int messageId = ((packet[index] & 0xFF) << 8) | (packet[index + 1] & 0xFF);
195 196 index += 2;
196 197 log.info("消息序号: {}", messageId);
197   -
  198 +
198 199 // 解析包体数据
199 200 if (index < packet.length) {
200 201 String payload = new String(packet, index, packet.length - index);
... ...
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/TcpJsonDataPacketExamples.java
... ... @@ -7,9 +7,10 @@ import java.nio.charset.StandardCharsets;
7 7 import java.util.HashMap;
8 8 import java.util.Map;
9 9  
  10 +// TODO @haohao:这种写成单测,会好点
10 11 /**
11 12 * TCP JSON格式数据包示例
12   - *
  13 + *
13 14 * 演示如何使用新的JSON格式进行TCP消息编解码
14 15 *
15 16 * @author 芋道源码
... ... @@ -19,22 +20,22 @@ public class TcpJsonDataPacketExamples {
19 20  
20 21 public static void main(String[] args) {
21 22 IotTcpJsonDeviceMessageCodec codec = new IotTcpJsonDeviceMessageCodec();
22   -
  23 +
23 24 // 1. 数据上报示例
24 25 demonstrateDataReport(codec);
25   -
  26 +
26 27 // 2. 心跳示例
27 28 demonstrateHeartbeat(codec);
28   -
  29 +
29 30 // 3. 事件上报示例
30 31 demonstrateEventReport(codec);
31   -
  32 +
32 33 // 4. 复杂数据上报示例
33 34 demonstrateComplexDataReport(codec);
34   -
  35 +
35 36 // 5. 便捷方法示例
36 37 demonstrateConvenienceMethods();
37   -
  38 +
38 39 // 6. EMQX兼容性示例
39 40 demonstrateEmqxCompatibility();
40 41 }
... ... @@ -44,24 +45,24 @@ public class TcpJsonDataPacketExamples {
44 45 */
45 46 private static void demonstrateDataReport(IotTcpJsonDeviceMessageCodec codec) {
46 47 log.info("=== JSON格式数据上报示例 ===");
47   -
  48 +
48 49 // 创建传感器数据
49 50 Map<String, Object> sensorData = new HashMap<>();
50 51 sensorData.put("temperature", 25.5);
51 52 sensorData.put("humidity", 60.2);
52 53 sensorData.put("pressure", 1013.25);
53 54 sensorData.put("battery", 85);
54   -
  55 +
55 56 // 创建设备消息
56 57 IotDeviceMessage message = IotDeviceMessage.requestOf("thing.property.post", sensorData);
57 58 message.setDeviceId(123456L);
58   -
  59 +
59 60 // 编码
60 61 byte[] packet = codec.encode(message);
61 62 String jsonString = new String(packet, StandardCharsets.UTF_8);
62 63 log.info("编码后JSON: {}", jsonString);
63 64 log.info("数据包长度: {} 字节", packet.length);
64   -
  65 +
65 66 // 解码验证
66 67 IotDeviceMessage decoded = codec.decode(packet);
67 68 log.info("解码后消息ID: {}", decoded.getId());
... ... @@ -69,7 +70,7 @@ public class TcpJsonDataPacketExamples {
69 70 log.info("解码后设备ID: {}", decoded.getDeviceId());
70 71 log.info("解码后服务ID: {}", decoded.getServerId());
71 72 log.info("解码后参数: {}", decoded.getParams());
72   -
  73 +
73 74 System.out.println();
74 75 }
75 76  
... ... @@ -78,24 +79,24 @@ public class TcpJsonDataPacketExamples {
78 79 */
79 80 private static void demonstrateHeartbeat(IotTcpJsonDeviceMessageCodec codec) {
80 81 log.info("=== JSON格式心跳示例 ===");
81   -
  82 +
82 83 // 创建心跳消息
83 84 IotDeviceMessage heartbeat = IotDeviceMessage.requestOf("thing.state.online", null);
84 85 heartbeat.setDeviceId(123456L);
85   -
  86 +
86 87 // 编码
87 88 byte[] packet = codec.encode(heartbeat);
88 89 String jsonString = new String(packet, StandardCharsets.UTF_8);
89 90 log.info("编码后JSON: {}", jsonString);
90 91 log.info("心跳包长度: {} 字节", packet.length);
91   -
  92 +
92 93 // 解码验证
93 94 IotDeviceMessage decoded = codec.decode(packet);
94 95 log.info("解码后消息ID: {}", decoded.getId());
95 96 log.info("解码后方法: {}", decoded.getMethod());
96 97 log.info("解码后设备ID: {}", decoded.getDeviceId());
97 98 log.info("解码后服务ID: {}", decoded.getServerId());
98   -
  99 +
99 100 System.out.println();
100 101 }
101 102  
... ... @@ -104,31 +105,31 @@ public class TcpJsonDataPacketExamples {
104 105 */
105 106 private static void demonstrateEventReport(IotTcpJsonDeviceMessageCodec codec) {
106 107 log.info("=== JSON格式事件上报示例 ===");
107   -
  108 +
108 109 // 创建事件数据
109 110 Map<String, Object> eventData = new HashMap<>();
110 111 eventData.put("eventType", "alarm");
111 112 eventData.put("level", "warning");
112 113 eventData.put("description", "温度过高");
113 114 eventData.put("value", 45.8);
114   -
  115 +
115 116 // 创建事件消息
116 117 IotDeviceMessage event = IotDeviceMessage.requestOf("thing.event.post", eventData);
117 118 event.setDeviceId(123456L);
118   -
  119 +
119 120 // 编码
120 121 byte[] packet = codec.encode(event);
121 122 String jsonString = new String(packet, StandardCharsets.UTF_8);
122 123 log.info("编码后JSON: {}", jsonString);
123 124 log.info("事件包长度: {} 字节", packet.length);
124   -
  125 +
125 126 // 解码验证
126 127 IotDeviceMessage decoded = codec.decode(packet);
127 128 log.info("解码后消息ID: {}", decoded.getId());
128 129 log.info("解码后方法: {}", decoded.getMethod());
129 130 log.info("解码后设备ID: {}", decoded.getDeviceId());
130 131 log.info("解码后参数: {}", decoded.getParams());
131   -
  132 +
132 133 System.out.println();
133 134 }
134 135  
... ... @@ -137,10 +138,10 @@ public class TcpJsonDataPacketExamples {
137 138 */
138 139 private static void demonstrateComplexDataReport(IotTcpJsonDeviceMessageCodec codec) {
139 140 log.info("=== JSON格式复杂数据上报示例 ===");
140   -
  141 +
141 142 // 创建复杂设备数据(类似EMQX格式)
142 143 Map<String, Object> deviceData = new HashMap<>();
143   -
  144 +
144 145 // 环境数据
145 146 Map<String, Object> environment = new HashMap<>();
146 147 environment.put("temperature", 23.8);
... ... @@ -148,7 +149,7 @@ public class TcpJsonDataPacketExamples {
148 149 environment.put("co2", 420);
149 150 environment.put("pm25", 35);
150 151 deviceData.put("environment", environment);
151   -
  152 +
152 153 // GPS数据
153 154 Map<String, Object> location = new HashMap<>();
154 155 location.put("latitude", 39.9042);
... ... @@ -156,7 +157,7 @@ public class TcpJsonDataPacketExamples {
156 157 location.put("altitude", 43.5);
157 158 location.put("speed", 0.0);
158 159 deviceData.put("location", location);
159   -
  160 +
160 161 // 设备状态
161 162 Map<String, Object> status = new HashMap<>();
162 163 status.put("battery", 78);
... ... @@ -164,24 +165,24 @@ public class TcpJsonDataPacketExamples {
164 165 status.put("online", true);
165 166 status.put("version", "1.2.3");
166 167 deviceData.put("status", status);
167   -
  168 +
168 169 // 创建设备消息
169 170 IotDeviceMessage message = IotDeviceMessage.requestOf("thing.property.post", deviceData);
170 171 message.setDeviceId(789012L);
171   -
  172 +
172 173 // 编码
173 174 byte[] packet = codec.encode(message);
174 175 String jsonString = new String(packet, StandardCharsets.UTF_8);
175 176 log.info("编码后JSON: {}", jsonString);
176 177 log.info("复杂数据包长度: {} 字节", packet.length);
177   -
  178 +
178 179 // 解码验证
179 180 IotDeviceMessage decoded = codec.decode(packet);
180 181 log.info("解码后消息ID: {}", decoded.getId());
181 182 log.info("解码后方法: {}", decoded.getMethod());
182 183 log.info("解码后设备ID: {}", decoded.getDeviceId());
183 184 log.info("解码后参数: {}", decoded.getParams());
184   -
  185 +
185 186 System.out.println();
186 187 }
187 188  
... ... @@ -190,9 +191,9 @@ public class TcpJsonDataPacketExamples {
190 191 */
191 192 private static void demonstrateConvenienceMethods() {
192 193 log.info("=== 便捷方法示例 ===");
193   -
  194 +
194 195 IotTcpJsonDeviceMessageCodec codec = new IotTcpJsonDeviceMessageCodec();
195   -
  196 +
196 197 // 使用便捷方法编码数据上报
197 198 Map<String, Object> sensorData = Map.of(
198 199 "temperature", 26.5,
... ... @@ -200,11 +201,11 @@ public class TcpJsonDataPacketExamples {
200 201 );
201 202 byte[] dataPacket = codec.encodeDataReport(sensorData, 123456L, "smart_sensor", "device_001");
202 203 log.info("便捷方法编码数据上报: {}", new String(dataPacket, StandardCharsets.UTF_8));
203   -
  204 +
204 205 // 使用便捷方法编码心跳
205 206 byte[] heartbeatPacket = codec.encodeHeartbeat(123456L, "smart_sensor", "device_001");
206 207 log.info("便捷方法编码心跳: {}", new String(heartbeatPacket, StandardCharsets.UTF_8));
207   -
  208 +
208 209 // 使用便捷方法编码事件
209 210 Map<String, Object> eventData = Map.of(
210 211 "eventType", "maintenance",
... ... @@ -212,7 +213,7 @@ public class TcpJsonDataPacketExamples {
212 213 );
213 214 byte[] eventPacket = codec.encodeEventReport(eventData, 123456L, "smart_sensor", "device_001");
214 215 log.info("便捷方法编码事件: {}", new String(eventPacket, StandardCharsets.UTF_8));
215   -
  216 +
216 217 System.out.println();
217 218 }
218 219  
... ... @@ -221,7 +222,7 @@ public class TcpJsonDataPacketExamples {
221 222 */
222 223 private static void demonstrateEmqxCompatibility() {
223 224 log.info("=== EMQX格式兼容性示例 ===");
224   -
  225 +
225 226 // 模拟EMQX风格的消息格式
226 227 String emqxStyleJson = """
227 228 {
... ... @@ -235,19 +236,19 @@ public class TcpJsonDataPacketExamples {
235 236 "timestamp": 1642781234567
236 237 }
237 238 """;
238   -
  239 +
239 240 IotTcpJsonDeviceMessageCodec codec = new IotTcpJsonDeviceMessageCodec();
240   -
  241 +
241 242 // 解码EMQX风格的消息
242 243 byte[] emqxBytes = emqxStyleJson.getBytes(StandardCharsets.UTF_8);
243 244 IotDeviceMessage decoded = codec.decode(emqxBytes);
244   -
  245 +
245 246 log.info("EMQX风格消息解码成功:");
246 247 log.info("消息ID: {}", decoded.getId());
247 248 log.info("方法: {}", decoded.getMethod());
248 249 log.info("设备ID: {}", decoded.getDeviceId());
249 250 log.info("参数: {}", decoded.getParams());
250   -
  251 +
251 252 System.out.println();
252 253 }
253 254 }
... ...