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