Commit 4319220750a142bf62c5ae42baa99472708be047

Authored by YunaiV
1 parent e275cb57

feat(iot):【协议改造】modbus-tcp:改造新的协议方式

Showing 11 changed files with 300 additions and 323 deletions
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
1 1 package cn.iocoder.yudao.module.iot.gateway.config;
2 2  
3   -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 3 import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolManager;
5   -import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxAuthEventProtocol;
6   -import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxDownstreamSubscriber;
7   -import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol;
8   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.*;
9   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.client.IotModbusTcpClient;
10   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.codec.IotModbusDataConverter;
11   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConfigCacheService;
12   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConnectionManager;
13   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpPollScheduler;
14   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router.IotModbusTcpDownstreamHandler;
15   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router.IotModbusTcpUpstreamHandler;
16   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber;
17   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
18   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager;
19   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstreamHandler;
20   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.IotModbusTcpDownstreamSubscriber;
21   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.IotModbusTcpUpstreamProtocol;
22   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.client.IotModbusTcpClient;
23   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.codec.IotModbusDataConverter;
24   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConfigCacheService;
25   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConnectionManager;
26   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpPollScheduler;
27   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router.IotModbusTcpDownstreamHandler;
28   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router.IotModbusTcpUpstreamHandler;
29 4 import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager;
30   -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
31   -import io.vertx.core.Vertx;
32   -import lombok.extern.slf4j.Slf4j;
33   -import org.redisson.api.RedissonClient;
34   -import org.springframework.beans.factory.annotation.Qualifier;
35   -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
36   -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
37   -import io.vertx.core.Vertx;
38   -import lombok.extern.slf4j.Slf4j;
39   -import org.redisson.api.RedissonClient;
40   -import org.springframework.beans.factory.annotation.Qualifier;
41   -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
42 5 import org.springframework.boot.context.properties.EnableConfigurationProperties;
43 6 import org.springframework.context.annotation.Bean;
44 7 import org.springframework.context.annotation.Configuration;
45 8  
  9 +/**
  10 + * IoT 网关配置类
  11 + *
  12 + * @author 芋道源码
  13 + */
46 14 @Configuration
47 15 @EnableConfigurationProperties(IotGatewayProperties.class)
48 16 public class IotGatewayConfiguration {
... ... @@ -57,86 +25,4 @@ public class IotGatewayConfiguration {
57 25 return new IotProtocolManager(gatewayProperties);
58 26 }
59 27  
60   - /**
61   - * IoT 网关 Modbus TCP 协议配置类
62   - */
63   - @Configuration
64   - @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.modbus-tcp", name = "enabled", havingValue = "true")
65   - @Slf4j
66   - public static class ModbusTcpProtocolConfiguration {
67   -
68   - @Bean(name = "modbusTcpVertx", destroyMethod = "close")
69   - public Vertx modbusTcpVertx() {
70   - return Vertx.vertx();
71   - }
72   -
73   - @Bean
74   - public IotModbusDataConverter iotModbusDataConverter() {
75   - return new IotModbusDataConverter();
76   - }
77   -
78   - @Bean
79   - public IotModbusTcpClient iotModbusTcpClient() {
80   - return new IotModbusTcpClient();
81   - }
82   -
83   - @Bean
84   - public IotModbusTcpConnectionManager iotModbusTcpConnectionManager(
85   - RedissonClient redissonClient,
86   - @Qualifier("modbusTcpVertx") Vertx modbusTcpVertx) {
87   - return new IotModbusTcpConnectionManager(redissonClient, modbusTcpVertx);
88   - }
89   -
90   - @Bean
91   - public IotModbusTcpConfigCacheService iotModbusTcpConfigCacheService(
92   - cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi deviceApi) {
93   - return new IotModbusTcpConfigCacheService(deviceApi);
94   - }
95   -
96   - @Bean
97   - public IotModbusTcpUpstreamHandler iotModbusTcpUpstreamHandler(
98   - IotDeviceMessageService messageService,
99   - IotModbusDataConverter dataConverter) {
100   - return new IotModbusTcpUpstreamHandler(messageService, dataConverter);
101   - }
102   -
103   - @Bean
104   - public IotModbusTcpPollScheduler iotModbusTcpPollScheduler(
105   - @Qualifier("modbusTcpVertx") Vertx modbusTcpVertx,
106   - IotModbusTcpConnectionManager connectionManager,
107   - IotModbusTcpClient modbusClient,
108   - IotModbusTcpUpstreamHandler upstreamHandler) {
109   - return new IotModbusTcpPollScheduler(modbusTcpVertx, connectionManager, modbusClient, upstreamHandler);
110   - }
111   -
112   - @Bean
113   - public IotModbusTcpDownstreamHandler iotModbusTcpDownstreamHandler(
114   - IotModbusTcpConnectionManager connectionManager,
115   - IotModbusTcpClient modbusClient,
116   - IotModbusDataConverter dataConverter,
117   - IotModbusTcpConfigCacheService configCacheService) {
118   - return new IotModbusTcpDownstreamHandler(connectionManager, modbusClient, dataConverter, configCacheService);
119   - }
120   -
121   - @Bean
122   - public IotModbusTcpUpstreamProtocol iotModbusTcpUpstreamProtocol(IotGatewayProperties gatewayProperties,
123   - IotDeviceMessageService messageService,
124   - IotModbusTcpConnectionManager connectionManager,
125   - IotModbusTcpPollScheduler pollScheduler,
126   - IotModbusTcpConfigCacheService configCacheService,
127   - IotModbusTcpUpstreamHandler upstreamHandler,
128   - @Qualifier("modbusTcpVertx") Vertx modbusTcpVertx) {
129   - return new IotModbusTcpUpstreamProtocol(gatewayProperties.getProtocol().getModbusTcp(),
130   - messageService, connectionManager, pollScheduler, configCacheService, upstreamHandler, modbusTcpVertx);
131   - }
132   -
133   - @Bean
134   - public IotModbusTcpDownstreamSubscriber iotModbusTcpDownstreamSubscriber(IotModbusTcpUpstreamProtocol upstreamProtocol,
135   - IotModbusTcpDownstreamHandler downstreamHandler,
136   - IotMessageBus messageBus) {
137   - return new IotModbusTcpDownstreamSubscriber(upstreamProtocol, downstreamHandler, messageBus);
138   - }
139   -
140   - }
141   -
142 28 }
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java
... ... @@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
4 4 import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapConfig;
5 5 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxConfig;
6 6 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpConfig;
  7 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.IotModbusTcpConfig;
7 8 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttConfig;
8 9 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpConfig;
9 10 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpConfig;
... ... @@ -166,6 +167,12 @@ public class IotGatewayProperties {
166 167 @Valid
167 168 private IotEmqxConfig emqx;
168 169  
  170 + /**
  171 + * Modbus TCP 协议配置
  172 + */
  173 + @Valid
  174 + private IotModbusTcpConfig modbusTcp;
  175 +
169 176 }
170 177  
171 178 /**
... ... @@ -216,31 +223,4 @@ public class IotGatewayProperties {
216 223  
217 224 }
218 225  
219   - @Data
220   - public static class ProtocolProperties {
221   -
222   - /**
223   - * Modbus TCP 组件配置
224   - */
225   - private ModbusTcpProperties modbusTcp;
226   -
227   - }
228   -
229   - @Data
230   - public static class ModbusTcpProperties {
231   -
232   - /**
233   - * 是否开启
234   - */
235   - @NotNull(message = "是否开启不能为空")
236   - private Boolean enabled;
237   -
238   - /**
239   - * 配置刷新间隔(秒)
240   - */
241   - @NotNull(message = "配置刷新间隔不能为空")
242   - private Integer configRefreshInterval = 30;
243   -
244   - }
245   -
246 226 }
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocolManager.java
... ... @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
7 7 import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapProtocol;
8 8 import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxProtocol;
9 9 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpProtocol;
  10 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.IotModbusTcpProtocol;
10 11 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttProtocol;
11 12 import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpProtocol;
12 13 import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpProtocol;
... ... @@ -112,6 +113,8 @@ public class IotProtocolManager implements SmartLifecycle {
112 113 return createMqttProtocol(config);
113 114 case EMQX:
114 115 return createEmqxProtocol(config);
  116 + case MODBUS_TCP:
  117 + return createModbusTcpProtocol(config);
115 118 default:
116 119 throw new IllegalArgumentException(String.format(
117 120 "[createProtocol][协议实例 %s 的协议类型 %s 暂不支持]", config.getId(), protocolType));
... ... @@ -188,4 +191,14 @@ public class IotProtocolManager implements SmartLifecycle {
188 191 return new IotEmqxProtocol(config);
189 192 }
190 193  
  194 + /**
  195 + * 创建 Modbus TCP 协议实例
  196 + *
  197 + * @param config 协议实例配置
  198 + * @return Modbus TCP 协议实例
  199 + */
  200 + private IotModbusTcpProtocol createModbusTcpProtocol(IotGatewayProperties.ProtocolProperties config) {
  201 + return new IotModbusTcpProtocol(config);
  202 + }
  203 +
191 204 }
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConfig.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp;
  2 +
  3 +import jakarta.validation.constraints.Min;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +/**
  8 + * IoT Modbus TCP 协议配置
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +@Data
  13 +public class IotModbusTcpConfig {
  14 +
  15 + /**
  16 + * 配置刷新间隔(秒)
  17 + */
  18 + @NotNull(message = "配置刷新间隔不能为空")
  19 + @Min(value = 1, message = "配置刷新间隔不能小于 1 秒")
  20 + private Integer configRefreshInterval = 30;
  21 +
  22 +}
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpProtocol.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.hutool.extra.spring.SpringUtil;
  5 +import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
  6 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;
  7 +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
  8 +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
  9 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  10 +import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolProperties;
  11 +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol;
  12 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.client.IotModbusTcpClient;
  13 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.codec.IotModbusDataConverter;
  14 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.handler.downstream.IotModbusTcpDownstreamHandler;
  15 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.handler.downstream.IotModbusTcpDownstreamSubscriber;
  16 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.handler.upstream.IotModbusTcpUpstreamHandler;
  17 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConfigCacheService;
  18 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConnectionManager;
  19 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpPollScheduler;
  20 +import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
  21 +import io.vertx.core.Vertx;
  22 +import lombok.Getter;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.redisson.api.RedissonClient;
  25 +
  26 +import java.util.List;
  27 +import java.util.concurrent.TimeUnit;
  28 +
  29 +/**
  30 + * IoT 网关 Modbus TCP 协议:主动轮询 Modbus 从站设备数据
  31 + *
  32 + * @author 芋道源码
  33 + */
  34 +@Slf4j
  35 +public class IotModbusTcpProtocol implements IotProtocol {
  36 +
  37 + /**
  38 + * 协议配置
  39 + */
  40 + private final ProtocolProperties properties;
  41 + /**
  42 + * 服务器 ID(用于消息追踪,全局唯一)
  43 + */
  44 + @Getter
  45 + private final String serverId;
  46 +
  47 + /**
  48 + * 运行状态
  49 + */
  50 + @Getter
  51 + private volatile boolean running = false;
  52 +
  53 + /**
  54 + * Vert.x 实例
  55 + */
  56 + private final Vertx vertx;
  57 + /**
  58 + * 配置刷新定时器 ID
  59 + */
  60 + private Long configRefreshTimerId;
  61 +
  62 + /**
  63 + * 连接管理器
  64 + */
  65 + private final IotModbusTcpConnectionManager connectionManager;
  66 + /**
  67 + * 下行消息订阅者
  68 + */
  69 + private final IotModbusTcpDownstreamSubscriber downstreamSubscriber;
  70 +
  71 + private final IotModbusTcpConfigCacheService configCacheService;
  72 + private final IotModbusTcpPollScheduler pollScheduler;
  73 +
  74 + public IotModbusTcpProtocol(ProtocolProperties properties) {
  75 + IotModbusTcpConfig modbusTcpConfig = properties.getModbusTcp();
  76 + Assert.notNull(modbusTcpConfig, "Modbus TCP 协议配置(modbusTcp)不能为空");
  77 + this.properties = properties;
  78 + this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort());
  79 +
  80 + // 初始化 Vertx
  81 + this.vertx = Vertx.vertx();
  82 +
  83 + // 初始化 Manager
  84 + RedissonClient redissonClient = SpringUtil.getBean(RedissonClient.class);
  85 + IotDeviceCommonApi deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);
  86 + this.connectionManager = new IotModbusTcpConnectionManager(redissonClient, vertx);
  87 + this.configCacheService = new IotModbusTcpConfigCacheService(deviceApi);
  88 +
  89 + // 初始化 Handler
  90 + IotModbusDataConverter dataConverter = new IotModbusDataConverter();
  91 + IotModbusTcpClient modbusClient = new IotModbusTcpClient();
  92 + IotDeviceMessageService messageService = SpringUtil.getBean(IotDeviceMessageService.class);
  93 + IotModbusTcpUpstreamHandler upstreamHandler = new IotModbusTcpUpstreamHandler(messageService, dataConverter, serverId);
  94 + IotModbusTcpDownstreamHandler downstreamHandler = new IotModbusTcpDownstreamHandler(connectionManager,
  95 + modbusClient, dataConverter, configCacheService);
  96 +
  97 + // 初始化轮询调度器
  98 + this.pollScheduler = new IotModbusTcpPollScheduler(vertx, connectionManager, modbusClient, upstreamHandler);
  99 +
  100 + // 初始化下行消息订阅者
  101 + IotMessageBus messageBus = SpringUtil.getBean(IotMessageBus.class);
  102 + this.downstreamSubscriber = new IotModbusTcpDownstreamSubscriber(this, downstreamHandler, messageBus);
  103 + }
  104 +
  105 + @Override
  106 + public String getId() {
  107 + return properties.getId();
  108 + }
  109 +
  110 + @Override
  111 + public IotProtocolTypeEnum getType() {
  112 + return IotProtocolTypeEnum.MODBUS_TCP;
  113 + }
  114 +
  115 + @Override
  116 + public void start() {
  117 + if (running) {
  118 + log.warn("[start][IoT Modbus TCP 协议 {} 已经在运行中]", getId());
  119 + return;
  120 + }
  121 +
  122 + try {
  123 + // 1.1 首次加载配置
  124 + refreshConfig();
  125 + // 1.2 启动配置刷新定时器
  126 + int refreshInterval = properties.getModbusTcp().getConfigRefreshInterval();
  127 + configRefreshTimerId = vertx.setPeriodic(
  128 + TimeUnit.SECONDS.toMillis(refreshInterval),
  129 + id -> refreshConfig()
  130 + );
  131 + running = true;
  132 + log.info("[start][IoT Modbus TCP 协议 {} 启动成功,serverId={}]", getId(), serverId);
  133 +
  134 + // 2. 启动下行消息订阅者
  135 + this.downstreamSubscriber.start();
  136 + } catch (Exception e) {
  137 + log.error("[start][IoT Modbus TCP 协议 {} 启动失败]", getId(), e);
  138 + // 启动失败时关闭资源
  139 + if (vertx != null) {
  140 + vertx.close();
  141 + }
  142 + throw e;
  143 + }
  144 + }
  145 +
  146 + @Override
  147 + public void stop() {
  148 + if (!running) {
  149 + return;
  150 + }
  151 + // 1. 停止下行消息订阅者
  152 + try {
  153 + downstreamSubscriber.stop();
  154 + log.info("[stop][IoT Modbus TCP 协议 {} 下行消息订阅者已停止]", getId());
  155 + } catch (Exception e) {
  156 + log.error("[stop][IoT Modbus TCP 协议 {} 下行消息订阅者停止失败]", getId(), e);
  157 + }
  158 +
  159 + // 2.1 取消配置刷新定时器
  160 + if (configRefreshTimerId != null) {
  161 + vertx.cancelTimer(configRefreshTimerId);
  162 + configRefreshTimerId = null;
  163 + }
  164 + // 2.2 停止轮询调度器
  165 + pollScheduler.stopAll();
  166 + log.info("[stop][IoT Modbus TCP 协议 {} 轮询调度器已停止]", getId());
  167 + // 2.3 关闭所有连接
  168 + connectionManager.closeAll();
  169 + log.info("[stop][IoT Modbus TCP 协议 {} 连接管理器已关闭]", getId());
  170 +
  171 + // 3. 关闭 Vert.x 实例
  172 + if (vertx != null) {
  173 + try {
  174 + vertx.close().result();
  175 + log.info("[stop][IoT Modbus TCP 协议 {} Vertx 已关闭]", getId());
  176 + } catch (Exception e) {
  177 + log.error("[stop][IoT Modbus TCP 协议 {} Vertx 关闭失败]", getId(), e);
  178 + }
  179 + }
  180 + running = false;
  181 + log.info("[stop][IoT Modbus TCP 协议 {} 已停止]", getId());
  182 + }
  183 +
  184 + /**
  185 + * 刷新配置
  186 + */
  187 + private synchronized void refreshConfig() {
  188 + try {
  189 + // 1. 从 biz 拉取最新配置
  190 + List<IotModbusDeviceConfigRespDTO> configs = configCacheService.refreshConfig();
  191 + log.debug("[refreshConfig][获取到 {} 个 Modbus 设备配置]", configs.size());
  192 +
  193 + // 2. 更新连接和轮询任务
  194 + for (IotModbusDeviceConfigRespDTO config : configs) {
  195 + try {
  196 + // 2.1 确保连接存在
  197 + connectionManager.ensureConnection(config);
  198 + // 2.2 更新轮询任务
  199 + pollScheduler.updatePolling(config);
  200 + } catch (Exception e) {
  201 + log.error("[refreshConfig][处理设备配置失败, deviceId={}]", config.getDeviceId(), e);
  202 + }
  203 + }
  204 +
  205 + // 3. 清理已删除设备的资源
  206 + configCacheService.cleanupRemovedDevices(configs, deviceId -> {
  207 + pollScheduler.stopPolling(deviceId);
  208 + connectionManager.removeDevice(deviceId);
  209 + });
  210 + } catch (Exception e) {
  211 + log.error("[refreshConfig][刷新配置失败]", e);
  212 + }
  213 + }
  214 +
  215 +}
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamProtocol.java deleted
1   -package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp;
2   -
3   -import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;
4   -import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
5   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConfigCacheService;
6   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConnectionManager;
7   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpPollScheduler;
8   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router.IotModbusTcpUpstreamHandler;
9   -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
10   -import io.vertx.core.Vertx;
11   -import jakarta.annotation.PostConstruct;
12   -import jakarta.annotation.PreDestroy;
13   -import lombok.Getter;
14   -import lombok.RequiredArgsConstructor;
15   -import lombok.extern.slf4j.Slf4j;
16   -
17   -import java.util.List;
18   -import java.util.UUID;
19   -import java.util.concurrent.TimeUnit;
20   -
21   -/**
22   - * IoT Modbus TCP 上行协议:定时拉取配置、管理连接、调度轮询任务
23   - *
24   - * @author 芋道源码
25   - */
26   -@RequiredArgsConstructor
27   -@Slf4j
28   -public class IotModbusTcpUpstreamProtocol {
29   -
30   - private final IotGatewayProperties.ModbusTcpProperties modbusTcpProperties;
31   - private final IotDeviceMessageService messageService;
32   - private final IotModbusTcpConnectionManager connectionManager;
33   - private final IotModbusTcpPollScheduler pollScheduler;
34   - private final IotModbusTcpConfigCacheService configCacheService;
35   - private final IotModbusTcpUpstreamHandler upstreamHandler;
36   - private final Vertx vertx;
37   -
38   - /**
39   - * 服务器 ID,用于标识当前网关实例
40   - */
41   - @Getter
42   - private final String serverId = UUID.randomUUID().toString();
43   -
44   - /**
45   - * 配置刷新定时器 ID
46   - */
47   - private Long configRefreshTimerId;
48   -
49   - @PostConstruct
50   - public void start() {
51   - log.info("[start][Modbus TCP 协议启动, serverId={}]", serverId);
52   -
53   - // 0. 设置 serverId 到上行处理器
54   - upstreamHandler.setServerId(serverId);
55   -
56   - // 1. 首次加载配置
57   - refreshConfig();
58   -
59   - // 2. 启动配置定时刷新
60   - int refreshInterval = modbusTcpProperties.getConfigRefreshInterval();
61   - configRefreshTimerId = vertx.setPeriodic(
62   - TimeUnit.SECONDS.toMillis(refreshInterval),
63   - id -> refreshConfig()
64   - );
65   - log.info("[start][配置刷新定时器已启动, 间隔={}秒]", refreshInterval);
66   - }
67   -
68   - @PreDestroy
69   - public void stop() {
70   - log.info("[stop][Modbus TCP 协议停止]");
71   -
72   - // 1. 取消配置刷新定时器
73   - if (configRefreshTimerId != null) {
74   - vertx.cancelTimer(configRefreshTimerId);
75   - }
76   -
77   - // 2. 停止轮询调度器
78   - pollScheduler.stopAll();
79   -
80   - // 3. 关闭所有连接
81   - connectionManager.closeAll();
82   - }
83   -
84   - /**
85   - * 刷新配置
86   - */
87   - private void refreshConfig() {
88   - try {
89   - // 1. 从 biz 拉取最新配置
90   - List<IotModbusDeviceConfigRespDTO> configs = configCacheService.refreshConfig();
91   - log.debug("[refreshConfig][获取到 {} 个 Modbus 设备配置]", configs.size());
92   -
93   - // 2. 更新连接和轮询任务
94   - for (IotModbusDeviceConfigRespDTO config : configs) {
95   - try {
96   - // 2.1 确保连接存在
97   - connectionManager.ensureConnection(config);
98   - // 2.2 更新轮询任务
99   - pollScheduler.updatePolling(config);
100   - } catch (Exception e) {
101   - log.error("[refreshConfig][处理设备配置失败, deviceId={}]", config.getDeviceId(), e);
102   - }
103   - }
104   -
105   - // 3. 清理已删除设备的资源
106   - configCacheService.cleanupRemovedDevices(configs, deviceId -> {
107   - pollScheduler.stopPolling(deviceId);
108   - connectionManager.removeDevice(deviceId);
109   - });
110   - } catch (Exception e) {
111   - log.error("[refreshConfig][刷新配置失败]", e);
112   - }
113   - }
114   -
115   -}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/router/IotModbusTcpDownstreamHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/handler/downstream/IotModbusTcpDownstreamHandler.java
1   -package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router;
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.handler.downstream;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;
5 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;
  6 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
6 7 import cn.iocoder.yudao.module.iot.core.enums.IotModbusFunctionCodeEnum;
7 8 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
8   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.codec.IotModbusDataConverter;
9 9 import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.client.IotModbusTcpClient;
  10 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.codec.IotModbusDataConverter;
10 11 import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConfigCacheService;
11 12 import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConnectionManager;
12 13 import lombok.RequiredArgsConstructor;
... ... @@ -16,7 +17,7 @@ import java.util.Map;
16 17  
17 18 /**
18 19 * IoT Modbus TCP 下行消息处理器
19   - *
  20 + * <p>
20 21 * 负责:
21 22 * 1. 处理下行消息(如属性设置 thing.service.property.set)
22 23 * 2. 执行 Modbus 写入操作
... ... @@ -38,8 +39,7 @@ public class IotModbusTcpDownstreamHandler {
38 39 @SuppressWarnings("unchecked")
39 40 public void handle(IotDeviceMessage message) {
40 41 // 1.1 检查是否是属性设置消息
41   - // TODO @AI:要使用枚举
42   - if (!"thing.service.property.set".equals(message.getMethod())) {
  42 + if (!IotDeviceMessageMethodEnum.PROPERTY_SET.getMethod().equals(message.getMethod())) {
43 43 log.debug("[handle][忽略非属性设置消息: {}]", message.getMethod());
44 44 return;
45 45 }
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpDownstreamSubscriber.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/handler/downstream/IotModbusTcpDownstreamSubscriber.java
1   -package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp;
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.handler.downstream;
2 2  
3 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 4 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
5 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 6 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
7   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router.IotModbusTcpDownstreamHandler;
8   -import jakarta.annotation.PostConstruct;
  7 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.IotModbusTcpProtocol;
9 8 import lombok.RequiredArgsConstructor;
10 9 import lombok.extern.slf4j.Slf4j;
11 10  
... ... @@ -18,19 +17,29 @@ import lombok.extern.slf4j.Slf4j;
18 17 @Slf4j
19 18 public class IotModbusTcpDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
20 19  
21   - private final IotModbusTcpUpstreamProtocol upstreamProtocol;
  20 + private final IotModbusTcpProtocol protocol;
22 21 private final IotModbusTcpDownstreamHandler downstreamHandler;
23 22 private final IotMessageBus messageBus;
24 23  
25   - @PostConstruct
26   - public void subscribe() {
  24 + /**
  25 + * 启动订阅
  26 + */
  27 + public void start() {
27 28 messageBus.register(this);
28   - log.info("[subscribe][Modbus TCP 下行消息订阅器已启动, topic={}]", getTopic());
  29 + log.info("[start][Modbus TCP 下行消息订阅器已启动, topic={}]", getTopic());
  30 + }
  31 +
  32 + /**
  33 + * 停止订阅
  34 + */
  35 + public void stop() {
  36 + messageBus.unregister(this);
  37 + log.info("[stop][Modbus TCP 下行消息订阅器已停止]");
29 38 }
30 39  
31 40 @Override
32 41 public String getTopic() {
33   - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(upstreamProtocol.getServerId());
  42 + return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId());
34 43 }
35 44  
36 45 @Override
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/router/IotModbusTcpUpstreamHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/handler/upstream/IotModbusTcpUpstreamHandler.java
1   -package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router;
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.handler.upstream;
2 2  
3 3 import cn.hutool.core.map.MapUtil;
4 4 import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;
... ... @@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
7 7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
8 8 import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.codec.IotModbusDataConverter;
9 9 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
10   -import lombok.Setter;
11 10 import lombok.extern.slf4j.Slf4j;
12 11  
13 12 import java.util.Map;
... ... @@ -22,14 +21,14 @@ public class IotModbusTcpUpstreamHandler {
22 21  
23 22 private final IotDeviceMessageService messageService;
24 23 private final IotModbusDataConverter dataConverter;
25   -
26   - @Setter
27   - private String serverId;
  24 + private final String serverId;
28 25  
29 26 public IotModbusTcpUpstreamHandler(IotDeviceMessageService messageService,
30   - IotModbusDataConverter dataConverter) {
  27 + IotModbusDataConverter dataConverter,
  28 + String serverId) {
31 29 this.messageService = messageService;
32 30 this.dataConverter = dataConverter;
  31 + this.serverId = serverId;
33 32 }
34 33  
35 34 /**
... ... @@ -40,8 +39,8 @@ public class IotModbusTcpUpstreamHandler {
40 39 * @param rawValue 原始值(int 数组)
41 40 */
42 41 public void handleReadResult(IotModbusDeviceConfigRespDTO config,
43   - IotModbusPointRespDTO point,
44   - int[] rawValue) {
  42 + IotModbusPointRespDTO point,
  43 + int[] rawValue) {
45 44 try {
46 45 // 1.1 转换原始值为物模型属性值
47 46 Object convertedValue = dataConverter.convertToPropertyValue(rawValue, point);
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/manager/IotModbusTcpPollScheduler.java
... ... @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;
5 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;
6   -import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router.IotModbusTcpUpstreamHandler;
7 6 import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.client.IotModbusTcpClient;
  7 +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.handler.upstream.IotModbusTcpUpstreamHandler;
8 8 import io.vertx.core.Vertx;
9 9 import lombok.AllArgsConstructor;
10 10 import lombok.Data;
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application.yaml
... ... @@ -51,8 +51,6 @@ yudao:
51 51 protocol: http
52 52 port: 8092
53 53 enabled: false
54   - http:
55   - ssl-enabled: false
56 54 # ====================================
57 55 # 针对引入的 TCP 组件的配置
58 56 # ====================================
... ... @@ -64,7 +62,6 @@ yudao:
64 62 tcp:
65 63 max-connections: 1000
66 64 keep-alive-timeout-ms: 30000
67   - ssl-enabled: false
68 65 codec:
69 66 type: delimiter # 拆包类型:length_field / delimiter / fixed_length
70 67 delimiter: "\\n" # 分隔符(支持转义:\\n=换行, \\r=回车, \\t=制表符)
... ... @@ -101,7 +98,6 @@ yudao:
101 98 max-message-size: 65536 # 最大消息大小(字节,默认 64KB)
102 99 max-frame-size: 65536 # 最大帧大小(字节,默认 64KB)
103 100 idle-timeout-seconds: 60 # 空闲超时时间(秒,默认 60)
104   - ssl-enabled: false # 是否启用 SSL(wss://)
105 101 # ====================================
106 102 # 针对引入的 CoAP 组件的配置
107 103 # ====================================
... ... @@ -117,14 +113,13 @@ yudao:
117 113 # 针对引入的 MQTT 组件的配置
118 114 # ====================================
119 115 - id: mqtt-json
120   - enabled: true
  116 + enabled: false
121 117 protocol: mqtt
122 118 port: 1883
123 119 serialize: json
124 120 mqtt:
125 121 max-message-size: 8192 # 最大消息大小(字节)
126 122 connect-timeout-seconds: 60 # 连接超时时间(秒)
127   - ssl-enabled: false # 是否启用 SSL
128 123 # ====================================
129 124 # 针对引入的 EMQX 组件的配置
130 125 # ====================================
... ... @@ -168,43 +163,15 @@ yudao:
168 163 key-store-password: "your-keystore-password" # 客户端证书库密码
169 164 trust-store-path: "classpath:certs/trust.jks" # 信任的 CA 证书库路径
170 165 trust-store-password: "your-truststore-password" # 信任的 CA 证书库密码
171   - http-port: 8090 # MQTT HTTP 服务端口
172   - mqtt-host: 127.0.0.1 # MQTT Broker 地址
173   - mqtt-port: 1883 # MQTT Broker 端口
174   - mqtt-username: admin # MQTT 用户名
175   - mqtt-password: public # MQTT 密码
176   - mqtt-client-id: iot-gateway-mqtt # MQTT 客户端 ID
177   - mqtt-ssl: false # 是否开启 SSL
178   - mqtt-topics:
179   - - "/sys/#" # 系统主题
180   - clean-session: true # 是否启用 Clean Session (默认: true)
181   - keep-alive-interval-seconds: 60 # 心跳间隔,单位秒 (默认: 60)
182   - max-inflight-queue: 10000 # 最大飞行消息队列,单位:条
183   - connect-timeout-seconds: 10 # 连接超时,单位:秒
184   - # 是否信任所有 SSL 证书 (默认: false)。警告:生产环境必须为 false!
185   - # 仅在开发环境或内网测试时,如果使用了自签名证书,可以临时设置为 true
186   - trust-all: true # 在 dev 环境可以设为 true
187   - # 遗嘱消息配置 (用于网关异常下线时通知其他系统)
188   - will:
189   - enabled: true # 生产环境强烈建议开启
190   - topic: "gateway/status/${yudao.iot.gateway.emqx.mqtt-client-id}" # 遗嘱消息主题
191   - payload: "offline" # 遗嘱消息负载
192   - qos: 1 # 遗嘱消息 QoS
193   - retain: true # 遗嘱消息是否保留
194   - # 高级 SSL/TLS 配置 (当 trust-all: false 且 mqtt-ssl: true 时生效)
195   - ssl-options:
196   - key-store-path: "classpath:certs/client.jks" # 客户端证书库路径
197   - key-store-password: "your-keystore-password" # 客户端证书库密码
198   - trust-store-path: "classpath:certs/trust.jks" # 信任的 CA 证书库路径
199   - trust-store-password: "your-truststore-password" # 信任的 CA 证书库密码
200 166 # ====================================
201   - # 针对引入的 MQTT 组件的配置
  167 + # 针对引入的 Modbus TCP 组件的配置
202 168 # ====================================
203   - # 协议配置(旧版,保持兼容)
204   - protocol:
205   - modbus-tcp:
  169 + - id: modbus-tcp-1
206 170 enabled: true
207   - config-refresh-interval: 30 # 配置刷新间隔(秒)
  171 + protocol: modbus_tcp
  172 + port: 502
  173 + modbus-tcp:
  174 + config-refresh-interval: 30 # 配置刷新间隔(秒)
208 175  
209 176 --- #################### 日志相关配置 ####################
210 177  
... ... @@ -226,6 +193,7 @@ logging:
226 193 cn.iocoder.yudao.module.iot.gateway.protocol.mqtt: DEBUG
227 194 cn.iocoder.yudao.module.iot.gateway.protocol.coap: DEBUG
228 195 cn.iocoder.yudao.module.iot.gateway.protocol.websocket: DEBUG
  196 + cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp: DEBUG
229 197 # 根日志级别
230 198 root: INFO
231 199  
... ...