Commit d6e334848ebe2c1d64d5fe7ad73e057a1ee4c5ea
Committed by
Gitee
!1496 修复了一些 Iot 模块 TODO 提到的问题
Merge pull request !1496 from puhui999/feature/iot
Showing
13 changed files
with
457 additions
and
46 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotSceneRuleDO.java
| @@ -21,6 +21,7 @@ import lombok.Builder; | @@ -21,6 +21,7 @@ import lombok.Builder; | ||
| 21 | import lombok.Data; | 21 | import lombok.Data; |
| 22 | import lombok.NoArgsConstructor; | 22 | import lombok.NoArgsConstructor; |
| 23 | 23 | ||
| 24 | +import java.time.LocalDateTime; | ||
| 24 | import java.util.List; | 25 | import java.util.List; |
| 25 | 26 | ||
| 26 | /** | 27 | /** |
| @@ -57,6 +58,11 @@ public class IotSceneRuleDO extends TenantBaseDO { | @@ -57,6 +58,11 @@ public class IotSceneRuleDO extends TenantBaseDO { | ||
| 57 | private Integer status; | 58 | private Integer status; |
| 58 | 59 | ||
| 59 | /** | 60 | /** |
| 61 | + * 最后触发时间 | ||
| 62 | + */ | ||
| 63 | + private LocalDateTime lastTriggerTime; | ||
| 64 | + | ||
| 65 | + /** | ||
| 60 | * 场景定义配置 | 66 | * 场景定义配置 |
| 61 | */ | 67 | */ |
| 62 | @TableField(typeHandler = JacksonTypeHandler.class) | 68 | @TableField(typeHandler = JacksonTypeHandler.class) |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkTcpConfig.java
| @@ -11,6 +11,35 @@ import lombok.Data; | @@ -11,6 +11,35 @@ import lombok.Data; | ||
| 11 | public class IotDataSinkTcpConfig extends IotAbstractDataSinkConfig { | 11 | public class IotDataSinkTcpConfig extends IotAbstractDataSinkConfig { |
| 12 | 12 | ||
| 13 | /** | 13 | /** |
| 14 | + * 默认连接超时时间(毫秒) | ||
| 15 | + */ | ||
| 16 | + public static final int DEFAULT_CONNECT_TIMEOUT_MS = 5000; | ||
| 17 | + /** | ||
| 18 | + * 默认读取超时时间(毫秒) | ||
| 19 | + */ | ||
| 20 | + public static final int DEFAULT_READ_TIMEOUT_MS = 10000; | ||
| 21 | + /** | ||
| 22 | + * 默认是否启用 SSL | ||
| 23 | + */ | ||
| 24 | + public static final boolean DEFAULT_SSL = false; | ||
| 25 | + /** | ||
| 26 | + * 默认数据格式 | ||
| 27 | + */ | ||
| 28 | + public static final String DEFAULT_DATA_FORMAT = "JSON"; | ||
| 29 | + /** | ||
| 30 | + * 默认心跳间隔时间(毫秒) | ||
| 31 | + */ | ||
| 32 | + public static final long DEFAULT_HEARTBEAT_INTERVAL_MS = 30000L; | ||
| 33 | + /** | ||
| 34 | + * 默认重连间隔时间(毫秒) | ||
| 35 | + */ | ||
| 36 | + public static final long DEFAULT_RECONNECT_INTERVAL_MS = 5000L; | ||
| 37 | + /** | ||
| 38 | + * 默认最大重连次数 | ||
| 39 | + */ | ||
| 40 | + public static final int DEFAULT_MAX_RECONNECT_ATTEMPTS = 3; | ||
| 41 | + | ||
| 42 | + /** | ||
| 14 | * TCP 服务器地址 | 43 | * TCP 服务器地址 |
| 15 | */ | 44 | */ |
| 16 | private String host; | 45 | private String host; |
| @@ -23,17 +52,17 @@ public class IotDataSinkTcpConfig extends IotAbstractDataSinkConfig { | @@ -23,17 +52,17 @@ public class IotDataSinkTcpConfig extends IotAbstractDataSinkConfig { | ||
| 23 | /** | 52 | /** |
| 24 | * 连接超时时间(毫秒) | 53 | * 连接超时时间(毫秒) |
| 25 | */ | 54 | */ |
| 26 | - private Integer connectTimeoutMs = 5000; | 55 | + private Integer connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS; |
| 27 | 56 | ||
| 28 | /** | 57 | /** |
| 29 | * 读取超时时间(毫秒) | 58 | * 读取超时时间(毫秒) |
| 30 | */ | 59 | */ |
| 31 | - private Integer readTimeoutMs = 10000; | 60 | + private Integer readTimeoutMs = DEFAULT_READ_TIMEOUT_MS; |
| 32 | 61 | ||
| 33 | /** | 62 | /** |
| 34 | * 是否启用 SSL | 63 | * 是否启用 SSL |
| 35 | */ | 64 | */ |
| 36 | - private Boolean ssl = false; | 65 | + private Boolean ssl = DEFAULT_SSL; |
| 37 | 66 | ||
| 38 | /** | 67 | /** |
| 39 | * SSL 证书路径(当 ssl=true 时需要) | 68 | * SSL 证书路径(当 ssl=true 时需要) |
| @@ -43,21 +72,21 @@ public class IotDataSinkTcpConfig extends IotAbstractDataSinkConfig { | @@ -43,21 +72,21 @@ public class IotDataSinkTcpConfig extends IotAbstractDataSinkConfig { | ||
| 43 | /** | 72 | /** |
| 44 | * 数据格式:JSON 或 BINARY | 73 | * 数据格式:JSON 或 BINARY |
| 45 | */ | 74 | */ |
| 46 | - private String dataFormat = "JSON"; | 75 | + private String dataFormat = DEFAULT_DATA_FORMAT; |
| 47 | 76 | ||
| 48 | /** | 77 | /** |
| 49 | * 心跳间隔时间(毫秒),0 表示不启用心跳 | 78 | * 心跳间隔时间(毫秒),0 表示不启用心跳 |
| 50 | */ | 79 | */ |
| 51 | - private Long heartbeatIntervalMs = 30000L; | 80 | + private Long heartbeatIntervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS; |
| 52 | 81 | ||
| 53 | /** | 82 | /** |
| 54 | * 重连间隔时间(毫秒) | 83 | * 重连间隔时间(毫秒) |
| 55 | */ | 84 | */ |
| 56 | - private Long reconnectIntervalMs = 5000L; | 85 | + private Long reconnectIntervalMs = DEFAULT_RECONNECT_INTERVAL_MS; |
| 57 | 86 | ||
| 58 | /** | 87 | /** |
| 59 | * 最大重连次数 | 88 | * 最大重连次数 |
| 60 | */ | 89 | */ |
| 61 | - private Integer maxReconnectAttempts = 3; | 90 | + private Integer maxReconnectAttempts = DEFAULT_MAX_RECONNECT_ATTEMPTS; |
| 62 | 91 | ||
| 63 | } | 92 | } |
| 64 | \ No newline at end of file | 93 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkWebSocketConfig.java
| @@ -14,6 +14,51 @@ import lombok.Data; | @@ -14,6 +14,51 @@ import lombok.Data; | ||
| 14 | public class IotDataSinkWebSocketConfig extends IotAbstractDataSinkConfig { | 14 | public class IotDataSinkWebSocketConfig extends IotAbstractDataSinkConfig { |
| 15 | 15 | ||
| 16 | /** | 16 | /** |
| 17 | + * 默认连接超时时间(毫秒) | ||
| 18 | + */ | ||
| 19 | + public static final int DEFAULT_CONNECT_TIMEOUT_MS = 5000; | ||
| 20 | + /** | ||
| 21 | + * 默认发送超时时间(毫秒) | ||
| 22 | + */ | ||
| 23 | + public static final int DEFAULT_SEND_TIMEOUT_MS = 10000; | ||
| 24 | + /** | ||
| 25 | + * 默认心跳间隔时间(毫秒) | ||
| 26 | + */ | ||
| 27 | + public static final long DEFAULT_HEARTBEAT_INTERVAL_MS = 30000L; | ||
| 28 | + /** | ||
| 29 | + * 默认心跳消息内容 | ||
| 30 | + */ | ||
| 31 | + public static final String DEFAULT_HEARTBEAT_MESSAGE = "{\"type\":\"heartbeat\"}"; | ||
| 32 | + /** | ||
| 33 | + * 默认是否启用 SSL 证书验证 | ||
| 34 | + */ | ||
| 35 | + public static final boolean DEFAULT_VERIFY_SSL_CERT = true; | ||
| 36 | + /** | ||
| 37 | + * 默认数据格式 | ||
| 38 | + */ | ||
| 39 | + public static final String DEFAULT_DATA_FORMAT = "JSON"; | ||
| 40 | + /** | ||
| 41 | + * 默认重连间隔时间(毫秒) | ||
| 42 | + */ | ||
| 43 | + public static final long DEFAULT_RECONNECT_INTERVAL_MS = 5000L; | ||
| 44 | + /** | ||
| 45 | + * 默认最大重连次数 | ||
| 46 | + */ | ||
| 47 | + public static final int DEFAULT_MAX_RECONNECT_ATTEMPTS = 3; | ||
| 48 | + /** | ||
| 49 | + * 默认是否启用压缩 | ||
| 50 | + */ | ||
| 51 | + public static final boolean DEFAULT_ENABLE_COMPRESSION = false; | ||
| 52 | + /** | ||
| 53 | + * 默认消息发送重试次数 | ||
| 54 | + */ | ||
| 55 | + public static final int DEFAULT_SEND_RETRY_COUNT = 1; | ||
| 56 | + /** | ||
| 57 | + * 默认消息发送重试间隔(毫秒) | ||
| 58 | + */ | ||
| 59 | + public static final long DEFAULT_SEND_RETRY_INTERVAL_MS = 1000L; | ||
| 60 | + | ||
| 61 | + /** | ||
| 17 | * WebSocket 服务器地址 | 62 | * WebSocket 服务器地址 |
| 18 | * 例如:ws://localhost:8080/ws 或 wss://example.com/ws | 63 | * 例如:ws://localhost:8080/ws 或 wss://example.com/ws |
| 19 | */ | 64 | */ |
| @@ -22,22 +67,22 @@ public class IotDataSinkWebSocketConfig extends IotAbstractDataSinkConfig { | @@ -22,22 +67,22 @@ public class IotDataSinkWebSocketConfig extends IotAbstractDataSinkConfig { | ||
| 22 | /** | 67 | /** |
| 23 | * 连接超时时间(毫秒) | 68 | * 连接超时时间(毫秒) |
| 24 | */ | 69 | */ |
| 25 | - private Integer connectTimeoutMs = 5000; | 70 | + private Integer connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS; |
| 26 | 71 | ||
| 27 | /** | 72 | /** |
| 28 | * 发送超时时间(毫秒) | 73 | * 发送超时时间(毫秒) |
| 29 | */ | 74 | */ |
| 30 | - private Integer sendTimeoutMs = 10000; | 75 | + private Integer sendTimeoutMs = DEFAULT_SEND_TIMEOUT_MS; |
| 31 | 76 | ||
| 32 | /** | 77 | /** |
| 33 | * 心跳间隔时间(毫秒),0 表示不启用心跳 | 78 | * 心跳间隔时间(毫秒),0 表示不启用心跳 |
| 34 | */ | 79 | */ |
| 35 | - private Long heartbeatIntervalMs = 30000L; | 80 | + private Long heartbeatIntervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS; |
| 36 | 81 | ||
| 37 | /** | 82 | /** |
| 38 | * 心跳消息内容(JSON 格式) | 83 | * 心跳消息内容(JSON 格式) |
| 39 | */ | 84 | */ |
| 40 | - private String heartbeatMessage = "{\"type\":\"heartbeat\"}"; | 85 | + private String heartbeatMessage = DEFAULT_HEARTBEAT_MESSAGE; |
| 41 | 86 | ||
| 42 | /** | 87 | /** |
| 43 | * 子协议列表(逗号分隔) | 88 | * 子协议列表(逗号分隔) |
| @@ -52,36 +97,36 @@ public class IotDataSinkWebSocketConfig extends IotAbstractDataSinkConfig { | @@ -52,36 +97,36 @@ public class IotDataSinkWebSocketConfig extends IotAbstractDataSinkConfig { | ||
| 52 | /** | 97 | /** |
| 53 | * 是否启用 SSL 证书验证(仅对 wss:// 生效) | 98 | * 是否启用 SSL 证书验证(仅对 wss:// 生效) |
| 54 | */ | 99 | */ |
| 55 | - private Boolean verifySslCert = true; | 100 | + private Boolean verifySslCert = DEFAULT_VERIFY_SSL_CERT; |
| 56 | 101 | ||
| 57 | /** | 102 | /** |
| 58 | * 数据格式:JSON 或 TEXT | 103 | * 数据格式:JSON 或 TEXT |
| 59 | */ | 104 | */ |
| 60 | - private String dataFormat = "JSON"; | 105 | + private String dataFormat = DEFAULT_DATA_FORMAT; |
| 61 | 106 | ||
| 62 | /** | 107 | /** |
| 63 | * 重连间隔时间(毫秒) | 108 | * 重连间隔时间(毫秒) |
| 64 | */ | 109 | */ |
| 65 | - private Long reconnectIntervalMs = 5000L; | 110 | + private Long reconnectIntervalMs = DEFAULT_RECONNECT_INTERVAL_MS; |
| 66 | 111 | ||
| 67 | /** | 112 | /** |
| 68 | * 最大重连次数 | 113 | * 最大重连次数 |
| 69 | */ | 114 | */ |
| 70 | - private Integer maxReconnectAttempts = 3; | 115 | + private Integer maxReconnectAttempts = DEFAULT_MAX_RECONNECT_ATTEMPTS; |
| 71 | 116 | ||
| 72 | /** | 117 | /** |
| 73 | * 是否启用压缩 | 118 | * 是否启用压缩 |
| 74 | */ | 119 | */ |
| 75 | - private Boolean enableCompression = false; | 120 | + private Boolean enableCompression = DEFAULT_ENABLE_COMPRESSION; |
| 76 | 121 | ||
| 77 | /** | 122 | /** |
| 78 | * 消息发送重试次数 | 123 | * 消息发送重试次数 |
| 79 | */ | 124 | */ |
| 80 | - private Integer sendRetryCount = 1; | 125 | + private Integer sendRetryCount = DEFAULT_SEND_RETRY_COUNT; |
| 81 | 126 | ||
| 82 | /** | 127 | /** |
| 83 | * 消息发送重试间隔(毫秒) | 128 | * 消息发送重试间隔(毫秒) |
| 84 | */ | 129 | */ |
| 85 | - private Long sendRetryIntervalMs = 1000L; | 130 | + private Long sendRetryIntervalMs = DEFAULT_SEND_RETRY_INTERVAL_MS; |
| 86 | 131 | ||
| 87 | } | 132 | } |
| 88 | \ No newline at end of file | 133 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataSinkTypeEnum.java
| @@ -16,8 +16,8 @@ import java.util.Arrays; | @@ -16,8 +16,8 @@ import java.util.Arrays; | ||
| 16 | public enum IotDataSinkTypeEnum implements ArrayValuable<Integer> { | 16 | public enum IotDataSinkTypeEnum implements ArrayValuable<Integer> { |
| 17 | 17 | ||
| 18 | HTTP(1, "HTTP"), | 18 | HTTP(1, "HTTP"), |
| 19 | - TCP(2, "TCP"), // TODO @puhui999:待实现; | ||
| 20 | - WEBSOCKET(3, "WebSocket"), // TODO @puhui999:待实现; | 19 | + TCP(2, "TCP"), |
| 20 | + WEBSOCKET(3, "WebSocket"), | ||
| 21 | 21 | ||
| 22 | MQTT(10, "MQTT"), // TODO 待实现; | 22 | MQTT(10, "MQTT"), // TODO 待实现; |
| 23 | 23 |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java
| 1 | package cn.iocoder.yudao.module.iot.service.device.property; | 1 | package cn.iocoder.yudao.module.iot.service.device.property; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.convert.Convert; | ||
| 4 | import cn.hutool.core.date.LocalDateTimeUtil; | 5 | import cn.hutool.core.date.LocalDateTimeUtil; |
| 5 | import cn.hutool.core.map.MapUtil; | 6 | import cn.hutool.core.map.MapUtil; |
| 6 | import cn.hutool.core.util.StrUtil; | 7 | import cn.hutool.core.util.StrUtil; |
| @@ -145,6 +146,12 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { | @@ -145,6 +146,12 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { | ||
| 145 | IotDataSpecsDataTypeEnum.STRUCT.getDataType(), IotDataSpecsDataTypeEnum.ARRAY.getDataType())) { | 146 | IotDataSpecsDataTypeEnum.STRUCT.getDataType(), IotDataSpecsDataTypeEnum.ARRAY.getDataType())) { |
| 146 | // 特殊:STRUCT 和 ARRAY 类型,在 TDengine 里,有没对应数据类型,只能通过 JSON 来存储 | 147 | // 特殊:STRUCT 和 ARRAY 类型,在 TDengine 里,有没对应数据类型,只能通过 JSON 来存储 |
| 147 | properties.put((String) key, JsonUtils.toJsonString(value)); | 148 | properties.put((String) key, JsonUtils.toJsonString(value)); |
| 149 | + } else if (IotDataSpecsDataTypeEnum.DOUBLE.getDataType().equals(thingModel.getProperty().getDataType())) { | ||
| 150 | + properties.put((String) key, Convert.toDouble(value)); | ||
| 151 | + } else if (IotDataSpecsDataTypeEnum.FLOAT.getDataType().equals(thingModel.getProperty().getDataType())) { | ||
| 152 | + properties.put((String) key, Convert.toFloat(value)); | ||
| 153 | + } else if (IotDataSpecsDataTypeEnum.BOOL.getDataType().equals(thingModel.getProperty().getDataType())) { | ||
| 154 | + properties.put((String) key, Convert.toByte(value)); | ||
| 148 | } else { | 155 | } else { |
| 149 | properties.put((String) key, value); | 156 | properties.put((String) key, value); |
| 150 | } | 157 | } |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotTcpDataRuleAction.java
| @@ -7,8 +7,6 @@ import cn.iocoder.yudao.module.iot.service.rule.data.action.tcp.IotTcpClient; | @@ -7,8 +7,6 @@ import cn.iocoder.yudao.module.iot.service.rule.data.action.tcp.IotTcpClient; | ||
| 7 | import lombok.extern.slf4j.Slf4j; | 7 | import lombok.extern.slf4j.Slf4j; |
| 8 | import org.springframework.stereotype.Component; | 8 | import org.springframework.stereotype.Component; |
| 9 | 9 | ||
| 10 | -import java.time.Duration; | ||
| 11 | - | ||
| 12 | /** | 10 | /** |
| 13 | * TCP 的 {@link IotDataRuleAction} 实现类 | 11 | * TCP 的 {@link IotDataRuleAction} 实现类 |
| 14 | * <p> | 12 | * <p> |
| @@ -23,9 +21,6 @@ import java.time.Duration; | @@ -23,9 +21,6 @@ import java.time.Duration; | ||
| 23 | public class IotTcpDataRuleAction extends | 21 | public class IotTcpDataRuleAction extends |
| 24 | IotDataRuleCacheableAction<IotDataSinkTcpConfig, IotTcpClient> { | 22 | IotDataRuleCacheableAction<IotDataSinkTcpConfig, IotTcpClient> { |
| 25 | 23 | ||
| 26 | - private static final Duration CONNECT_TIMEOUT = Duration.ofSeconds(5); | ||
| 27 | - private static final Duration SEND_TIMEOUT = Duration.ofSeconds(10); | ||
| 28 | - | ||
| 29 | @Override | 24 | @Override |
| 30 | public Integer getType() { | 25 | public Integer getType() { |
| 31 | return IotDataSinkTypeEnum.TCP.getType(); | 26 | return IotDataSinkTypeEnum.TCP.getType(); |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotWebSocketDataRuleAction.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.rule.data.action; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 4 | +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkWebSocketConfig; | ||
| 5 | +import cn.iocoder.yudao.module.iot.enums.rule.IotDataSinkTypeEnum; | ||
| 6 | +import cn.iocoder.yudao.module.iot.service.rule.data.action.websocket.IotWebSocketClient; | ||
| 7 | +import lombok.extern.slf4j.Slf4j; | ||
| 8 | +import org.springframework.stereotype.Component; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * WebSocket 的 {@link IotDataRuleAction} 实现类 | ||
| 12 | + * <p> | ||
| 13 | + * 负责将设备消息发送到外部 WebSocket 服务器 | ||
| 14 | + * 支持 ws:// 和 wss:// 协议,支持 JSON 和 TEXT 数据格式 | ||
| 15 | + * 使用连接池管理 WebSocket 连接,提高性能和资源利用率 | ||
| 16 | + * | ||
| 17 | + * @author HUIHUI | ||
| 18 | + */ | ||
| 19 | +@Component | ||
| 20 | +@Slf4j | ||
| 21 | +public class IotWebSocketDataRuleAction extends | ||
| 22 | + IotDataRuleCacheableAction<IotDataSinkWebSocketConfig, IotWebSocketClient> { | ||
| 23 | + | ||
| 24 | + @Override | ||
| 25 | + public Integer getType() { | ||
| 26 | + return IotDataSinkTypeEnum.WEBSOCKET.getType(); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + @Override | ||
| 30 | + protected IotWebSocketClient initProducer(IotDataSinkWebSocketConfig config) throws Exception { | ||
| 31 | + // 1.1 参数校验 | ||
| 32 | + if (config.getServerUrl() == null || config.getServerUrl().trim().isEmpty()) { | ||
| 33 | + throw new IllegalArgumentException("WebSocket 服务器地址不能为空"); | ||
| 34 | + } | ||
| 35 | + if (!config.getServerUrl().startsWith("ws://") && !config.getServerUrl().startsWith("wss://")) { | ||
| 36 | + throw new IllegalArgumentException("WebSocket 服务器地址必须以 ws:// 或 wss:// 开头"); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + // 2.1 创建 WebSocket 客户端 | ||
| 40 | + IotWebSocketClient webSocketClient = new IotWebSocketClient( | ||
| 41 | + config.getServerUrl(), | ||
| 42 | + config.getConnectTimeoutMs(), | ||
| 43 | + config.getSendTimeoutMs(), | ||
| 44 | + config.getDataFormat() | ||
| 45 | + ); | ||
| 46 | + // 2.2 连接服务器 | ||
| 47 | + webSocketClient.connect(); | ||
| 48 | + log.info("[initProducer][WebSocket 客户端创建并连接成功,服务器: {},数据格式: {}]", | ||
| 49 | + config.getServerUrl(), config.getDataFormat()); | ||
| 50 | + return webSocketClient; | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + @Override | ||
| 54 | + protected void closeProducer(IotWebSocketClient producer) throws Exception { | ||
| 55 | + if (producer != null) { | ||
| 56 | + producer.close(); | ||
| 57 | + } | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + @Override | ||
| 61 | + protected void execute(IotDeviceMessage message, IotDataSinkWebSocketConfig config) throws Exception { | ||
| 62 | + try { | ||
| 63 | + // 1.1 获取或创建 WebSocket 客户端 | ||
| 64 | + IotWebSocketClient webSocketClient = getProducer(config); | ||
| 65 | + // 1.2 检查连接状态,如果断开则重新连接 | ||
| 66 | + if (!webSocketClient.isConnected()) { | ||
| 67 | + log.warn("[execute][WebSocket 连接已断开,尝试重新连接,服务器: {}]", config.getServerUrl()); | ||
| 68 | + webSocketClient.connect(); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + // 2.1 发送消息 | ||
| 72 | + webSocketClient.sendMessage(message); | ||
| 73 | + // 2.2 记录发送成功日志 | ||
| 74 | + log.info("[execute][message({}) config({}) 发送成功,WebSocket 服务器: {}]", | ||
| 75 | + message, config, config.getServerUrl()); | ||
| 76 | + } catch (Exception e) { | ||
| 77 | + log.error("[execute][message({}) config({}) 发送失败,WebSocket 服务器: {}]", | ||
| 78 | + message, config, config.getServerUrl(), e); | ||
| 79 | + throw e; | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClient.java
| @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.rule.data.action.tcp; | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.rule.data.action.tcp; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 3 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 5 | +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkTcpConfig; | ||
| 5 | import lombok.extern.slf4j.Slf4j; | 6 | import lombok.extern.slf4j.Slf4j; |
| 6 | 7 | ||
| 7 | import javax.net.ssl.SSLSocketFactory; | 8 | import javax.net.ssl.SSLSocketFactory; |
| @@ -38,16 +39,15 @@ public class IotTcpClient { | @@ -38,16 +39,15 @@ public class IotTcpClient { | ||
| 38 | private BufferedReader reader; | 39 | private BufferedReader reader; |
| 39 | private final AtomicBoolean connected = new AtomicBoolean(false); | 40 | private final AtomicBoolean connected = new AtomicBoolean(false); |
| 40 | 41 | ||
| 41 | - // TODO @puhui999:default 值,IotDataSinkTcpConfig.java 枚举起来哈; | ||
| 42 | public IotTcpClient(String host, Integer port, Integer connectTimeoutMs, Integer readTimeoutMs, | 42 | public IotTcpClient(String host, Integer port, Integer connectTimeoutMs, Integer readTimeoutMs, |
| 43 | Boolean ssl, String sslCertPath, String dataFormat) { | 43 | Boolean ssl, String sslCertPath, String dataFormat) { |
| 44 | this.host = host; | 44 | this.host = host; |
| 45 | this.port = port; | 45 | this.port = port; |
| 46 | - this.connectTimeoutMs = connectTimeoutMs != null ? connectTimeoutMs : 5000; | ||
| 47 | - this.readTimeoutMs = readTimeoutMs != null ? readTimeoutMs : 10000; | ||
| 48 | - this.ssl = ssl != null ? ssl : false; | 46 | + this.connectTimeoutMs = connectTimeoutMs != null ? connectTimeoutMs : IotDataSinkTcpConfig.DEFAULT_CONNECT_TIMEOUT_MS; |
| 47 | + this.readTimeoutMs = readTimeoutMs != null ? readTimeoutMs : IotDataSinkTcpConfig.DEFAULT_READ_TIMEOUT_MS; | ||
| 48 | + this.ssl = ssl != null ? ssl : IotDataSinkTcpConfig.DEFAULT_SSL; | ||
| 49 | this.sslCertPath = sslCertPath; | 49 | this.sslCertPath = sslCertPath; |
| 50 | - this.dataFormat = dataFormat != null ? dataFormat : "JSON"; | 50 | + this.dataFormat = dataFormat != null ? dataFormat : IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT; |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | /** | 53 | /** |
| @@ -99,9 +99,8 @@ public class IotTcpClient { | @@ -99,9 +99,8 @@ public class IotTcpClient { | ||
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | try { | 101 | try { |
| 102 | - // TODO @puhui999:枚举值 | ||
| 103 | String messageData; | 102 | String messageData; |
| 104 | - if ("JSON".equalsIgnoreCase(dataFormat)) { | 103 | + if (IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT.equalsIgnoreCase(dataFormat)) { |
| 105 | // JSON 格式 | 104 | // JSON 格式 |
| 106 | messageData = JsonUtils.toJsonString(message); | 105 | messageData = JsonUtils.toJsonString(message); |
| 107 | } else { | 106 | } else { |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/websocket/IotWebSocketClient.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.rule.data.action.websocket; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 5 | +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkWebSocketConfig; | ||
| 6 | +import lombok.extern.slf4j.Slf4j; | ||
| 7 | + | ||
| 8 | +import java.net.URI; | ||
| 9 | +import java.net.http.HttpClient; | ||
| 10 | +import java.net.http.WebSocket; | ||
| 11 | +import java.time.Duration; | ||
| 12 | +import java.util.concurrent.CompletableFuture; | ||
| 13 | +import java.util.concurrent.CompletionStage; | ||
| 14 | +import java.util.concurrent.TimeUnit; | ||
| 15 | +import java.util.concurrent.atomic.AtomicBoolean; | ||
| 16 | + | ||
| 17 | +/** | ||
| 18 | + * IoT WebSocket 客户端 | ||
| 19 | + * <p> | ||
| 20 | + * 负责与外部 WebSocket 服务器建立连接并发送设备消息 | ||
| 21 | + * 支持 ws:// 和 wss:// 协议,支持 JSON 和 TEXT 数据格式 | ||
| 22 | + * 基于 Java 11+ 内置的 java.net.http.WebSocket 实现 | ||
| 23 | + * | ||
| 24 | + * @author HUIHUI | ||
| 25 | + */ | ||
| 26 | +@Slf4j | ||
| 27 | +public class IotWebSocketClient implements WebSocket.Listener { | ||
| 28 | + | ||
| 29 | + private final String serverUrl; | ||
| 30 | + private final Integer connectTimeoutMs; | ||
| 31 | + private final Integer sendTimeoutMs; | ||
| 32 | + private final String dataFormat; | ||
| 33 | + | ||
| 34 | + private WebSocket webSocket; | ||
| 35 | + private final AtomicBoolean connected = new AtomicBoolean(false); | ||
| 36 | + private final StringBuilder messageBuffer = new StringBuilder(); | ||
| 37 | + | ||
| 38 | + public IotWebSocketClient(String serverUrl, Integer connectTimeoutMs, Integer sendTimeoutMs, String dataFormat) { | ||
| 39 | + this.serverUrl = serverUrl; | ||
| 40 | + this.connectTimeoutMs = connectTimeoutMs != null ? connectTimeoutMs : IotDataSinkWebSocketConfig.DEFAULT_CONNECT_TIMEOUT_MS; | ||
| 41 | + this.sendTimeoutMs = sendTimeoutMs != null ? sendTimeoutMs : IotDataSinkWebSocketConfig.DEFAULT_SEND_TIMEOUT_MS; | ||
| 42 | + this.dataFormat = dataFormat != null ? dataFormat : IotDataSinkWebSocketConfig.DEFAULT_DATA_FORMAT; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 连接到 WebSocket 服务器 | ||
| 47 | + */ | ||
| 48 | + public void connect() throws Exception { | ||
| 49 | + if (connected.get()) { | ||
| 50 | + log.warn("[connect][WebSocket 客户端已经连接,无需重复连接]"); | ||
| 51 | + return; | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + try { | ||
| 55 | + HttpClient httpClient = HttpClient.newBuilder() | ||
| 56 | + .connectTimeout(Duration.ofMillis(connectTimeoutMs)) | ||
| 57 | + .build(); | ||
| 58 | + | ||
| 59 | + CompletableFuture<WebSocket> future = httpClient.newWebSocketBuilder() | ||
| 60 | + .connectTimeout(Duration.ofMillis(connectTimeoutMs)) | ||
| 61 | + .buildAsync(URI.create(serverUrl), this); | ||
| 62 | + | ||
| 63 | + // 等待连接完成 | ||
| 64 | + webSocket = future.get(connectTimeoutMs, TimeUnit.MILLISECONDS); | ||
| 65 | + connected.set(true); | ||
| 66 | + log.info("[connect][WebSocket 客户端连接成功,服务器地址: {}]", serverUrl); | ||
| 67 | + } catch (Exception e) { | ||
| 68 | + close(); | ||
| 69 | + log.error("[connect][WebSocket 客户端连接失败,服务器地址: {}]", serverUrl, e); | ||
| 70 | + throw e; | ||
| 71 | + } | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + @Override | ||
| 75 | + public void onOpen(WebSocket webSocket) { | ||
| 76 | + log.debug("[onOpen][WebSocket 连接已打开]"); | ||
| 77 | + webSocket.request(1); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + @Override | ||
| 81 | + public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) { | ||
| 82 | + messageBuffer.append(data); | ||
| 83 | + if (last) { | ||
| 84 | + log.debug("[onText][收到 WebSocket 消息: {}]", messageBuffer); | ||
| 85 | + messageBuffer.setLength(0); | ||
| 86 | + } | ||
| 87 | + webSocket.request(1); | ||
| 88 | + return null; | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + @Override | ||
| 92 | + public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) { | ||
| 93 | + connected.set(false); | ||
| 94 | + log.info("[onClose][WebSocket 连接已关闭,状态码: {},原因: {}]", statusCode, reason); | ||
| 95 | + return null; | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + @Override | ||
| 99 | + public void onError(WebSocket webSocket, Throwable error) { | ||
| 100 | + connected.set(false); | ||
| 101 | + log.error("[onError][WebSocket 发生错误]", error); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + /** | ||
| 105 | + * 发送设备消息 | ||
| 106 | + * | ||
| 107 | + * @param message 设备消息 | ||
| 108 | + * @throws Exception 发送异常 | ||
| 109 | + */ | ||
| 110 | + public void sendMessage(IotDeviceMessage message) throws Exception { | ||
| 111 | + if (!connected.get() || webSocket == null) { | ||
| 112 | + throw new IllegalStateException("WebSocket 客户端未连接"); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + try { | ||
| 116 | + String messageData; | ||
| 117 | + if (IotDataSinkWebSocketConfig.DEFAULT_DATA_FORMAT.equalsIgnoreCase(dataFormat)) { | ||
| 118 | + messageData = JsonUtils.toJsonString(message); | ||
| 119 | + } else { | ||
| 120 | + messageData = message.toString(); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + // 发送消息并等待完成 | ||
| 124 | + CompletableFuture<WebSocket> future = webSocket.sendText(messageData, true); | ||
| 125 | + future.get(sendTimeoutMs, TimeUnit.MILLISECONDS); | ||
| 126 | + log.debug("[sendMessage][发送消息成功,设备 ID: {},消息长度: {}]", | ||
| 127 | + message.getDeviceId(), messageData.length()); | ||
| 128 | + } catch (Exception e) { | ||
| 129 | + log.error("[sendMessage][发送消息失败,设备 ID: {}]", message.getDeviceId(), e); | ||
| 130 | + throw e; | ||
| 131 | + } | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + /** | ||
| 135 | + * 关闭连接 | ||
| 136 | + */ | ||
| 137 | + public void close() { | ||
| 138 | + if (!connected.get() && webSocket == null) { | ||
| 139 | + return; | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + try { | ||
| 143 | + if (webSocket != null) { | ||
| 144 | + webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "客户端主动关闭") | ||
| 145 | + .orTimeout(5, TimeUnit.SECONDS) | ||
| 146 | + .exceptionally(e -> { | ||
| 147 | + log.warn("[close][发送关闭帧失败]", e); | ||
| 148 | + return null; | ||
| 149 | + }); | ||
| 150 | + } | ||
| 151 | + connected.set(false); | ||
| 152 | + log.info("[close][WebSocket 客户端连接已关闭,服务器地址: {}]", serverUrl); | ||
| 153 | + } catch (Exception e) { | ||
| 154 | + log.error("[close][关闭 WebSocket 客户端连接异常]", e); | ||
| 155 | + } | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + /** | ||
| 159 | + * 检查连接状态 | ||
| 160 | + * | ||
| 161 | + * @return 是否已连接 | ||
| 162 | + */ | ||
| 163 | + public boolean isConnected() { | ||
| 164 | + return connected.get() && webSocket != null; | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + @Override | ||
| 168 | + public String toString() { | ||
| 169 | + return "IotWebSocketClient{" + | ||
| 170 | + "serverUrl='" + serverUrl + '\'' + | ||
| 171 | + ", dataFormat='" + dataFormat + '\'' + | ||
| 172 | + ", connected=" + connected.get() + | ||
| 173 | + '}'; | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java
| @@ -30,6 +30,7 @@ import org.springframework.cache.annotation.Cacheable; | @@ -30,6 +30,7 @@ import org.springframework.cache.annotation.Cacheable; | ||
| 30 | import org.springframework.stereotype.Service; | 30 | import org.springframework.stereotype.Service; |
| 31 | import org.springframework.validation.annotation.Validated; | 31 | import org.springframework.validation.annotation.Validated; |
| 32 | 32 | ||
| 33 | +import java.time.LocalDateTime; | ||
| 33 | import java.util.Collection; | 34 | import java.util.Collection; |
| 34 | import java.util.List; | 35 | import java.util.List; |
| 35 | 36 | ||
| @@ -392,9 +393,28 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { | @@ -392,9 +393,28 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { | ||
| 392 | } | 393 | } |
| 393 | }); | 394 | }); |
| 394 | }); | 395 | }); |
| 396 | + | ||
| 397 | + // 3. 更新最后触发时间 | ||
| 398 | + updateLastTriggerTime(sceneRule.getId()); | ||
| 395 | }); | 399 | }); |
| 396 | } | 400 | } |
| 397 | 401 | ||
| 402 | + /** | ||
| 403 | + * 更新规则场景的最后触发时间 | ||
| 404 | + * | ||
| 405 | + * @param id 规则场景编号 | ||
| 406 | + */ | ||
| 407 | + private void updateLastTriggerTime(Long id) { | ||
| 408 | + try { | ||
| 409 | + IotSceneRuleDO updateObj = new IotSceneRuleDO() | ||
| 410 | + .setId(id) | ||
| 411 | + .setLastTriggerTime(LocalDateTime.now()); | ||
| 412 | + sceneRuleMapper.updateById(updateObj); | ||
| 413 | + } catch (Exception e) { | ||
| 414 | + log.error("[updateLastTriggerTime][规则场景编号({}) 更新最后触发时间异常]", id, e); | ||
| 415 | + } | ||
| 416 | + } | ||
| 417 | + | ||
| 398 | private IotSceneRuleServiceImpl getSelf() { | 418 | private IotSceneRuleServiceImpl getSelf() { |
| 399 | return SpringUtil.getBean(IotSceneRuleServiceImpl.class); | 419 | return SpringUtil.getBean(IotSceneRuleServiceImpl.class); |
| 400 | } | 420 | } |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java
| @@ -36,11 +36,11 @@ public class IotDevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerM | @@ -36,11 +36,11 @@ public class IotDevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerM | ||
| 36 | return false; | 36 | return false; |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | - // 1.3 检查标识符是否匹配 | ||
| 40 | - String messageIdentifier = IotDeviceMessageUtils.getIdentifier(message); | ||
| 41 | - if (!IotSceneRuleMatcherHelper.isIdentifierMatched(trigger.getIdentifier(), messageIdentifier)) { | ||
| 42 | - IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "标识符不匹配,期望: " + | ||
| 43 | - trigger.getIdentifier() + ", 实际: " + messageIdentifier); | 39 | + // 1.3 检查消息中是否包含触发器指定的属性标识符 |
| 40 | + // 注意:属性上报可能同时上报多个属性,所以需要判断 trigger.getIdentifier() 是否在 message 的 params 中 | ||
| 41 | + if (!IotDeviceMessageUtils.containsIdentifier(message, trigger.getIdentifier())) { | ||
| 42 | + IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中不包含属性: " + | ||
| 43 | + trigger.getIdentifier()); | ||
| 44 | return false; | 44 | return false; |
| 45 | } | 45 | } |
| 46 | 46 |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java
| @@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil; | @@ -5,6 +5,7 @@ import cn.hutool.core.map.MapUtil; | ||
| 5 | import cn.hutool.core.util.IdUtil; | 5 | import cn.hutool.core.util.IdUtil; |
| 6 | import cn.hutool.core.util.StrUtil; | 6 | import cn.hutool.core.util.StrUtil; |
| 7 | import cn.hutool.system.SystemUtil; | 7 | import cn.hutool.system.SystemUtil; |
| 8 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 8 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | 9 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; |
| 9 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 10 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 10 | 11 | ||
| @@ -70,6 +71,55 @@ public class IotDeviceMessageUtils { | @@ -70,6 +71,55 @@ public class IotDeviceMessageUtils { | ||
| 70 | } | 71 | } |
| 71 | 72 | ||
| 72 | /** | 73 | /** |
| 74 | + * 判断消息中是否包含指定的标识符 | ||
| 75 | + * | ||
| 76 | + * 对于不同消息类型的处理: | ||
| 77 | + * - EVENT_POST/SERVICE_INVOKE:检查 params.identifier 是否匹配 | ||
| 78 | + * - STATE_UPDATE:检查 params.state 是否匹配 | ||
| 79 | + * - PROPERTY_POST:检查 params 中是否包含该属性 key | ||
| 80 | + * | ||
| 81 | + * @param message 消息 | ||
| 82 | + * @param identifier 要检查的标识符 | ||
| 83 | + * @return 是否包含 | ||
| 84 | + */ | ||
| 85 | + public static boolean containsIdentifier(IotDeviceMessage message, String identifier) { | ||
| 86 | + if (message.getParams() == null || StrUtil.isBlank(identifier)) { | ||
| 87 | + return false; | ||
| 88 | + } | ||
| 89 | + // EVENT_POST / SERVICE_INVOKE / STATE_UPDATE:使用原有逻辑 | ||
| 90 | + String messageIdentifier = getIdentifier(message); | ||
| 91 | + if (messageIdentifier != null) { | ||
| 92 | + return identifier.equals(messageIdentifier); | ||
| 93 | + } | ||
| 94 | + // PROPERTY_POST:检查 params 中是否包含该属性 key | ||
| 95 | + if (StrUtil.equals(message.getMethod(), IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())) { | ||
| 96 | + Map<String, Object> params = parseParamsToMap(message.getParams()); | ||
| 97 | + return params != null && params.containsKey(identifier); | ||
| 98 | + } | ||
| 99 | + return false; | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + /** | ||
| 103 | + * 将 params 解析为 Map | ||
| 104 | + * | ||
| 105 | + * @param params 参数(可能是 Map 或 JSON 字符串) | ||
| 106 | + * @return Map,解析失败返回 null | ||
| 107 | + */ | ||
| 108 | + @SuppressWarnings("unchecked") | ||
| 109 | + private static Map<String, Object> parseParamsToMap(Object params) { | ||
| 110 | + if (params instanceof Map) { | ||
| 111 | + return (Map<String, Object>) params; | ||
| 112 | + } | ||
| 113 | + if (params instanceof String) { | ||
| 114 | + try { | ||
| 115 | + return JsonUtils.parseObject((String) params, Map.class); | ||
| 116 | + } catch (Exception ignored) { | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + return null; | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + /** | ||
| 73 | * 从设备消息中提取指定标识符的属性值 | 123 | * 从设备消息中提取指定标识符的属性值 |
| 74 | * - 支持多种消息格式和属性值提取策略 | 124 | * - 支持多种消息格式和属性值提取策略 |
| 75 | * - 兼容现有的消息结构 | 125 | * - 兼容现有的消息结构 |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
| @@ -21,6 +21,7 @@ import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; | @@ -21,6 +21,7 @@ import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; | ||
| 21 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | 21 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; |
| 22 | import io.vertx.core.Vertx; | 22 | import io.vertx.core.Vertx; |
| 23 | import lombok.extern.slf4j.Slf4j; | 23 | import lombok.extern.slf4j.Slf4j; |
| 24 | +import org.springframework.beans.factory.annotation.Qualifier; | ||
| 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 25 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 25 | import org.springframework.boot.context.properties.EnableConfigurationProperties; | 26 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 26 | import org.springframework.context.annotation.Bean; | 27 | import org.springframework.context.annotation.Bean; |
| @@ -59,20 +60,20 @@ public class IotGatewayConfiguration { | @@ -59,20 +60,20 @@ public class IotGatewayConfiguration { | ||
| 59 | @Slf4j | 60 | @Slf4j |
| 60 | public static class EmqxProtocolConfiguration { | 61 | public static class EmqxProtocolConfiguration { |
| 61 | 62 | ||
| 62 | - @Bean(destroyMethod = "close") | 63 | + @Bean(name = "emqxVertx", destroyMethod = "close") |
| 63 | public Vertx emqxVertx() { | 64 | public Vertx emqxVertx() { |
| 64 | return Vertx.vertx(); | 65 | return Vertx.vertx(); |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | @Bean | 68 | @Bean |
| 68 | public IotEmqxAuthEventProtocol iotEmqxAuthEventProtocol(IotGatewayProperties gatewayProperties, | 69 | public IotEmqxAuthEventProtocol iotEmqxAuthEventProtocol(IotGatewayProperties gatewayProperties, |
| 69 | - Vertx emqxVertx) { | 70 | + @Qualifier("emqxVertx") Vertx emqxVertx) { |
| 70 | return new IotEmqxAuthEventProtocol(gatewayProperties.getProtocol().getEmqx(), emqxVertx); | 71 | return new IotEmqxAuthEventProtocol(gatewayProperties.getProtocol().getEmqx(), emqxVertx); |
| 71 | } | 72 | } |
| 72 | 73 | ||
| 73 | @Bean | 74 | @Bean |
| 74 | public IotEmqxUpstreamProtocol iotEmqxUpstreamProtocol(IotGatewayProperties gatewayProperties, | 75 | public IotEmqxUpstreamProtocol iotEmqxUpstreamProtocol(IotGatewayProperties gatewayProperties, |
| 75 | - Vertx emqxVertx) { | 76 | + @Qualifier("emqxVertx") Vertx emqxVertx) { |
| 76 | return new IotEmqxUpstreamProtocol(gatewayProperties.getProtocol().getEmqx(), emqxVertx); | 77 | return new IotEmqxUpstreamProtocol(gatewayProperties.getProtocol().getEmqx(), emqxVertx); |
| 77 | } | 78 | } |
| 78 | 79 | ||
| @@ -91,7 +92,7 @@ public class IotGatewayConfiguration { | @@ -91,7 +92,7 @@ public class IotGatewayConfiguration { | ||
| 91 | @Slf4j | 92 | @Slf4j |
| 92 | public static class TcpProtocolConfiguration { | 93 | public static class TcpProtocolConfiguration { |
| 93 | 94 | ||
| 94 | - @Bean(destroyMethod = "close") | 95 | + @Bean(name = "tcpVertx", destroyMethod = "close") |
| 95 | public Vertx tcpVertx() { | 96 | public Vertx tcpVertx() { |
| 96 | return Vertx.vertx(); | 97 | return Vertx.vertx(); |
| 97 | } | 98 | } |
| @@ -101,7 +102,7 @@ public class IotGatewayConfiguration { | @@ -101,7 +102,7 @@ public class IotGatewayConfiguration { | ||
| 101 | IotDeviceService deviceService, | 102 | IotDeviceService deviceService, |
| 102 | IotDeviceMessageService messageService, | 103 | IotDeviceMessageService messageService, |
| 103 | IotTcpConnectionManager connectionManager, | 104 | IotTcpConnectionManager connectionManager, |
| 104 | - Vertx tcpVertx) { | 105 | + @Qualifier("tcpVertx") Vertx tcpVertx) { |
| 105 | return new IotTcpUpstreamProtocol(gatewayProperties.getProtocol().getTcp(), | 106 | return new IotTcpUpstreamProtocol(gatewayProperties.getProtocol().getTcp(), |
| 106 | deviceService, messageService, connectionManager, tcpVertx); | 107 | deviceService, messageService, connectionManager, tcpVertx); |
| 107 | } | 108 | } |
| @@ -126,7 +127,7 @@ public class IotGatewayConfiguration { | @@ -126,7 +127,7 @@ public class IotGatewayConfiguration { | ||
| 126 | @Slf4j | 127 | @Slf4j |
| 127 | public static class MqttProtocolConfiguration { | 128 | public static class MqttProtocolConfiguration { |
| 128 | 129 | ||
| 129 | - @Bean(destroyMethod = "close") | 130 | + @Bean(name = "mqttVertx", destroyMethod = "close") |
| 130 | public Vertx mqttVertx() { | 131 | public Vertx mqttVertx() { |
| 131 | return Vertx.vertx(); | 132 | return Vertx.vertx(); |
| 132 | } | 133 | } |
| @@ -135,7 +136,7 @@ public class IotGatewayConfiguration { | @@ -135,7 +136,7 @@ public class IotGatewayConfiguration { | ||
| 135 | public IotMqttUpstreamProtocol iotMqttUpstreamProtocol(IotGatewayProperties gatewayProperties, | 136 | public IotMqttUpstreamProtocol iotMqttUpstreamProtocol(IotGatewayProperties gatewayProperties, |
| 136 | IotDeviceMessageService messageService, | 137 | IotDeviceMessageService messageService, |
| 137 | IotMqttConnectionManager connectionManager, | 138 | IotMqttConnectionManager connectionManager, |
| 138 | - Vertx mqttVertx) { | 139 | + @Qualifier("mqttVertx") Vertx mqttVertx) { |
| 139 | return new IotMqttUpstreamProtocol(gatewayProperties.getProtocol().getMqtt(), messageService, | 140 | return new IotMqttUpstreamProtocol(gatewayProperties.getProtocol().getMqtt(), messageService, |
| 140 | connectionManager, mqttVertx); | 141 | connectionManager, mqttVertx); |
| 141 | } | 142 | } |
| @@ -163,7 +164,7 @@ public class IotGatewayConfiguration { | @@ -163,7 +164,7 @@ public class IotGatewayConfiguration { | ||
| 163 | @Slf4j | 164 | @Slf4j |
| 164 | public static class MqttWsProtocolConfiguration { | 165 | public static class MqttWsProtocolConfiguration { |
| 165 | 166 | ||
| 166 | - @Bean(destroyMethod = "close") | 167 | + @Bean(name = "mqttWsVertx", destroyMethod = "close") |
| 167 | public Vertx mqttWsVertx() { | 168 | public Vertx mqttWsVertx() { |
| 168 | return Vertx.vertx(); | 169 | return Vertx.vertx(); |
| 169 | } | 170 | } |
| @@ -172,7 +173,7 @@ public class IotGatewayConfiguration { | @@ -172,7 +173,7 @@ public class IotGatewayConfiguration { | ||
| 172 | public IotMqttWsUpstreamProtocol iotMqttWsUpstreamProtocol(IotGatewayProperties gatewayProperties, | 173 | public IotMqttWsUpstreamProtocol iotMqttWsUpstreamProtocol(IotGatewayProperties gatewayProperties, |
| 173 | IotDeviceMessageService messageService, | 174 | IotDeviceMessageService messageService, |
| 174 | IotMqttWsConnectionManager connectionManager, | 175 | IotMqttWsConnectionManager connectionManager, |
| 175 | - Vertx mqttWsVertx) { | 176 | + @Qualifier("mqttWsVertx") Vertx mqttWsVertx) { |
| 176 | return new IotMqttWsUpstreamProtocol(gatewayProperties.getProtocol().getMqttWs(), | 177 | return new IotMqttWsUpstreamProtocol(gatewayProperties.getProtocol().getMqttWs(), |
| 177 | messageService, connectionManager, mqttWsVertx); | 178 | messageService, connectionManager, mqttWsVertx); |
| 178 | } | 179 | } |