Commit a77e1780ccfa9c42d325fe5da7d8e8a1fde7dfcf
1 parent
cc0d786d
feat(iot):【协议改造】移除 codecType,使用 protocolType 替代
Showing
25 changed files
with
159 additions
and
662 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java
| @@ -58,7 +58,7 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { | @@ -58,7 +58,7 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { | ||
| 58 | return success(BeanUtils.toBean(device, IotDeviceRespDTO.class, deviceDTO -> { | 58 | return success(BeanUtils.toBean(device, IotDeviceRespDTO.class, deviceDTO -> { |
| 59 | IotProductDO product = productService.getProductFromCache(deviceDTO.getProductId()); | 59 | IotProductDO product = productService.getProductFromCache(deviceDTO.getProductId()); |
| 60 | if (product != null) { | 60 | if (product != null) { |
| 61 | - deviceDTO.setCodecType(product.getCodecType()); | 61 | + deviceDTO.setProtocolType(product.getProtocolType()).setSerializeType(product.getSerializeType()); |
| 62 | } | 62 | } |
| 63 | })); | 63 | })); |
| 64 | } | 64 | } |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java
| @@ -67,10 +67,15 @@ public class IotProductRespVO { | @@ -67,10 +67,15 @@ public class IotProductRespVO { | ||
| 67 | @DictFormat(DictTypeConstants.NET_TYPE) | 67 | @DictFormat(DictTypeConstants.NET_TYPE) |
| 68 | private Integer netType; | 68 | private Integer netType; |
| 69 | 69 | ||
| 70 | - @Schema(description = "数据格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | ||
| 71 | - @ExcelProperty(value = "数据格式", converter = DictConvert.class) | ||
| 72 | - @DictFormat(DictTypeConstants.CODEC_TYPE) | ||
| 73 | - private String codecType; | 70 | + @Schema(description = "协议类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "mqtt") |
| 71 | + @ExcelProperty(value = "协议类型", converter = DictConvert.class) | ||
| 72 | + @DictFormat(DictTypeConstants.PROTOCOL_TYPE) | ||
| 73 | + private String protocolType; | ||
| 74 | + | ||
| 75 | + @Schema(description = "序列化类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "json") | ||
| 76 | + @ExcelProperty(value = "序列化类型", converter = DictConvert.class) | ||
| 77 | + @DictFormat(DictTypeConstants.SERIALIZE_TYPE) | ||
| 78 | + private String serializeType; | ||
| 74 | 79 | ||
| 75 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | 80 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 76 | @ExcelProperty("创建时间") | 81 | @ExcelProperty("创建时间") |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java
| 1 | package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; | 1 | package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.validation.InEnum; | 3 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 4 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum; | ||
| 4 | import cn.iocoder.yudao.module.iot.enums.product.IotNetTypeEnum; | 6 | import cn.iocoder.yudao.module.iot.enums.product.IotNetTypeEnum; |
| 5 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; | 7 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; |
| 6 | import io.swagger.v3.oas.annotations.media.Schema; | 8 | import io.swagger.v3.oas.annotations.media.Schema; |
| @@ -44,9 +46,15 @@ public class IotProductSaveReqVO { | @@ -44,9 +46,15 @@ public class IotProductSaveReqVO { | ||
| 44 | @InEnum(value = IotNetTypeEnum.class, message = "联网方式必须是 {value}") | 46 | @InEnum(value = IotNetTypeEnum.class, message = "联网方式必须是 {value}") |
| 45 | private Integer netType; | 47 | private Integer netType; |
| 46 | 48 | ||
| 47 | - @Schema(description = "数据格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | ||
| 48 | - @NotEmpty(message = "数据格式不能为空") | ||
| 49 | - private String codecType; | 49 | + @Schema(description = "协议类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "mqtt") |
| 50 | + @InEnum(value = IotProtocolTypeEnum.class, message = "协议类型必须是 {value}") | ||
| 51 | + @NotEmpty(message = "协议类型不能为空") | ||
| 52 | + private String protocolType; | ||
| 53 | + | ||
| 54 | + @Schema(description = "序列化类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "json") | ||
| 55 | + @InEnum(value = IotSerializeTypeEnum.class, message = "序列化类型必须是 {value}") | ||
| 56 | + @NotEmpty(message = "序列化类型不能为空") | ||
| 57 | + private String serializeType; | ||
| 50 | 58 | ||
| 51 | @Schema(description = "是否开启动态注册", example = "false") | 59 | @Schema(description = "是否开启动态注册", example = "false") |
| 52 | @NotNull(message = "是否开启动态注册不能为空") | 60 | @NotNull(message = "是否开启动态注册不能为空") |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java
| @@ -78,12 +78,16 @@ public class IotProductDO extends TenantBaseDO { | @@ -78,12 +78,16 @@ public class IotProductDO extends TenantBaseDO { | ||
| 78 | */ | 78 | */ |
| 79 | private Integer netType; | 79 | private Integer netType; |
| 80 | /** | 80 | /** |
| 81 | - * 数据格式(编解码器类型) | 81 | + * 协议类型 |
| 82 | * <p> | 82 | * <p> |
| 83 | - * 字典 {@link cn.iocoder.yudao.module.iot.enums.DictTypeConstants#CODEC_TYPE} | ||
| 84 | - * | ||
| 85 | - * 目的:用于 gateway-server 解析消息格式 | 83 | + * 枚举 {@link cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum} |
| 86 | */ | 84 | */ |
| 87 | - private String codecType; | 85 | + private String protocolType; |
| 86 | + /** | ||
| 87 | + * 序列化类型 | ||
| 88 | + * <p> | ||
| 89 | + * 枚举 {@link cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum} | ||
| 90 | + */ | ||
| 91 | + private String serializeType; | ||
| 88 | 92 | ||
| 89 | } | 93 | } |
| 90 | \ No newline at end of file | 94 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java
| @@ -8,8 +8,8 @@ package cn.iocoder.yudao.module.iot.enums; | @@ -8,8 +8,8 @@ package cn.iocoder.yudao.module.iot.enums; | ||
| 8 | public class DictTypeConstants { | 8 | public class DictTypeConstants { |
| 9 | 9 | ||
| 10 | public static final String NET_TYPE = "iot_net_type"; | 10 | public static final String NET_TYPE = "iot_net_type"; |
| 11 | - public static final String LOCATION_TYPE = "iot_location_type"; | ||
| 12 | - public static final String CODEC_TYPE = "iot_codec_type"; | 11 | + public static final String PROTOCOL_TYPE = "iot_protocol_type"; |
| 12 | + public static final String SERIALIZE_TYPE = "iot_serialize_type"; | ||
| 13 | 13 | ||
| 14 | public static final String PRODUCT_STATUS = "iot_product_status"; | 14 | public static final String PRODUCT_STATUS = "iot_product_status"; |
| 15 | public static final String PRODUCT_DEVICE_TYPE = "iot_product_device_type"; | 15 | public static final String PRODUCT_DEVICE_TYPE = "iot_product_device_type"; |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceRespDTO.java
| @@ -34,8 +34,12 @@ public class IotDeviceRespDTO { | @@ -34,8 +34,12 @@ public class IotDeviceRespDTO { | ||
| 34 | */ | 34 | */ |
| 35 | private Long productId; | 35 | private Long productId; |
| 36 | /** | 36 | /** |
| 37 | - * 编解码器类型 | 37 | + * 协议类型 |
| 38 | */ | 38 | */ |
| 39 | - private String codecType; | 39 | + private String protocolType; |
| 40 | + /** | ||
| 41 | + * 序列化类型 | ||
| 42 | + */ | ||
| 43 | + private String serializeType; | ||
| 40 | 44 | ||
| 41 | } | 45 | } |
| 42 | \ No newline at end of file | 46 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/IotDeviceMessageCodec.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.gateway.codec; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 4 | - | ||
| 5 | -/** | ||
| 6 | - * {@link cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage} 的编解码器 | ||
| 7 | - * | ||
| 8 | - * @author 芋道源码 | ||
| 9 | - */ | ||
| 10 | -public interface IotDeviceMessageCodec { | ||
| 11 | - | ||
| 12 | - /** | ||
| 13 | - * 编码消息 | ||
| 14 | - * | ||
| 15 | - * @param message 消息 | ||
| 16 | - * @return 编码后的消息内容 | ||
| 17 | - */ | ||
| 18 | - byte[] encode(IotDeviceMessage message); | ||
| 19 | - | ||
| 20 | - /** | ||
| 21 | - * 解码消息 | ||
| 22 | - * | ||
| 23 | - * @param bytes 消息内容 | ||
| 24 | - * @return 解码后的消息内容 | ||
| 25 | - */ | ||
| 26 | - IotDeviceMessage decode(byte[] bytes); | ||
| 27 | - | ||
| 28 | - /** | ||
| 29 | - * @return 数据格式(编码器类型) | ||
| 30 | - */ | ||
| 31 | - String type(); | ||
| 32 | - | ||
| 33 | -} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/alink/IotAlinkDeviceMessageCodec.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.gateway.codec.alink; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.lang.Assert; | ||
| 4 | -import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 5 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 6 | -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 7 | -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec; | ||
| 8 | -import lombok.AllArgsConstructor; | ||
| 9 | -import lombok.Data; | ||
| 10 | -import lombok.NoArgsConstructor; | ||
| 11 | -import org.springframework.stereotype.Component; | ||
| 12 | - | ||
| 13 | -/** | ||
| 14 | - * 阿里云 Alink {@link IotDeviceMessage} 的编解码器 | ||
| 15 | - * | ||
| 16 | - * @author 芋道源码 | ||
| 17 | - */ | ||
| 18 | -@Component | ||
| 19 | -public class IotAlinkDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 20 | - | ||
| 21 | - public static final String TYPE = "Alink"; | ||
| 22 | - | ||
| 23 | - @Data | ||
| 24 | - @NoArgsConstructor | ||
| 25 | - @AllArgsConstructor | ||
| 26 | - private static class AlinkMessage { | ||
| 27 | - | ||
| 28 | - public static final String VERSION_1 = "1.0"; | ||
| 29 | - | ||
| 30 | - /** | ||
| 31 | - * 消息 ID,且每个消息 ID 在当前设备具有唯一性 | ||
| 32 | - */ | ||
| 33 | - private String id; | ||
| 34 | - | ||
| 35 | - /** | ||
| 36 | - * 版本号 | ||
| 37 | - */ | ||
| 38 | - private String version; | ||
| 39 | - | ||
| 40 | - /** | ||
| 41 | - * 请求方法 | ||
| 42 | - */ | ||
| 43 | - private String method; | ||
| 44 | - | ||
| 45 | - /** | ||
| 46 | - * 请求参数 | ||
| 47 | - */ | ||
| 48 | - private Object params; | ||
| 49 | - | ||
| 50 | - /** | ||
| 51 | - * 响应结果 | ||
| 52 | - */ | ||
| 53 | - private Object data; | ||
| 54 | - /** | ||
| 55 | - * 响应错误码 | ||
| 56 | - */ | ||
| 57 | - private Integer code; | ||
| 58 | - /** | ||
| 59 | - * 响应提示 | ||
| 60 | - * | ||
| 61 | - * 特殊:这里阿里云是 message,为了保持和项目的 {@link CommonResult#getMsg()} 一致。 | ||
| 62 | - */ | ||
| 63 | - private String msg; | ||
| 64 | - | ||
| 65 | - } | ||
| 66 | - | ||
| 67 | - @Override | ||
| 68 | - public String type() { | ||
| 69 | - return TYPE; | ||
| 70 | - } | ||
| 71 | - | ||
| 72 | - @Override | ||
| 73 | - public byte[] encode(IotDeviceMessage message) { | ||
| 74 | - AlinkMessage alinkMessage = new AlinkMessage(message.getRequestId(), AlinkMessage.VERSION_1, | ||
| 75 | - message.getMethod(), message.getParams(), message.getData(), message.getCode(), message.getMsg()); | ||
| 76 | - return JsonUtils.toJsonByte(alinkMessage); | ||
| 77 | - } | ||
| 78 | - | ||
| 79 | - @Override | ||
| 80 | - @SuppressWarnings("DataFlowIssue") | ||
| 81 | - public IotDeviceMessage decode(byte[] bytes) { | ||
| 82 | - AlinkMessage alinkMessage = JsonUtils.parseObject(bytes, AlinkMessage.class); | ||
| 83 | - Assert.notNull(alinkMessage, "消息不能为空"); | ||
| 84 | - Assert.equals(alinkMessage.getVersion(), AlinkMessage.VERSION_1, "消息版本号必须是 1.0"); | ||
| 85 | - return IotDeviceMessage.of(alinkMessage.getId(), alinkMessage.getMethod(), alinkMessage.getParams(), | ||
| 86 | - alinkMessage.getData(), alinkMessage.getCode(), alinkMessage.getMsg()); | ||
| 87 | - } | ||
| 88 | - | ||
| 89 | -} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/package-info.java deleted
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/IotTcpBinaryDeviceMessageCodec.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.gateway.codec.tcp; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.lang.Assert; | ||
| 4 | -import cn.hutool.core.util.StrUtil; | ||
| 5 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 6 | -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 7 | -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 8 | -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec; | ||
| 9 | -import io.vertx.core.buffer.Buffer; | ||
| 10 | -import lombok.extern.slf4j.Slf4j; | ||
| 11 | -import org.springframework.stereotype.Component; | ||
| 12 | - | ||
| 13 | -import java.nio.charset.StandardCharsets; | ||
| 14 | - | ||
| 15 | -/** | ||
| 16 | - * TCP/UDP 二进制格式 {@link IotDeviceMessage} 编解码器 | ||
| 17 | - * <p> | ||
| 18 | - * 二进制协议格式(所有数值使用大端序): | ||
| 19 | - * | ||
| 20 | - * <pre> | ||
| 21 | - * +--------+--------+--------+---------------------------+--------+--------+ | ||
| 22 | - * | 魔术字 | 版本号 | 消息类型| 消息长度(4 字节) | | ||
| 23 | - * +--------+--------+--------+---------------------------+--------+--------+ | ||
| 24 | - * | 消息 ID 长度(2 字节) | 消息 ID (变长字符串) | | ||
| 25 | - * +--------+--------+--------+--------+--------+--------+--------+--------+ | ||
| 26 | - * | 方法名长度(2 字节) | 方法名(变长字符串) | | ||
| 27 | - * +--------+--------+--------+--------+--------+--------+--------+--------+ | ||
| 28 | - * | 消息体数据(变长) | | ||
| 29 | - * +--------+--------+--------+--------+--------+--------+--------+--------+ | ||
| 30 | - * </pre> | ||
| 31 | - * <p> | ||
| 32 | - * 消息体格式: | ||
| 33 | - * - 请求消息:params 数据(JSON) | ||
| 34 | - * - 响应消息:code (4字节) + msg 长度(2字节) + msg 字符串 + data 数据(JSON) | ||
| 35 | - * <p> | ||
| 36 | - * 注意:deviceId 不包含在协议中,由服务器根据连接上下文自动设置 | ||
| 37 | - * | ||
| 38 | - * @author 芋道源码 | ||
| 39 | - */ | ||
| 40 | -@Slf4j | ||
| 41 | -@Component | ||
| 42 | -public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 43 | - | ||
| 44 | - public static final String TYPE = "TCP_BINARY"; | ||
| 45 | - | ||
| 46 | - /** | ||
| 47 | - * 协议魔术字,用于协议识别 | ||
| 48 | - */ | ||
| 49 | - private static final byte MAGIC_NUMBER = (byte) 0x7E; | ||
| 50 | - | ||
| 51 | - /** | ||
| 52 | - * 协议版本号 | ||
| 53 | - */ | ||
| 54 | - private static final byte PROTOCOL_VERSION = (byte) 0x01; | ||
| 55 | - | ||
| 56 | - /** | ||
| 57 | - * 请求消息类型 | ||
| 58 | - */ | ||
| 59 | - private static final byte REQUEST = (byte) 0x01; | ||
| 60 | - | ||
| 61 | - /** | ||
| 62 | - * 响应消息类型 | ||
| 63 | - */ | ||
| 64 | - private static final byte RESPONSE = (byte) 0x02; | ||
| 65 | - | ||
| 66 | - /** | ||
| 67 | - * 协议头部固定长度(魔术字 + 版本号 + 消息类型 + 消息长度) | ||
| 68 | - */ | ||
| 69 | - private static final int HEADER_FIXED_LENGTH = 7; | ||
| 70 | - | ||
| 71 | - /** | ||
| 72 | - * 最小消息长度(头部 + 消息ID长度 + 方法名长度) | ||
| 73 | - */ | ||
| 74 | - private static final int MIN_MESSAGE_LENGTH = HEADER_FIXED_LENGTH + 4; | ||
| 75 | - | ||
| 76 | - @Override | ||
| 77 | - public String type() { | ||
| 78 | - return TYPE; | ||
| 79 | - } | ||
| 80 | - | ||
| 81 | - @Override | ||
| 82 | - public byte[] encode(IotDeviceMessage message) { | ||
| 83 | - Assert.notNull(message, "消息不能为空"); | ||
| 84 | - Assert.notBlank(message.getMethod(), "消息方法不能为空"); | ||
| 85 | - try { | ||
| 86 | - // 1. 确定消息类型 | ||
| 87 | - byte messageType = determineMessageType(message); | ||
| 88 | - // 2. 构建消息体 | ||
| 89 | - byte[] bodyData = buildMessageBody(message, messageType); | ||
| 90 | - // 3. 构建完整消息 | ||
| 91 | - return buildCompleteMessage(message, messageType, bodyData); | ||
| 92 | - } catch (Exception e) { | ||
| 93 | - log.error("[encode][TCP 二进制消息编码失败,消息: {}]", message, e); | ||
| 94 | - throw new RuntimeException("TCP 二进制消息编码失败: " + e.getMessage(), e); | ||
| 95 | - } | ||
| 96 | - } | ||
| 97 | - | ||
| 98 | - @Override | ||
| 99 | - public IotDeviceMessage decode(byte[] bytes) { | ||
| 100 | - Assert.notNull(bytes, "待解码数据不能为空"); | ||
| 101 | - Assert.isTrue(bytes.length >= MIN_MESSAGE_LENGTH, "数据包长度不足"); | ||
| 102 | - try { | ||
| 103 | - Buffer buffer = Buffer.buffer(bytes); | ||
| 104 | - // 解析协议头部和消息内容 | ||
| 105 | - int index = 0; | ||
| 106 | - // 1. 验证魔术字 | ||
| 107 | - byte magic = buffer.getByte(index++); | ||
| 108 | - Assert.isTrue(magic == MAGIC_NUMBER, "无效的协议魔术字: " + magic); | ||
| 109 | - | ||
| 110 | - // 2. 验证版本号 | ||
| 111 | - byte version = buffer.getByte(index++); | ||
| 112 | - Assert.isTrue(version == PROTOCOL_VERSION, "不支持的协议版本: " + version); | ||
| 113 | - | ||
| 114 | - // 3. 读取消息类型 | ||
| 115 | - byte messageType = buffer.getByte(index++); | ||
| 116 | - // 直接验证消息类型,无需抽取方法 | ||
| 117 | - Assert.isTrue(messageType == REQUEST || messageType == RESPONSE, | ||
| 118 | - "无效的消息类型: " + messageType); | ||
| 119 | - | ||
| 120 | - // 4. 读取消息长度 | ||
| 121 | - int messageLength = buffer.getInt(index); | ||
| 122 | - index += 4; | ||
| 123 | - Assert.isTrue(messageLength == buffer.length(), | ||
| 124 | - "消息长度不匹配,期望: " + messageLength + ", 实际: " + buffer.length()); | ||
| 125 | - | ||
| 126 | - // 5. 读取消息 ID | ||
| 127 | - short messageIdLength = buffer.getShort(index); | ||
| 128 | - index += 2; | ||
| 129 | - String messageId = buffer.getString(index, index + messageIdLength, StandardCharsets.UTF_8.name()); | ||
| 130 | - index += messageIdLength; | ||
| 131 | - | ||
| 132 | - // 6. 读取方法名 | ||
| 133 | - short methodLength = buffer.getShort(index); | ||
| 134 | - index += 2; | ||
| 135 | - String method = buffer.getString(index, index + methodLength, StandardCharsets.UTF_8.name()); | ||
| 136 | - index += methodLength; | ||
| 137 | - | ||
| 138 | - // 7. 解析消息体 | ||
| 139 | - return parseMessageBody(buffer, index, messageType, messageId, method); | ||
| 140 | - } catch (Exception e) { | ||
| 141 | - log.error("[decode][TCP 二进制消息解码失败,数据长度: {}]", bytes.length, e); | ||
| 142 | - throw new RuntimeException("TCP 二进制消息解码失败: " + e.getMessage(), e); | ||
| 143 | - } | ||
| 144 | - } | ||
| 145 | - | ||
| 146 | - /** | ||
| 147 | - * 确定消息类型 | ||
| 148 | - * 优化后的判断逻辑:有响应字段就是响应消息,否则就是请求消息 | ||
| 149 | - */ | ||
| 150 | - private byte determineMessageType(IotDeviceMessage message) { | ||
| 151 | - // 判断是否为响应消息:有响应码或响应消息时为响应 | ||
| 152 | - if (message.getCode() != null) { | ||
| 153 | - return RESPONSE; | ||
| 154 | - } | ||
| 155 | - // 默认为请求消息 | ||
| 156 | - return REQUEST; | ||
| 157 | - } | ||
| 158 | - | ||
| 159 | - /** | ||
| 160 | - * 构建消息体 | ||
| 161 | - */ | ||
| 162 | - private byte[] buildMessageBody(IotDeviceMessage message, byte messageType) { | ||
| 163 | - Buffer bodyBuffer = Buffer.buffer(); | ||
| 164 | - if (messageType == RESPONSE) { | ||
| 165 | - // code | ||
| 166 | - bodyBuffer.appendInt(message.getCode() != null ? message.getCode() : 0); | ||
| 167 | - // msg | ||
| 168 | - String msg = message.getMsg() != null ? message.getMsg() : ""; | ||
| 169 | - byte[] msgBytes = StrUtil.utf8Bytes(msg); | ||
| 170 | - bodyBuffer.appendShort((short) msgBytes.length); | ||
| 171 | - bodyBuffer.appendBytes(msgBytes); | ||
| 172 | - // data | ||
| 173 | - if (message.getData() != null) { | ||
| 174 | - bodyBuffer.appendBytes(JsonUtils.toJsonByte(message.getData())); | ||
| 175 | - } | ||
| 176 | - } else { | ||
| 177 | - // 请求消息只处理 params 参数 | ||
| 178 | - // TODO @haohao:如果为空,是不是得写个长度 0 哈? | ||
| 179 | - if (message.getParams() != null) { | ||
| 180 | - bodyBuffer.appendBytes(JsonUtils.toJsonByte(message.getParams())); | ||
| 181 | - } | ||
| 182 | - } | ||
| 183 | - return bodyBuffer.getBytes(); | ||
| 184 | - } | ||
| 185 | - | ||
| 186 | - /** | ||
| 187 | - * 构建完整消息 | ||
| 188 | - */ | ||
| 189 | - private byte[] buildCompleteMessage(IotDeviceMessage message, byte messageType, byte[] bodyData) { | ||
| 190 | - Buffer buffer = Buffer.buffer(); | ||
| 191 | - // 1. 写入协议头部 | ||
| 192 | - buffer.appendByte(MAGIC_NUMBER); | ||
| 193 | - buffer.appendByte(PROTOCOL_VERSION); | ||
| 194 | - buffer.appendByte(messageType); | ||
| 195 | - // 2. 预留消息长度位置(在 5. 更新消息长度) | ||
| 196 | - int lengthPosition = buffer.length(); | ||
| 197 | - buffer.appendInt(0); | ||
| 198 | - // 3. 写入消息 ID | ||
| 199 | - String messageId = StrUtil.isNotBlank(message.getRequestId()) ? message.getRequestId() | ||
| 200 | - : IotDeviceMessageUtils.generateMessageId(); | ||
| 201 | - byte[] messageIdBytes = StrUtil.utf8Bytes(messageId); | ||
| 202 | - buffer.appendShort((short) messageIdBytes.length); | ||
| 203 | - buffer.appendBytes(messageIdBytes); | ||
| 204 | - // 4. 写入方法名 | ||
| 205 | - byte[] methodBytes = StrUtil.utf8Bytes(message.getMethod()); | ||
| 206 | - buffer.appendShort((short) methodBytes.length); | ||
| 207 | - buffer.appendBytes(methodBytes); | ||
| 208 | - // 5. 写入消息体 | ||
| 209 | - buffer.appendBytes(bodyData); | ||
| 210 | - // 6. 更新消息长度 | ||
| 211 | - buffer.setInt(lengthPosition, buffer.length()); | ||
| 212 | - return buffer.getBytes(); | ||
| 213 | - } | ||
| 214 | - | ||
| 215 | - /** | ||
| 216 | - * 解析消息体 | ||
| 217 | - */ | ||
| 218 | - private IotDeviceMessage parseMessageBody(Buffer buffer, int startIndex, byte messageType, | ||
| 219 | - String messageId, String method) { | ||
| 220 | - if (startIndex >= buffer.length()) { | ||
| 221 | - // 空消息体 | ||
| 222 | - return IotDeviceMessage.of(messageId, method, null, null, null, null); | ||
| 223 | - } | ||
| 224 | - | ||
| 225 | - if (messageType == RESPONSE) { | ||
| 226 | - // 响应消息:解析 code + msg + data | ||
| 227 | - return parseResponseMessage(buffer, startIndex, messageId, method); | ||
| 228 | - } else { | ||
| 229 | - // 请求消息:解析 payload | ||
| 230 | - Object payload = parseJsonData(buffer, startIndex, buffer.length()); | ||
| 231 | - return IotDeviceMessage.of(messageId, method, payload, null, null, null); | ||
| 232 | - } | ||
| 233 | - } | ||
| 234 | - | ||
| 235 | - /** | ||
| 236 | - * 解析响应消息 | ||
| 237 | - */ | ||
| 238 | - private IotDeviceMessage parseResponseMessage(Buffer buffer, int startIndex, String messageId, String method) { | ||
| 239 | - int index = startIndex; | ||
| 240 | - | ||
| 241 | - // 1. 读取响应码 | ||
| 242 | - Integer code = buffer.getInt(index); | ||
| 243 | - index += 4; | ||
| 244 | - | ||
| 245 | - // 2. 读取响应消息 | ||
| 246 | - short msgLength = buffer.getShort(index); | ||
| 247 | - index += 2; | ||
| 248 | - String msg = msgLength > 0 ? buffer.getString(index, index + msgLength, StandardCharsets.UTF_8.name()) : null; | ||
| 249 | - index += msgLength; | ||
| 250 | - | ||
| 251 | - // 3. 读取响应数据 | ||
| 252 | - Object data = null; | ||
| 253 | - if (index < buffer.length()) { | ||
| 254 | - data = parseJsonData(buffer, index, buffer.length()); | ||
| 255 | - } | ||
| 256 | - | ||
| 257 | - return IotDeviceMessage.of(messageId, method, null, data, code, msg); | ||
| 258 | - } | ||
| 259 | - | ||
| 260 | - /** | ||
| 261 | - * 解析 JSON 数据 | ||
| 262 | - */ | ||
| 263 | - private Object parseJsonData(Buffer buffer, int startIndex, int endIndex) { | ||
| 264 | - if (startIndex >= endIndex) { | ||
| 265 | - return null; | ||
| 266 | - } | ||
| 267 | - try { | ||
| 268 | - String jsonStr = buffer.getString(startIndex, endIndex, StandardCharsets.UTF_8.name()); | ||
| 269 | - return JsonUtils.parseObject(jsonStr, Object.class); | ||
| 270 | - } catch (Exception e) { | ||
| 271 | - log.warn("[parseJsonData][JSON 解析失败,返回原始字符串]", e); | ||
| 272 | - return buffer.getString(startIndex, endIndex, StandardCharsets.UTF_8.name()); | ||
| 273 | - } | ||
| 274 | - } | ||
| 275 | - | ||
| 276 | - /** | ||
| 277 | - * 快速检测是否为二进制格式 | ||
| 278 | - * | ||
| 279 | - * @param data 数据 | ||
| 280 | - * @return 是否为二进制格式 | ||
| 281 | - */ | ||
| 282 | - public static boolean isBinaryFormatQuick(byte[] data) { | ||
| 283 | - return data != null && data.length >= 1 && data[0] == MAGIC_NUMBER; | ||
| 284 | - } | ||
| 285 | - | ||
| 286 | -} | ||
| 287 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/IotTcpJsonDeviceMessageCodec.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.gateway.codec.tcp; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.lang.Assert; | ||
| 4 | -import cn.hutool.core.util.StrUtil; | ||
| 5 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 6 | -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 7 | -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec; | ||
| 8 | -import lombok.AllArgsConstructor; | ||
| 9 | -import lombok.Data; | ||
| 10 | -import lombok.NoArgsConstructor; | ||
| 11 | -import org.springframework.stereotype.Component; | ||
| 12 | - | ||
| 13 | -/** | ||
| 14 | - * TCP/UDP JSON 格式 {@link IotDeviceMessage} 编解码器 | ||
| 15 | - * | ||
| 16 | - * 采用纯 JSON 格式传输,格式如下: | ||
| 17 | - * { | ||
| 18 | - * "id": "消息 ID", | ||
| 19 | - * "method": "消息方法", | ||
| 20 | - * "params": {...}, // 请求参数 | ||
| 21 | - * "data": {...}, // 响应结果 | ||
| 22 | - * "code": 200, // 响应错误码 | ||
| 23 | - * "msg": "success", // 响应提示 | ||
| 24 | - * "timestamp": 时间戳 | ||
| 25 | - * } | ||
| 26 | - * | ||
| 27 | - * @author 芋道源码 | ||
| 28 | - */ | ||
| 29 | -@Component | ||
| 30 | -public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 31 | - | ||
| 32 | - public static final String TYPE = "TCP_JSON"; | ||
| 33 | - | ||
| 34 | - @Data | ||
| 35 | - @NoArgsConstructor | ||
| 36 | - @AllArgsConstructor | ||
| 37 | - private static class TcpJsonMessage { | ||
| 38 | - | ||
| 39 | - /** | ||
| 40 | - * 消息 ID,且每个消息 ID 在当前设备具有唯一性 | ||
| 41 | - */ | ||
| 42 | - private String id; | ||
| 43 | - | ||
| 44 | - /** | ||
| 45 | - * 请求方法 | ||
| 46 | - */ | ||
| 47 | - private String method; | ||
| 48 | - | ||
| 49 | - /** | ||
| 50 | - * 请求参数 | ||
| 51 | - */ | ||
| 52 | - private Object params; | ||
| 53 | - | ||
| 54 | - /** | ||
| 55 | - * 响应结果 | ||
| 56 | - */ | ||
| 57 | - private Object data; | ||
| 58 | - | ||
| 59 | - /** | ||
| 60 | - * 响应错误码 | ||
| 61 | - */ | ||
| 62 | - private Integer code; | ||
| 63 | - | ||
| 64 | - /** | ||
| 65 | - * 响应提示 | ||
| 66 | - */ | ||
| 67 | - private String msg; | ||
| 68 | - | ||
| 69 | - /** | ||
| 70 | - * 时间戳 | ||
| 71 | - */ | ||
| 72 | - private Long timestamp; | ||
| 73 | - | ||
| 74 | - } | ||
| 75 | - | ||
| 76 | - @Override | ||
| 77 | - public String type() { | ||
| 78 | - return TYPE; | ||
| 79 | - } | ||
| 80 | - | ||
| 81 | - @Override | ||
| 82 | - public byte[] encode(IotDeviceMessage message) { | ||
| 83 | - TcpJsonMessage tcpJsonMessage = new TcpJsonMessage( | ||
| 84 | - message.getRequestId(), | ||
| 85 | - message.getMethod(), | ||
| 86 | - message.getParams(), | ||
| 87 | - message.getData(), | ||
| 88 | - message.getCode(), | ||
| 89 | - message.getMsg(), | ||
| 90 | - System.currentTimeMillis()); | ||
| 91 | - return JsonUtils.toJsonByte(tcpJsonMessage); | ||
| 92 | - } | ||
| 93 | - | ||
| 94 | - @Override | ||
| 95 | - @SuppressWarnings("DataFlowIssue") | ||
| 96 | - public IotDeviceMessage decode(byte[] bytes) { | ||
| 97 | - String jsonStr = StrUtil.utf8Str(bytes).trim(); | ||
| 98 | - TcpJsonMessage tcpJsonMessage = JsonUtils.parseObject(jsonStr, TcpJsonMessage.class); | ||
| 99 | - Assert.notNull(tcpJsonMessage, "消息不能为空"); | ||
| 100 | - Assert.notBlank(tcpJsonMessage.getMethod(), "消息方法不能为空"); | ||
| 101 | - return IotDeviceMessage.of( | ||
| 102 | - tcpJsonMessage.getId(), | ||
| 103 | - tcpJsonMessage.getMethod(), | ||
| 104 | - tcpJsonMessage.getParams(), | ||
| 105 | - tcpJsonMessage.getData(), | ||
| 106 | - tcpJsonMessage.getCode(), | ||
| 107 | - tcpJsonMessage.getMsg()); | ||
| 108 | - } | ||
| 109 | - | ||
| 110 | -} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/handler/downstream/IotEmqxDownstreamHandler.java
| @@ -53,9 +53,10 @@ public class IotEmqxDownstreamHandler { | @@ -53,9 +53,10 @@ public class IotEmqxDownstreamHandler { | ||
| 53 | return; | 53 | return; |
| 54 | } | 54 | } |
| 55 | // 2.2 构建载荷 | 55 | // 2.2 构建载荷 |
| 56 | - byte[] payload = deviceMessageService.encodeDeviceMessage(message, deviceInfo.getProductKey(), | 56 | + byte[] payload = deviceMessageService.serializeDeviceMessage(message, deviceInfo.getProductKey(), |
| 57 | deviceInfo.getDeviceName()); | 57 | deviceInfo.getDeviceName()); |
| 58 | - // 2.3 发布消息 | 58 | + |
| 59 | + // 3. 发布消息 | ||
| 59 | protocol.publishMessage(topic, payload); | 60 | protocol.publishMessage(topic, payload); |
| 60 | } | 61 | } |
| 61 | 62 |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/handler/upstream/IotEmqxAuthEventHandler.java
| @@ -452,16 +452,17 @@ public class IotEmqxAuthEventHandler { | @@ -452,16 +452,17 @@ public class IotEmqxAuthEventHandler { | ||
| 452 | IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username); | 452 | IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username); |
| 453 | Assert.notNull(deviceInfo, "设备信息不能为空"); | 453 | Assert.notNull(deviceInfo, "设备信息不能为空"); |
| 454 | try { | 454 | try { |
| 455 | - // 1. 构建响应消息 | 455 | + // 1.1 构建响应消息 |
| 456 | String method = IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(); | 456 | String method = IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(); |
| 457 | IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(null, method, result, 0, null); | 457 | IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(null, method, result, 0, null); |
| 458 | - | ||
| 459 | - // 2. 编码消息 | ||
| 460 | - byte[] encodedData = deviceMessageService.encodeDeviceMessage(responseMessage, "Alink"); | ||
| 461 | - | ||
| 462 | - // 3. 构建响应主题并延迟发布(等待设备连接成功并完成订阅) | 458 | + // 1.2 序列化消息 |
| 459 | + byte[] encodedData = deviceMessageService.serializeDeviceMessage(responseMessage, | ||
| 460 | + cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum.JSON); | ||
| 461 | + // 1.3 构建响应主题 | ||
| 463 | String replyTopic = IotMqttTopicUtils.buildTopicByMethod(method, | 462 | String replyTopic = IotMqttTopicUtils.buildTopicByMethod(method, |
| 464 | deviceInfo.getProductKey(), deviceInfo.getDeviceName(), true); | 463 | deviceInfo.getProductKey(), deviceInfo.getDeviceName(), true); |
| 464 | + | ||
| 465 | + // 2. 构建响应主题,并延迟发布(等待设备连接成功并完成订阅) | ||
| 465 | protocol.publishDelayMessage(replyTopic, encodedData, 5000); | 466 | protocol.publishDelayMessage(replyTopic, encodedData, 5000); |
| 466 | log.info("[sendRegisterResultMessage][发送注册结果: topic={}]", replyTopic); | 467 | log.info("[sendRegisterResultMessage][发送注册结果: topic={}]", replyTopic); |
| 467 | } catch (Exception e) { | 468 | } catch (Exception e) { |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/handler/upstream/IotEmqxUpstreamHandler.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.upstream; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.upstream; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.ArrayUtil; | ||
| 3 | import cn.hutool.core.util.StrUtil; | 4 | import cn.hutool.core.util.StrUtil; |
| 4 | import cn.hutool.extra.spring.SpringUtil; | 5 | import cn.hutool.extra.spring.SpringUtil; |
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 6 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| @@ -34,22 +35,21 @@ public class IotEmqxUpstreamHandler { | @@ -34,22 +35,21 @@ public class IotEmqxUpstreamHandler { | ||
| 34 | try { | 35 | try { |
| 35 | // 1. 解析主题,一次性获取所有信息 | 36 | // 1. 解析主题,一次性获取所有信息 |
| 36 | String[] topicParts = topic.split("/"); | 37 | String[] topicParts = topic.split("/"); |
| 37 | - if (topicParts.length < 4 || StrUtil.hasBlank(topicParts[2], topicParts[3])) { | 38 | + String productKey = ArrayUtil.get(topicParts, 2); |
| 39 | + String deviceName = ArrayUtil.get(topicParts, 3); | ||
| 40 | + if (topicParts.length < 4 || StrUtil.hasBlank(productKey, deviceName)) { | ||
| 38 | log.warn("[handle][topic({}) 格式不正确,无法解析有效的 productKey 和 deviceName]", topic); | 41 | log.warn("[handle][topic({}) 格式不正确,无法解析有效的 productKey 和 deviceName]", topic); |
| 39 | return; | 42 | return; |
| 40 | } | 43 | } |
| 41 | 44 | ||
| 42 | - String productKey = topicParts[2]; | ||
| 43 | - String deviceName = topicParts[3]; | ||
| 44 | - | ||
| 45 | - // 3. 解码消息 | ||
| 46 | - IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(payload, productKey, deviceName); | 45 | + // 2. 反序列化消息 |
| 46 | + IotDeviceMessage message = deviceMessageService.deserializeDeviceMessage(payload, productKey, deviceName); | ||
| 47 | if (message == null) { | 47 | if (message == null) { |
| 48 | log.warn("[handle][topic({}) payload({}) 消息解码失败]", topic, new String(payload)); | 48 | log.warn("[handle][topic({}) payload({}) 消息解码失败]", topic, new String(payload)); |
| 49 | return; | 49 | return; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | - // 4. 发送消息到队列 | 52 | + // 3. 发送消息到队列 |
| 53 | deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId); | 53 | deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId); |
| 54 | } catch (Exception e) { | 54 | } catch (Exception e) { |
| 55 | log.error("[handle][topic({}) payload({}) 处理异常]", topic, new String(payload), e); | 55 | log.error("[handle][topic({}) payload({}) 处理异常]", topic, new String(payload), e); |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/downstream/IotMqttDownstreamHandler.java
| @@ -43,7 +43,7 @@ public class IotMqttDownstreamHandler { | @@ -43,7 +43,7 @@ public class IotMqttDownstreamHandler { | ||
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | // 2.1 序列化消息 | 45 | // 2.1 序列化消息 |
| 46 | - byte[] payload = deviceMessageService.encodeDeviceMessage(message, connectionInfo.getProductKey(), | 46 | + byte[] payload = deviceMessageService.serializeDeviceMessage(message, connectionInfo.getProductKey(), |
| 47 | connectionInfo.getDeviceName()); | 47 | connectionInfo.getDeviceName()); |
| 48 | Assert.isTrue(payload != null && payload.length > 0, "消息编码结果不能为空"); | 48 | Assert.isTrue(payload != null && payload.length > 0, "消息编码结果不能为空"); |
| 49 | // 2.2 构建主题 | 49 | // 2.2 构建主题 |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/upstream/IotMqttAbstractHandler.java
| @@ -21,12 +21,6 @@ import lombok.extern.slf4j.Slf4j; | @@ -21,12 +21,6 @@ import lombok.extern.slf4j.Slf4j; | ||
| 21 | @RequiredArgsConstructor | 21 | @RequiredArgsConstructor |
| 22 | public abstract class IotMqttAbstractHandler { | 22 | public abstract class IotMqttAbstractHandler { |
| 23 | 23 | ||
| 24 | - // TODO @AI:当前使用 Alink 序列化类型,后续可考虑支持更多序列化方式 | ||
| 25 | - /** | ||
| 26 | - * 默认编解码类型(MQTT 使用 Alink 协议) | ||
| 27 | - */ | ||
| 28 | - protected static final String DEFAULT_CODEC_TYPE = "Alink"; | ||
| 29 | - | ||
| 30 | protected final IotMqttConnectionManager connectionManager; | 24 | protected final IotMqttConnectionManager connectionManager; |
| 31 | protected final IotDeviceMessageService deviceMessageService; | 25 | protected final IotDeviceMessageService deviceMessageService; |
| 32 | 26 | ||
| @@ -40,8 +34,9 @@ public abstract class IotMqttAbstractHandler { | @@ -40,8 +34,9 @@ public abstract class IotMqttAbstractHandler { | ||
| 40 | * @param method 方法名 | 34 | * @param method 方法名 |
| 41 | * @param data 响应数据 | 35 | * @param data 响应数据 |
| 42 | */ | 36 | */ |
| 37 | + @SuppressWarnings("SameParameterValue") | ||
| 43 | protected void sendSuccessResponse(MqttEndpoint endpoint, String productKey, String deviceName, | 38 | protected void sendSuccessResponse(MqttEndpoint endpoint, String productKey, String deviceName, |
| 44 | - String requestId, String method, Object data) { | 39 | + String requestId, String method, Object data) { |
| 45 | IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, data, 0, null); | 40 | IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, data, 0, null); |
| 46 | writeResponse(endpoint, productKey, deviceName, method, responseMessage); | 41 | writeResponse(endpoint, productKey, deviceName, method, responseMessage); |
| 47 | } | 42 | } |
| @@ -75,11 +70,12 @@ public abstract class IotMqttAbstractHandler { | @@ -75,11 +70,12 @@ public abstract class IotMqttAbstractHandler { | ||
| 75 | private void writeResponse(MqttEndpoint endpoint, String productKey, String deviceName, | 70 | private void writeResponse(MqttEndpoint endpoint, String productKey, String deviceName, |
| 76 | String method, IotDeviceMessage responseMessage) { | 71 | String method, IotDeviceMessage responseMessage) { |
| 77 | try { | 72 | try { |
| 78 | - // 1. 编码消息(使用默认编解码器) | ||
| 79 | - byte[] encodedData = deviceMessageService.encodeDeviceMessage(responseMessage, DEFAULT_CODEC_TYPE); | ||
| 80 | - | ||
| 81 | - // 2. 构建响应主题,并发送 | 73 | + // 1.1 序列化消息(根据设备配置的序列化类型) |
| 74 | + byte[] encodedData = deviceMessageService.serializeDeviceMessage(responseMessage, productKey, deviceName); | ||
| 75 | + // 1.2 构建响应主题 | ||
| 82 | String replyTopic = IotMqttTopicUtils.buildTopicByMethod(method, productKey, deviceName, true); | 76 | String replyTopic = IotMqttTopicUtils.buildTopicByMethod(method, productKey, deviceName, true); |
| 77 | + | ||
| 78 | + // 2. 发送响应消息 | ||
| 83 | endpoint.publish(replyTopic, Buffer.buffer(encodedData), MqttQoS.AT_LEAST_ONCE, false, false); | 79 | endpoint.publish(replyTopic, Buffer.buffer(encodedData), MqttQoS.AT_LEAST_ONCE, false, false); |
| 84 | log.debug("[writeResponse][发送响应,主题: {},code: {}]", replyTopic, responseMessage.getCode()); | 80 | log.debug("[writeResponse][发送响应,主题: {},code: {}]", replyTopic, responseMessage.getCode()); |
| 85 | } catch (Exception e) { | 81 | } catch (Exception e) { |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/upstream/IotMqttRegisterHandler.java
| @@ -14,6 +14,8 @@ import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessa | @@ -14,6 +14,8 @@ import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessa | ||
| 14 | import io.vertx.mqtt.MqttEndpoint; | 14 | import io.vertx.mqtt.MqttEndpoint; |
| 15 | import lombok.extern.slf4j.Slf4j; | 15 | import lombok.extern.slf4j.Slf4j; |
| 16 | 16 | ||
| 17 | +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; | ||
| 18 | + | ||
| 17 | /** | 19 | /** |
| 18 | * IoT 网关 MQTT 设备注册处理器:处理设备动态注册消息(一型一密) | 20 | * IoT 网关 MQTT 设备注册处理器:处理设备动态注册消息(一型一密) |
| 19 | * | 21 | * |
| @@ -76,7 +78,8 @@ public class IotMqttRegisterHandler extends IotMqttAbstractHandler { | @@ -76,7 +78,8 @@ public class IotMqttRegisterHandler extends IotMqttAbstractHandler { | ||
| 76 | log.warn("[handleRegister][注册失败,客户端 ID: {},错误: {}]", clientId, e.getMessage()); | 78 | log.warn("[handleRegister][注册失败,客户端 ID: {},错误: {}]", clientId, e.getMessage()); |
| 77 | // 接受连接,并发送错误响应 | 79 | // 接受连接,并发送错误响应 |
| 78 | endpoint.accept(false); | 80 | endpoint.accept(false); |
| 79 | - sendErrorResponse(endpoint, productKey, deviceName, null, method, 500, e.getMessage()); | 81 | + sendErrorResponse(endpoint, productKey, deviceName, null, method, |
| 82 | + INTERNAL_SERVER_ERROR.getCode(), e.getMessage()); | ||
| 80 | } finally { | 83 | } finally { |
| 81 | // 注册完成后关闭连接(一型一密只用于获取 deviceSecret,不保持连接) | 84 | // 注册完成后关闭连接(一型一密只用于获取 deviceSecret,不保持连接) |
| 82 | endpoint.close(); | 85 | endpoint.close(); |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/upstream/IotMqttUpstreamHandler.java
| @@ -60,7 +60,7 @@ public class IotMqttUpstreamHandler extends IotMqttAbstractHandler { | @@ -60,7 +60,7 @@ public class IotMqttUpstreamHandler extends IotMqttAbstractHandler { | ||
| 60 | Assert.equals(deviceName, connectionInfo.getDeviceName(), "设备名称不匹配"); | 60 | Assert.equals(deviceName, connectionInfo.getDeviceName(), "设备名称不匹配"); |
| 61 | 61 | ||
| 62 | // 2. 反序列化消息 | 62 | // 2. 反序列化消息 |
| 63 | - message = deviceMessageService.decodeDeviceMessage(payload, productKey, deviceName); | 63 | + message = deviceMessageService.deserializeDeviceMessage(payload, productKey, deviceName); |
| 64 | if (message == null) { | 64 | if (message == null) { |
| 65 | log.warn("[handleBusinessRequest][消息解码失败,客户端 ID: {},主题: {}]", clientId, topic); | 65 | log.warn("[handleBusinessRequest][消息解码失败,客户端 ID: {},主题: {}]", clientId, topic); |
| 66 | sendErrorResponse(endpoint, productKey, deviceName, null, null, | 66 | sendErrorResponse(endpoint, productKey, deviceName, null, null, |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/package-info.java
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/package-info.java
0 → 100644
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageService.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.service.device.message; | 1 | package cn.iocoder.yudao.module.iot.gateway.service.device.message; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum; | ||
| 3 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 4 | 5 | ||
| 5 | /** | 6 | /** |
| @@ -10,45 +11,45 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | @@ -10,45 +11,45 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 10 | public interface IotDeviceMessageService { | 11 | public interface IotDeviceMessageService { |
| 11 | 12 | ||
| 12 | /** | 13 | /** |
| 13 | - * 编码消息 | 14 | + * 序列化消息 |
| 14 | * | 15 | * |
| 15 | * @param message 消息 | 16 | * @param message 消息 |
| 16 | * @param productKey 产品 Key | 17 | * @param productKey 产品 Key |
| 17 | * @param deviceName 设备名称 | 18 | * @param deviceName 设备名称 |
| 18 | - * @return 编码后的消息内容 | 19 | + * @return 序列化后的消息内容 |
| 19 | */ | 20 | */ |
| 20 | - byte[] encodeDeviceMessage(IotDeviceMessage message, | ||
| 21 | - String productKey, String deviceName); | 21 | + byte[] serializeDeviceMessage(IotDeviceMessage message, |
| 22 | + String productKey, String deviceName); | ||
| 22 | 23 | ||
| 23 | /** | 24 | /** |
| 24 | - * 编码消息 | 25 | + * 序列化消息 |
| 25 | * | 26 | * |
| 26 | - * @param message 消息 | ||
| 27 | - * @param codecType 编解码器类型 | ||
| 28 | - * @return 编码后的消息内容 | 27 | + * @param message 消息 |
| 28 | + * @param serializeType 序列化类型 | ||
| 29 | + * @return 序列化后的消息内容 | ||
| 29 | */ | 30 | */ |
| 30 | - byte[] encodeDeviceMessage(IotDeviceMessage message, | ||
| 31 | - String codecType); | 31 | + byte[] serializeDeviceMessage(IotDeviceMessage message, |
| 32 | + IotSerializeTypeEnum serializeType); | ||
| 32 | 33 | ||
| 33 | /** | 34 | /** |
| 34 | - * 解码消息 | 35 | + * 反序列化消息 |
| 35 | * | 36 | * |
| 36 | * @param bytes 消息内容 | 37 | * @param bytes 消息内容 |
| 37 | * @param productKey 产品 Key | 38 | * @param productKey 产品 Key |
| 38 | * @param deviceName 设备名称 | 39 | * @param deviceName 设备名称 |
| 39 | - * @return 解码后的消息内容 | 40 | + * @return 反序列化后的消息内容 |
| 40 | */ | 41 | */ |
| 41 | - IotDeviceMessage decodeDeviceMessage(byte[] bytes, | ||
| 42 | - String productKey, String deviceName); | 42 | + IotDeviceMessage deserializeDeviceMessage(byte[] bytes, |
| 43 | + String productKey, String deviceName); | ||
| 43 | 44 | ||
| 44 | /** | 45 | /** |
| 45 | - * 解码消息 | 46 | + * 反序列化消息 |
| 46 | * | 47 | * |
| 47 | - * @param bytes 消息内容 | ||
| 48 | - * @param codecType 编解码器类型 | ||
| 49 | - * @return 解码后的消息内容 | 48 | + * @param bytes 消息内容 |
| 49 | + * @param serializeType 序列化类型 | ||
| 50 | + * @return 反序列化后的消息内容 | ||
| 50 | */ | 51 | */ |
| 51 | - IotDeviceMessage decodeDeviceMessage(byte[] bytes, String codecType); | 52 | + IotDeviceMessage deserializeDeviceMessage(byte[] bytes, IotSerializeTypeEnum serializeType); |
| 52 | 53 | ||
| 53 | /** | 54 | /** |
| 54 | * 发送消息 | 55 | * 发送消息 |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageServiceImpl.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.service.device.message; | 1 | package cn.iocoder.yudao.module.iot.gateway.service.device.message; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 3 | import cn.hutool.core.util.StrUtil; | 4 | import cn.hutool.core.util.StrUtil; |
| 4 | -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||
| 5 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; | 5 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; |
| 6 | +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum; | ||
| 6 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 7 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 7 | import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; | 8 | import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; |
| 8 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 9 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; |
| 9 | -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec; | 10 | +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer; |
| 11 | +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager; | ||
| 10 | import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; | 12 | import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; |
| 11 | import jakarta.annotation.Resource; | 13 | import jakarta.annotation.Resource; |
| 12 | import lombok.extern.slf4j.Slf4j; | 14 | import lombok.extern.slf4j.Slf4j; |
| 13 | import org.springframework.stereotype.Service; | 15 | import org.springframework.stereotype.Service; |
| 14 | 16 | ||
| 15 | import java.time.LocalDateTime; | 17 | import java.time.LocalDateTime; |
| 16 | -import java.util.List; | ||
| 17 | -import java.util.Map; | ||
| 18 | 18 | ||
| 19 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 19 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 20 | import static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_NOT_EXISTS; | 20 | import static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_NOT_EXISTS; |
| @@ -28,80 +28,70 @@ import static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVIC | @@ -28,80 +28,70 @@ import static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVIC | ||
| 28 | @Slf4j | 28 | @Slf4j |
| 29 | public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { | 29 | public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { |
| 30 | 30 | ||
| 31 | - /** | ||
| 32 | - * 编解码器 | ||
| 33 | - */ | ||
| 34 | - private final Map<String, IotDeviceMessageCodec> codes; | ||
| 35 | - | ||
| 36 | @Resource | 31 | @Resource |
| 37 | private IotDeviceService deviceService; | 32 | private IotDeviceService deviceService; |
| 38 | 33 | ||
| 39 | @Resource | 34 | @Resource |
| 40 | private IotDeviceMessageProducer deviceMessageProducer; | 35 | private IotDeviceMessageProducer deviceMessageProducer; |
| 41 | 36 | ||
| 42 | - public IotDeviceMessageServiceImpl(List<IotDeviceMessageCodec> codes) { | ||
| 43 | - this.codes = CollectionUtils.convertMap(codes, IotDeviceMessageCodec::type); | ||
| 44 | - } | 37 | + @Resource |
| 38 | + private IotMessageSerializerManager messageSerializerManager; | ||
| 45 | 39 | ||
| 46 | @Override | 40 | @Override |
| 47 | - public byte[] encodeDeviceMessage(IotDeviceMessage message, | ||
| 48 | - String productKey, String deviceName) { | 41 | + public byte[] serializeDeviceMessage(IotDeviceMessage message, |
| 42 | + String productKey, String deviceName) { | ||
| 49 | // 1.1 获取设备信息 | 43 | // 1.1 获取设备信息 |
| 50 | IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); | 44 | IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); |
| 51 | if (device == null) { | 45 | if (device == null) { |
| 52 | throw exception(DEVICE_NOT_EXISTS, productKey, deviceName); | 46 | throw exception(DEVICE_NOT_EXISTS, productKey, deviceName); |
| 53 | } | 47 | } |
| 54 | - // 1.2 获取编解码器 | ||
| 55 | - IotDeviceMessageCodec codec = codes.get(device.getCodecType()); | ||
| 56 | - if (codec == null) { | ||
| 57 | - throw new IllegalArgumentException(StrUtil.format("编解码器({}) 不存在", device.getCodecType())); | ||
| 58 | - } | 48 | + // 1.2 获取序列化器 |
| 49 | + IotSerializeTypeEnum serializeType = IotSerializeTypeEnum.of(device.getSerializeType()); | ||
| 50 | + Assert.notNull(serializeType, "设备序列化类型不能为空"); | ||
| 59 | 51 | ||
| 60 | - // 2. 编码消息 | ||
| 61 | - return codec.encode(message); | 52 | + // 2. 序列化消息 |
| 53 | + return serializeDeviceMessage(message, serializeType); | ||
| 62 | } | 54 | } |
| 63 | 55 | ||
| 64 | @Override | 56 | @Override |
| 65 | - public byte[] encodeDeviceMessage(IotDeviceMessage message, | ||
| 66 | - String codecType) { | ||
| 67 | - // 1. 获取编解码器 | ||
| 68 | - IotDeviceMessageCodec codec = codes.get(codecType); | ||
| 69 | - if (codec == null) { | ||
| 70 | - throw new IllegalArgumentException(StrUtil.format("编解码器({}) 不存在", codecType)); | 57 | + public byte[] serializeDeviceMessage(IotDeviceMessage message, |
| 58 | + IotSerializeTypeEnum serializeType) { | ||
| 59 | + // 1. 获取序列化器 | ||
| 60 | + IotMessageSerializer serializer = messageSerializerManager.get(serializeType); | ||
| 61 | + if (serializer == null) { | ||
| 62 | + throw new IllegalArgumentException(StrUtil.format("序列化器({}) 不存在", serializeType)); | ||
| 71 | } | 63 | } |
| 72 | 64 | ||
| 73 | - // 2. 编码消息 | ||
| 74 | - return codec.encode(message); | 65 | + // 2. 序列化消息 |
| 66 | + return serializer.serialize(message); | ||
| 75 | } | 67 | } |
| 76 | 68 | ||
| 77 | @Override | 69 | @Override |
| 78 | - public IotDeviceMessage decodeDeviceMessage(byte[] bytes, | ||
| 79 | - String productKey, String deviceName) { | 70 | + public IotDeviceMessage deserializeDeviceMessage(byte[] bytes, |
| 71 | + String productKey, String deviceName) { | ||
| 80 | // 1.1 获取设备信息 | 72 | // 1.1 获取设备信息 |
| 81 | IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); | 73 | IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); |
| 82 | if (device == null) { | 74 | if (device == null) { |
| 83 | throw exception(DEVICE_NOT_EXISTS, productKey, deviceName); | 75 | throw exception(DEVICE_NOT_EXISTS, productKey, deviceName); |
| 84 | } | 76 | } |
| 85 | - // 1.2 获取编解码器 | ||
| 86 | - IotDeviceMessageCodec codec = codes.get(device.getCodecType()); | ||
| 87 | - if (codec == null) { | ||
| 88 | - throw new IllegalArgumentException(StrUtil.format("编解码器({}) 不存在", device.getCodecType())); | ||
| 89 | - } | 77 | + // 1.2 获取序列化器 |
| 78 | + IotSerializeTypeEnum serializeType = IotSerializeTypeEnum.of(device.getSerializeType()); | ||
| 79 | + Assert.notNull(serializeType, "设备序列化类型不能为空"); | ||
| 90 | 80 | ||
| 91 | - // 2. 解码消息 | ||
| 92 | - return codec.decode(bytes); | 81 | + // 2. 反序列化消息 |
| 82 | + return deserializeDeviceMessage(bytes, serializeType); | ||
| 93 | } | 83 | } |
| 94 | 84 | ||
| 95 | @Override | 85 | @Override |
| 96 | - public IotDeviceMessage decodeDeviceMessage(byte[] bytes, String codecType) { | ||
| 97 | - // 1. 获取编解码器 | ||
| 98 | - IotDeviceMessageCodec codec = codes.get(codecType); | ||
| 99 | - if (codec == null) { | ||
| 100 | - throw new IllegalArgumentException(StrUtil.format("编解码器({}) 不存在", codecType)); | 86 | + public IotDeviceMessage deserializeDeviceMessage(byte[] bytes, IotSerializeTypeEnum serializeType) { |
| 87 | + // 1. 获取序列化器 | ||
| 88 | + IotMessageSerializer serializer = messageSerializerManager.get(serializeType); | ||
| 89 | + if (serializer == null) { | ||
| 90 | + throw new IllegalArgumentException(StrUtil.format("序列化器({}) 不存在", serializeType)); | ||
| 101 | } | 91 | } |
| 102 | 92 | ||
| 103 | - // 2. 解码消息 | ||
| 104 | - return codec.decode(bytes); | 93 | + // 2. 反序列化消息 |
| 94 | + return serializer.deserialize(bytes); | ||
| 105 | } | 95 | } |
| 106 | 96 | ||
| 107 | @Override | 97 | @Override |
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotDirectDeviceMqttProtocolIntegrationTest.java
| @@ -8,8 +8,8 @@ import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO; | @@ -8,8 +8,8 @@ import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO; | ||
| 8 | import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO; | 8 | import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO; |
| 9 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; | 9 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; |
| 10 | import cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils; | 10 | import cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils; |
| 11 | -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec; | ||
| 12 | -import cn.iocoder.yudao.module.iot.gateway.codec.alink.IotAlinkDeviceMessageCodec; | 11 | +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer; |
| 12 | +import cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer; | ||
| 13 | import io.netty.handler.codec.mqtt.MqttQoS; | 13 | import io.netty.handler.codec.mqtt.MqttQoS; |
| 14 | import io.vertx.core.Vertx; | 14 | import io.vertx.core.Vertx; |
| 15 | import io.vertx.core.buffer.Buffer; | 15 | import io.vertx.core.buffer.Buffer; |
| @@ -57,9 +57,9 @@ public class IotDirectDeviceMqttProtocolIntegrationTest { | @@ -57,9 +57,9 @@ public class IotDirectDeviceMqttProtocolIntegrationTest { | ||
| 57 | 57 | ||
| 58 | private static Vertx vertx; | 58 | private static Vertx vertx; |
| 59 | 59 | ||
| 60 | - // ===================== 编解码器(MQTT 使用 Alink 协议) ===================== | 60 | + // ===================== 序列化器 ===================== |
| 61 | 61 | ||
| 62 | - private static final IotDeviceMessageCodec CODEC = new IotAlinkDeviceMessageCodec(); | 62 | + private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer(); |
| 63 | 63 | ||
| 64 | // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询) ===================== | 64 | // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询) ===================== |
| 65 | 65 | ||
| @@ -211,7 +211,7 @@ public class IotDirectDeviceMqttProtocolIntegrationTest { | @@ -211,7 +211,7 @@ public class IotDirectDeviceMqttProtocolIntegrationTest { | ||
| 211 | client.publishHandler(message -> { | 211 | client.publishHandler(message -> { |
| 212 | log.info("[testDeviceRegister][收到响应: topic={}, payload={}]", | 212 | log.info("[testDeviceRegister][收到响应: topic={}, payload={}]", |
| 213 | message.topicName(), message.payload().toString()); | 213 | message.topicName(), message.payload().toString()); |
| 214 | - IotDeviceMessage response = CODEC.decode(message.payload().getBytes()); | 214 | + IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes()); |
| 215 | responseFuture.complete(response); | 215 | responseFuture.complete(response); |
| 216 | }); | 216 | }); |
| 217 | // 3.2 订阅 _reply 主题 | 217 | // 3.2 订阅 _reply 主题 |
| @@ -314,14 +314,14 @@ public class IotDirectDeviceMqttProtocolIntegrationTest { | @@ -314,14 +314,14 @@ public class IotDirectDeviceMqttProtocolIntegrationTest { | ||
| 314 | client.publishHandler(message -> { | 314 | client.publishHandler(message -> { |
| 315 | log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]", | 315 | log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]", |
| 316 | message.topicName(), message.payload().toString()); | 316 | message.topicName(), message.payload().toString()); |
| 317 | - IotDeviceMessage response = CODEC.decode(message.payload().getBytes()); | 317 | + IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes()); |
| 318 | responseFuture.complete(response); | 318 | responseFuture.complete(response); |
| 319 | }); | 319 | }); |
| 320 | 320 | ||
| 321 | - // 2. 编码并发布消息 | ||
| 322 | - byte[] payload = CODEC.encode(request); | ||
| 323 | - log.info("[publishAndWaitReply][Codec: {}, 发送消息: topic={}, payload={}]", | ||
| 324 | - CODEC.type(), topic, new String(payload)); | 321 | + // 2. 序列化并发布消息 |
| 322 | + byte[] payload = SERIALIZER.serialize(request); | ||
| 323 | + log.info("[publishAndWaitReply][Serializer: {}, 发送消息: topic={}, payload={}]", | ||
| 324 | + SERIALIZER.getType(), topic, new String(payload)); | ||
| 325 | client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false) | 325 | client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false) |
| 326 | .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); | 326 | .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); |
| 327 | log.info("[publishAndWaitReply][消息发布成功]"); | 327 | log.info("[publishAndWaitReply][消息发布成功]"); |
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotGatewayDeviceMqttProtocolIntegrationTest.java
| @@ -12,8 +12,8 @@ import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO; | @@ -12,8 +12,8 @@ import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO; | ||
| 12 | import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO; | 12 | import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO; |
| 13 | import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO; | 13 | import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO; |
| 14 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; | 14 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; |
| 15 | -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec; | ||
| 16 | -import cn.iocoder.yudao.module.iot.gateway.codec.alink.IotAlinkDeviceMessageCodec; | 15 | +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer; |
| 16 | +import cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer; | ||
| 17 | import io.netty.handler.codec.mqtt.MqttQoS; | 17 | import io.netty.handler.codec.mqtt.MqttQoS; |
| 18 | import io.vertx.core.Vertx; | 18 | import io.vertx.core.Vertx; |
| 19 | import io.vertx.core.buffer.Buffer; | 19 | import io.vertx.core.buffer.Buffer; |
| @@ -65,9 +65,9 @@ public class IotGatewayDeviceMqttProtocolIntegrationTest { | @@ -65,9 +65,9 @@ public class IotGatewayDeviceMqttProtocolIntegrationTest { | ||
| 65 | 65 | ||
| 66 | private static Vertx vertx; | 66 | private static Vertx vertx; |
| 67 | 67 | ||
| 68 | - // ===================== 编解码器(MQTT 使用 Alink 协议) ===================== | 68 | + // ===================== 序列化器 ===================== |
| 69 | 69 | ||
| 70 | - private static final IotDeviceMessageCodec CODEC = new IotAlinkDeviceMessageCodec(); | 70 | + private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer(); |
| 71 | 71 | ||
| 72 | // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) ===================== | 72 | // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) ===================== |
| 73 | 73 | ||
| @@ -399,14 +399,14 @@ public class IotGatewayDeviceMqttProtocolIntegrationTest { | @@ -399,14 +399,14 @@ public class IotGatewayDeviceMqttProtocolIntegrationTest { | ||
| 399 | client.publishHandler(message -> { | 399 | client.publishHandler(message -> { |
| 400 | log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]", | 400 | log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]", |
| 401 | message.topicName(), message.payload().toString()); | 401 | message.topicName(), message.payload().toString()); |
| 402 | - IotDeviceMessage response = CODEC.decode(message.payload().getBytes()); | 402 | + IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes()); |
| 403 | responseFuture.complete(response); | 403 | responseFuture.complete(response); |
| 404 | }); | 404 | }); |
| 405 | 405 | ||
| 406 | - // 2. 编码并发布消息 | ||
| 407 | - byte[] payload = CODEC.encode(request); | ||
| 408 | - log.info("[publishAndWaitReply][Codec: {}, 发送消息: topic={}, payload={}]", | ||
| 409 | - CODEC.type(), topic, new String(payload)); | 406 | + // 2. 序列化并发布消息 |
| 407 | + byte[] payload = SERIALIZER.serialize(request); | ||
| 408 | + log.info("[publishAndWaitReply][Serializer: {}, 发送消息: topic={}, payload={}]", | ||
| 409 | + SERIALIZER.getType(), topic, new String(payload)); | ||
| 410 | client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false) | 410 | client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false) |
| 411 | .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); | 411 | .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); |
| 412 | log.info("[publishAndWaitReply][消息发布成功]"); | 412 | log.info("[publishAndWaitReply][消息发布成功]"); |
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotGatewaySubDeviceMqttProtocolIntegrationTest.java
| @@ -7,8 +7,8 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | @@ -7,8 +7,8 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 7 | import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO; | 7 | import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO; |
| 8 | import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO; | 8 | import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO; |
| 9 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; | 9 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; |
| 10 | -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec; | ||
| 11 | -import cn.iocoder.yudao.module.iot.gateway.codec.alink.IotAlinkDeviceMessageCodec; | 10 | +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer; |
| 11 | +import cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer; | ||
| 12 | import io.netty.handler.codec.mqtt.MqttQoS; | 12 | import io.netty.handler.codec.mqtt.MqttQoS; |
| 13 | import io.vertx.core.Vertx; | 13 | import io.vertx.core.Vertx; |
| 14 | import io.vertx.core.buffer.Buffer; | 14 | import io.vertx.core.buffer.Buffer; |
| @@ -59,9 +59,9 @@ public class IotGatewaySubDeviceMqttProtocolIntegrationTest { | @@ -59,9 +59,9 @@ public class IotGatewaySubDeviceMqttProtocolIntegrationTest { | ||
| 59 | 59 | ||
| 60 | private static Vertx vertx; | 60 | private static Vertx vertx; |
| 61 | 61 | ||
| 62 | - // ===================== 编解码器(MQTT 使用 Alink 协议) ===================== | 62 | + // ===================== 序列化器 ===================== |
| 63 | 63 | ||
| 64 | - private static final IotDeviceMessageCodec CODEC = new IotAlinkDeviceMessageCodec(); | 64 | + private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer(); |
| 65 | 65 | ||
| 66 | // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== | 66 | // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== |
| 67 | 67 | ||
| @@ -236,14 +236,14 @@ public class IotGatewaySubDeviceMqttProtocolIntegrationTest { | @@ -236,14 +236,14 @@ public class IotGatewaySubDeviceMqttProtocolIntegrationTest { | ||
| 236 | client.publishHandler(message -> { | 236 | client.publishHandler(message -> { |
| 237 | log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]", | 237 | log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]", |
| 238 | message.topicName(), message.payload().toString()); | 238 | message.topicName(), message.payload().toString()); |
| 239 | - IotDeviceMessage response = CODEC.decode(message.payload().getBytes()); | 239 | + IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes()); |
| 240 | responseFuture.complete(response); | 240 | responseFuture.complete(response); |
| 241 | }); | 241 | }); |
| 242 | 242 | ||
| 243 | - // 2. 编码并发布消息 | ||
| 244 | - byte[] payload = CODEC.encode(request); | ||
| 245 | - log.info("[publishAndWaitReply][Codec: {}, 发送消息: topic={}, payload={}]", | ||
| 246 | - CODEC.type(), topic, new String(payload)); | 243 | + // 2. 序列化并发布消息 |
| 244 | + byte[] payload = SERIALIZER.serialize(request); | ||
| 245 | + log.info("[publishAndWaitReply][Serializer: {}, 发送消息: topic={}, payload={}]", | ||
| 246 | + SERIALIZER.getType(), topic, new String(payload)); | ||
| 247 | client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false) | 247 | client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false) |
| 248 | .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); | 248 | .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); |
| 249 | log.info("[publishAndWaitReply][消息发布成功]"); | 249 | log.info("[publishAndWaitReply][消息发布成功]"); |