Commit e89fc2bfbd1ac7d88ff59bb4a580cecc564a20fb
1 parent
f4ba6e75
feat(iot):【协议改造】http 初步改造
Showing
40 changed files
with
1467 additions
and
633 deletions
yudao-module-iot/yudao-module-iot-biz/pom.xml
| @@ -101,18 +101,6 @@ | @@ -101,18 +101,6 @@ | ||
| 101 | <artifactId>spring-boot-starter-amqp</artifactId> | 101 | <artifactId>spring-boot-starter-amqp</artifactId> |
| 102 | <optional>true</optional> | 102 | <optional>true</optional> |
| 103 | </dependency> | 103 | </dependency> |
| 104 | - | ||
| 105 | - <!-- IoT 网络组件:接收来自设备的上行数据 --> | ||
| 106 | - <!-- <dependency>--> | ||
| 107 | - <!-- <groupId>cn.iocoder.boot</groupId>--> | ||
| 108 | - <!-- <artifactId>yudao-module-iot-net-component-http</artifactId>--> | ||
| 109 | - <!-- <version>${revision}</version>--> | ||
| 110 | - <!-- </dependency>--> | ||
| 111 | - <!-- <dependency>--> | ||
| 112 | - <!-- <groupId>cn.iocoder.boot</groupId>--> | ||
| 113 | - <!-- <artifactId>yudao-module-iot-net-component-emqx</artifactId>--> | ||
| 114 | - <!-- <version>${revision}</version>--> | ||
| 115 | - <!-- </dependency>--> | ||
| 116 | </dependencies> | 104 | </dependencies> |
| 117 | 105 | ||
| 118 | </project> | 106 | </project> |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotProtocolTypeEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.enums; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.ArrayUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.common.core.ArrayValuable; | ||
| 5 | +import lombok.Getter; | ||
| 6 | +import lombok.RequiredArgsConstructor; | ||
| 7 | + | ||
| 8 | +import java.util.Arrays; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * IoT 协议类型枚举 | ||
| 12 | + * | ||
| 13 | + * 用于定义传输层协议类型 | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +@RequiredArgsConstructor | ||
| 18 | +@Getter | ||
| 19 | +public enum IotProtocolTypeEnum implements ArrayValuable<String> { | ||
| 20 | + | ||
| 21 | + TCP("tcp"), | ||
| 22 | + UDP("udp"), | ||
| 23 | + WEBSOCKET("websocket"), | ||
| 24 | + HTTP("http"), | ||
| 25 | + MQTT("mqtt"), | ||
| 26 | + EMQX("emqx"), | ||
| 27 | + COAP("coap"), | ||
| 28 | + MODBUS_TCP("modbus_tcp"); | ||
| 29 | + | ||
| 30 | + public static final String[] ARRAYS = Arrays.stream(values()).map(IotProtocolTypeEnum::getType).toArray(String[]::new); | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 类型 | ||
| 34 | + */ | ||
| 35 | + private final String type; | ||
| 36 | + | ||
| 37 | + @Override | ||
| 38 | + public String[] array() { | ||
| 39 | + return ARRAYS; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + public static IotProtocolTypeEnum of(String type) { | ||
| 43 | + return ArrayUtil.firstMatch(e -> e.getType().equals(type), values()); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotSerializeTypeEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.enums; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.ArrayUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.common.core.ArrayValuable; | ||
| 5 | +import lombok.Getter; | ||
| 6 | +import lombok.RequiredArgsConstructor; | ||
| 7 | + | ||
| 8 | +import java.util.Arrays; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * IoT 序列化类型枚举 | ||
| 12 | + * | ||
| 13 | + * 用于定义设备消息的序列化格式 | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +@RequiredArgsConstructor | ||
| 18 | +@Getter | ||
| 19 | +public enum IotSerializeTypeEnum implements ArrayValuable<String> { | ||
| 20 | + | ||
| 21 | + JSON("json"), | ||
| 22 | + BINARY("binary"); | ||
| 23 | + | ||
| 24 | + public static final String[] ARRAYS = Arrays.stream(values()).map(IotSerializeTypeEnum::getType).toArray(String[]::new); | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 类型 | ||
| 28 | + */ | ||
| 29 | + private final String type; | ||
| 30 | + | ||
| 31 | + @Override | ||
| 32 | + public String[] array() { | ||
| 33 | + return ARRAYS; | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + public static IotSerializeTypeEnum of(String type) { | ||
| 37 | + return ArrayUtil.firstMatch(e -> e.getType().equals(type), values()); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/IotMessageBus.java
| @@ -24,4 +24,14 @@ public interface IotMessageBus { | @@ -24,4 +24,14 @@ public interface IotMessageBus { | ||
| 24 | */ | 24 | */ |
| 25 | void register(IotMessageSubscriber<?> subscriber); | 25 | void register(IotMessageSubscriber<?> subscriber); |
| 26 | 26 | ||
| 27 | + /** | ||
| 28 | + * 取消注册消息订阅者 | ||
| 29 | + * | ||
| 30 | + * @param subscriber 订阅者 | ||
| 31 | + */ | ||
| 32 | + default void unregister(IotMessageSubscriber<?> subscriber) { | ||
| 33 | + // TODO 芋艿:暂时不实现,需求量不大,但是 | ||
| 34 | + // throw new UnsupportedOperationException("取消注册消息订阅者功能,尚未实现"); | ||
| 35 | + } | ||
| 36 | + | ||
| 27 | } | 37 | } |
| 28 | \ No newline at end of file | 38 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/IotMessageSubscriber.java
| @@ -26,4 +26,16 @@ public interface IotMessageSubscriber<T> { | @@ -26,4 +26,16 @@ public interface IotMessageSubscriber<T> { | ||
| 26 | */ | 26 | */ |
| 27 | void onMessage(T message); | 27 | void onMessage(T message); |
| 28 | 28 | ||
| 29 | + /** | ||
| 30 | + * 启动订阅 | ||
| 31 | + */ | ||
| 32 | + default void start() { | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 停止订阅 | ||
| 37 | + */ | ||
| 38 | + default void stop() { | ||
| 39 | + } | ||
| 40 | + | ||
| 29 | } | 41 | } |
| 30 | \ No newline at end of file | 42 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/message/IotDeviceMessage.java
| @@ -60,7 +60,7 @@ public class IotDeviceMessage { | @@ -60,7 +60,7 @@ public class IotDeviceMessage { | ||
| 60 | */ | 60 | */ |
| 61 | private String serverId; | 61 | private String serverId; |
| 62 | 62 | ||
| 63 | - // ========== codec(编解码)字段 ========== | 63 | + // ========== serialize(序列化)相关字段 ========== |
| 64 | 64 | ||
| 65 | /** | 65 | /** |
| 66 | * 请求编号 | 66 | * 请求编号 |
| @@ -94,7 +94,7 @@ public class IotDeviceMessage { | @@ -94,7 +94,7 @@ public class IotDeviceMessage { | ||
| 94 | */ | 94 | */ |
| 95 | private String msg; | 95 | private String msg; |
| 96 | 96 | ||
| 97 | - // ========== 基础方法:只传递"codec(编解码)字段" ========== | 97 | + // ========== 基础方法:只传递"serialize(序列化)相关字段" ========== |
| 98 | 98 | ||
| 99 | public static IotDeviceMessage requestOf(String method) { | 99 | public static IotDeviceMessage requestOf(String method) { |
| 100 | return requestOf(null, method, null); | 100 | return requestOf(null, method, null); |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.config; | 1 | package cn.iocoder.yudao.module.iot.gateway.config; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 4 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolManager; | ||
| 5 | +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager; | ||
| 4 | import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapDownstreamSubscriber; | 6 | import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapDownstreamSubscriber; |
| 5 | import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapUpstreamProtocol; | 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapUpstreamProtocol; |
| 6 | import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxAuthEventProtocol; | 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxAuthEventProtocol; |
| 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxDownstreamSubscriber; | 9 | import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxDownstreamSubscriber; |
| 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol; | 10 | import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol; |
| 9 | -import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber; | ||
| 10 | -import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; | ||
| 11 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber; | 11 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber; |
| 12 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; | 12 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; |
| 13 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager; | 13 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager; |
| @@ -15,12 +15,15 @@ import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstrea | @@ -15,12 +15,15 @@ import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstrea | ||
| 15 | import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpDownstreamSubscriber; | 15 | import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpDownstreamSubscriber; |
| 16 | import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpUpstreamProtocol; | 16 | import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpUpstreamProtocol; |
| 17 | import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager; | 17 | import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager; |
| 18 | +import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpDownstreamHandler; | ||
| 18 | import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpDownstreamSubscriber; | 19 | import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpDownstreamSubscriber; |
| 19 | import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpUpstreamProtocol; | 20 | import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpUpstreamProtocol; |
| 20 | import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager; | 21 | import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager; |
| 22 | +import cn.iocoder.yudao.module.iot.gateway.protocol.udp.router.IotUdpDownstreamHandler; | ||
| 21 | import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketDownstreamSubscriber; | 23 | import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketDownstreamSubscriber; |
| 22 | import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketUpstreamProtocol; | 24 | import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketUpstreamProtocol; |
| 23 | import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager; | 25 | import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager; |
| 26 | +import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.router.IotWebSocketDownstreamHandler; | ||
| 24 | import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; | 27 | import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; |
| 25 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | 28 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; |
| 26 | import io.vertx.core.Vertx; | 29 | import io.vertx.core.Vertx; |
| @@ -31,35 +34,22 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties | @@ -31,35 +34,22 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties | ||
| 31 | import org.springframework.context.annotation.Bean; | 34 | import org.springframework.context.annotation.Bean; |
| 32 | import org.springframework.context.annotation.Configuration; | 35 | import org.springframework.context.annotation.Configuration; |
| 33 | 36 | ||
| 37 | + | ||
| 34 | @Configuration | 38 | @Configuration |
| 35 | @EnableConfigurationProperties(IotGatewayProperties.class) | 39 | @EnableConfigurationProperties(IotGatewayProperties.class) |
| 36 | @Slf4j | 40 | @Slf4j |
| 37 | public class IotGatewayConfiguration { | 41 | public class IotGatewayConfiguration { |
| 38 | 42 | ||
| 39 | - /** | ||
| 40 | - * IoT 网关 HTTP 协议配置类 | ||
| 41 | - */ | ||
| 42 | - @Configuration | ||
| 43 | - @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.http", name = "enabled", havingValue = "true") | ||
| 44 | - @Slf4j | ||
| 45 | - public static class HttpProtocolConfiguration { | ||
| 46 | - | ||
| 47 | - @Bean(name = "httpVertx", destroyMethod = "close") | ||
| 48 | - public Vertx httpVertx() { | ||
| 49 | - return Vertx.vertx(); | ||
| 50 | - } | ||
| 51 | - | ||
| 52 | - @Bean | ||
| 53 | - public IotHttpUpstreamProtocol iotHttpUpstreamProtocol(IotGatewayProperties gatewayProperties, | ||
| 54 | - @Qualifier("httpVertx") Vertx httpVertx) { | ||
| 55 | - return new IotHttpUpstreamProtocol(gatewayProperties.getProtocol().getHttp(), httpVertx); | ||
| 56 | - } | 43 | + @Bean |
| 44 | + public IotMessageSerializerManager iotMessageSerializerManager() { | ||
| 45 | + return new IotMessageSerializerManager(); | ||
| 46 | + } | ||
| 57 | 47 | ||
| 58 | - @Bean | ||
| 59 | - public IotHttpDownstreamSubscriber iotHttpDownstreamSubscriber(IotHttpUpstreamProtocol httpUpstreamProtocol, | ||
| 60 | - IotMessageBus messageBus) { | ||
| 61 | - return new IotHttpDownstreamSubscriber(httpUpstreamProtocol, messageBus); | ||
| 62 | - } | 48 | + @Bean |
| 49 | + public IotProtocolManager iotProtocolManager(IotGatewayProperties gatewayProperties, | ||
| 50 | + IotMessageSerializerManager serializerManager, | ||
| 51 | + IotMessageBus messageBus) { | ||
| 52 | + return new IotProtocolManager(gatewayProperties, serializerManager, messageBus); | ||
| 63 | } | 53 | } |
| 64 | 54 | ||
| 65 | /** | 55 | /** |
| @@ -118,11 +108,16 @@ public class IotGatewayConfiguration { | @@ -118,11 +108,16 @@ public class IotGatewayConfiguration { | ||
| 118 | } | 108 | } |
| 119 | 109 | ||
| 120 | @Bean | 110 | @Bean |
| 111 | + public IotTcpDownstreamHandler iotTcpDownstreamHandler(IotDeviceMessageService messageService, | ||
| 112 | + IotTcpConnectionManager connectionManager) { | ||
| 113 | + return new IotTcpDownstreamHandler(messageService, connectionManager); | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + @Bean | ||
| 121 | public IotTcpDownstreamSubscriber iotTcpDownstreamSubscriber(IotTcpUpstreamProtocol protocolHandler, | 117 | public IotTcpDownstreamSubscriber iotTcpDownstreamSubscriber(IotTcpUpstreamProtocol protocolHandler, |
| 122 | - IotDeviceMessageService messageService, | ||
| 123 | - IotTcpConnectionManager connectionManager, | 118 | + IotTcpDownstreamHandler downstreamHandler, |
| 124 | IotMessageBus messageBus) { | 119 | IotMessageBus messageBus) { |
| 125 | - return new IotTcpDownstreamSubscriber(protocolHandler, messageService, connectionManager, messageBus); | 120 | + return new IotTcpDownstreamSubscriber(protocolHandler, downstreamHandler, messageBus); |
| 126 | } | 121 | } |
| 127 | 122 | ||
| 128 | } | 123 | } |
| @@ -188,11 +183,17 @@ public class IotGatewayConfiguration { | @@ -188,11 +183,17 @@ public class IotGatewayConfiguration { | ||
| 188 | } | 183 | } |
| 189 | 184 | ||
| 190 | @Bean | 185 | @Bean |
| 186 | + public IotUdpDownstreamHandler iotUdpDownstreamHandler(IotDeviceMessageService messageService, | ||
| 187 | + IotUdpSessionManager sessionManager, | ||
| 188 | + IotUdpUpstreamProtocol protocol) { | ||
| 189 | + return new IotUdpDownstreamHandler(messageService, sessionManager, protocol); | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + @Bean | ||
| 191 | public IotUdpDownstreamSubscriber iotUdpDownstreamSubscriber(IotUdpUpstreamProtocol protocolHandler, | 193 | public IotUdpDownstreamSubscriber iotUdpDownstreamSubscriber(IotUdpUpstreamProtocol protocolHandler, |
| 192 | - IotDeviceMessageService messageService, | ||
| 193 | - IotUdpSessionManager sessionManager, | 194 | + IotUdpDownstreamHandler downstreamHandler, |
| 194 | IotMessageBus messageBus) { | 195 | IotMessageBus messageBus) { |
| 195 | - return new IotUdpDownstreamSubscriber(protocolHandler, messageService, sessionManager, messageBus); | 196 | + return new IotUdpDownstreamSubscriber(protocolHandler, downstreamHandler, messageBus); |
| 196 | } | 197 | } |
| 197 | 198 | ||
| 198 | } | 199 | } |
| @@ -242,11 +243,16 @@ public class IotGatewayConfiguration { | @@ -242,11 +243,16 @@ public class IotGatewayConfiguration { | ||
| 242 | } | 243 | } |
| 243 | 244 | ||
| 244 | @Bean | 245 | @Bean |
| 246 | + public IotWebSocketDownstreamHandler iotWebSocketDownstreamHandler(IotDeviceMessageService messageService, | ||
| 247 | + IotWebSocketConnectionManager connectionManager) { | ||
| 248 | + return new IotWebSocketDownstreamHandler(messageService, connectionManager); | ||
| 249 | + } | ||
| 250 | + | ||
| 251 | + @Bean | ||
| 245 | public IotWebSocketDownstreamSubscriber iotWebSocketDownstreamSubscriber(IotWebSocketUpstreamProtocol protocolHandler, | 252 | public IotWebSocketDownstreamSubscriber iotWebSocketDownstreamSubscriber(IotWebSocketUpstreamProtocol protocolHandler, |
| 246 | - IotDeviceMessageService messageService, | ||
| 247 | - IotWebSocketConnectionManager connectionManager, | 253 | + IotWebSocketDownstreamHandler downstreamHandler, |
| 248 | IotMessageBus messageBus) { | 254 | IotMessageBus messageBus) { |
| 249 | - return new IotWebSocketDownstreamSubscriber(protocolHandler, messageService, connectionManager, messageBus); | 255 | + return new IotWebSocketDownstreamSubscriber(protocolHandler, downstreamHandler, messageBus); |
| 250 | } | 256 | } |
| 251 | 257 | ||
| 252 | } | 258 | } |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.config; | 1 | package cn.iocoder.yudao.module.iot.gateway.config; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 4 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpConfig; | ||
| 5 | +import jakarta.validation.Valid; | ||
| 3 | import jakarta.validation.constraints.NotEmpty; | 6 | import jakarta.validation.constraints.NotEmpty; |
| 4 | import jakarta.validation.constraints.NotNull; | 7 | import jakarta.validation.constraints.NotNull; |
| 5 | import lombok.Data; | 8 | import lombok.Data; |
| @@ -24,10 +27,15 @@ public class IotGatewayProperties { | @@ -24,10 +27,15 @@ public class IotGatewayProperties { | ||
| 24 | private TokenProperties token; | 27 | private TokenProperties token; |
| 25 | 28 | ||
| 26 | /** | 29 | /** |
| 27 | - * 协议配置 | 30 | + * 协议配置(旧版,保持兼容) |
| 28 | */ | 31 | */ |
| 29 | private ProtocolProperties protocol; | 32 | private ProtocolProperties protocol; |
| 30 | 33 | ||
| 34 | + /** | ||
| 35 | + * 协议实例列表(新版) | ||
| 36 | + */ | ||
| 37 | + private List<ProtocolInstanceProperties> protocols; | ||
| 38 | + | ||
| 31 | @Data | 39 | @Data |
| 32 | public static class RpcProperties { | 40 | public static class RpcProperties { |
| 33 | 41 | ||
| @@ -89,11 +97,6 @@ public class IotGatewayProperties { | @@ -89,11 +97,6 @@ public class IotGatewayProperties { | ||
| 89 | private MqttProperties mqtt; | 97 | private MqttProperties mqtt; |
| 90 | 98 | ||
| 91 | /** | 99 | /** |
| 92 | - * MQTT WebSocket 组件配置 | ||
| 93 | - */ | ||
| 94 | - private MqttWsProperties mqttWs; | ||
| 95 | - | ||
| 96 | - /** | ||
| 97 | * UDP 组件配置 | 100 | * UDP 组件配置 |
| 98 | */ | 101 | */ |
| 99 | private UdpProperties udp; | 102 | private UdpProperties udp; |
| @@ -423,102 +426,6 @@ public class IotGatewayProperties { | @@ -423,102 +426,6 @@ public class IotGatewayProperties { | ||
| 423 | } | 426 | } |
| 424 | 427 | ||
| 425 | @Data | 428 | @Data |
| 426 | - public static class MqttWsProperties { | ||
| 427 | - | ||
| 428 | - /** | ||
| 429 | - * 是否开启 | ||
| 430 | - */ | ||
| 431 | - @NotNull(message = "是否开启不能为空") | ||
| 432 | - private Boolean enabled; | ||
| 433 | - | ||
| 434 | - /** | ||
| 435 | - * WebSocket 服务器端口(默认:8083) | ||
| 436 | - */ | ||
| 437 | - private Integer port = 8083; | ||
| 438 | - | ||
| 439 | - /** | ||
| 440 | - * WebSocket 路径(默认:/mqtt) | ||
| 441 | - */ | ||
| 442 | - @NotEmpty(message = "WebSocket 路径不能为空") | ||
| 443 | - private String path = "/mqtt"; | ||
| 444 | - | ||
| 445 | - /** | ||
| 446 | - * 最大消息大小(字节) | ||
| 447 | - */ | ||
| 448 | - private Integer maxMessageSize = 8192; | ||
| 449 | - | ||
| 450 | - /** | ||
| 451 | - * 连接超时时间(秒) | ||
| 452 | - */ | ||
| 453 | - private Integer connectTimeoutSeconds = 60; | ||
| 454 | - | ||
| 455 | - /** | ||
| 456 | - * 保持连接超时时间(秒) | ||
| 457 | - */ | ||
| 458 | - private Integer keepAliveTimeoutSeconds = 300; | ||
| 459 | - | ||
| 460 | - /** | ||
| 461 | - * 是否启用 SSL(wss://) | ||
| 462 | - */ | ||
| 463 | - private Boolean sslEnabled = false; | ||
| 464 | - | ||
| 465 | - /** | ||
| 466 | - * SSL 配置 | ||
| 467 | - */ | ||
| 468 | - private SslOptions sslOptions = new SslOptions(); | ||
| 469 | - | ||
| 470 | - /** | ||
| 471 | - * WebSocket 子协议(通常为 "mqtt" 或 "mqttv3.1") | ||
| 472 | - */ | ||
| 473 | - @NotEmpty(message = "WebSocket 子协议不能为空") | ||
| 474 | - private String subProtocol = "mqtt"; | ||
| 475 | - | ||
| 476 | - /** | ||
| 477 | - * 最大帧大小(字节) | ||
| 478 | - */ | ||
| 479 | - private Integer maxFrameSize = 65536; | ||
| 480 | - | ||
| 481 | - /** | ||
| 482 | - * SSL 配置选项 | ||
| 483 | - */ | ||
| 484 | - @Data | ||
| 485 | - public static class SslOptions { | ||
| 486 | - | ||
| 487 | - /** | ||
| 488 | - * 密钥证书选项 | ||
| 489 | - */ | ||
| 490 | - private io.vertx.core.net.KeyCertOptions keyCertOptions; | ||
| 491 | - | ||
| 492 | - /** | ||
| 493 | - * 信任选项 | ||
| 494 | - */ | ||
| 495 | - private io.vertx.core.net.TrustOptions trustOptions; | ||
| 496 | - | ||
| 497 | - /** | ||
| 498 | - * SSL 证书路径 | ||
| 499 | - */ | ||
| 500 | - private String certPath; | ||
| 501 | - | ||
| 502 | - /** | ||
| 503 | - * SSL 私钥路径 | ||
| 504 | - */ | ||
| 505 | - private String keyPath; | ||
| 506 | - | ||
| 507 | - /** | ||
| 508 | - * 信任存储路径 | ||
| 509 | - */ | ||
| 510 | - private String trustStorePath; | ||
| 511 | - | ||
| 512 | - /** | ||
| 513 | - * 信任存储密码 | ||
| 514 | - */ | ||
| 515 | - private String trustStorePassword; | ||
| 516 | - | ||
| 517 | - } | ||
| 518 | - | ||
| 519 | - } | ||
| 520 | - | ||
| 521 | - @Data | ||
| 522 | public static class UdpProperties { | 429 | public static class UdpProperties { |
| 523 | 430 | ||
| 524 | /** | 431 | /** |
| @@ -643,4 +550,125 @@ public class IotGatewayProperties { | @@ -643,4 +550,125 @@ public class IotGatewayProperties { | ||
| 643 | 550 | ||
| 644 | } | 551 | } |
| 645 | 552 | ||
| 553 | + // TODO @AI:【暂时忽略】改成 ProtocolProperties | ||
| 554 | + /** | ||
| 555 | + * 协议实例配置 | ||
| 556 | + */ | ||
| 557 | + @Data | ||
| 558 | + public static class ProtocolInstanceProperties { | ||
| 559 | + | ||
| 560 | + /** | ||
| 561 | + * 协议实例 ID,如 "http-alink"、"tcp-binary" | ||
| 562 | + */ | ||
| 563 | + @NotEmpty(message = "协议实例 ID 不能为空") | ||
| 564 | + private String id; | ||
| 565 | + /** | ||
| 566 | + * 是否启用 | ||
| 567 | + */ | ||
| 568 | + @NotNull(message = "是否启用不能为空") | ||
| 569 | + private Boolean enabled = true; | ||
| 570 | + /** | ||
| 571 | + * 协议类型 | ||
| 572 | + * | ||
| 573 | + * @see cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum | ||
| 574 | + */ | ||
| 575 | + @NotEmpty(message = "协议类型不能为空") | ||
| 576 | + private String type; | ||
| 577 | + /** | ||
| 578 | + * 服务端口 | ||
| 579 | + */ | ||
| 580 | + @NotNull(message = "服务端口不能为空") | ||
| 581 | + private Integer port; | ||
| 582 | + /** | ||
| 583 | + * 序列化类型(可选) | ||
| 584 | + * | ||
| 585 | + * @see cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum | ||
| 586 | + * | ||
| 587 | + * 为什么是可选的呢? | ||
| 588 | + * 1. {@link IotProtocolTypeEnum#HTTP}、${@link IotProtocolTypeEnum#COAP} 协议,目前强制是 JSON 格式 | ||
| 589 | + * 2. {@link IotProtocolTypeEnum#EMQX} 协议,目前支持根据产品(设备)配置的序列化类型来解析 | ||
| 590 | + */ | ||
| 591 | + private String serialize; | ||
| 592 | + | ||
| 593 | + /** | ||
| 594 | + * HTTP 协议配置 | ||
| 595 | + */ | ||
| 596 | + @Valid | ||
| 597 | + private IotHttpConfig http; | ||
| 598 | + | ||
| 599 | + // TODO @AI:后续改下; | ||
| 600 | + /** | ||
| 601 | + * TCP 协议配置(后续扩展) | ||
| 602 | + */ | ||
| 603 | + @Valid | ||
| 604 | + private TcpInstanceConfig tcp; | ||
| 605 | + | ||
| 606 | + } | ||
| 607 | + | ||
| 608 | + /** | ||
| 609 | + * TCP 协议实例配置(后续扩展) | ||
| 610 | + */ | ||
| 611 | + @Data | ||
| 612 | + public static class TcpInstanceConfig { | ||
| 613 | + | ||
| 614 | + /** | ||
| 615 | + * 最大连接数 | ||
| 616 | + */ | ||
| 617 | + private Integer maxConnections = 1000; | ||
| 618 | + | ||
| 619 | + /** | ||
| 620 | + * 心跳超时时间(毫秒) | ||
| 621 | + */ | ||
| 622 | + private Long keepAliveTimeoutMs = 30000L; | ||
| 623 | + | ||
| 624 | + /** | ||
| 625 | + * 是否启用 SSL | ||
| 626 | + */ | ||
| 627 | + private Boolean sslEnabled = false; | ||
| 628 | + | ||
| 629 | + /** | ||
| 630 | + * SSL 证书路径 | ||
| 631 | + */ | ||
| 632 | + private String sslCertPath; | ||
| 633 | + | ||
| 634 | + /** | ||
| 635 | + * SSL 私钥路径 | ||
| 636 | + */ | ||
| 637 | + private String sslKeyPath; | ||
| 638 | + | ||
| 639 | + /** | ||
| 640 | + * 拆包配置 | ||
| 641 | + */ | ||
| 642 | + private CodecConfig codec; | ||
| 643 | + | ||
| 644 | + /** | ||
| 645 | + * TCP 拆包配置 | ||
| 646 | + */ | ||
| 647 | + @Data | ||
| 648 | + public static class CodecConfig { | ||
| 649 | + | ||
| 650 | + /** | ||
| 651 | + * 拆包类型:LENGTH_FIELD / DELIMITER | ||
| 652 | + */ | ||
| 653 | + private String type; | ||
| 654 | + | ||
| 655 | + /** | ||
| 656 | + * LENGTH_FIELD: 偏移量 | ||
| 657 | + */ | ||
| 658 | + private Integer lengthFieldOffset; | ||
| 659 | + | ||
| 660 | + /** | ||
| 661 | + * LENGTH_FIELD: 长度字段长度 | ||
| 662 | + */ | ||
| 663 | + private Integer lengthFieldLength; | ||
| 664 | + | ||
| 665 | + /** | ||
| 666 | + * DELIMITER: 分隔符 | ||
| 667 | + */ | ||
| 668 | + private String delimiter; | ||
| 669 | + | ||
| 670 | + } | ||
| 671 | + | ||
| 672 | + } | ||
| 673 | + | ||
| 646 | } | 674 | } |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocol.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * IoT 协议接口 | ||
| 7 | + * | ||
| 8 | + * 定义传输层协议的生命周期管理 | ||
| 9 | + * | ||
| 10 | + * @author 芋道源码 | ||
| 11 | + */ | ||
| 12 | +public interface IotProtocol { | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * 获取协议实例 ID | ||
| 16 | + * | ||
| 17 | + * @return 协议实例 ID,如 "http-alink"、"tcp-binary" | ||
| 18 | + */ | ||
| 19 | + String getId(); | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 获取服务器 ID(用于消息追踪,全局唯一) | ||
| 23 | + * | ||
| 24 | + * @return 服务器 ID | ||
| 25 | + */ | ||
| 26 | + String getServerId(); | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 获取协议类型 | ||
| 30 | + * | ||
| 31 | + * @return 协议类型枚举 | ||
| 32 | + */ | ||
| 33 | + IotProtocolTypeEnum getType(); | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 启动协议服务 | ||
| 37 | + */ | ||
| 38 | + void start(); | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 停止协议服务 | ||
| 42 | + */ | ||
| 43 | + void stop(); | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 检查协议服务是否正在运行 | ||
| 47 | + * | ||
| 48 | + * @return 是否正在运行 | ||
| 49 | + */ | ||
| 50 | + boolean isRunning(); | ||
| 51 | + | ||
| 52 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocolDownstreamSubscriber.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | ||
| 6 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 7 | +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 8 | +import lombok.AllArgsConstructor; | ||
| 9 | +import lombok.extern.slf4j.Slf4j; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * IoT 协议下行消息订阅者抽象类 | ||
| 13 | + * | ||
| 14 | + * 负责接收来自消息总线的下行消息,并委托给子类进行业务处理 | ||
| 15 | + * | ||
| 16 | + * @author 芋道源码 | ||
| 17 | + */ | ||
| 18 | +@AllArgsConstructor | ||
| 19 | +@Slf4j | ||
| 20 | +public abstract class IotProtocolDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { | ||
| 21 | + | ||
| 22 | + private final IotProtocol protocol; | ||
| 23 | + | ||
| 24 | + private final IotMessageBus messageBus; | ||
| 25 | + | ||
| 26 | + @Override | ||
| 27 | + public String getTopic() { | ||
| 28 | + return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId()); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | ||
| 33 | + */ | ||
| 34 | + @Override | ||
| 35 | + public String getGroup() { | ||
| 36 | + return getTopic(); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + @Override | ||
| 40 | + public void start() { | ||
| 41 | + messageBus.register(this); | ||
| 42 | + log.info("[start][{} 下行消息订阅成功,Topic:{}]", protocol.getType().name(), getTopic()); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + @Override | ||
| 46 | + public void stop() { | ||
| 47 | + messageBus.unregister(this); | ||
| 48 | + log.info("[stop][{} 下行消息订阅已停止,Topic:{}]", protocol.getType().name(), getTopic()); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + @Override | ||
| 52 | + public void onMessage(IotDeviceMessage message) { | ||
| 53 | + log.debug("[onMessage][接收到下行消息, messageId: {}, method: {}, deviceId: {}]", | ||
| 54 | + message.getId(), message.getMethod(), message.getDeviceId()); | ||
| 55 | + try { | ||
| 56 | + // 1. 校验 | ||
| 57 | + String method = message.getMethod(); | ||
| 58 | + if (StrUtil.isBlank(method)) { | ||
| 59 | + log.warn("[onMessage][消息方法为空, messageId: {}, deviceId: {}]", | ||
| 60 | + message.getId(), message.getDeviceId()); | ||
| 61 | + return; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + // 2. 处理下行消息 | ||
| 65 | + handleMessage(message); | ||
| 66 | + } catch (Exception e) { | ||
| 67 | + log.error("[onMessage][处理下行消息失败, messageId: {}, method: {}, deviceId: {}]", | ||
| 68 | + message.getId(), message.getMethod(), message.getDeviceId(), e); | ||
| 69 | + } | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + /** | ||
| 73 | + * 处理下行消息 | ||
| 74 | + * | ||
| 75 | + * @param message 下行消息 | ||
| 76 | + */ | ||
| 77 | + protected abstract void handleMessage(IotDeviceMessage message); | ||
| 78 | + | ||
| 79 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocolManager.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.hutool.core.util.BooleanUtil; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 6 | +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | ||
| 7 | +import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; | ||
| 8 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpProtocol; | ||
| 9 | +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager; | ||
| 10 | +import lombok.extern.slf4j.Slf4j; | ||
| 11 | +import org.springframework.context.SmartLifecycle; | ||
| 12 | + | ||
| 13 | +import java.util.ArrayList; | ||
| 14 | +import java.util.List; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * IoT 协议管理器 | ||
| 18 | + * | ||
| 19 | + * 负责根据配置创建和管理协议实例 | ||
| 20 | + * | ||
| 21 | + * @author 芋道源码 | ||
| 22 | + */ | ||
| 23 | +@Slf4j | ||
| 24 | +public class IotProtocolManager implements SmartLifecycle { | ||
| 25 | + | ||
| 26 | + private final IotGatewayProperties gatewayProperties; | ||
| 27 | + | ||
| 28 | + private final IotMessageSerializerManager serializerManager; | ||
| 29 | + | ||
| 30 | + private final IotMessageBus messageBus; | ||
| 31 | + | ||
| 32 | + private final List<IotProtocol> protocols = new ArrayList<>(); | ||
| 33 | + | ||
| 34 | + private volatile boolean running = false; | ||
| 35 | + | ||
| 36 | + public IotProtocolManager(IotGatewayProperties gatewayProperties, | ||
| 37 | + IotMessageSerializerManager serializerManager, | ||
| 38 | + IotMessageBus messageBus) { | ||
| 39 | + this.gatewayProperties = gatewayProperties; | ||
| 40 | + this.serializerManager = serializerManager; | ||
| 41 | + this.messageBus = messageBus; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + @Override | ||
| 45 | + public void start() { | ||
| 46 | + if (running) { | ||
| 47 | + return; | ||
| 48 | + } | ||
| 49 | + List<IotGatewayProperties.ProtocolInstanceProperties> protocolConfigs = gatewayProperties.getProtocols(); | ||
| 50 | + if (CollUtil.isEmpty(protocolConfigs)) { | ||
| 51 | + log.info("[start][没有配置协议实例,跳过启动]"); | ||
| 52 | + return; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + for (IotGatewayProperties.ProtocolInstanceProperties config : protocolConfigs) { | ||
| 56 | + if (BooleanUtil.isFalse(config.getEnabled())) { | ||
| 57 | + log.info("[start][协议实例 {} 未启用,跳过]", config.getId()); | ||
| 58 | + continue; | ||
| 59 | + } | ||
| 60 | + IotProtocol protocol = createProtocol(config); | ||
| 61 | + if (protocol == null) { | ||
| 62 | + continue; | ||
| 63 | + } | ||
| 64 | + protocol.start(); | ||
| 65 | + protocols.add(protocol); | ||
| 66 | + } | ||
| 67 | + running = true; | ||
| 68 | + log.info("[start][协议管理器启动完成,共启动 {} 个协议实例]", protocols.size()); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + @Override | ||
| 72 | + public void stop() { | ||
| 73 | + if (!running) { | ||
| 74 | + return; | ||
| 75 | + } | ||
| 76 | + for (IotProtocol protocol : protocols) { | ||
| 77 | + try { | ||
| 78 | + protocol.stop(); | ||
| 79 | + } catch (Exception e) { | ||
| 80 | + log.error("[stop][协议实例 {} 停止失败]", protocol.getId(), e); | ||
| 81 | + } | ||
| 82 | + } | ||
| 83 | + protocols.clear(); | ||
| 84 | + running = false; | ||
| 85 | + log.info("[stop][协议管理器已停止]"); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + @Override | ||
| 89 | + public boolean isRunning() { | ||
| 90 | + return running; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + /** | ||
| 94 | + * 创建协议实例 | ||
| 95 | + * | ||
| 96 | + * @param config 协议实例配置 | ||
| 97 | + * @return 协议实例 | ||
| 98 | + */ | ||
| 99 | + @SuppressWarnings({"SwitchStatementWithTooFewBranches", "EnhancedSwitchMigration"}) | ||
| 100 | + private IotProtocol createProtocol(IotGatewayProperties.ProtocolInstanceProperties config) { | ||
| 101 | + IotProtocolTypeEnum protocolType = IotProtocolTypeEnum.of(config.getType()); | ||
| 102 | + if (protocolType == null) { | ||
| 103 | + log.error("[createProtocol][协议实例 {} 的协议类型 {} 不存在]", config.getId(), config.getType()); | ||
| 104 | + return null; | ||
| 105 | + } | ||
| 106 | + switch (protocolType) { | ||
| 107 | + case HTTP: | ||
| 108 | + return createHttpProtocol(config); | ||
| 109 | + // TODO 后续添加其他协议类型 | ||
| 110 | + default: | ||
| 111 | + throw new IllegalArgumentException(String.format( | ||
| 112 | + "[createProtocol][协议实例 %s 的协议类型 %s 暂不支持]", config.getId(), protocolType)); | ||
| 113 | + } | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + /** | ||
| 117 | + * 创建 HTTP 协议实例 | ||
| 118 | + * | ||
| 119 | + * @param config 协议实例配置 | ||
| 120 | + * @return HTTP 协议实例 | ||
| 121 | + */ | ||
| 122 | + private IotHttpProtocol createHttpProtocol(IotGatewayProperties.ProtocolInstanceProperties config) { | ||
| 123 | + return new IotHttpProtocol(config, messageBus); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapDownstreamSubscriber.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.coap; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.coap; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 4 | -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | ||
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 7 | -import jakarta.annotation.PostConstruct; | ||
| 8 | -import lombok.RequiredArgsConstructor; | 5 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber; |
| 9 | import lombok.extern.slf4j.Slf4j; | 6 | import lombok.extern.slf4j.Slf4j; |
| 10 | 7 | ||
| 11 | /** | 8 | /** |
| @@ -13,34 +10,17 @@ import lombok.extern.slf4j.Slf4j; | @@ -13,34 +10,17 @@ import lombok.extern.slf4j.Slf4j; | ||
| 13 | * | 10 | * |
| 14 | * @author 芋道源码 | 11 | * @author 芋道源码 |
| 15 | */ | 12 | */ |
| 16 | -@RequiredArgsConstructor | ||
| 17 | @Slf4j | 13 | @Slf4j |
| 18 | -public class IotCoapDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { | 14 | +public class IotCoapDownstreamSubscriber extends IotProtocolDownstreamSubscriber { |
| 19 | 15 | ||
| 20 | - private final IotCoapUpstreamProtocol protocol; | ||
| 21 | - | ||
| 22 | - private final IotMessageBus messageBus; | ||
| 23 | - | ||
| 24 | - @PostConstruct | ||
| 25 | - public void init() { | ||
| 26 | - messageBus.register(this); | ||
| 27 | - } | ||
| 28 | - | ||
| 29 | - @Override | ||
| 30 | - public String getTopic() { | ||
| 31 | - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId()); | ||
| 32 | - } | ||
| 33 | - | ||
| 34 | - @Override | ||
| 35 | - public String getGroup() { | ||
| 36 | - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | ||
| 37 | - return getTopic(); | 16 | + public IotCoapDownstreamSubscriber(IotCoapUpstreamProtocol protocol, IotMessageBus messageBus) { |
| 17 | + super(protocol, messageBus); | ||
| 38 | } | 18 | } |
| 39 | 19 | ||
| 40 | @Override | 20 | @Override |
| 41 | - public void onMessage(IotDeviceMessage message) { | 21 | + protected void handleMessage(IotDeviceMessage message) { |
| 42 | // 如需支持,可通过 CoAP Observe 模式实现(设备订阅资源,服务器推送变更) | 22 | // 如需支持,可通过 CoAP Observe 模式实现(设备订阅资源,服务器推送变更) |
| 43 | - log.warn("[onMessage][IoT 网关 CoAP 协议暂不支持下行消息,忽略消息:{}]", message); | 23 | + log.warn("[handleMessage][IoT 网关 CoAP 协议暂不支持下行消息,忽略消息:{}]", message); |
| 44 | } | 24 | } |
| 45 | 25 | ||
| 46 | } | 26 | } |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapUpstreamProtocol.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.coap; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.coap; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 3 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 4 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; |
| 4 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; | 5 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; |
| 6 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol; | ||
| 5 | import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapAuthHandler; | 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapAuthHandler; |
| 6 | import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapAuthResource; | 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapAuthResource; |
| 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapRegisterHandler; | 9 | import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapRegisterHandler; |
| @@ -30,7 +32,9 @@ import java.util.concurrent.TimeUnit; | @@ -30,7 +32,9 @@ import java.util.concurrent.TimeUnit; | ||
| 30 | * @author 芋道源码 | 32 | * @author 芋道源码 |
| 31 | */ | 33 | */ |
| 32 | @Slf4j | 34 | @Slf4j |
| 33 | -public class IotCoapUpstreamProtocol { | 35 | +public class IotCoapUpstreamProtocol implements IotProtocol { |
| 36 | + | ||
| 37 | + private static final String ID = "coap"; | ||
| 34 | 38 | ||
| 35 | private final IotGatewayProperties.CoapProperties coapProperties; | 39 | private final IotGatewayProperties.CoapProperties coapProperties; |
| 36 | 40 | ||
| @@ -39,11 +43,24 @@ public class IotCoapUpstreamProtocol { | @@ -39,11 +43,24 @@ public class IotCoapUpstreamProtocol { | ||
| 39 | @Getter | 43 | @Getter |
| 40 | private final String serverId; | 44 | private final String serverId; |
| 41 | 45 | ||
| 46 | + private volatile boolean running = false; | ||
| 47 | + | ||
| 42 | public IotCoapUpstreamProtocol(IotGatewayProperties.CoapProperties coapProperties) { | 48 | public IotCoapUpstreamProtocol(IotGatewayProperties.CoapProperties coapProperties) { |
| 43 | this.coapProperties = coapProperties; | 49 | this.coapProperties = coapProperties; |
| 44 | this.serverId = IotDeviceMessageUtils.generateServerId(coapProperties.getPort()); | 50 | this.serverId = IotDeviceMessageUtils.generateServerId(coapProperties.getPort()); |
| 45 | } | 51 | } |
| 46 | 52 | ||
| 53 | + @Override | ||
| 54 | + public String getId() { | ||
| 55 | + return ID; | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + @Override | ||
| 59 | + public IotProtocolTypeEnum getType() { | ||
| 60 | + return IotProtocolTypeEnum.COAP; | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + @Override | ||
| 47 | @PostConstruct | 64 | @PostConstruct |
| 48 | public void start() { | 65 | public void start() { |
| 49 | try { | 66 | try { |
| @@ -73,6 +90,7 @@ public class IotCoapUpstreamProtocol { | @@ -73,6 +90,7 @@ public class IotCoapUpstreamProtocol { | ||
| 73 | 90 | ||
| 74 | // 3. 启动服务器 | 91 | // 3. 启动服务器 |
| 75 | coapServer.start(); | 92 | coapServer.start(); |
| 93 | + running = true; | ||
| 76 | log.info("[start][IoT 网关 CoAP 协议启动成功,端口:{},资源:/auth, /auth/register/device, /topic]", coapProperties.getPort()); | 94 | log.info("[start][IoT 网关 CoAP 协议启动成功,端口:{},资源:/auth, /auth/register/device, /topic]", coapProperties.getPort()); |
| 77 | } catch (Exception e) { | 95 | } catch (Exception e) { |
| 78 | log.error("[start][IoT 网关 CoAP 协议启动失败]", e); | 96 | log.error("[start][IoT 网关 CoAP 协议启动失败]", e); |
| @@ -80,11 +98,13 @@ public class IotCoapUpstreamProtocol { | @@ -80,11 +98,13 @@ public class IotCoapUpstreamProtocol { | ||
| 80 | } | 98 | } |
| 81 | } | 99 | } |
| 82 | 100 | ||
| 101 | + @Override | ||
| 83 | @PreDestroy | 102 | @PreDestroy |
| 84 | public void stop() { | 103 | public void stop() { |
| 85 | if (coapServer != null) { | 104 | if (coapServer != null) { |
| 86 | try { | 105 | try { |
| 87 | coapServer.stop(); | 106 | coapServer.stop(); |
| 107 | + running = false; | ||
| 88 | log.info("[stop][IoT 网关 CoAP 协议已停止]"); | 108 | log.info("[stop][IoT 网关 CoAP 协议已停止]"); |
| 89 | } catch (Exception e) { | 109 | } catch (Exception e) { |
| 90 | log.error("[stop][IoT 网关 CoAP 协议停止失败]", e); | 110 | log.error("[stop][IoT 网关 CoAP 协议停止失败]", e); |
| @@ -92,4 +112,9 @@ public class IotCoapUpstreamProtocol { | @@ -92,4 +112,9 @@ public class IotCoapUpstreamProtocol { | ||
| 92 | } | 112 | } |
| 93 | } | 113 | } |
| 94 | 114 | ||
| 115 | + @Override | ||
| 116 | + public boolean isRunning() { | ||
| 117 | + return running; | ||
| 118 | + } | ||
| 119 | + | ||
| 95 | } | 120 | } |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/router/IotCoapAuthHandler.java
| @@ -24,8 +24,6 @@ import java.util.Map; | @@ -24,8 +24,6 @@ import java.util.Map; | ||
| 24 | /** | 24 | /** |
| 25 | * IoT 网关 CoAP 协议的【认证】处理器 | 25 | * IoT 网关 CoAP 协议的【认证】处理器 |
| 26 | * | 26 | * |
| 27 | - * 参考 {@link cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAuthHandler} | ||
| 28 | - * | ||
| 29 | * @author 芋道源码 | 27 | * @author 芋道源码 |
| 30 | */ | 28 | */ |
| 31 | @Slf4j | 29 | @Slf4j |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/router/IotCoapRegisterHandler.java
| @@ -22,7 +22,6 @@ import java.util.Map; | @@ -22,7 +22,6 @@ import java.util.Map; | ||
| 22 | * | 22 | * |
| 23 | * @author 芋道源码 | 23 | * @author 芋道源码 |
| 24 | * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a> | 24 | * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a> |
| 25 | - * @see cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpRegisterHandler | ||
| 26 | */ | 25 | */ |
| 27 | @Slf4j | 26 | @Slf4j |
| 28 | public class IotCoapRegisterHandler { | 27 | public class IotCoapRegisterHandler { |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxDownstreamSubscriber.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.emqx; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.emqx; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 4 | -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | ||
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 5 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol; |
| 6 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber; | ||
| 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router.IotEmqxDownstreamHandler; | 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router.IotEmqxDownstreamHandler; |
| 8 | -import jakarta.annotation.PostConstruct; | ||
| 9 | import lombok.extern.slf4j.Slf4j; | 8 | import lombok.extern.slf4j.Slf4j; |
| 10 | 9 | ||
| 11 | /** | 10 | /** |
| @@ -14,55 +13,18 @@ import lombok.extern.slf4j.Slf4j; | @@ -14,55 +13,18 @@ import lombok.extern.slf4j.Slf4j; | ||
| 14 | * @author 芋道源码 | 13 | * @author 芋道源码 |
| 15 | */ | 14 | */ |
| 16 | @Slf4j | 15 | @Slf4j |
| 17 | -public class IotEmqxDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { | 16 | +public class IotEmqxDownstreamSubscriber extends IotProtocolDownstreamSubscriber { |
| 18 | 17 | ||
| 19 | private final IotEmqxDownstreamHandler downstreamHandler; | 18 | private final IotEmqxDownstreamHandler downstreamHandler; |
| 20 | 19 | ||
| 21 | - private final IotMessageBus messageBus; | ||
| 22 | - | ||
| 23 | - private final IotEmqxUpstreamProtocol protocol; | ||
| 24 | - | ||
| 25 | public IotEmqxDownstreamSubscriber(IotEmqxUpstreamProtocol protocol, IotMessageBus messageBus) { | 20 | public IotEmqxDownstreamSubscriber(IotEmqxUpstreamProtocol protocol, IotMessageBus messageBus) { |
| 26 | - this.protocol = protocol; | ||
| 27 | - this.messageBus = messageBus; | 21 | + super(protocol, messageBus); |
| 28 | this.downstreamHandler = new IotEmqxDownstreamHandler(protocol); | 22 | this.downstreamHandler = new IotEmqxDownstreamHandler(protocol); |
| 29 | } | 23 | } |
| 30 | 24 | ||
| 31 | - @PostConstruct | ||
| 32 | - public void init() { | ||
| 33 | - messageBus.register(this); | ||
| 34 | - } | ||
| 35 | - | ||
| 36 | @Override | 25 | @Override |
| 37 | - public String getTopic() { | ||
| 38 | - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId()); | ||
| 39 | - } | ||
| 40 | - | ||
| 41 | - @Override | ||
| 42 | - public String getGroup() { | ||
| 43 | - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | ||
| 44 | - return getTopic(); | ||
| 45 | - } | ||
| 46 | - | ||
| 47 | - @Override | ||
| 48 | - public void onMessage(IotDeviceMessage message) { | ||
| 49 | - log.debug("[onMessage][接收到下行消息, messageId: {}, method: {}, deviceId: {}]", | ||
| 50 | - message.getId(), message.getMethod(), message.getDeviceId()); | ||
| 51 | - try { | ||
| 52 | - // 1. 校验 | ||
| 53 | - String method = message.getMethod(); | ||
| 54 | - if (method == null) { | ||
| 55 | - log.warn("[onMessage][消息方法为空, messageId: {}, deviceId: {}]", | ||
| 56 | - message.getId(), message.getDeviceId()); | ||
| 57 | - return; | ||
| 58 | - } | ||
| 59 | - | ||
| 60 | - // 2. 处理下行消息 | ||
| 61 | - downstreamHandler.handle(message); | ||
| 62 | - } catch (Exception e) { | ||
| 63 | - log.error("[onMessage][处理下行消息失败, messageId: {}, method: {}, deviceId: {}]", | ||
| 64 | - message.getId(), message.getMethod(), message.getDeviceId(), e); | ||
| 65 | - } | 26 | + protected void handleMessage(IotDeviceMessage message) { |
| 27 | + downstreamHandler.handle(message); | ||
| 66 | } | 28 | } |
| 67 | 29 | ||
| 68 | -} | ||
| 69 | \ No newline at end of file | 30 | \ No newline at end of file |
| 31 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxUpstreamProtocol.java
| @@ -2,8 +2,10 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.emqx; | @@ -2,8 +2,10 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.emqx; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.lang.Assert; | 3 | import cn.hutool.core.lang.Assert; |
| 4 | import cn.hutool.core.util.StrUtil; | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 5 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 6 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; |
| 6 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; | 7 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; |
| 8 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol; | ||
| 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router.IotEmqxUpstreamHandler; | 9 | import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router.IotEmqxUpstreamHandler; |
| 8 | import io.netty.handler.codec.mqtt.MqttQoS; | 10 | import io.netty.handler.codec.mqtt.MqttQoS; |
| 9 | import io.vertx.core.Vertx; | 11 | import io.vertx.core.Vertx; |
| @@ -28,11 +30,13 @@ import java.util.concurrent.atomic.AtomicBoolean; | @@ -28,11 +30,13 @@ import java.util.concurrent.atomic.AtomicBoolean; | ||
| 28 | * @author 芋道源码 | 30 | * @author 芋道源码 |
| 29 | */ | 31 | */ |
| 30 | @Slf4j | 32 | @Slf4j |
| 31 | -public class IotEmqxUpstreamProtocol { | 33 | +public class IotEmqxUpstreamProtocol implements IotProtocol { |
| 34 | + | ||
| 35 | + private static final String ID = "emqx"; | ||
| 32 | 36 | ||
| 33 | private final IotGatewayProperties.EmqxProperties emqxProperties; | 37 | private final IotGatewayProperties.EmqxProperties emqxProperties; |
| 34 | 38 | ||
| 35 | - private volatile boolean isRunning = false; | 39 | + private volatile boolean running = false; |
| 36 | 40 | ||
| 37 | private final Vertx vertx; | 41 | private final Vertx vertx; |
| 38 | 42 | ||
| @@ -50,9 +54,20 @@ public class IotEmqxUpstreamProtocol { | @@ -50,9 +54,20 @@ public class IotEmqxUpstreamProtocol { | ||
| 50 | this.vertx = vertx; | 54 | this.vertx = vertx; |
| 51 | } | 55 | } |
| 52 | 56 | ||
| 57 | + @Override | ||
| 58 | + public String getId() { | ||
| 59 | + return ID; | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + @Override | ||
| 63 | + public IotProtocolTypeEnum getType() { | ||
| 64 | + return IotProtocolTypeEnum.EMQX; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + @Override | ||
| 53 | @PostConstruct | 68 | @PostConstruct |
| 54 | public void start() { | 69 | public void start() { |
| 55 | - if (isRunning) { | 70 | + if (running) { |
| 56 | return; | 71 | return; |
| 57 | } | 72 | } |
| 58 | 73 | ||
| @@ -61,7 +76,7 @@ public class IotEmqxUpstreamProtocol { | @@ -61,7 +76,7 @@ public class IotEmqxUpstreamProtocol { | ||
| 61 | startMqttClient(); | 76 | startMqttClient(); |
| 62 | 77 | ||
| 63 | // 2. 标记服务为运行状态 | 78 | // 2. 标记服务为运行状态 |
| 64 | - isRunning = true; | 79 | + running = true; |
| 65 | log.info("[start][IoT 网关 EMQX 协议启动成功]"); | 80 | log.info("[start][IoT 网关 EMQX 协议启动成功]"); |
| 66 | } catch (Exception e) { | 81 | } catch (Exception e) { |
| 67 | log.error("[start][IoT 网关 EMQX 协议服务启动失败,应用将关闭]", e); | 82 | log.error("[start][IoT 网关 EMQX 协议服务启动失败,应用将关闭]", e); |
| @@ -88,9 +103,10 @@ public class IotEmqxUpstreamProtocol { | @@ -88,9 +103,10 @@ public class IotEmqxUpstreamProtocol { | ||
| 88 | } | 103 | } |
| 89 | } | 104 | } |
| 90 | 105 | ||
| 106 | + @Override | ||
| 91 | @PreDestroy | 107 | @PreDestroy |
| 92 | public void stop() { | 108 | public void stop() { |
| 93 | - if (!isRunning) { | 109 | + if (!running) { |
| 94 | return; | 110 | return; |
| 95 | } | 111 | } |
| 96 | 112 | ||
| @@ -98,10 +114,15 @@ public class IotEmqxUpstreamProtocol { | @@ -98,10 +114,15 @@ public class IotEmqxUpstreamProtocol { | ||
| 98 | stopMqttClient(); | 114 | stopMqttClient(); |
| 99 | 115 | ||
| 100 | // 2. 标记服务为停止状态 | 116 | // 2. 标记服务为停止状态 |
| 101 | - isRunning = false; | 117 | + running = false; |
| 102 | log.info("[stop][IoT 网关 MQTT 协议服务已停止]"); | 118 | log.info("[stop][IoT 网关 MQTT 协议服务已停止]"); |
| 103 | } | 119 | } |
| 104 | 120 | ||
| 121 | + @Override | ||
| 122 | + public boolean isRunning() { | ||
| 123 | + return running; | ||
| 124 | + } | ||
| 125 | + | ||
| 105 | /** | 126 | /** |
| 106 | * 启动 MQTT 客户端 | 127 | * 启动 MQTT 客户端 |
| 107 | */ | 128 | */ |
| @@ -185,7 +206,7 @@ public class IotEmqxUpstreamProtocol { | @@ -185,7 +206,7 @@ public class IotEmqxUpstreamProtocol { | ||
| 185 | * 延迟重连 | 206 | * 延迟重连 |
| 186 | */ | 207 | */ |
| 187 | private void reconnectWithDelay() { | 208 | private void reconnectWithDelay() { |
| 188 | - if (!isRunning) { | 209 | + if (!running) { |
| 189 | return; | 210 | return; |
| 190 | } | 211 | } |
| 191 | if (mqttClient != null && mqttClient.isConnected()) { | 212 | if (mqttClient != null && mqttClient.isConnected()) { |
| @@ -195,7 +216,7 @@ public class IotEmqxUpstreamProtocol { | @@ -195,7 +216,7 @@ public class IotEmqxUpstreamProtocol { | ||
| 195 | long delay = emqxProperties.getReconnectDelayMs(); | 216 | long delay = emqxProperties.getReconnectDelayMs(); |
| 196 | log.info("[reconnectWithDelay][将在 {} 毫秒后尝试重连 MQTT Broker]", delay); | 217 | log.info("[reconnectWithDelay][将在 {} 毫秒后尝试重连 MQTT Broker]", delay); |
| 197 | vertx.setTimer(delay, timerId -> { | 218 | vertx.setTimer(delay, timerId -> { |
| 198 | - if (!isRunning) { | 219 | + if (!running) { |
| 199 | return; | 220 | return; |
| 200 | } | 221 | } |
| 201 | if (mqttClient != null && mqttClient.isConnected()) { | 222 | if (mqttClient != null && mqttClient.isConnected()) { |
| @@ -305,7 +326,7 @@ public class IotEmqxUpstreamProtocol { | @@ -305,7 +326,7 @@ public class IotEmqxUpstreamProtocol { | ||
| 305 | private void setupMqttHandlers() { | 326 | private void setupMqttHandlers() { |
| 306 | // 1. 设置断开重连监听器 | 327 | // 1. 设置断开重连监听器 |
| 307 | mqttClient.closeHandler(closeEvent -> { | 328 | mqttClient.closeHandler(closeEvent -> { |
| 308 | - if (!isRunning) { | 329 | + if (!running) { |
| 309 | return; | 330 | return; |
| 310 | } | 331 | } |
| 311 | log.warn("[closeHandler][MQTT 连接已断开, 准备重连]"); | 332 | log.warn("[closeHandler][MQTT 连接已断开, 准备重连]"); |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpConfig.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.http; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * IoT HTTP 协议配置 | ||
| 7 | + * | ||
| 8 | + * @author 芋道源码 | ||
| 9 | + */ | ||
| 10 | +@Data | ||
| 11 | +public class IotHttpConfig { | ||
| 12 | + | ||
| 13 | + /** | ||
| 14 | + * 是否启用 SSL | ||
| 15 | + */ | ||
| 16 | + private Boolean sslEnabled = false; | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * SSL 证书路径 | ||
| 20 | + */ | ||
| 21 | + private String sslCertPath; | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * SSL 私钥路径 | ||
| 25 | + */ | ||
| 26 | + private String sslKeyPath; | ||
| 27 | + | ||
| 28 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpDownstreamSubscriber.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.http; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | ||
| 4 | -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | ||
| 5 | -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 6 | -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 7 | -import jakarta.annotation.PostConstruct; | ||
| 8 | -import lombok.RequiredArgsConstructor; | ||
| 9 | -import lombok.extern.slf4j.Slf4j; | ||
| 10 | - | ||
| 11 | -/** | ||
| 12 | - * IoT 网关 HTTP 订阅者:接收下行给设备的消息 | ||
| 13 | - * | ||
| 14 | - * @author 芋道源码 | ||
| 15 | - */ | ||
| 16 | -@RequiredArgsConstructor | ||
| 17 | -@Slf4j | ||
| 18 | -public class IotHttpDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { | ||
| 19 | - | ||
| 20 | - private final IotHttpUpstreamProtocol protocol; | ||
| 21 | - | ||
| 22 | - private final IotMessageBus messageBus; | ||
| 23 | - | ||
| 24 | - @PostConstruct | ||
| 25 | - public void init() { | ||
| 26 | - messageBus.register(this); | ||
| 27 | - } | ||
| 28 | - | ||
| 29 | - @Override | ||
| 30 | - public String getTopic() { | ||
| 31 | - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId()); | ||
| 32 | - } | ||
| 33 | - | ||
| 34 | - @Override | ||
| 35 | - public String getGroup() { | ||
| 36 | - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | ||
| 37 | - return getTopic(); | ||
| 38 | - } | ||
| 39 | - | ||
| 40 | - @Override | ||
| 41 | - public void onMessage(IotDeviceMessage message) { | ||
| 42 | - log.info("[onMessage][IoT 网关 HTTP 协议不支持下行消息,忽略消息:{}]", message); | ||
| 43 | - } | ||
| 44 | - | ||
| 45 | -} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpProtocol.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.http; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 6 | +import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolInstanceProperties; | ||
| 7 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol; | ||
| 8 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.downstream.IotHttpDownstreamSubscriber; | ||
| 9 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpAuthHandler; | ||
| 10 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpRegisterHandler; | ||
| 11 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpRegisterSubHandler; | ||
| 12 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpUpstreamHandler; | ||
| 13 | +import io.vertx.core.Vertx; | ||
| 14 | +import io.vertx.core.http.HttpServer; | ||
| 15 | +import io.vertx.core.http.HttpServerOptions; | ||
| 16 | +import io.vertx.core.net.PemKeyCertOptions; | ||
| 17 | +import io.vertx.ext.web.Router; | ||
| 18 | +import io.vertx.ext.web.handler.BodyHandler; | ||
| 19 | +import lombok.Getter; | ||
| 20 | +import lombok.extern.slf4j.Slf4j; | ||
| 21 | + | ||
| 22 | +/** | ||
| 23 | + * IoT HTTP 协议实现 | ||
| 24 | + * <p> | ||
| 25 | + * 基于 Vert.x 实现 HTTP 服务器,接收设备上行消息 | ||
| 26 | + * | ||
| 27 | + * @author 芋道源码 | ||
| 28 | + */ | ||
| 29 | +@Slf4j | ||
| 30 | +public class IotHttpProtocol implements IotProtocol { | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 协议配置 | ||
| 34 | + */ | ||
| 35 | + private final ProtocolInstanceProperties properties; | ||
| 36 | + /** | ||
| 37 | + * 消息总线 | ||
| 38 | + */ | ||
| 39 | + private final IotMessageBus messageBus; | ||
| 40 | + /** | ||
| 41 | + * 服务器 ID(用于消息追踪,全局唯一) | ||
| 42 | + */ | ||
| 43 | + @Getter | ||
| 44 | + private final String serverId; | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * Vert.x 实例(每个 Protocol 自己管理) | ||
| 48 | + */ | ||
| 49 | + private Vertx vertx; | ||
| 50 | + /** | ||
| 51 | + * HTTP 服务器 | ||
| 52 | + */ | ||
| 53 | + private HttpServer httpServer; | ||
| 54 | + /** | ||
| 55 | + * 下行消息订阅者 | ||
| 56 | + */ | ||
| 57 | + private IotHttpDownstreamSubscriber downstreamSubscriber; | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * 运行状态 | ||
| 61 | + */ | ||
| 62 | + private volatile boolean running = false; | ||
| 63 | + | ||
| 64 | + public IotHttpProtocol(ProtocolInstanceProperties properties, IotMessageBus messageBus) { | ||
| 65 | + this.properties = properties; | ||
| 66 | + this.messageBus = messageBus; | ||
| 67 | + this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort()); | ||
| 68 | + this.downstreamSubscriber = new IotHttpDownstreamSubscriber(this, messageBus); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + @Override | ||
| 72 | + public String getId() { | ||
| 73 | + return properties.getId(); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + @Override | ||
| 77 | + public IotProtocolTypeEnum getType() { | ||
| 78 | + return IotProtocolTypeEnum.HTTP; | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + @Override | ||
| 82 | + public void start() { | ||
| 83 | + if (running) { | ||
| 84 | + log.warn("[start][IoT HTTP 协议 {} 已经在运行中]", getId()); | ||
| 85 | + return; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + // 1.1 创建 Vertx 实例(每个 Protocol 独立管理) | ||
| 89 | + this.vertx = Vertx.vertx(); | ||
| 90 | + | ||
| 91 | + // 1.2 创建路由 | ||
| 92 | + Router router = Router.router(vertx); | ||
| 93 | + router.route().handler(BodyHandler.create()); | ||
| 94 | + | ||
| 95 | + // 1.3 创建处理器,添加路由处理器 | ||
| 96 | + IotHttpAuthHandler authHandler = new IotHttpAuthHandler(this); | ||
| 97 | + router.post(IotHttpAuthHandler.PATH).handler(authHandler); | ||
| 98 | + IotHttpRegisterHandler registerHandler = new IotHttpRegisterHandler(); | ||
| 99 | + router.post(IotHttpRegisterHandler.PATH).handler(registerHandler); | ||
| 100 | + IotHttpRegisterSubHandler registerSubHandler = new IotHttpRegisterSubHandler(); | ||
| 101 | + router.post(IotHttpRegisterSubHandler.PATH).handler(registerSubHandler); | ||
| 102 | + IotHttpUpstreamHandler upstreamHandler = new IotHttpUpstreamHandler(this); | ||
| 103 | + router.post(IotHttpUpstreamHandler.PATH).handler(upstreamHandler); | ||
| 104 | + | ||
| 105 | + // 1.4 启动 HTTP 服务器 | ||
| 106 | + IotHttpConfig httpConfig = properties.getHttp(); | ||
| 107 | + HttpServerOptions options = new HttpServerOptions().setPort(properties.getPort()); | ||
| 108 | + if (httpConfig != null && Boolean.TRUE.equals(httpConfig.getSslEnabled())) { | ||
| 109 | + PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions() | ||
| 110 | + .setKeyPath(httpConfig.getSslKeyPath()) | ||
| 111 | + .setCertPath(httpConfig.getSslCertPath()); | ||
| 112 | + options = options.setSsl(true).setKeyCertOptions(pemKeyCertOptions); | ||
| 113 | + } | ||
| 114 | + try { | ||
| 115 | + httpServer = vertx.createHttpServer(options) | ||
| 116 | + .requestHandler(router) | ||
| 117 | + .listen() | ||
| 118 | + .result(); | ||
| 119 | + running = true; | ||
| 120 | + log.info("[start][IoT HTTP 协议 {} 启动成功,端口:{},serverId:{}]", | ||
| 121 | + getId(), properties.getPort(), serverId); | ||
| 122 | + | ||
| 123 | + // 2. 启动下行消息订阅者 | ||
| 124 | + this.downstreamSubscriber = new IotHttpDownstreamSubscriber(this, messageBus); | ||
| 125 | + this.downstreamSubscriber.start(); | ||
| 126 | + } catch (Exception e) { | ||
| 127 | + log.error("[start][IoT HTTP 协议 {} 启动失败]", getId(), e); | ||
| 128 | + // 启动失败时关闭 Vertx | ||
| 129 | + if (vertx != null) { | ||
| 130 | + vertx.close(); | ||
| 131 | + vertx = null; | ||
| 132 | + } | ||
| 133 | + throw e; | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + // 2. 启动下行消息订阅者 | ||
| 137 | + this.downstreamSubscriber.start(); | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + @Override | ||
| 141 | + public void stop() { | ||
| 142 | + if (!running) { | ||
| 143 | + return; | ||
| 144 | + } | ||
| 145 | + // 1. 停止下行消息订阅者 | ||
| 146 | + if (downstreamSubscriber != null) { | ||
| 147 | + try { | ||
| 148 | + downstreamSubscriber.stop(); | ||
| 149 | + log.info("[stop][IoT HTTP 协议 {} 下行消息订阅者已停止]", getId()); | ||
| 150 | + } catch (Exception e) { | ||
| 151 | + log.error("[stop][IoT HTTP 协议 {} 下行消息订阅者停止失败]", getId(), e); | ||
| 152 | + } | ||
| 153 | + downstreamSubscriber = null; | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + // 2.1 关闭 HTTP 服务器 | ||
| 157 | + if (httpServer != null) { | ||
| 158 | + try { | ||
| 159 | + httpServer.close().result(); | ||
| 160 | + log.info("[stop][IoT HTTP 协议 {} 服务器已停止]", getId()); | ||
| 161 | + } catch (Exception e) { | ||
| 162 | + log.error("[stop][IoT HTTP 协议 {} 服务器停止失败]", getId(), e); | ||
| 163 | + } | ||
| 164 | + httpServer = null; | ||
| 165 | + } | ||
| 166 | + // 2.2 关闭 Vertx 实例 | ||
| 167 | + if (vertx != null) { | ||
| 168 | + try { | ||
| 169 | + vertx.close().result(); | ||
| 170 | + log.info("[stop][IoT HTTP 协议 {} Vertx 已关闭]", getId()); | ||
| 171 | + } catch (Exception e) { | ||
| 172 | + log.error("[stop][IoT HTTP 协议 {} Vertx 关闭失败]", getId(), e); | ||
| 173 | + } | ||
| 174 | + vertx = null; | ||
| 175 | + } | ||
| 176 | + running = false; | ||
| 177 | + log.info("[stop][IoT HTTP 协议 {} 已停止]", getId()); | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + @Override | ||
| 181 | + public boolean isRunning() { | ||
| 182 | + return running; | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpUpstreamProtocol.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.http; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 4 | -import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; | ||
| 5 | -import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAuthHandler; | ||
| 6 | -import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpRegisterHandler; | ||
| 7 | -import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpRegisterSubHandler; | ||
| 8 | -import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpUpstreamHandler; | ||
| 9 | -import io.vertx.core.Vertx; | ||
| 10 | -import io.vertx.core.http.HttpServer; | ||
| 11 | -import io.vertx.core.http.HttpServerOptions; | ||
| 12 | -import io.vertx.core.net.PemKeyCertOptions; | ||
| 13 | -import io.vertx.ext.web.Router; | ||
| 14 | -import io.vertx.ext.web.handler.BodyHandler; | ||
| 15 | -import jakarta.annotation.PostConstruct; | ||
| 16 | -import jakarta.annotation.PreDestroy; | ||
| 17 | -import lombok.Getter; | ||
| 18 | -import lombok.extern.slf4j.Slf4j; | ||
| 19 | - | ||
| 20 | -/** | ||
| 21 | - * IoT 网关 HTTP 协议:接收设备上行消息 | ||
| 22 | - * | ||
| 23 | - * @author 芋道源码 | ||
| 24 | - */ | ||
| 25 | -@Slf4j | ||
| 26 | -public class IotHttpUpstreamProtocol { | ||
| 27 | - | ||
| 28 | - private final IotGatewayProperties.HttpProperties httpProperties; | ||
| 29 | - | ||
| 30 | - private final Vertx vertx; | ||
| 31 | - | ||
| 32 | - private HttpServer httpServer; | ||
| 33 | - | ||
| 34 | - @Getter | ||
| 35 | - private final String serverId; | ||
| 36 | - | ||
| 37 | - public IotHttpUpstreamProtocol(IotGatewayProperties.HttpProperties httpProperties, Vertx vertx) { | ||
| 38 | - this.httpProperties = httpProperties; | ||
| 39 | - this.vertx = vertx; | ||
| 40 | - this.serverId = IotDeviceMessageUtils.generateServerId(httpProperties.getServerPort()); | ||
| 41 | - } | ||
| 42 | - | ||
| 43 | - @PostConstruct | ||
| 44 | - public void start() { | ||
| 45 | - // 创建路由 | ||
| 46 | - Router router = Router.router(vertx); | ||
| 47 | - router.route().handler(BodyHandler.create()); | ||
| 48 | - | ||
| 49 | - // 创建处理器,添加路由处理器 | ||
| 50 | - IotHttpAuthHandler authHandler = new IotHttpAuthHandler(this); | ||
| 51 | - router.post(IotHttpAuthHandler.PATH).handler(authHandler); | ||
| 52 | - IotHttpRegisterHandler registerHandler = new IotHttpRegisterHandler(); | ||
| 53 | - router.post(IotHttpRegisterHandler.PATH).handler(registerHandler); | ||
| 54 | - IotHttpRegisterSubHandler registerSubHandler = new IotHttpRegisterSubHandler(); | ||
| 55 | - router.post(IotHttpRegisterSubHandler.PATH).handler(registerSubHandler); | ||
| 56 | - IotHttpUpstreamHandler upstreamHandler = new IotHttpUpstreamHandler(this); | ||
| 57 | - router.post(IotHttpUpstreamHandler.PATH).handler(upstreamHandler); | ||
| 58 | - | ||
| 59 | - // 启动 HTTP 服务器 | ||
| 60 | - HttpServerOptions options = new HttpServerOptions() | ||
| 61 | - .setPort(httpProperties.getServerPort()); | ||
| 62 | - if (Boolean.TRUE.equals(httpProperties.getSslEnabled())) { | ||
| 63 | - PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions().setKeyPath(httpProperties.getSslKeyPath()) | ||
| 64 | - .setCertPath(httpProperties.getSslCertPath()); | ||
| 65 | - options = options.setSsl(true).setKeyCertOptions(pemKeyCertOptions); | ||
| 66 | - } | ||
| 67 | - try { | ||
| 68 | - httpServer = vertx.createHttpServer(options) | ||
| 69 | - .requestHandler(router) | ||
| 70 | - .listen() | ||
| 71 | - .result(); | ||
| 72 | - log.info("[start][IoT 网关 HTTP 协议启动成功,端口:{}]", httpProperties.getServerPort()); | ||
| 73 | - } catch (Exception e) { | ||
| 74 | - log.error("[start][IoT 网关 HTTP 协议启动失败]", e); | ||
| 75 | - throw e; | ||
| 76 | - } | ||
| 77 | - } | ||
| 78 | - | ||
| 79 | - @PreDestroy | ||
| 80 | - public void stop() { | ||
| 81 | - if (httpServer != null) { | ||
| 82 | - try { | ||
| 83 | - httpServer.close().result(); | ||
| 84 | - log.info("[stop][IoT 网关 HTTP 协议已停止]"); | ||
| 85 | - } catch (Exception e) { | ||
| 86 | - log.error("[stop][IoT 网关 HTTP 协议停止失败]", e); | ||
| 87 | - } | ||
| 88 | - } | ||
| 89 | - } | ||
| 90 | - | ||
| 91 | -} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/downstream/IotHttpDownstreamSubscriber.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.downstream; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 5 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol; | ||
| 6 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber; | ||
| 7 | +import lombok.extern.slf4j.Slf4j; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * IoT 网关 HTTP 订阅者:接收下行给设备的消息 | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | + | ||
| 15 | +@Slf4j | ||
| 16 | +public class IotHttpDownstreamSubscriber extends IotProtocolDownstreamSubscriber { | ||
| 17 | + | ||
| 18 | + public IotHttpDownstreamSubscriber(IotProtocol protocol, IotMessageBus messageBus) { | ||
| 19 | + super(protocol, messageBus); | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + @Override | ||
| 23 | + protected void handleMessage(IotDeviceMessage message) { | ||
| 24 | + log.info("[handleMessage][IoT 网关 HTTP 协议不支持下行消息,忽略消息:{}]", message); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpAuthHandler.java
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; | 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.lang.Assert; | 3 | import cn.hutool.core.lang.Assert; |
| 4 | import cn.hutool.core.map.MapUtil; | 4 | import cn.hutool.core.map.MapUtil; |
| @@ -10,10 +10,10 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; | @@ -10,10 +10,10 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; | ||
| 10 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; | 10 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; |
| 11 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 11 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 12 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; | 12 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 13 | -import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; | 13 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpProtocol; |
| 14 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAbstractHandler; | ||
| 14 | import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; | 15 | import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; |
| 15 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | 16 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; |
| 16 | -import io.vertx.core.json.JsonObject; | ||
| 17 | import io.vertx.ext.web.RoutingContext; | 17 | import io.vertx.ext.web.RoutingContext; |
| 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; |
| @@ -32,7 +32,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { | @@ -32,7 +32,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { | ||
| 32 | 32 | ||
| 33 | public static final String PATH = "/auth"; | 33 | public static final String PATH = "/auth"; |
| 34 | 34 | ||
| 35 | - private final IotHttpUpstreamProtocol protocol; | 35 | + private final String serverId; |
| 36 | 36 | ||
| 37 | private final IotDeviceTokenService deviceTokenService; | 37 | private final IotDeviceTokenService deviceTokenService; |
| 38 | 38 | ||
| @@ -40,8 +40,8 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { | @@ -40,8 +40,8 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { | ||
| 40 | 40 | ||
| 41 | private final IotDeviceMessageService deviceMessageService; | 41 | private final IotDeviceMessageService deviceMessageService; |
| 42 | 42 | ||
| 43 | - public IotHttpAuthHandler(IotHttpUpstreamProtocol protocol) { | ||
| 44 | - this.protocol = protocol; | 43 | + public IotHttpAuthHandler(IotHttpProtocol protocol) { |
| 44 | + this.serverId = protocol.getServerId(); | ||
| 45 | this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); | 45 | this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); |
| 46 | this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class); | 46 | this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class); |
| 47 | this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); | 47 | this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); |
| @@ -50,32 +50,25 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { | @@ -50,32 +50,25 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { | ||
| 50 | @Override | 50 | @Override |
| 51 | public CommonResult<Object> handle0(RoutingContext context) { | 51 | public CommonResult<Object> handle0(RoutingContext context) { |
| 52 | // 1. 解析参数 | 52 | // 1. 解析参数 |
| 53 | - JsonObject body = context.body().asJsonObject(); | ||
| 54 | - if (body == null) { | ||
| 55 | - throw invalidParamException("请求体不能为空"); | ||
| 56 | - } | ||
| 57 | - String clientId = body.getString("clientId"); | ||
| 58 | - if (StrUtil.isEmpty(clientId)) { | 53 | + IotDeviceAuthReqDTO request = deserializeRequest(context, IotDeviceAuthReqDTO.class); |
| 54 | + if (StrUtil.isEmpty(request.getClientId())) { | ||
| 59 | throw invalidParamException("clientId 不能为空"); | 55 | throw invalidParamException("clientId 不能为空"); |
| 60 | } | 56 | } |
| 61 | - String username = body.getString("username"); | ||
| 62 | - if (StrUtil.isEmpty(username)) { | 57 | + if (StrUtil.isEmpty(request.getUsername())) { |
| 63 | throw invalidParamException("username 不能为空"); | 58 | throw invalidParamException("username 不能为空"); |
| 64 | } | 59 | } |
| 65 | - String password = body.getString("password"); | ||
| 66 | - if (StrUtil.isEmpty(password)) { | 60 | + if (StrUtil.isEmpty(request.getPassword())) { |
| 67 | throw invalidParamException("password 不能为空"); | 61 | throw invalidParamException("password 不能为空"); |
| 68 | } | 62 | } |
| 69 | 63 | ||
| 70 | // 2.1 执行认证 | 64 | // 2.1 执行认证 |
| 71 | - CommonResult<Boolean> result = deviceApi.authDevice(new IotDeviceAuthReqDTO() | ||
| 72 | - .setClientId(clientId).setUsername(username).setPassword(password)); | 65 | + CommonResult<Boolean> result = deviceApi.authDevice(request); |
| 73 | result.checkError(); | 66 | result.checkError(); |
| 74 | if (!BooleanUtil.isTrue(result.getData())) { | 67 | if (!BooleanUtil.isTrue(result.getData())) { |
| 75 | throw exception(DEVICE_AUTH_FAIL); | 68 | throw exception(DEVICE_AUTH_FAIL); |
| 76 | } | 69 | } |
| 77 | // 2.2 生成 Token | 70 | // 2.2 生成 Token |
| 78 | - IotDeviceIdentity deviceInfo = deviceTokenService.parseUsername(username); | 71 | + IotDeviceIdentity deviceInfo = deviceTokenService.parseUsername(request.getUsername()); |
| 79 | Assert.notNull(deviceInfo, "设备信息不能为空"); | 72 | Assert.notNull(deviceInfo, "设备信息不能为空"); |
| 80 | String token = deviceTokenService.createToken(deviceInfo.getProductKey(), deviceInfo.getDeviceName()); | 73 | String token = deviceTokenService.createToken(deviceInfo.getProductKey(), deviceInfo.getDeviceName()); |
| 81 | Assert.notBlank(token, "生成 token 不能为空位"); | 74 | Assert.notBlank(token, "生成 token 不能为空位"); |
| @@ -83,7 +76,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { | @@ -83,7 +76,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { | ||
| 83 | // 3. 执行上线 | 76 | // 3. 执行上线 |
| 84 | IotDeviceMessage message = IotDeviceMessage.buildStateUpdateOnline(); | 77 | IotDeviceMessage message = IotDeviceMessage.buildStateUpdateOnline(); |
| 85 | deviceMessageService.sendDeviceMessage(message, | 78 | deviceMessageService.sendDeviceMessage(message, |
| 86 | - deviceInfo.getProductKey(), deviceInfo.getDeviceName(), protocol.getServerId()); | 79 | + deviceInfo.getProductKey(), deviceInfo.getDeviceName(), serverId); |
| 87 | 80 | ||
| 88 | // 构建响应数据 | 81 | // 构建响应数据 |
| 89 | return success(MapUtil.of("token", token)); | 82 | return success(MapUtil.of("token", token)); |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpRegisterHandler.java
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; | 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.StrUtil; | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | import cn.hutool.extra.spring.SpringUtil; | 4 | import cn.hutool.extra.spring.SpringUtil; |
| @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; | @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 6 | import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; | 6 | import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; |
| 7 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; | 7 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; |
| 8 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; | 8 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; |
| 9 | -import io.vertx.core.json.JsonObject; | 9 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAbstractHandler; |
| 10 | import io.vertx.ext.web.RoutingContext; | 10 | import io.vertx.ext.web.RoutingContext; |
| 11 | 11 | ||
| 12 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; | 12 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; |
| @@ -33,27 +33,19 @@ public class IotHttpRegisterHandler extends IotHttpAbstractHandler { | @@ -33,27 +33,19 @@ public class IotHttpRegisterHandler extends IotHttpAbstractHandler { | ||
| 33 | @Override | 33 | @Override |
| 34 | public CommonResult<Object> handle0(RoutingContext context) { | 34 | public CommonResult<Object> handle0(RoutingContext context) { |
| 35 | // 1. 解析参数 | 35 | // 1. 解析参数 |
| 36 | - JsonObject body = context.body().asJsonObject(); | ||
| 37 | - if (body == null) { | ||
| 38 | - throw invalidParamException("请求体不能为空"); | ||
| 39 | - } | ||
| 40 | - String productKey = body.getString("productKey"); | ||
| 41 | - if (StrUtil.isEmpty(productKey)) { | 36 | + IotDeviceRegisterReqDTO request = deserializeRequest(context, IotDeviceRegisterReqDTO.class); |
| 37 | + if (StrUtil.isEmpty(request.getProductKey())) { | ||
| 42 | throw invalidParamException("productKey 不能为空"); | 38 | throw invalidParamException("productKey 不能为空"); |
| 43 | } | 39 | } |
| 44 | - String deviceName = body.getString("deviceName"); | ||
| 45 | - if (StrUtil.isEmpty(deviceName)) { | 40 | + if (StrUtil.isEmpty(request.getDeviceName())) { |
| 46 | throw invalidParamException("deviceName 不能为空"); | 41 | throw invalidParamException("deviceName 不能为空"); |
| 47 | } | 42 | } |
| 48 | - String productSecret = body.getString("productSecret"); | ||
| 49 | - if (StrUtil.isEmpty(productSecret)) { | 43 | + if (StrUtil.isEmpty(request.getProductSecret())) { |
| 50 | throw invalidParamException("productSecret 不能为空"); | 44 | throw invalidParamException("productSecret 不能为空"); |
| 51 | } | 45 | } |
| 52 | 46 | ||
| 53 | // 2. 调用动态注册 | 47 | // 2. 调用动态注册 |
| 54 | - IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO() | ||
| 55 | - .setProductKey(productKey).setDeviceName(deviceName).setProductSecret(productSecret); | ||
| 56 | - CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(reqDTO); | 48 | + CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(request); |
| 57 | result.checkError(); | 49 | result.checkError(); |
| 58 | 50 | ||
| 59 | // 3. 返回结果 | 51 | // 3. 返回结果 |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterSubHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpRegisterSubHandler.java
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; | 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 3 | import cn.hutool.extra.spring.SpringUtil; | 4 | import cn.hutool.extra.spring.SpringUtil; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 5 | 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.biz.IotDeviceCommonApi; | 6 | import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; |
| 7 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; | 7 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; |
| 8 | +import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO; | ||
| 8 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; | 9 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; |
| 9 | -import io.vertx.core.json.JsonObject; | 10 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAbstractHandler; |
| 10 | import io.vertx.ext.web.RoutingContext; | 11 | import io.vertx.ext.web.RoutingContext; |
| 12 | +import lombok.Data; | ||
| 11 | 13 | ||
| 12 | import java.util.List; | 14 | import java.util.List; |
| 13 | 15 | ||
| @@ -39,29 +41,32 @@ public class IotHttpRegisterSubHandler extends IotHttpAbstractHandler { | @@ -39,29 +41,32 @@ public class IotHttpRegisterSubHandler extends IotHttpAbstractHandler { | ||
| 39 | 41 | ||
| 40 | @Override | 42 | @Override |
| 41 | public CommonResult<Object> handle0(RoutingContext context) { | 43 | public CommonResult<Object> handle0(RoutingContext context) { |
| 42 | - // 1. 解析通用参数 | 44 | + // 1.1 解析通用参数 |
| 43 | String productKey = context.pathParam("productKey"); | 45 | String productKey = context.pathParam("productKey"); |
| 44 | String deviceName = context.pathParam("deviceName"); | 46 | String deviceName = context.pathParam("deviceName"); |
| 45 | - | ||
| 46 | - // 2. 解析子设备列表 | ||
| 47 | - JsonObject body = context.body().asJsonObject(); | ||
| 48 | - if (body == null) { | ||
| 49 | - throw invalidParamException("请求体不能为空"); | ||
| 50 | - } | ||
| 51 | - if (body.getJsonArray("params") == null) { | 47 | + // 1.2 解析子设备列表 |
| 48 | + SubDeviceRegisterRequest request = deserializeRequest(context, SubDeviceRegisterRequest.class); | ||
| 49 | + if (CollUtil.isEmpty(request.getParams())) { | ||
| 52 | throw invalidParamException("params 不能为空"); | 50 | throw invalidParamException("params 不能为空"); |
| 53 | } | 51 | } |
| 54 | - List<cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO> subDevices = JsonUtils.parseArray( | ||
| 55 | - body.getJsonArray("params").toString(), cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO.class); | ||
| 56 | 52 | ||
| 57 | - // 3. 调用子设备动态注册 | 53 | + // 2. 调用子设备动态注册 |
| 58 | IotSubDeviceRegisterFullReqDTO reqDTO = new IotSubDeviceRegisterFullReqDTO() | 54 | IotSubDeviceRegisterFullReqDTO reqDTO = new IotSubDeviceRegisterFullReqDTO() |
| 59 | - .setGatewayProductKey(productKey).setGatewayDeviceName(deviceName).setSubDevices(subDevices); | 55 | + .setGatewayProductKey(productKey) |
| 56 | + .setGatewayDeviceName(deviceName) | ||
| 57 | + .setSubDevices(request.getParams()); | ||
| 60 | CommonResult<List<IotSubDeviceRegisterRespDTO>> result = deviceApi.registerSubDevices(reqDTO); | 58 | CommonResult<List<IotSubDeviceRegisterRespDTO>> result = deviceApi.registerSubDevices(reqDTO); |
| 61 | result.checkError(); | 59 | result.checkError(); |
| 62 | 60 | ||
| 63 | - // 4. 返回结果 | 61 | + // 3. 返回结果 |
| 64 | return success(result.getData()); | 62 | return success(result.getData()); |
| 65 | } | 63 | } |
| 66 | 64 | ||
| 65 | + @Data | ||
| 66 | + public static class SubDeviceRegisterRequest { | ||
| 67 | + | ||
| 68 | + private List<IotSubDeviceRegisterReqDTO> params; | ||
| 69 | + | ||
| 70 | + } | ||
| 71 | + | ||
| 67 | } | 72 | } |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/handler/upstream/IotHttpUpstreamHandler.java
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; | 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.lang.Assert; | 3 | import cn.hutool.core.lang.Assert; |
| 4 | import cn.hutool.core.map.MapUtil; | 4 | import cn.hutool.core.map.MapUtil; |
| @@ -6,31 +6,28 @@ import cn.hutool.core.text.StrPool; | @@ -6,31 +6,28 @@ import cn.hutool.core.text.StrPool; | ||
| 6 | import cn.hutool.extra.spring.SpringUtil; | 6 | import cn.hutool.extra.spring.SpringUtil; |
| 7 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 7 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 8 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 8 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 9 | -import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; | 9 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpProtocol; |
| 10 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpAbstractHandler; | ||
| 10 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | 11 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; |
| 11 | import io.vertx.ext.web.RoutingContext; | 12 | import io.vertx.ext.web.RoutingContext; |
| 12 | -import lombok.RequiredArgsConstructor; | ||
| 13 | import lombok.extern.slf4j.Slf4j; | 13 | import lombok.extern.slf4j.Slf4j; |
| 14 | 14 | ||
| 15 | -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; | ||
| 16 | - | ||
| 17 | /** | 15 | /** |
| 18 | * IoT 网关 HTTP 协议的【上行】处理器 | 16 | * IoT 网关 HTTP 协议的【上行】处理器 |
| 19 | * | 17 | * |
| 20 | * @author 芋道源码 | 18 | * @author 芋道源码 |
| 21 | */ | 19 | */ |
| 22 | -@RequiredArgsConstructor | ||
| 23 | @Slf4j | 20 | @Slf4j |
| 24 | public class IotHttpUpstreamHandler extends IotHttpAbstractHandler { | 21 | public class IotHttpUpstreamHandler extends IotHttpAbstractHandler { |
| 25 | 22 | ||
| 26 | public static final String PATH = "/topic/sys/:productKey/:deviceName/*"; | 23 | public static final String PATH = "/topic/sys/:productKey/:deviceName/*"; |
| 27 | 24 | ||
| 28 | - private final IotHttpUpstreamProtocol protocol; | 25 | + private final String serverId; |
| 29 | 26 | ||
| 30 | private final IotDeviceMessageService deviceMessageService; | 27 | private final IotDeviceMessageService deviceMessageService; |
| 31 | 28 | ||
| 32 | - public IotHttpUpstreamHandler(IotHttpUpstreamProtocol protocol) { | ||
| 33 | - this.protocol = protocol; | 29 | + public IotHttpUpstreamHandler(IotHttpProtocol protocol) { |
| 30 | + this.serverId = protocol.getServerId(); | ||
| 34 | this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); | 31 | this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); |
| 35 | } | 32 | } |
| 36 | 33 | ||
| @@ -41,20 +38,15 @@ public class IotHttpUpstreamHandler extends IotHttpAbstractHandler { | @@ -41,20 +38,15 @@ public class IotHttpUpstreamHandler extends IotHttpAbstractHandler { | ||
| 41 | String deviceName = context.pathParam("deviceName"); | 38 | String deviceName = context.pathParam("deviceName"); |
| 42 | String method = context.pathParam("*").replaceAll(StrPool.SLASH, StrPool.DOT); | 39 | String method = context.pathParam("*").replaceAll(StrPool.SLASH, StrPool.DOT); |
| 43 | 40 | ||
| 44 | - // 2.1 解析消息 | ||
| 45 | - if (context.body().buffer() == null) { | ||
| 46 | - throw invalidParamException("请求体不能为空"); | ||
| 47 | - } | ||
| 48 | - byte[] bytes = context.body().buffer().getBytes(); | ||
| 49 | - IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(bytes, | ||
| 50 | - productKey, deviceName); | 41 | + // 2.1 根据 Content-Type 反序列化消息 |
| 42 | + IotDeviceMessage message = deserializeRequest(context, IotDeviceMessage.class); | ||
| 51 | Assert.equals(method, message.getMethod(), "method 不匹配"); | 43 | Assert.equals(method, message.getMethod(), "method 不匹配"); |
| 52 | // 2.2 发送消息 | 44 | // 2.2 发送消息 |
| 53 | deviceMessageService.sendDeviceMessage(message, | 45 | deviceMessageService.sendDeviceMessage(message, |
| 54 | - productKey, deviceName, protocol.getServerId()); | 46 | + productKey, deviceName, serverId); |
| 55 | 47 | ||
| 56 | // 3. 返回结果 | 48 | // 3. 返回结果 |
| 57 | return CommonResult.success(MapUtil.of("messageId", message.getId())); | 49 | return CommonResult.success(MapUtil.of("messageId", message.getId())); |
| 58 | } | 50 | } |
| 59 | 51 | ||
| 60 | -} | ||
| 61 | \ No newline at end of file | 52 | \ No newline at end of file |
| 53 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAbstractHandler.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.lang.Assert; | 3 | import cn.hutool.core.lang.Assert; |
| 4 | +import cn.hutool.core.util.ArrayUtil; | ||
| 4 | import cn.hutool.core.util.ObjUtil; | 5 | import cn.hutool.core.util.ObjUtil; |
| 5 | import cn.hutool.core.util.StrUtil; | 6 | import cn.hutool.core.util.StrUtil; |
| 6 | import cn.hutool.extra.spring.SpringUtil; | 7 | import cn.hutool.extra.spring.SpringUtil; |
| @@ -9,11 +10,13 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; | @@ -9,11 +10,13 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 9 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 10 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 10 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | 11 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 11 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; | 12 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 13 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpAuthHandler; | ||
| 14 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.handler.upstream.IotHttpRegisterHandler; | ||
| 15 | +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager; | ||
| 12 | import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; | 16 | import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; |
| 13 | import io.vertx.core.Handler; | 17 | import io.vertx.core.Handler; |
| 14 | import io.vertx.core.http.HttpHeaders; | 18 | import io.vertx.core.http.HttpHeaders; |
| 15 | import io.vertx.ext.web.RoutingContext; | 19 | import io.vertx.ext.web.RoutingContext; |
| 16 | -import lombok.RequiredArgsConstructor; | ||
| 17 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
| 18 | import org.springframework.http.MediaType; | 21 | import org.springframework.http.MediaType; |
| 19 | 22 | ||
| @@ -27,12 +30,13 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU | @@ -27,12 +30,13 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU | ||
| 27 | * | 30 | * |
| 28 | * @author 芋道源码 | 31 | * @author 芋道源码 |
| 29 | */ | 32 | */ |
| 30 | -@RequiredArgsConstructor | ||
| 31 | @Slf4j | 33 | @Slf4j |
| 32 | public abstract class IotHttpAbstractHandler implements Handler<RoutingContext> { | 34 | public abstract class IotHttpAbstractHandler implements Handler<RoutingContext> { |
| 33 | 35 | ||
| 34 | private final IotDeviceTokenService deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); | 36 | private final IotDeviceTokenService deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); |
| 35 | 37 | ||
| 38 | + private final IotMessageSerializerManager serializerManager = SpringUtil.getBean(IotMessageSerializerManager.class); | ||
| 39 | + | ||
| 36 | @Override | 40 | @Override |
| 37 | public final void handle(RoutingContext context) { | 41 | public final void handle(RoutingContext context) { |
| 38 | try { | 42 | try { |
| @@ -83,12 +87,26 @@ public abstract class IotHttpAbstractHandler implements Handler<RoutingContext> | @@ -83,12 +87,26 @@ public abstract class IotHttpAbstractHandler implements Handler<RoutingContext> | ||
| 83 | } | 87 | } |
| 84 | } | 88 | } |
| 85 | 89 | ||
| 90 | + // ========== 序列化相关方法 ========== | ||
| 91 | + | ||
| 92 | + protected static <T> T deserializeRequest(RoutingContext context, Class<T> clazz) { | ||
| 93 | + byte[] body = context.body().buffer() != null ? context.body().buffer().getBytes() : null; | ||
| 94 | + if (ArrayUtil.isEmpty(body)) { | ||
| 95 | + throw invalidParamException("请求体不能为空"); | ||
| 96 | + } | ||
| 97 | + return JsonUtils.parseObject(body, clazz); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + private static String serializeResponse(Object data) { | ||
| 101 | + return JsonUtils.toJsonString(data); | ||
| 102 | + } | ||
| 103 | + | ||
| 86 | @SuppressWarnings("deprecation") | 104 | @SuppressWarnings("deprecation") |
| 87 | public static void writeResponse(RoutingContext context, Object data) { | 105 | public static void writeResponse(RoutingContext context, Object data) { |
| 88 | context.response() | 106 | context.response() |
| 89 | .setStatusCode(200) | 107 | .setStatusCode(200) |
| 90 | .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) | 108 | .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) |
| 91 | - .end(JsonUtils.toJsonString(data)); | 109 | + .end(serializeResponse(data)); |
| 92 | } | 110 | } |
| 93 | 111 | ||
| 94 | } | 112 | } |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttDownstreamSubscriber.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 4 | -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | ||
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 5 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber; |
| 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstreamHandler; | 6 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstreamHandler; |
| 8 | -import jakarta.annotation.PostConstruct; | ||
| 9 | import lombok.extern.slf4j.Slf4j; | 7 | import lombok.extern.slf4j.Slf4j; |
| 10 | 8 | ||
| 11 | /** | 9 | /** |
| @@ -16,64 +14,27 @@ import lombok.extern.slf4j.Slf4j; | @@ -16,64 +14,27 @@ import lombok.extern.slf4j.Slf4j; | ||
| 16 | * @author 芋道源码 | 14 | * @author 芋道源码 |
| 17 | */ | 15 | */ |
| 18 | @Slf4j | 16 | @Slf4j |
| 19 | -public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { | ||
| 20 | - | ||
| 21 | - private final IotMqttUpstreamProtocol upstreamProtocol; | 17 | +public class IotMqttDownstreamSubscriber extends IotProtocolDownstreamSubscriber { |
| 22 | 18 | ||
| 23 | private final IotMqttDownstreamHandler downstreamHandler; | 19 | private final IotMqttDownstreamHandler downstreamHandler; |
| 24 | 20 | ||
| 25 | - private final IotMessageBus messageBus; | ||
| 26 | - | ||
| 27 | - public IotMqttDownstreamSubscriber(IotMqttUpstreamProtocol upstreamProtocol, | 21 | + public IotMqttDownstreamSubscriber(IotMqttUpstreamProtocol protocol, |
| 28 | IotMqttDownstreamHandler downstreamHandler, | 22 | IotMqttDownstreamHandler downstreamHandler, |
| 29 | IotMessageBus messageBus) { | 23 | IotMessageBus messageBus) { |
| 30 | - this.upstreamProtocol = upstreamProtocol; | 24 | + super(protocol, messageBus); |
| 31 | this.downstreamHandler = downstreamHandler; | 25 | this.downstreamHandler = downstreamHandler; |
| 32 | - this.messageBus = messageBus; | ||
| 33 | - } | ||
| 34 | - | ||
| 35 | - @PostConstruct | ||
| 36 | - public void subscribe() { | ||
| 37 | - messageBus.register(this); | ||
| 38 | - log.info("[subscribe][MQTT 协议下行消息订阅成功,主题:{}]", getTopic()); | ||
| 39 | - } | ||
| 40 | - | ||
| 41 | - @Override | ||
| 42 | - public String getTopic() { | ||
| 43 | - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(upstreamProtocol.getServerId()); | ||
| 44 | - } | ||
| 45 | - | ||
| 46 | - @Override | ||
| 47 | - public String getGroup() { | ||
| 48 | - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | ||
| 49 | - return getTopic(); | ||
| 50 | } | 26 | } |
| 51 | 27 | ||
| 52 | @Override | 28 | @Override |
| 53 | - public void onMessage(IotDeviceMessage message) { | ||
| 54 | - log.debug("[onMessage][接收到下行消息, messageId: {}, method: {}, deviceId: {}]", | ||
| 55 | - message.getId(), message.getMethod(), message.getDeviceId()); | ||
| 56 | - try { | ||
| 57 | - // 1. 校验 | ||
| 58 | - String method = message.getMethod(); | ||
| 59 | - if (method == null) { | ||
| 60 | - log.warn("[onMessage][消息方法为空, messageId: {}, deviceId: {}]", | ||
| 61 | - message.getId(), message.getDeviceId()); | ||
| 62 | - return; | ||
| 63 | - } | ||
| 64 | - | ||
| 65 | - // 2. 委托给下行处理器处理业务逻辑 | ||
| 66 | - boolean success = downstreamHandler.handleDownstreamMessage(message); | ||
| 67 | - if (success) { | ||
| 68 | - log.debug("[onMessage][下行消息处理成功, messageId: {}, method: {}, deviceId: {}]", | ||
| 69 | - message.getId(), message.getMethod(), message.getDeviceId()); | ||
| 70 | - } else { | ||
| 71 | - log.warn("[onMessage][下行消息处理失败, messageId: {}, method: {}, deviceId: {}]", | ||
| 72 | - message.getId(), message.getMethod(), message.getDeviceId()); | ||
| 73 | - } | ||
| 74 | - } catch (Exception e) { | ||
| 75 | - log.error("[onMessage][处理下行消息失败, messageId: {}, method: {}, deviceId: {}]", | ||
| 76 | - message.getId(), message.getMethod(), message.getDeviceId(), e); | 29 | + protected void handleMessage(IotDeviceMessage message) { |
| 30 | + boolean success = downstreamHandler.handleDownstreamMessage(message); | ||
| 31 | + if (success) { | ||
| 32 | + log.debug("[handleMessage][下行消息处理成功, messageId: {}, method: {}, deviceId: {}]", | ||
| 33 | + message.getId(), message.getMethod(), message.getDeviceId()); | ||
| 34 | + } else { | ||
| 35 | + log.warn("[handleMessage][下行消息处理失败, messageId: {}, method: {}, deviceId: {}]", | ||
| 36 | + message.getId(), message.getMethod(), message.getDeviceId()); | ||
| 77 | } | 37 | } |
| 78 | } | 38 | } |
| 39 | + | ||
| 79 | } | 40 | } |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttUpstreamProtocol.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 3 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 4 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; |
| 4 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; | 5 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; |
| 6 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol; | ||
| 5 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager; | 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager; |
| 6 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttUpstreamHandler; | 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttUpstreamHandler; |
| 7 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | 9 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; |
| @@ -19,7 +21,11 @@ import lombok.extern.slf4j.Slf4j; | @@ -19,7 +21,11 @@ import lombok.extern.slf4j.Slf4j; | ||
| 19 | * @author 芋道源码 | 21 | * @author 芋道源码 |
| 20 | */ | 22 | */ |
| 21 | @Slf4j | 23 | @Slf4j |
| 22 | -public class IotMqttUpstreamProtocol { | 24 | +public class IotMqttUpstreamProtocol implements IotProtocol { |
| 25 | + | ||
| 26 | + private static final String ID = "mqtt"; | ||
| 27 | + | ||
| 28 | + private volatile boolean running = false; | ||
| 23 | 29 | ||
| 24 | private final IotGatewayProperties.MqttProperties mqttProperties; | 30 | private final IotGatewayProperties.MqttProperties mqttProperties; |
| 25 | 31 | ||
| @@ -45,7 +51,23 @@ public class IotMqttUpstreamProtocol { | @@ -45,7 +51,23 @@ public class IotMqttUpstreamProtocol { | ||
| 45 | this.serverId = IotDeviceMessageUtils.generateServerId(mqttProperties.getPort()); | 51 | this.serverId = IotDeviceMessageUtils.generateServerId(mqttProperties.getPort()); |
| 46 | } | 52 | } |
| 47 | 53 | ||
| 54 | + @Override | ||
| 55 | + public String getId() { | ||
| 56 | + return ID; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + @Override | ||
| 60 | + public IotProtocolTypeEnum getType() { | ||
| 61 | + return IotProtocolTypeEnum.MQTT; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + @Override | ||
| 65 | + public boolean isRunning() { | ||
| 66 | + return running; | ||
| 67 | + } | ||
| 68 | + | ||
| 48 | // TODO @haohao:这里的编写,是不是和 tcp 对应的,风格保持一致哈; | 69 | // TODO @haohao:这里的编写,是不是和 tcp 对应的,风格保持一致哈; |
| 70 | + @Override | ||
| 49 | @PostConstruct | 71 | @PostConstruct |
| 50 | public void start() { | 72 | public void start() { |
| 51 | // 创建服务器选项 | 73 | // 创建服务器选项 |
| @@ -71,6 +93,7 @@ public class IotMqttUpstreamProtocol { | @@ -71,6 +93,7 @@ public class IotMqttUpstreamProtocol { | ||
| 71 | // 启动服务器 | 93 | // 启动服务器 |
| 72 | try { | 94 | try { |
| 73 | mqttServer.listen().result(); | 95 | mqttServer.listen().result(); |
| 96 | + running = true; | ||
| 74 | log.info("[start][IoT 网关 MQTT 协议启动成功,端口:{}]", mqttProperties.getPort()); | 97 | log.info("[start][IoT 网关 MQTT 协议启动成功,端口:{}]", mqttProperties.getPort()); |
| 75 | } catch (Exception e) { | 98 | } catch (Exception e) { |
| 76 | log.error("[start][IoT 网关 MQTT 协议启动失败]", e); | 99 | log.error("[start][IoT 网关 MQTT 协议启动失败]", e); |
| @@ -78,11 +101,13 @@ public class IotMqttUpstreamProtocol { | @@ -78,11 +101,13 @@ public class IotMqttUpstreamProtocol { | ||
| 78 | } | 101 | } |
| 79 | } | 102 | } |
| 80 | 103 | ||
| 104 | + @Override | ||
| 81 | @PreDestroy | 105 | @PreDestroy |
| 82 | public void stop() { | 106 | public void stop() { |
| 83 | if (mqttServer != null) { | 107 | if (mqttServer != null) { |
| 84 | try { | 108 | try { |
| 85 | mqttServer.close().result(); | 109 | mqttServer.close().result(); |
| 110 | + running = false; | ||
| 86 | log.info("[stop][IoT 网关 MQTT 协议已停止]"); | 111 | log.info("[stop][IoT 网关 MQTT 协议已停止]"); |
| 87 | } catch (Exception e) { | 112 | } catch (Exception e) { |
| 88 | log.error("[stop][IoT 网关 MQTT 协议停止失败]", e); | 113 | log.error("[stop][IoT 网关 MQTT 协议停止失败]", e); |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpDownstreamSubscriber.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.tcp; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.tcp; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 4 | -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | ||
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 7 | -import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager; | 5 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber; |
| 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpDownstreamHandler; | 6 | import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpDownstreamHandler; |
| 9 | -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | ||
| 10 | -import jakarta.annotation.PostConstruct; | ||
| 11 | -import lombok.RequiredArgsConstructor; | ||
| 12 | import lombok.extern.slf4j.Slf4j; | 7 | import lombok.extern.slf4j.Slf4j; |
| 13 | 8 | ||
| 14 | /** | 9 | /** |
| @@ -17,48 +12,20 @@ import lombok.extern.slf4j.Slf4j; | @@ -17,48 +12,20 @@ import lombok.extern.slf4j.Slf4j; | ||
| 17 | * @author 芋道源码 | 12 | * @author 芋道源码 |
| 18 | */ | 13 | */ |
| 19 | @Slf4j | 14 | @Slf4j |
| 20 | -@RequiredArgsConstructor | ||
| 21 | -public class IotTcpDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { | 15 | +public class IotTcpDownstreamSubscriber extends IotProtocolDownstreamSubscriber { |
| 22 | 16 | ||
| 23 | - private final IotTcpUpstreamProtocol protocol; | 17 | + private final IotTcpDownstreamHandler downstreamHandler; |
| 24 | 18 | ||
| 25 | - private final IotDeviceMessageService messageService; | ||
| 26 | - | ||
| 27 | - private final IotTcpConnectionManager connectionManager; | ||
| 28 | - | ||
| 29 | - private final IotMessageBus messageBus; | ||
| 30 | - | ||
| 31 | - private IotTcpDownstreamHandler downstreamHandler; | ||
| 32 | - | ||
| 33 | - @PostConstruct | ||
| 34 | - public void init() { | ||
| 35 | - // 初始化下游处理器 | ||
| 36 | - this.downstreamHandler = new IotTcpDownstreamHandler(messageService, connectionManager); | ||
| 37 | - // 注册下游订阅者 | ||
| 38 | - messageBus.register(this); | ||
| 39 | - log.info("[init][TCP 下游订阅者初始化完成,服务器 ID: {},Topic: {}]", | ||
| 40 | - protocol.getServerId(), getTopic()); | ||
| 41 | - } | ||
| 42 | - | ||
| 43 | - @Override | ||
| 44 | - public String getTopic() { | ||
| 45 | - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId()); | ||
| 46 | - } | ||
| 47 | - | ||
| 48 | - @Override | ||
| 49 | - public String getGroup() { | ||
| 50 | - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | ||
| 51 | - return getTopic(); | 19 | + public IotTcpDownstreamSubscriber(IotTcpUpstreamProtocol protocol, |
| 20 | + IotTcpDownstreamHandler downstreamHandler, | ||
| 21 | + IotMessageBus messageBus) { | ||
| 22 | + super(protocol, messageBus); | ||
| 23 | + this.downstreamHandler = downstreamHandler; | ||
| 52 | } | 24 | } |
| 53 | 25 | ||
| 54 | @Override | 26 | @Override |
| 55 | - public void onMessage(IotDeviceMessage message) { | ||
| 56 | - try { | ||
| 57 | - downstreamHandler.handle(message); | ||
| 58 | - } catch (Exception e) { | ||
| 59 | - log.error("[onMessage][处理下行消息失败,设备 ID: {},方法: {},消息 ID: {}]", | ||
| 60 | - message.getDeviceId(), message.getMethod(), message.getId(), e); | ||
| 61 | - } | 27 | + protected void handleMessage(IotDeviceMessage message) { |
| 28 | + downstreamHandler.handle(message); | ||
| 62 | } | 29 | } |
| 63 | 30 | ||
| 64 | -} | ||
| 65 | \ No newline at end of file | 31 | \ No newline at end of file |
| 32 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotTcpUpstreamProtocol.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.tcp; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.tcp; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 3 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 4 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; |
| 4 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; | 5 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; |
| 6 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol; | ||
| 5 | import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager; | 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager; |
| 6 | import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpUpstreamHandler; | 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.IotTcpUpstreamHandler; |
| 7 | import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; | 9 | import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; |
| @@ -21,7 +23,9 @@ import lombok.extern.slf4j.Slf4j; | @@ -21,7 +23,9 @@ import lombok.extern.slf4j.Slf4j; | ||
| 21 | * @author 芋道源码 | 23 | * @author 芋道源码 |
| 22 | */ | 24 | */ |
| 23 | @Slf4j | 25 | @Slf4j |
| 24 | -public class IotTcpUpstreamProtocol { | 26 | +public class IotTcpUpstreamProtocol implements IotProtocol { |
| 27 | + | ||
| 28 | + private static final String ID = "tcp"; | ||
| 25 | 29 | ||
| 26 | private final IotGatewayProperties.TcpProperties tcpProperties; | 30 | private final IotGatewayProperties.TcpProperties tcpProperties; |
| 27 | 31 | ||
| @@ -38,6 +42,8 @@ public class IotTcpUpstreamProtocol { | @@ -38,6 +42,8 @@ public class IotTcpUpstreamProtocol { | ||
| 38 | 42 | ||
| 39 | private NetServer tcpServer; | 43 | private NetServer tcpServer; |
| 40 | 44 | ||
| 45 | + private volatile boolean running = false; | ||
| 46 | + | ||
| 41 | public IotTcpUpstreamProtocol(IotGatewayProperties.TcpProperties tcpProperties, | 47 | public IotTcpUpstreamProtocol(IotGatewayProperties.TcpProperties tcpProperties, |
| 42 | IotDeviceService deviceService, | 48 | IotDeviceService deviceService, |
| 43 | IotDeviceMessageService messageService, | 49 | IotDeviceMessageService messageService, |
| @@ -51,6 +57,17 @@ public class IotTcpUpstreamProtocol { | @@ -51,6 +57,17 @@ public class IotTcpUpstreamProtocol { | ||
| 51 | this.serverId = IotDeviceMessageUtils.generateServerId(tcpProperties.getPort()); | 57 | this.serverId = IotDeviceMessageUtils.generateServerId(tcpProperties.getPort()); |
| 52 | } | 58 | } |
| 53 | 59 | ||
| 60 | + @Override | ||
| 61 | + public String getId() { | ||
| 62 | + return ID; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + @Override | ||
| 66 | + public IotProtocolTypeEnum getType() { | ||
| 67 | + return IotProtocolTypeEnum.TCP; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Override | ||
| 54 | @PostConstruct | 71 | @PostConstruct |
| 55 | public void start() { | 72 | public void start() { |
| 56 | // 创建服务器选项 | 73 | // 创建服务器选项 |
| @@ -78,6 +95,7 @@ public class IotTcpUpstreamProtocol { | @@ -78,6 +95,7 @@ public class IotTcpUpstreamProtocol { | ||
| 78 | // 启动服务器 | 95 | // 启动服务器 |
| 79 | try { | 96 | try { |
| 80 | tcpServer.listen().result(); | 97 | tcpServer.listen().result(); |
| 98 | + running = true; | ||
| 81 | log.info("[start][IoT 网关 TCP 协议启动成功,端口:{}]", tcpProperties.getPort()); | 99 | log.info("[start][IoT 网关 TCP 协议启动成功,端口:{}]", tcpProperties.getPort()); |
| 82 | } catch (Exception e) { | 100 | } catch (Exception e) { |
| 83 | log.error("[start][IoT 网关 TCP 协议启动失败]", e); | 101 | log.error("[start][IoT 网关 TCP 协议启动失败]", e); |
| @@ -85,15 +103,23 @@ public class IotTcpUpstreamProtocol { | @@ -85,15 +103,23 @@ public class IotTcpUpstreamProtocol { | ||
| 85 | } | 103 | } |
| 86 | } | 104 | } |
| 87 | 105 | ||
| 106 | + @Override | ||
| 88 | @PreDestroy | 107 | @PreDestroy |
| 89 | public void stop() { | 108 | public void stop() { |
| 90 | if (tcpServer != null) { | 109 | if (tcpServer != null) { |
| 91 | try { | 110 | try { |
| 92 | tcpServer.close().result(); | 111 | tcpServer.close().result(); |
| 112 | + running = false; | ||
| 93 | log.info("[stop][IoT 网关 TCP 协议已停止]"); | 113 | log.info("[stop][IoT 网关 TCP 协议已停止]"); |
| 94 | } catch (Exception e) { | 114 | } catch (Exception e) { |
| 95 | log.error("[stop][IoT 网关 TCP 协议停止失败]", e); | 115 | log.error("[stop][IoT 网关 TCP 协议停止失败]", e); |
| 96 | } | 116 | } |
| 97 | } | 117 | } |
| 98 | } | 118 | } |
| 99 | -} | ||
| 100 | \ No newline at end of file | 119 | \ No newline at end of file |
| 120 | + | ||
| 121 | + @Override | ||
| 122 | + public boolean isRunning() { | ||
| 123 | + return running; | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/IotUdpDownstreamSubscriber.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.udp; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.udp; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 4 | -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | ||
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 7 | -import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager; | 5 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber; |
| 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.udp.router.IotUdpDownstreamHandler; | 6 | import cn.iocoder.yudao.module.iot.gateway.protocol.udp.router.IotUdpDownstreamHandler; |
| 9 | -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | ||
| 10 | -import jakarta.annotation.PostConstruct; | ||
| 11 | -import lombok.RequiredArgsConstructor; | ||
| 12 | import lombok.extern.slf4j.Slf4j; | 7 | import lombok.extern.slf4j.Slf4j; |
| 13 | 8 | ||
| 14 | /** | 9 | /** |
| @@ -17,48 +12,20 @@ import lombok.extern.slf4j.Slf4j; | @@ -17,48 +12,20 @@ import lombok.extern.slf4j.Slf4j; | ||
| 17 | * @author 芋道源码 | 12 | * @author 芋道源码 |
| 18 | */ | 13 | */ |
| 19 | @Slf4j | 14 | @Slf4j |
| 20 | -@RequiredArgsConstructor | ||
| 21 | -public class IotUdpDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { | 15 | +public class IotUdpDownstreamSubscriber extends IotProtocolDownstreamSubscriber { |
| 22 | 16 | ||
| 23 | - private final IotUdpUpstreamProtocol protocol; | 17 | + private final IotUdpDownstreamHandler downstreamHandler; |
| 24 | 18 | ||
| 25 | - private final IotDeviceMessageService messageService; | ||
| 26 | - | ||
| 27 | - private final IotUdpSessionManager sessionManager; | ||
| 28 | - | ||
| 29 | - private final IotMessageBus messageBus; | ||
| 30 | - | ||
| 31 | - private IotUdpDownstreamHandler downstreamHandler; | ||
| 32 | - | ||
| 33 | - @PostConstruct | ||
| 34 | - public void init() { | ||
| 35 | - // 初始化下游处理器 | ||
| 36 | - this.downstreamHandler = new IotUdpDownstreamHandler(messageService, sessionManager, protocol); | ||
| 37 | - // 注册下游订阅者 | ||
| 38 | - messageBus.register(this); | ||
| 39 | - log.info("[init][UDP 下游订阅者初始化完成,服务器 ID: {},Topic: {}]", | ||
| 40 | - protocol.getServerId(), getTopic()); | ||
| 41 | - } | ||
| 42 | - | ||
| 43 | - @Override | ||
| 44 | - public String getTopic() { | ||
| 45 | - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId()); | ||
| 46 | - } | ||
| 47 | - | ||
| 48 | - @Override | ||
| 49 | - public String getGroup() { | ||
| 50 | - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | ||
| 51 | - return getTopic(); | 19 | + public IotUdpDownstreamSubscriber(IotUdpUpstreamProtocol protocol, |
| 20 | + IotUdpDownstreamHandler downstreamHandler, | ||
| 21 | + IotMessageBus messageBus) { | ||
| 22 | + super(protocol, messageBus); | ||
| 23 | + this.downstreamHandler = downstreamHandler; | ||
| 52 | } | 24 | } |
| 53 | 25 | ||
| 54 | @Override | 26 | @Override |
| 55 | - public void onMessage(IotDeviceMessage message) { | ||
| 56 | - try { | ||
| 57 | - downstreamHandler.handle(message); | ||
| 58 | - } catch (Exception e) { | ||
| 59 | - log.error("[onMessage][处理下行消息失败,设备 ID: {},方法: {},消息 ID: {}]", | ||
| 60 | - message.getDeviceId(), message.getMethod(), message.getId(), e); | ||
| 61 | - } | 27 | + protected void handleMessage(IotDeviceMessage message) { |
| 28 | + downstreamHandler.handle(message); | ||
| 62 | } | 29 | } |
| 63 | 30 | ||
| 64 | } | 31 | } |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/IotUdpUpstreamProtocol.java
| @@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.udp; | @@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.udp; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; | 4 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; |
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 6 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 7 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; |
| 7 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; | 8 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; |
| 9 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol; | ||
| 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager; | 10 | import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager; |
| 9 | import cn.iocoder.yudao.module.iot.gateway.protocol.udp.router.IotUdpUpstreamHandler; | 11 | import cn.iocoder.yudao.module.iot.gateway.protocol.udp.router.IotUdpUpstreamHandler; |
| 10 | import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; | 12 | import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; |
| @@ -30,7 +32,9 @@ import java.util.List; | @@ -30,7 +32,9 @@ import java.util.List; | ||
| 30 | * @author 芋道源码 | 32 | * @author 芋道源码 |
| 31 | */ | 33 | */ |
| 32 | @Slf4j | 34 | @Slf4j |
| 33 | -public class IotUdpUpstreamProtocol { | 35 | +public class IotUdpUpstreamProtocol implements IotProtocol { |
| 36 | + | ||
| 37 | + private static final String ID = "udp"; | ||
| 34 | 38 | ||
| 35 | private final IotGatewayProperties.UdpProperties udpProperties; | 39 | private final IotGatewayProperties.UdpProperties udpProperties; |
| 36 | 40 | ||
| @@ -55,6 +59,8 @@ public class IotUdpUpstreamProtocol { | @@ -55,6 +59,8 @@ public class IotUdpUpstreamProtocol { | ||
| 55 | 59 | ||
| 56 | private IotUdpUpstreamHandler upstreamHandler; | 60 | private IotUdpUpstreamHandler upstreamHandler; |
| 57 | 61 | ||
| 62 | + private volatile boolean running = false; | ||
| 63 | + | ||
| 58 | public IotUdpUpstreamProtocol(IotGatewayProperties.UdpProperties udpProperties, | 64 | public IotUdpUpstreamProtocol(IotGatewayProperties.UdpProperties udpProperties, |
| 59 | IotDeviceService deviceService, | 65 | IotDeviceService deviceService, |
| 60 | IotDeviceMessageService messageService, | 66 | IotDeviceMessageService messageService, |
| @@ -68,6 +74,17 @@ public class IotUdpUpstreamProtocol { | @@ -68,6 +74,17 @@ public class IotUdpUpstreamProtocol { | ||
| 68 | this.serverId = IotDeviceMessageUtils.generateServerId(udpProperties.getPort()); | 74 | this.serverId = IotDeviceMessageUtils.generateServerId(udpProperties.getPort()); |
| 69 | } | 75 | } |
| 70 | 76 | ||
| 77 | + @Override | ||
| 78 | + public String getId() { | ||
| 79 | + return ID; | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + @Override | ||
| 83 | + public IotProtocolTypeEnum getType() { | ||
| 84 | + return IotProtocolTypeEnum.UDP; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + @Override | ||
| 71 | @PostConstruct | 88 | @PostConstruct |
| 72 | public void start() { | 89 | public void start() { |
| 73 | // 1. 初始化上行消息处理器 | 90 | // 1. 初始化上行消息处理器 |
| @@ -90,6 +107,7 @@ public class IotUdpUpstreamProtocol { | @@ -90,6 +107,7 @@ public class IotUdpUpstreamProtocol { | ||
| 90 | } | 107 | } |
| 91 | // 设置数据包处理器 | 108 | // 设置数据包处理器 |
| 92 | udpSocket.handler(packet -> upstreamHandler.handle(packet, udpSocket)); | 109 | udpSocket.handler(packet -> upstreamHandler.handle(packet, udpSocket)); |
| 110 | + running = true; | ||
| 93 | log.info("[start][IoT 网关 UDP 协议启动成功,端口:{},接收缓冲区:{} 字节,发送缓冲区:{} 字节]", | 111 | log.info("[start][IoT 网关 UDP 协议启动成功,端口:{},接收缓冲区:{} 字节,发送缓冲区:{} 字节]", |
| 94 | udpProperties.getPort(), udpProperties.getReceiveBufferSize(), | 112 | udpProperties.getPort(), udpProperties.getReceiveBufferSize(), |
| 95 | udpProperties.getSendBufferSize()); | 113 | udpProperties.getSendBufferSize()); |
| @@ -99,6 +117,7 @@ public class IotUdpUpstreamProtocol { | @@ -99,6 +117,7 @@ public class IotUdpUpstreamProtocol { | ||
| 99 | }); | 117 | }); |
| 100 | } | 118 | } |
| 101 | 119 | ||
| 120 | + @Override | ||
| 102 | @PreDestroy | 121 | @PreDestroy |
| 103 | public void stop() { | 122 | public void stop() { |
| 104 | // 1. 取消会话清理定时器 | 123 | // 1. 取消会话清理定时器 |
| @@ -112,6 +131,7 @@ public class IotUdpUpstreamProtocol { | @@ -112,6 +131,7 @@ public class IotUdpUpstreamProtocol { | ||
| 112 | if (udpSocket != null) { | 131 | if (udpSocket != null) { |
| 113 | try { | 132 | try { |
| 114 | udpSocket.close().result(); | 133 | udpSocket.close().result(); |
| 134 | + running = false; | ||
| 115 | log.info("[stop][IoT 网关 UDP 协议已停止]"); | 135 | log.info("[stop][IoT 网关 UDP 协议已停止]"); |
| 116 | } catch (Exception e) { | 136 | } catch (Exception e) { |
| 117 | log.error("[stop][IoT 网关 UDP 协议停止失败]", e); | 137 | log.error("[stop][IoT 网关 UDP 协议停止失败]", e); |
| @@ -119,6 +139,11 @@ public class IotUdpUpstreamProtocol { | @@ -119,6 +139,11 @@ public class IotUdpUpstreamProtocol { | ||
| 119 | } | 139 | } |
| 120 | } | 140 | } |
| 121 | 141 | ||
| 142 | + @Override | ||
| 143 | + public boolean isRunning() { | ||
| 144 | + return running; | ||
| 145 | + } | ||
| 146 | + | ||
| 122 | /** | 147 | /** |
| 123 | * 启动会话清理定时器 | 148 | * 启动会话清理定时器 |
| 124 | */ | 149 | */ |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/IotWebSocketDownstreamSubscriber.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.websocket; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.websocket; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 4 | -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | ||
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 7 | -import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager; | 5 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber; |
| 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.router.IotWebSocketDownstreamHandler; | 6 | import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.router.IotWebSocketDownstreamHandler; |
| 9 | -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | ||
| 10 | -import jakarta.annotation.PostConstruct; | ||
| 11 | -import lombok.RequiredArgsConstructor; | ||
| 12 | import lombok.extern.slf4j.Slf4j; | 7 | import lombok.extern.slf4j.Slf4j; |
| 13 | 8 | ||
| 14 | /** | 9 | /** |
| @@ -17,48 +12,20 @@ import lombok.extern.slf4j.Slf4j; | @@ -17,48 +12,20 @@ import lombok.extern.slf4j.Slf4j; | ||
| 17 | * @author 芋道源码 | 12 | * @author 芋道源码 |
| 18 | */ | 13 | */ |
| 19 | @Slf4j | 14 | @Slf4j |
| 20 | -@RequiredArgsConstructor | ||
| 21 | -public class IotWebSocketDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { | 15 | +public class IotWebSocketDownstreamSubscriber extends IotProtocolDownstreamSubscriber { |
| 22 | 16 | ||
| 23 | - private final IotWebSocketUpstreamProtocol protocol; | 17 | + private final IotWebSocketDownstreamHandler downstreamHandler; |
| 24 | 18 | ||
| 25 | - private final IotDeviceMessageService messageService; | ||
| 26 | - | ||
| 27 | - private final IotWebSocketConnectionManager connectionManager; | ||
| 28 | - | ||
| 29 | - private final IotMessageBus messageBus; | ||
| 30 | - | ||
| 31 | - private IotWebSocketDownstreamHandler downstreamHandler; | ||
| 32 | - | ||
| 33 | - @PostConstruct | ||
| 34 | - public void init() { | ||
| 35 | - // 初始化下游处理器 | ||
| 36 | - this.downstreamHandler = new IotWebSocketDownstreamHandler(messageService, connectionManager); | ||
| 37 | - // 注册下游订阅者 | ||
| 38 | - messageBus.register(this); | ||
| 39 | - log.info("[init][WebSocket 下游订阅者初始化完成,服务器 ID: {},Topic: {}]", | ||
| 40 | - protocol.getServerId(), getTopic()); | ||
| 41 | - } | ||
| 42 | - | ||
| 43 | - @Override | ||
| 44 | - public String getTopic() { | ||
| 45 | - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId()); | ||
| 46 | - } | ||
| 47 | - | ||
| 48 | - @Override | ||
| 49 | - public String getGroup() { | ||
| 50 | - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | ||
| 51 | - return getTopic(); | 19 | + public IotWebSocketDownstreamSubscriber(IotWebSocketUpstreamProtocol protocol, |
| 20 | + IotWebSocketDownstreamHandler downstreamHandler, | ||
| 21 | + IotMessageBus messageBus) { | ||
| 22 | + super(protocol, messageBus); | ||
| 23 | + this.downstreamHandler = downstreamHandler; | ||
| 52 | } | 24 | } |
| 53 | 25 | ||
| 54 | @Override | 26 | @Override |
| 55 | - public void onMessage(IotDeviceMessage message) { | ||
| 56 | - try { | ||
| 57 | - downstreamHandler.handle(message); | ||
| 58 | - } catch (Exception e) { | ||
| 59 | - log.error("[onMessage][处理下行消息失败,设备 ID: {},方法: {},消息 ID: {}]", | ||
| 60 | - message.getDeviceId(), message.getMethod(), message.getId(), e); | ||
| 61 | - } | 27 | + protected void handleMessage(IotDeviceMessage message) { |
| 28 | + downstreamHandler.handle(message); | ||
| 62 | } | 29 | } |
| 63 | 30 | ||
| 64 | } | 31 | } |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/IotWebSocketUpstreamProtocol.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.websocket; | 1 | package cn.iocoder.yudao.module.iot.gateway.protocol.websocket; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.ObjUtil; | 3 | import cn.hutool.core.util.ObjUtil; |
| 4 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 4 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 5 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; |
| 5 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; | 6 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; |
| 7 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol; | ||
| 6 | import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager; | 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager; |
| 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.router.IotWebSocketUpstreamHandler; | 9 | import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.router.IotWebSocketUpstreamHandler; |
| 8 | import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; | 10 | import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; |
| @@ -22,7 +24,9 @@ import lombok.extern.slf4j.Slf4j; | @@ -22,7 +24,9 @@ import lombok.extern.slf4j.Slf4j; | ||
| 22 | * @author 芋道源码 | 24 | * @author 芋道源码 |
| 23 | */ | 25 | */ |
| 24 | @Slf4j | 26 | @Slf4j |
| 25 | -public class IotWebSocketUpstreamProtocol { | 27 | +public class IotWebSocketUpstreamProtocol implements IotProtocol { |
| 28 | + | ||
| 29 | + private static final String ID = "websocket"; | ||
| 26 | 30 | ||
| 27 | private final IotGatewayProperties.WebSocketProperties wsProperties; | 31 | private final IotGatewayProperties.WebSocketProperties wsProperties; |
| 28 | 32 | ||
| @@ -39,6 +43,8 @@ public class IotWebSocketUpstreamProtocol { | @@ -39,6 +43,8 @@ public class IotWebSocketUpstreamProtocol { | ||
| 39 | 43 | ||
| 40 | private HttpServer httpServer; | 44 | private HttpServer httpServer; |
| 41 | 45 | ||
| 46 | + private volatile boolean running = false; | ||
| 47 | + | ||
| 42 | public IotWebSocketUpstreamProtocol(IotGatewayProperties.WebSocketProperties wsProperties, | 48 | public IotWebSocketUpstreamProtocol(IotGatewayProperties.WebSocketProperties wsProperties, |
| 43 | IotDeviceService deviceService, | 49 | IotDeviceService deviceService, |
| 44 | IotDeviceMessageService messageService, | 50 | IotDeviceMessageService messageService, |
| @@ -52,6 +58,17 @@ public class IotWebSocketUpstreamProtocol { | @@ -52,6 +58,17 @@ public class IotWebSocketUpstreamProtocol { | ||
| 52 | this.serverId = IotDeviceMessageUtils.generateServerId(wsProperties.getPort()); | 58 | this.serverId = IotDeviceMessageUtils.generateServerId(wsProperties.getPort()); |
| 53 | } | 59 | } |
| 54 | 60 | ||
| 61 | + @Override | ||
| 62 | + public String getId() { | ||
| 63 | + return ID; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + @Override | ||
| 67 | + public IotProtocolTypeEnum getType() { | ||
| 68 | + return IotProtocolTypeEnum.WEBSOCKET; | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + @Override | ||
| 55 | @PostConstruct | 72 | @PostConstruct |
| 56 | @SuppressWarnings("deprecation") | 73 | @SuppressWarnings("deprecation") |
| 57 | public void start() { | 74 | public void start() { |
| @@ -88,6 +105,7 @@ public class IotWebSocketUpstreamProtocol { | @@ -88,6 +105,7 @@ public class IotWebSocketUpstreamProtocol { | ||
| 88 | // 3. 启动服务器 | 105 | // 3. 启动服务器 |
| 89 | try { | 106 | try { |
| 90 | httpServer.listen().result(); | 107 | httpServer.listen().result(); |
| 108 | + running = true; | ||
| 91 | log.info("[start][IoT 网关 WebSocket 协议启动成功,端口:{},路径:{}]", wsProperties.getPort(), wsProperties.getPath()); | 109 | log.info("[start][IoT 网关 WebSocket 协议启动成功,端口:{},路径:{}]", wsProperties.getPort(), wsProperties.getPath()); |
| 92 | } catch (Exception e) { | 110 | } catch (Exception e) { |
| 93 | log.error("[start][IoT 网关 WebSocket 协议启动失败]", e); | 111 | log.error("[start][IoT 网关 WebSocket 协议启动失败]", e); |
| @@ -95,11 +113,13 @@ public class IotWebSocketUpstreamProtocol { | @@ -95,11 +113,13 @@ public class IotWebSocketUpstreamProtocol { | ||
| 95 | } | 113 | } |
| 96 | } | 114 | } |
| 97 | 115 | ||
| 116 | + @Override | ||
| 98 | @PreDestroy | 117 | @PreDestroy |
| 99 | public void stop() { | 118 | public void stop() { |
| 100 | if (httpServer != null) { | 119 | if (httpServer != null) { |
| 101 | try { | 120 | try { |
| 102 | httpServer.close().result(); | 121 | httpServer.close().result(); |
| 122 | + running = false; | ||
| 103 | log.info("[stop][IoT 网关 WebSocket 协议已停止]"); | 123 | log.info("[stop][IoT 网关 WebSocket 协议已停止]"); |
| 104 | } catch (Exception e) { | 124 | } catch (Exception e) { |
| 105 | log.error("[stop][IoT 网关 WebSocket 协议停止失败]", e); | 125 | log.error("[stop][IoT 网关 WebSocket 协议停止失败]", e); |
| @@ -107,4 +127,9 @@ public class IotWebSocketUpstreamProtocol { | @@ -107,4 +127,9 @@ public class IotWebSocketUpstreamProtocol { | ||
| 107 | } | 127 | } |
| 108 | } | 128 | } |
| 109 | 129 | ||
| 130 | + @Override | ||
| 131 | + public boolean isRunning() { | ||
| 132 | + return running; | ||
| 133 | + } | ||
| 134 | + | ||
| 110 | } | 135 | } |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/IotMessageSerializer.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.serialize; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * IoT 设备消息序列化器接口 | ||
| 8 | + * | ||
| 9 | + * 用于序列化和反序列化设备消息 | ||
| 10 | + * | ||
| 11 | + * @author 芋道源码 | ||
| 12 | + */ | ||
| 13 | +public interface IotMessageSerializer { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 序列化消息 | ||
| 17 | + * | ||
| 18 | + * @param message 消息 | ||
| 19 | + * @return 编码后的消息内容 | ||
| 20 | + */ | ||
| 21 | + byte[] serialize(IotDeviceMessage message); | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 反序列化消息 | ||
| 25 | + * | ||
| 26 | + * @param bytes 消息内容 | ||
| 27 | + * @return 解码后的消息内容 | ||
| 28 | + */ | ||
| 29 | + IotDeviceMessage deserialize(byte[] bytes); | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 获取序列化类型 | ||
| 33 | + * | ||
| 34 | + * @return 序列化类型枚举 | ||
| 35 | + */ | ||
| 36 | + IotSerializeTypeEnum getType(); | ||
| 37 | + | ||
| 38 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/IotMessageSerializerManager.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.serialize; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum; | ||
| 4 | +import cn.iocoder.yudao.module.iot.gateway.serialize.binary.IotBinarySerializer; | ||
| 5 | +import cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer; | ||
| 6 | +import lombok.extern.slf4j.Slf4j; | ||
| 7 | + | ||
| 8 | +import java.util.EnumMap; | ||
| 9 | +import java.util.Map; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * IoT 序列化器管理器 | ||
| 13 | + * | ||
| 14 | + * 负责根据枚举创建和管理序列化器实例 | ||
| 15 | + * | ||
| 16 | + * @author 芋道源码 | ||
| 17 | + */ | ||
| 18 | +@Slf4j | ||
| 19 | +public class IotMessageSerializerManager { | ||
| 20 | + | ||
| 21 | + private final Map<IotSerializeTypeEnum, IotMessageSerializer> serializerMap = new EnumMap<>(IotSerializeTypeEnum.class); | ||
| 22 | + | ||
| 23 | + public IotMessageSerializerManager() { | ||
| 24 | + // 遍历枚举,创建对应的序列化器 | ||
| 25 | + for (IotSerializeTypeEnum type : IotSerializeTypeEnum.values()) { | ||
| 26 | + IotMessageSerializer serializer = createSerializer(type); | ||
| 27 | + serializerMap.put(type, serializer); | ||
| 28 | + log.info("[IotSerializerManager][序列化器 {} 创建成功]", type); | ||
| 29 | + } | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 根据类型创建序列化器 | ||
| 34 | + * | ||
| 35 | + * @param type 序列化类型 | ||
| 36 | + * @return 序列化器实例 | ||
| 37 | + */ | ||
| 38 | + @SuppressWarnings("EnhancedSwitchMigration") | ||
| 39 | + private IotMessageSerializer createSerializer(IotSerializeTypeEnum type) { | ||
| 40 | + switch (type) { | ||
| 41 | + case JSON: | ||
| 42 | + return new IotJsonSerializer(); | ||
| 43 | + case BINARY: | ||
| 44 | + return new IotBinarySerializer(); | ||
| 45 | + default: | ||
| 46 | + throw new IllegalArgumentException("未知的序列化类型:" + type); | ||
| 47 | + } | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * 获取序列化器 | ||
| 52 | + * | ||
| 53 | + * @param type 序列化类型 | ||
| 54 | + * @return 序列化器实例 | ||
| 55 | + */ | ||
| 56 | + public IotMessageSerializer get(IotSerializeTypeEnum type) { | ||
| 57 | + return serializerMap.get(type); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/binary/IotBinarySerializer.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.serialize.binary; | ||
| 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.enums.IotSerializeTypeEnum; | ||
| 7 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 8 | +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 9 | +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer; | ||
| 10 | +import io.vertx.core.buffer.Buffer; | ||
| 11 | +import lombok.extern.slf4j.Slf4j; | ||
| 12 | + | ||
| 13 | +import java.nio.charset.StandardCharsets; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * 二进制格式的消息序列化器 | ||
| 17 | + * | ||
| 18 | + * 二进制协议格式(所有数值使用大端序): | ||
| 19 | + * | ||
| 20 | + * <pre> | ||
| 21 | + * +--------+--------+--------+---------------------------+--------+--------+ | ||
| 22 | + * | 魔术字 | 版本号 | 消息类型| 消息长度(4 字节) | | ||
| 23 | + * +--------+--------+--------+---------------------------+--------+--------+ | ||
| 24 | + * | 消息 ID 长度(2 字节) | 消息 ID (变长字符串) | | ||
| 25 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ | ||
| 26 | + * | 方法名长度(2 字节) | 方法名(变长字符串) | | ||
| 27 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ | ||
| 28 | + * | 消息体数据(变长) | | ||
| 29 | + * +--------+--------+--------+--------+--------+--------+--------+--------+ | ||
| 30 | + * </pre> | ||
| 31 | + * | ||
| 32 | + * 消息体格式: | ||
| 33 | + * - 请求消息:params 数据(JSON) | ||
| 34 | + * - 响应消息:code (4字节) + msg 长度(2字节) + msg 字符串 + data 数据(JSON) | ||
| 35 | + * | ||
| 36 | + * @author 芋道源码 | ||
| 37 | + */ | ||
| 38 | +@Slf4j | ||
| 39 | +public class IotBinarySerializer implements IotMessageSerializer { | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 协议魔术字,用于协议识别 | ||
| 43 | + */ | ||
| 44 | + private static final byte MAGIC_NUMBER = (byte) 0x7E; | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * 协议版本号 | ||
| 48 | + */ | ||
| 49 | + private static final byte PROTOCOL_VERSION = (byte) 0x01; | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * 请求消息类型 | ||
| 53 | + */ | ||
| 54 | + private static final byte REQUEST = (byte) 0x01; | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * 响应消息类型 | ||
| 58 | + */ | ||
| 59 | + private static final byte RESPONSE = (byte) 0x02; | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 协议头部固定长度(魔术字 + 版本号 + 消息类型 + 消息长度) | ||
| 63 | + */ | ||
| 64 | + private static final int HEADER_FIXED_LENGTH = 7; | ||
| 65 | + | ||
| 66 | + /** | ||
| 67 | + * 最小消息长度(头部 + 消息ID长度 + 方法名长度) | ||
| 68 | + */ | ||
| 69 | + private static final int MIN_MESSAGE_LENGTH = HEADER_FIXED_LENGTH + 4; | ||
| 70 | + | ||
| 71 | + @Override | ||
| 72 | + public IotSerializeTypeEnum getType() { | ||
| 73 | + return IotSerializeTypeEnum.BINARY; | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + @Override | ||
| 77 | + public byte[] serialize(IotDeviceMessage message) { | ||
| 78 | + Assert.notNull(message, "消息不能为空"); | ||
| 79 | + Assert.notBlank(message.getMethod(), "消息方法不能为空"); | ||
| 80 | + try { | ||
| 81 | + // 1. 确定消息类型 | ||
| 82 | + byte messageType = determineMessageType(message); | ||
| 83 | + // 2. 构建消息体 | ||
| 84 | + byte[] bodyData = buildMessageBody(message, messageType); | ||
| 85 | + // 3. 构建完整消息 | ||
| 86 | + return buildCompleteMessage(message, messageType, bodyData); | ||
| 87 | + } catch (Exception e) { | ||
| 88 | + log.error("[encode][二进制消息编码失败,消息: {}]", message, e); | ||
| 89 | + throw new RuntimeException("二进制消息编码失败: " + e.getMessage(), e); | ||
| 90 | + } | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + @Override | ||
| 94 | + public IotDeviceMessage deserialize(byte[] bytes) { | ||
| 95 | + Assert.notNull(bytes, "待解码数据不能为空"); | ||
| 96 | + Assert.isTrue(bytes.length >= MIN_MESSAGE_LENGTH, "数据包长度不足"); | ||
| 97 | + try { | ||
| 98 | + Buffer buffer = Buffer.buffer(bytes); | ||
| 99 | + int index = 0; | ||
| 100 | + | ||
| 101 | + // 1. 验证魔术字 | ||
| 102 | + byte magic = buffer.getByte(index++); | ||
| 103 | + Assert.isTrue(magic == MAGIC_NUMBER, "无效的协议魔术字: " + magic); | ||
| 104 | + | ||
| 105 | + // 2. 验证版本号 | ||
| 106 | + byte version = buffer.getByte(index++); | ||
| 107 | + Assert.isTrue(version == PROTOCOL_VERSION, "不支持的协议版本: " + version); | ||
| 108 | + | ||
| 109 | + // 3. 读取消息类型 | ||
| 110 | + byte messageType = buffer.getByte(index++); | ||
| 111 | + Assert.isTrue(messageType == REQUEST || messageType == RESPONSE, "无效的消息类型: " + messageType); | ||
| 112 | + | ||
| 113 | + // 4. 读取消息长度 | ||
| 114 | + int messageLength = buffer.getInt(index); | ||
| 115 | + index += 4; | ||
| 116 | + Assert.isTrue(messageLength == buffer.length(), | ||
| 117 | + "消息长度不匹配,期望: " + messageLength + ", 实际: " + buffer.length()); | ||
| 118 | + | ||
| 119 | + // 5. 读取消息 ID | ||
| 120 | + short messageIdLength = buffer.getShort(index); | ||
| 121 | + index += 2; | ||
| 122 | + String messageId = buffer.getString(index, index + messageIdLength, StandardCharsets.UTF_8.name()); | ||
| 123 | + index += messageIdLength; | ||
| 124 | + | ||
| 125 | + // 6. 读取方法名 | ||
| 126 | + short methodLength = buffer.getShort(index); | ||
| 127 | + index += 2; | ||
| 128 | + String method = buffer.getString(index, index + methodLength, StandardCharsets.UTF_8.name()); | ||
| 129 | + index += methodLength; | ||
| 130 | + | ||
| 131 | + // 7. 解析消息体 | ||
| 132 | + return parseMessageBody(buffer, index, messageType, messageId, method); | ||
| 133 | + } catch (Exception e) { | ||
| 134 | + log.error("[decode][二进制消息解码失败,数据长度: {}]", bytes.length, e); | ||
| 135 | + throw new RuntimeException("二进制消息解码失败: " + e.getMessage(), e); | ||
| 136 | + } | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + /** | ||
| 140 | + * 快速检测是否为二进制格式 | ||
| 141 | + * | ||
| 142 | + * @param data 数据 | ||
| 143 | + * @return 是否为二进制格式 | ||
| 144 | + */ | ||
| 145 | + public static boolean isBinaryFormat(byte[] data) { | ||
| 146 | + return data != null && data.length >= 1 && data[0] == MAGIC_NUMBER; | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + private byte determineMessageType(IotDeviceMessage message) { | ||
| 150 | + if (message.getCode() != null) { | ||
| 151 | + return RESPONSE; | ||
| 152 | + } | ||
| 153 | + return REQUEST; | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + private byte[] buildMessageBody(IotDeviceMessage message, byte messageType) { | ||
| 157 | + Buffer bodyBuffer = Buffer.buffer(); | ||
| 158 | + if (messageType == RESPONSE) { | ||
| 159 | + // code | ||
| 160 | + bodyBuffer.appendInt(message.getCode() != null ? message.getCode() : 0); | ||
| 161 | + // msg | ||
| 162 | + String msg = message.getMsg() != null ? message.getMsg() : ""; | ||
| 163 | + byte[] msgBytes = StrUtil.utf8Bytes(msg); | ||
| 164 | + bodyBuffer.appendShort((short) msgBytes.length); | ||
| 165 | + bodyBuffer.appendBytes(msgBytes); | ||
| 166 | + // data | ||
| 167 | + if (message.getData() != null) { | ||
| 168 | + bodyBuffer.appendBytes(JsonUtils.toJsonByte(message.getData())); | ||
| 169 | + } | ||
| 170 | + } else { | ||
| 171 | + // 请求消息只处理 params 参数 | ||
| 172 | + if (message.getParams() != null) { | ||
| 173 | + bodyBuffer.appendBytes(JsonUtils.toJsonByte(message.getParams())); | ||
| 174 | + } | ||
| 175 | + } | ||
| 176 | + return bodyBuffer.getBytes(); | ||
| 177 | + } | ||
| 178 | + | ||
| 179 | + private byte[] buildCompleteMessage(IotDeviceMessage message, byte messageType, byte[] bodyData) { | ||
| 180 | + Buffer buffer = Buffer.buffer(); | ||
| 181 | + // 1. 写入协议头部 | ||
| 182 | + buffer.appendByte(MAGIC_NUMBER); | ||
| 183 | + buffer.appendByte(PROTOCOL_VERSION); | ||
| 184 | + buffer.appendByte(messageType); | ||
| 185 | + // 2. 预留消息长度位置 | ||
| 186 | + int lengthPosition = buffer.length(); | ||
| 187 | + buffer.appendInt(0); | ||
| 188 | + // 3. 写入消息 ID | ||
| 189 | + String messageId = StrUtil.isNotBlank(message.getRequestId()) ? message.getRequestId() | ||
| 190 | + : IotDeviceMessageUtils.generateMessageId(); | ||
| 191 | + byte[] messageIdBytes = StrUtil.utf8Bytes(messageId); | ||
| 192 | + buffer.appendShort((short) messageIdBytes.length); | ||
| 193 | + buffer.appendBytes(messageIdBytes); | ||
| 194 | + // 4. 写入方法名 | ||
| 195 | + byte[] methodBytes = StrUtil.utf8Bytes(message.getMethod()); | ||
| 196 | + buffer.appendShort((short) methodBytes.length); | ||
| 197 | + buffer.appendBytes(methodBytes); | ||
| 198 | + // 5. 写入消息体 | ||
| 199 | + buffer.appendBytes(bodyData); | ||
| 200 | + // 6. 更新消息长度 | ||
| 201 | + buffer.setInt(lengthPosition, buffer.length()); | ||
| 202 | + return buffer.getBytes(); | ||
| 203 | + } | ||
| 204 | + | ||
| 205 | + private IotDeviceMessage parseMessageBody(Buffer buffer, int startIndex, byte messageType, | ||
| 206 | + String messageId, String method) { | ||
| 207 | + if (startIndex >= buffer.length()) { | ||
| 208 | + return IotDeviceMessage.of(messageId, method, null, null, null, null); | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | + if (messageType == RESPONSE) { | ||
| 212 | + return parseResponseMessage(buffer, startIndex, messageId, method); | ||
| 213 | + } else { | ||
| 214 | + Object payload = parseJsonData(buffer, startIndex, buffer.length()); | ||
| 215 | + return IotDeviceMessage.of(messageId, method, payload, null, null, null); | ||
| 216 | + } | ||
| 217 | + } | ||
| 218 | + | ||
| 219 | + private IotDeviceMessage parseResponseMessage(Buffer buffer, int startIndex, String messageId, String method) { | ||
| 220 | + int index = startIndex; | ||
| 221 | + | ||
| 222 | + // 1. 读取响应码 | ||
| 223 | + Integer code = buffer.getInt(index); | ||
| 224 | + index += 4; | ||
| 225 | + | ||
| 226 | + // 2. 读取响应消息 | ||
| 227 | + short msgLength = buffer.getShort(index); | ||
| 228 | + index += 2; | ||
| 229 | + String msg = msgLength > 0 ? buffer.getString(index, index + msgLength, StandardCharsets.UTF_8.name()) : null; | ||
| 230 | + index += msgLength; | ||
| 231 | + | ||
| 232 | + // 3. 读取响应数据 | ||
| 233 | + Object data = null; | ||
| 234 | + if (index < buffer.length()) { | ||
| 235 | + data = parseJsonData(buffer, index, buffer.length()); | ||
| 236 | + } | ||
| 237 | + | ||
| 238 | + return IotDeviceMessage.of(messageId, method, null, data, code, msg); | ||
| 239 | + } | ||
| 240 | + | ||
| 241 | + private Object parseJsonData(Buffer buffer, int startIndex, int endIndex) { | ||
| 242 | + if (startIndex >= endIndex) { | ||
| 243 | + return null; | ||
| 244 | + } | ||
| 245 | + try { | ||
| 246 | + String jsonStr = buffer.getString(startIndex, endIndex, StandardCharsets.UTF_8.name()); | ||
| 247 | + return JsonUtils.parseObject(jsonStr, Object.class); | ||
| 248 | + } catch (Exception e) { | ||
| 249 | + log.warn("[parseJsonData][JSON 解析失败,返回原始字符串]", e); | ||
| 250 | + return buffer.getString(startIndex, endIndex, StandardCharsets.UTF_8.name()); | ||
| 251 | + } | ||
| 252 | + } | ||
| 253 | + | ||
| 254 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/json/IotJsonSerializer.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.serialize.json; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 4 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 5 | +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.gateway.serialize.IotMessageSerializer; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * JSON 格式的消息序列化器 | ||
| 11 | + * | ||
| 12 | + * 直接使用 JsonUtils 序列化/反序列化 {@link IotDeviceMessage},不包装额外字段 | ||
| 13 | + * | ||
| 14 | + * @author 芋道源码 | ||
| 15 | + */ | ||
| 16 | +public class IotJsonSerializer implements IotMessageSerializer { | ||
| 17 | + | ||
| 18 | + @Override | ||
| 19 | + public IotSerializeTypeEnum getType() { | ||
| 20 | + return IotSerializeTypeEnum.JSON; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + public byte[] serialize(IotDeviceMessage message) { | ||
| 25 | + Assert.notNull(message, "消息不能为空"); | ||
| 26 | + return JsonUtils.toJsonByte(message); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + @Override | ||
| 30 | + public IotDeviceMessage deserialize(byte[] bytes) { | ||
| 31 | + Assert.notNull(bytes, "待解码数据不能为空"); | ||
| 32 | + IotDeviceMessage message = JsonUtils.parseObject(bytes, IotDeviceMessage.class); | ||
| 33 | + Assert.notNull(message, "消息解码失败"); | ||
| 34 | + return message; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application.yaml
| @@ -42,14 +42,20 @@ yudao: | @@ -42,14 +42,20 @@ yudao: | ||
| 42 | secret: yudaoIotGatewayTokenSecret123456789 # Token 密钥,至少32位 | 42 | secret: yudaoIotGatewayTokenSecret123456789 # Token 密钥,至少32位 |
| 43 | expiration: 7d | 43 | expiration: 7d |
| 44 | 44 | ||
| 45 | - # 协议配置 | ||
| 46 | - protocol: | 45 | + # 协议实例列表(新版配置方式) |
| 46 | + protocols: | ||
| 47 | # ==================================== | 47 | # ==================================== |
| 48 | # 针对引入的 HTTP 组件的配置 | 48 | # 针对引入的 HTTP 组件的配置 |
| 49 | # ==================================== | 49 | # ==================================== |
| 50 | - http: | 50 | + - id: http-json |
| 51 | + type: http | ||
| 52 | + port: 8092 | ||
| 51 | enabled: true | 53 | enabled: true |
| 52 | - server-port: 8092 | 54 | + http: |
| 55 | + ssl-enabled: false | ||
| 56 | + | ||
| 57 | + # 协议配置(旧版,保持兼容) | ||
| 58 | + protocol: | ||
| 53 | # ==================================== | 59 | # ==================================== |
| 54 | # 针对引入的 EMQX 组件的配置 | 60 | # 针对引入的 EMQX 组件的配置 |
| 55 | # ==================================== | 61 | # ==================================== |