diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
index c2bf96d..e9800c3 100644
--- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
+++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
@@ -2,8 +2,6 @@ package cn.iocoder.yudao.module.iot.gateway.config;
import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolManager;
-import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapDownstreamSubscriber;
-import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapUpstreamProtocol;
import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxAuthEventProtocol;
import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxDownstreamSubscriber;
import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol;
@@ -11,12 +9,7 @@ import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscr
import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager;
import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstreamHandler;
-import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketDownstreamSubscriber;
-import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketUpstreamProtocol;
-import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager;
-import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.router.IotWebSocketDownstreamHandler;
import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager;
-import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
import io.vertx.core.Vertx;
import lombok.extern.slf4j.Slf4j;
@@ -111,63 +104,4 @@ public class IotGatewayConfiguration {
}
- /**
- * IoT 网关 CoAP 协议配置类
- */
- @Configuration
- @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.coap", name = "enabled", havingValue = "true")
- @Slf4j
- public static class CoapProtocolConfiguration {
-
- @Bean
- public IotCoapUpstreamProtocol iotCoapUpstreamProtocol(IotGatewayProperties gatewayProperties) {
- return new IotCoapUpstreamProtocol(gatewayProperties.getProtocol().getCoap());
- }
-
- @Bean
- public IotCoapDownstreamSubscriber iotCoapDownstreamSubscriber(IotCoapUpstreamProtocol coapUpstreamProtocol,
- IotMessageBus messageBus) {
- return new IotCoapDownstreamSubscriber(coapUpstreamProtocol, messageBus);
- }
-
- }
-
- /**
- * IoT 网关 WebSocket 协议配置类
- */
- @Configuration
- @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.websocket", name = "enabled", havingValue = "true")
- @Slf4j
- public static class WebSocketProtocolConfiguration {
-
- @Bean(name = "websocketVertx", destroyMethod = "close")
- public Vertx websocketVertx() {
- return Vertx.vertx();
- }
-
- @Bean
- public IotWebSocketUpstreamProtocol iotWebSocketUpstreamProtocol(IotGatewayProperties gatewayProperties,
- IotDeviceService deviceService,
- IotDeviceMessageService messageService,
- IotWebSocketConnectionManager connectionManager,
- @Qualifier("websocketVertx") Vertx websocketVertx) {
- return new IotWebSocketUpstreamProtocol(gatewayProperties.getProtocol().getWebsocket(),
- deviceService, messageService, connectionManager, websocketVertx);
- }
-
- @Bean
- public IotWebSocketDownstreamHandler iotWebSocketDownstreamHandler(IotDeviceMessageService messageService,
- IotWebSocketConnectionManager connectionManager) {
- return new IotWebSocketDownstreamHandler(messageService, connectionManager);
- }
-
- @Bean
- public IotWebSocketDownstreamSubscriber iotWebSocketDownstreamSubscriber(IotWebSocketUpstreamProtocol protocolHandler,
- IotWebSocketDownstreamHandler downstreamHandler,
- IotMessageBus messageBus) {
- return new IotWebSocketDownstreamSubscriber(protocolHandler, downstreamHandler, messageBus);
- }
-
- }
-
}
diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java
index 1f0dccd..dc5d545 100644
--- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java
+++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java
@@ -1,9 +1,11 @@
package cn.iocoder.yudao.module.iot.gateway.config;
import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
+import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapConfig;
import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpConfig;
import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpConfig;
import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpConfig;
+import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketConfig;
import io.vertx.core.net.KeyCertOptions;
import io.vertx.core.net.TrustOptions;
import jakarta.validation.Valid;
@@ -90,16 +92,6 @@ public class IotGatewayProperties {
*/
private MqttProperties mqtt;
- /**
- * CoAP 组件配置
- */
- private CoapProperties coap;
-
- /**
- * WebSocket 组件配置
- */
- private WebSocketProperties websocket;
-
}
@Data
@@ -344,93 +336,6 @@ public class IotGatewayProperties {
}
- @Data
- public static class CoapProperties {
-
- /**
- * 是否开启
- */
- @NotNull(message = "是否开启不能为空")
- private Boolean enabled;
-
- /**
- * 服务端口(CoAP 默认端口 5683)
- */
- @NotNull(message = "服务端口不能为空")
- private Integer port = 5683;
-
- /**
- * 最大消息大小(字节)
- */
- @NotNull(message = "最大消息大小不能为空")
- private Integer maxMessageSize = 1024;
-
- /**
- * ACK 超时时间(毫秒)
- */
- @NotNull(message = "ACK 超时时间不能为空")
- private Integer ackTimeout = 2000;
-
- /**
- * 最大重传次数
- */
- @NotNull(message = "最大重传次数不能为空")
- private Integer maxRetransmit = 4;
-
- }
-
- @Data
- public static class WebSocketProperties {
-
- /**
- * 是否开启
- */
- @NotNull(message = "是否开启不能为空")
- private Boolean enabled;
-
- /**
- * 服务器端口(默认:8094)
- */
- private Integer port = 8094;
-
- /**
- * WebSocket 路径(默认:/ws)
- */
- @NotEmpty(message = "WebSocket 路径不能为空")
- private String path = "/ws";
-
- /**
- * 最大消息大小(字节,默认 64KB)
- */
- private Integer maxMessageSize = 65536;
-
- /**
- * 最大帧大小(字节,默认 64KB)
- */
- private Integer maxFrameSize = 65536;
-
- /**
- * 空闲超时时间(秒,默认 60)
- */
- private Integer idleTimeoutSeconds = 60;
-
- /**
- * 是否启用 SSL(wss://)
- */
- private Boolean sslEnabled = false;
-
- /**
- * SSL 证书路径
- */
- private String sslCertPath;
-
- /**
- * SSL 私钥路径
- */
- private String sslKeyPath;
-
- }
-
// TODO @AI:【暂时忽略】改成 ProtocolProperties
/**
* 协议实例配置
@@ -489,6 +394,18 @@ public class IotGatewayProperties {
@Valid
private IotUdpConfig udp;
+ /**
+ * CoAP 协议配置
+ */
+ @Valid
+ private IotCoapConfig coap;
+
+ /**
+ * WebSocket 协议配置
+ */
+ @Valid
+ private IotWebSocketConfig websocket;
+
}
}
diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocolManager.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocolManager.java
index c13b44e..ed60897 100644
--- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocolManager.java
+++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocolManager.java
@@ -4,9 +4,11 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
+import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapProtocol;
import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpProtocol;
import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpProtocol;
import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpProtocol;
+import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketUpstreamProtocol;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.SmartLifecycle;
@@ -100,6 +102,10 @@ public class IotProtocolManager implements SmartLifecycle {
return createTcpProtocol(config);
case UDP:
return createUdpProtocol(config);
+ case COAP:
+ return createCoapProtocol(config);
+ case WEBSOCKET:
+ return createWebSocketProtocol(config);
default:
throw new IllegalArgumentException(String.format(
"[createProtocol][协议实例 %s 的协议类型 %s 暂不支持]", config.getId(), protocolType));
@@ -136,4 +142,24 @@ public class IotProtocolManager implements SmartLifecycle {
return new IotUdpProtocol(config);
}
+ /**
+ * 创建 CoAP 协议实例
+ *
+ * @param config 协议实例配置
+ * @return CoAP 协议实例
+ */
+ private IotCoapProtocol createCoapProtocol(IotGatewayProperties.ProtocolInstanceProperties config) {
+ return new IotCoapProtocol(config);
+ }
+
+ /**
+ * 创建 WebSocket 协议实例
+ *
+ * @param config 协议实例配置
+ * @return WebSocket 协议实例
+ */
+ private IotWebSocketUpstreamProtocol createWebSocketProtocol(IotGatewayProperties.ProtocolInstanceProperties config) {
+ return new IotWebSocketUpstreamProtocol(config);
+ }
+
}
diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapConfig.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapConfig.java
new file mode 100644
index 0000000..45fe300
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapConfig.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.iot.gateway.protocol.coap;
+
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * IoT CoAP 协议配置
+ *
+ * @author 芋道源码
+ */
+@Data
+public class IotCoapConfig {
+
+ /**
+ * 最大消息大小(字节)
+ */
+ @NotNull(message = "最大消息大小不能为空")
+ @Min(value = 64, message = "最大消息大小必须大于 64 字节")
+ private Integer maxMessageSize = 1024;
+
+ /**
+ * ACK 超时时间(毫秒)
+ */
+ @NotNull(message = "ACK 超时时间不能为空")
+ @Min(value = 100, message = "ACK 超时时间必须大于 100 毫秒")
+ private Integer ackTimeoutMs = 2000;
+
+ /**
+ * 最大重传次数
+ */
+ @NotNull(message = "最大重传次数不能为空")
+ @Min(value = 0, message = "最大重传次数必须大于等于 0")
+ private Integer maxRetransmit = 4;
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapProtocol.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapProtocol.java
new file mode 100644
index 0000000..2749bd2
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapProtocol.java
@@ -0,0 +1,173 @@
+package cn.iocoder.yudao.module.iot.gateway.protocol.coap;
+
+import cn.hutool.extra.spring.SpringUtil;
+import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
+import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
+import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
+import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolInstanceProperties;
+import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;
+import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.downstream.IotCoapDownstreamSubscriber;
+import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstrem.IotCoapAuthHandler;
+import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstrem.IotCoapAuthResource;
+import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstrem.IotCoapRegisterHandler;
+import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstrem.IotCoapRegisterResource;
+import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstrem.IotCoapRegisterSubHandler;
+import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstrem.IotCoapRegisterSubResource;
+import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstrem.IotCoapUpstreamHandler;
+import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstrem.IotCoapUpstreamTopicResource;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.californium.core.CoapResource;
+import org.eclipse.californium.core.CoapServer;
+import org.eclipse.californium.core.config.CoapConfig;
+import org.eclipse.californium.elements.config.Configuration;
+import org.springframework.util.Assert;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * IoT CoAP 协议实现
+ *
+ * 基于 Eclipse Californium 实现,支持:
+ * 1. 认证:POST /auth
+ * 2. 设备动态注册:POST /auth/register/device
+ * 3. 子设备动态注册:POST /auth/register/sub-device/{productKey}/{deviceName}
+ * 4. 属性上报:POST /topic/sys/{productKey}/{deviceName}/thing/property/post
+ * 5. 事件上报:POST /topic/sys/{productKey}/{deviceName}/thing/event/post
+ *
+ * @author 芋道源码
+ */
+@Slf4j
+public class IotCoapProtocol implements IotProtocol {
+
+ /**
+ * 协议配置
+ */
+ private final ProtocolInstanceProperties properties;
+ /**
+ * 服务器 ID(用于消息追踪,全局唯一)
+ */
+ @Getter
+ private final String serverId;
+
+ /**
+ * 运行状态
+ */
+ @Getter
+ private volatile boolean running = false;
+
+ /**
+ * CoAP 服务器
+ */
+ private CoapServer coapServer;
+
+ /**
+ * 下行消息订阅者
+ */
+ private final IotCoapDownstreamSubscriber downstreamSubscriber;
+
+ public IotCoapProtocol(ProtocolInstanceProperties properties) {
+ IotCoapConfig coapConfig = properties.getCoap();
+ Assert.notNull(coapConfig, "CoAP 协议配置(coap)不能为空");
+ this.properties = properties;
+ this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());
+
+ // 初始化下行消息订阅者
+ IotMessageBus messageBus = SpringUtil.getBean(IotMessageBus.class);
+ this.downstreamSubscriber = new IotCoapDownstreamSubscriber(this, messageBus);
+ }
+
+ @Override
+ public String getId() {
+ return properties.getId();
+ }
+
+ @Override
+ public IotProtocolTypeEnum getType() {
+ return IotProtocolTypeEnum.COAP;
+ }
+
+ @Override
+ public void start() {
+ if (running) {
+ log.warn("[start][IoT CoAP 协议 {} 已经在运行中]", getId());
+ return;
+ }
+
+ IotCoapConfig coapConfig = properties.getCoap();
+ try {
+ // 1.1 创建 CoAP 配置
+ Configuration config = Configuration.createStandardWithoutFile();
+ config.set(CoapConfig.COAP_PORT, properties.getPort());
+ config.set(CoapConfig.MAX_MESSAGE_SIZE, coapConfig.getMaxMessageSize());
+ config.set(CoapConfig.ACK_TIMEOUT, coapConfig.getAckTimeoutMs(), TimeUnit.MILLISECONDS);
+ config.set(CoapConfig.MAX_RETRANSMIT, coapConfig.getMaxRetransmit());
+ // 1.2 创建 CoAP 服务器
+ coapServer = new CoapServer(config);
+
+ // 2.1 添加 /auth 认证资源
+ IotCoapAuthHandler authHandler = new IotCoapAuthHandler(serverId);
+ IotCoapAuthResource authResource = new IotCoapAuthResource(authHandler);
+ coapServer.add(authResource);
+ // 2.2 添加 /auth/register/device 设备动态注册资源(一型一密)
+ IotCoapRegisterHandler registerHandler = new IotCoapRegisterHandler();
+ IotCoapRegisterResource registerResource = new IotCoapRegisterResource(registerHandler);
+ // 2.3 添加 /auth/register/sub-device/{productKey}/{deviceName} 子设备动态注册资源
+ IotCoapRegisterSubHandler registerSubHandler = new IotCoapRegisterSubHandler();
+ IotCoapRegisterSubResource registerSubResource = new IotCoapRegisterSubResource(registerSubHandler);
+ authResource.add(new CoapResource("register") {{
+ add(registerResource);
+ add(registerSubResource);
+ }});
+ // 2.4 添加 /topic 根资源(用于上行消息)
+ IotCoapUpstreamHandler upstreamHandler = new IotCoapUpstreamHandler(serverId);
+ IotCoapUpstreamTopicResource topicResource = new IotCoapUpstreamTopicResource(serverId, upstreamHandler);
+ coapServer.add(topicResource);
+
+ // 3. 启动服务器
+ coapServer.start();
+ running = true;
+ log.info("[start][IoT CoAP 协议 {} 启动成功,端口:{},serverId:{}]",
+ getId(), properties.getPort(), serverId);
+
+ // 4. 启动下行消息订阅者
+ this.downstreamSubscriber.start();
+ } catch (Exception e) {
+ log.error("[start][IoT CoAP 协议 {} 启动失败]", getId(), e);
+ if (coapServer != null) {
+ coapServer.destroy();
+ coapServer = null;
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void stop() {
+ if (!running) {
+ return;
+ }
+ // 1. 停止下行消息订阅者
+ try {
+ downstreamSubscriber.stop();
+ log.info("[stop][IoT CoAP 协议 {} 下行消息订阅者已停止]", getId());
+ } catch (Exception e) {
+ log.error("[stop][IoT CoAP 协议 {} 下行消息订阅者停止失败]", getId(), e);
+ }
+
+ // 2. 关闭 CoAP 服务器
+ if (coapServer != null) {
+ try {
+ coapServer.stop();
+ coapServer.destroy();
+ coapServer = null;
+ log.info("[stop][IoT CoAP 协议 {} 服务器已停止]", getId());
+ } catch (Exception e) {
+ log.error("[stop][IoT CoAP 协议 {} 服务器停止失败]", getId(), e);
+ }
+ }
+ running = false;
+ log.info("[stop][IoT CoAP 协议 {} 已停止]", getId());
+ }
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapUpstreamProtocol.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapUpstreamProtocol.java
deleted file mode 100644
index 771a33a..0000000
--- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapUpstreamProtocol.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package cn.iocoder.yudao.module.iot.gateway.protocol.coap;
-
-import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
-import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
-import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
-import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;
-import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapAuthHandler;
-import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapAuthResource;
-import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapRegisterHandler;
-import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapRegisterResource;
-import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapUpstreamTopicResource;
-import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapUpstreamHandler;
-import jakarta.annotation.PostConstruct;
-import jakarta.annotation.PreDestroy;
-import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-import org.eclipse.californium.core.CoapResource;
-import org.eclipse.californium.core.CoapServer;
-import org.eclipse.californium.core.config.CoapConfig;
-import org.eclipse.californium.elements.config.Configuration;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * IoT 网关 CoAP 协议:接收设备上行消息
- *
- * 基于 Eclipse Californium 实现,支持:
- * 1. 认证:POST /auth
- * 2. 属性上报:POST /topic/sys/{productKey}/{deviceName}/thing/property/post
- * 3. 事件上报:POST /topic/sys/{productKey}/{deviceName}/thing/event/post
- *
- * @author 芋道源码
- */
-@Slf4j
-public class IotCoapUpstreamProtocol implements IotProtocol {
-
- private static final String ID = "coap";
-
- private final IotGatewayProperties.CoapProperties coapProperties;
-
- private CoapServer coapServer;
-
- @Getter
- private final String serverId;
-
- private volatile boolean running = false;
-
- public IotCoapUpstreamProtocol(IotGatewayProperties.CoapProperties coapProperties) {
- this.coapProperties = coapProperties;
- this.serverId = IotDeviceMessageUtils.generateServerId(coapProperties.getPort());
- }
-
- @Override
- public String getId() {
- return ID;
- }
-
- @Override
- public IotProtocolTypeEnum getType() {
- return IotProtocolTypeEnum.COAP;
- }
-
- @Override
- @PostConstruct
- public void start() {
- try {
- // 1.1 创建网络配置(Californium 3.x API)
- Configuration config = Configuration.createStandardWithoutFile();
- config.set(CoapConfig.COAP_PORT, coapProperties.getPort());
- config.set(CoapConfig.MAX_MESSAGE_SIZE, coapProperties.getMaxMessageSize());
- config.set(CoapConfig.ACK_TIMEOUT, coapProperties.getAckTimeout(), TimeUnit.MILLISECONDS);
- config.set(CoapConfig.MAX_RETRANSMIT, coapProperties.getMaxRetransmit());
- // 1.2 创建 CoAP 服务器
- coapServer = new CoapServer(config);
-
- // 2.1 添加 /auth 认证资源
- IotCoapAuthHandler authHandler = new IotCoapAuthHandler();
- IotCoapAuthResource authResource = new IotCoapAuthResource(this, authHandler);
- coapServer.add(authResource);
- // 2.2 添加 /auth/register/device 设备动态注册资源(一型一密)
- IotCoapRegisterHandler registerHandler = new IotCoapRegisterHandler();
- IotCoapRegisterResource registerResource = new IotCoapRegisterResource(registerHandler);
- authResource.add(new CoapResource("register") {{
- add(registerResource);
- }});
- // 2.3 添加 /topic 根资源(用于上行消息)
- IotCoapUpstreamHandler upstreamHandler = new IotCoapUpstreamHandler();
- IotCoapUpstreamTopicResource topicResource = new IotCoapUpstreamTopicResource(this, upstreamHandler);
- coapServer.add(topicResource);
-
- // 3. 启动服务器
- coapServer.start();
- running = true;
- log.info("[start][IoT 网关 CoAP 协议启动成功,端口:{},资源:/auth, /auth/register/device, /topic]", coapProperties.getPort());
- } catch (Exception e) {
- log.error("[start][IoT 网关 CoAP 协议启动失败]", e);
- throw e;
- }
- }
-
- @Override
- @PreDestroy
- public void stop() {
- if (coapServer != null) {
- try {
- coapServer.stop();
- running = false;
- log.info("[stop][IoT 网关 CoAP 协议已停止]");
- } catch (Exception e) {
- log.error("[stop][IoT 网关 CoAP 协议停止失败]", e);
- }
- }
- }
-
- @Override
- public boolean isRunning() {
- return running;
- }
-
-}
diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapDownstreamSubscriber.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/downstream/IotCoapDownstreamSubscriber.java
index 8003602..188d2e6 100644
--- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapDownstreamSubscriber.java
+++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/downstream/IotCoapDownstreamSubscriber.java
@@ -1,8 +1,9 @@
-package cn.iocoder.yudao.module.iot.gateway.protocol.coap;
+package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.downstream;
import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolDownstreamSubscriber;
+import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapProtocol;
import lombok.extern.slf4j.Slf4j;
/**
@@ -13,7 +14,7 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class IotCoapDownstreamSubscriber extends IotProtocolDownstreamSubscriber {
- public IotCoapDownstreamSubscriber(IotCoapUpstreamProtocol protocol, IotMessageBus messageBus) {
+ public IotCoapDownstreamSubscriber(IotCoapProtocol protocol, IotMessageBus messageBus) {
super(protocol, messageBus);
}
diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstrem/IotCoapAbstractHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstrem/IotCoapAbstractHandler.java
new file mode 100644
index 0000000..cda8466
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstrem/IotCoapAbstractHandler.java
@@ -0,0 +1,186 @@
+package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstrem;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
+import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.californium.core.coap.CoAP;
+import org.eclipse.californium.core.coap.MediaTypeRegistry;
+import org.eclipse.californium.core.coap.Option;
+import org.eclipse.californium.core.server.resources.CoapExchange;
+
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+
+/**
+ * IoT 网关 CoAP 协议的处理器抽象基类:提供通用的前置处理(认证)、请求解析、响应处理、全局的异常捕获等
+ *
+ * @author 芋道源码
+ */
+@Slf4j
+public abstract class IotCoapAbstractHandler {
+
+ /**
+ * 自定义 CoAP Option 编号,用于携带 Token
+ *
+ * CoAP Option 范围 2048-65535 属于实验/自定义范围
+ */
+ public static final int OPTION_TOKEN = 2088;
+
+ private final IotDeviceTokenService deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class);
+
+ /**
+ * 处理 CoAP 请求(模板方法)
+ *
+ * @param exchange CoAP 交换对象
+ */
+ public final void handle(CoapExchange exchange) {
+ try {
+ // 1. 前置处理
+ beforeHandle(exchange);
+
+ // 2. 执行业务逻辑
+ CommonResult