Commit 3bc1bfe1d738549c40f07438372c4842505d863f
1 parent
593455a0
feat:【iot】modbus-tcp 协议接入 40%:1)优化包结构;2)增加 ModbusTcpSlaveSimulatorTest 模拟从站设备
Showing
13 changed files
with
222 additions
and
14 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java
| @@ -22,6 +22,7 @@ import org.springframework.web.bind.annotation.PostMapping; | @@ -22,6 +22,7 @@ import org.springframework.web.bind.annotation.PostMapping; | ||
| 22 | import org.springframework.web.bind.annotation.RequestBody; | 22 | import org.springframework.web.bind.annotation.RequestBody; |
| 23 | import org.springframework.web.bind.annotation.RestController; | 23 | import org.springframework.web.bind.annotation.RestController; |
| 24 | 24 | ||
| 25 | +import java.math.BigDecimal; | ||
| 25 | import java.util.ArrayList; | 26 | import java.util.ArrayList; |
| 26 | import java.util.List; | 27 | import java.util.List; |
| 27 | import java.util.Map; | 28 | import java.util.Map; |
| @@ -74,6 +75,11 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { | @@ -74,6 +75,11 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { | ||
| 74 | @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/modbus/enabled-configs") | 75 | @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/modbus/enabled-configs") |
| 75 | @PermitAll | 76 | @PermitAll |
| 76 | public CommonResult<List<IotModbusDeviceConfigRespDTO>> getEnabledModbusDeviceConfigs() { | 77 | public CommonResult<List<IotModbusDeviceConfigRespDTO>> getEnabledModbusDeviceConfigs() { |
| 78 | + // TODO @芋艿:临时 mock 数据,用于测试 ModbusTcpSlaveSimulator | ||
| 79 | + if (true) { | ||
| 80 | + return success(buildMockModbusConfigs()); | ||
| 81 | + } | ||
| 82 | + | ||
| 77 | // 1. 获取所有启用的 Modbus 连接配置 | 83 | // 1. 获取所有启用的 Modbus 连接配置 |
| 78 | List<IotDeviceModbusConfigDO> configList = modbusConfigService.getEnabledDeviceModbusConfigList(); | 84 | List<IotDeviceModbusConfigDO> configList = modbusConfigService.getEnabledDeviceModbusConfigList(); |
| 79 | if (CollUtil.isEmpty(configList)) { | 85 | if (CollUtil.isEmpty(configList)) { |
| @@ -105,4 +111,74 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { | @@ -105,4 +111,74 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { | ||
| 105 | return success(result); | 111 | return success(result); |
| 106 | } | 112 | } |
| 107 | 113 | ||
| 114 | + /** | ||
| 115 | + * 构建 Mock Modbus 配置,对接 ModbusTcpSlaveSimulator | ||
| 116 | + * | ||
| 117 | + * 设备:productKey=4aymZgOTOOCrDKRT, deviceName=small | ||
| 118 | + * 物模型字段:width, height, oneThree | ||
| 119 | + */ | ||
| 120 | + private List<IotModbusDeviceConfigRespDTO> buildMockModbusConfigs() { | ||
| 121 | + List<IotModbusDeviceConfigRespDTO> configs = new ArrayList<>(); | ||
| 122 | + | ||
| 123 | + // 设备配置 | ||
| 124 | + IotModbusDeviceConfigRespDTO config = new IotModbusDeviceConfigRespDTO(); | ||
| 125 | + config.setDeviceId(1L); | ||
| 126 | + config.setProductKey("4aymZgOTOOCrDKRT"); | ||
| 127 | + config.setDeviceName("small"); | ||
| 128 | + config.setIp("127.0.0.1"); | ||
| 129 | + config.setPort(5020); // 对应 ModbusTcpSlaveSimulator 的端口 | ||
| 130 | + config.setSlaveId(1); | ||
| 131 | + config.setTimeout(3000); | ||
| 132 | + config.setRetryInterval(5000); | ||
| 133 | + | ||
| 134 | + // 点位配置(对应物模型字段:width, height, oneThree) | ||
| 135 | + List<IotModbusPointRespDTO> points = new ArrayList<>(); | ||
| 136 | + | ||
| 137 | + // 点位 1:width - 读取保持寄存器地址 0(功能码 03) | ||
| 138 | + IotModbusPointRespDTO point1 = new IotModbusPointRespDTO(); | ||
| 139 | + point1.setId(1L); | ||
| 140 | + point1.setIdentifier("width"); | ||
| 141 | + point1.setName("宽度"); | ||
| 142 | + point1.setFunctionCode(3); // 读保持寄存器 | ||
| 143 | + point1.setRegisterAddress(0); | ||
| 144 | + point1.setRegisterCount(1); | ||
| 145 | + point1.setByteOrder("AB"); | ||
| 146 | + point1.setRawDataType("INT16"); | ||
| 147 | + point1.setScale(BigDecimal.ONE); | ||
| 148 | + point1.setPollInterval(3000); // 3 秒轮询 | ||
| 149 | + points.add(point1); | ||
| 150 | + | ||
| 151 | + // 点位 2:height - 读取保持寄存器地址 1(功能码 03) | ||
| 152 | + IotModbusPointRespDTO point2 = new IotModbusPointRespDTO(); | ||
| 153 | + point2.setId(2L); | ||
| 154 | + point2.setIdentifier("height"); | ||
| 155 | + point2.setName("高度"); | ||
| 156 | + point2.setFunctionCode(3); // 读保持寄存器 | ||
| 157 | + point2.setRegisterAddress(1); | ||
| 158 | + point2.setRegisterCount(1); | ||
| 159 | + point2.setByteOrder("AB"); | ||
| 160 | + point2.setRawDataType("INT16"); | ||
| 161 | + point2.setScale(BigDecimal.ONE); | ||
| 162 | + point2.setPollInterval(3000); // 3 秒轮询 | ||
| 163 | + points.add(point2); | ||
| 164 | + | ||
| 165 | + // 点位 3:oneThree - 读取保持寄存器地址 2(功能码 03) | ||
| 166 | + IotModbusPointRespDTO point3 = new IotModbusPointRespDTO(); | ||
| 167 | + point3.setId(3L); | ||
| 168 | + point3.setIdentifier("oneThree"); | ||
| 169 | + point3.setName("一三"); | ||
| 170 | + point3.setFunctionCode(3); // 读保持寄存器 | ||
| 171 | + point3.setRegisterAddress(2); | ||
| 172 | + point3.setRegisterCount(1); | ||
| 173 | + point3.setByteOrder("AB"); | ||
| 174 | + point3.setRawDataType("INT16"); | ||
| 175 | + point3.setScale(BigDecimal.ONE); | ||
| 176 | + point3.setPollInterval(3000); // 3 秒轮询 | ||
| 177 | + points.add(point3); | ||
| 178 | + | ||
| 179 | + config.setPoints(points); | ||
| 180 | + configs.add(config); | ||
| 181 | + return configs; | ||
| 182 | + } | ||
| 183 | + | ||
| 108 | } | 184 | } |
| 109 | \ No newline at end of file | 185 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
| @@ -7,6 +7,13 @@ import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol | @@ -7,6 +7,13 @@ import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol | ||
| 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber; | 7 | import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber; |
| 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; | 8 | import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; |
| 9 | import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.*; | 9 | import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.*; |
| 10 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.client.IotModbusTcpClient; | ||
| 11 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.codec.IotModbusDataConverter; | ||
| 12 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConfigCacheService; | ||
| 13 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConnectionManager; | ||
| 14 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpPollScheduler; | ||
| 15 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router.IotModbusTcpDownstreamHandler; | ||
| 16 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router.IotModbusTcpUpstreamHandler; | ||
| 10 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber; | 17 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber; |
| 11 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; | 18 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; |
| 12 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager; | 19 | import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager; |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpDownstreamSubscriber.java
| @@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | @@ -4,6 +4,7 @@ import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | ||
| 4 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | 4 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; |
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 6 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; |
| 7 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router.IotModbusTcpDownstreamHandler; | ||
| 7 | import jakarta.annotation.PostConstruct; | 8 | import jakarta.annotation.PostConstruct; |
| 8 | import lombok.RequiredArgsConstructor; | 9 | import lombok.RequiredArgsConstructor; |
| 9 | import lombok.extern.slf4j.Slf4j; | 10 | import lombok.extern.slf4j.Slf4j; |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamProtocol.java
| @@ -2,6 +2,10 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; | @@ -2,6 +2,10 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO; | 3 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO; |
| 4 | import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; | 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; | ||
| 5 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | 9 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; |
| 6 | import io.vertx.core.Vertx; | 10 | import io.vertx.core.Vertx; |
| 7 | import jakarta.annotation.PostConstruct; | 11 | import jakarta.annotation.PostConstruct; |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpClient.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/client/IotModbusTcpClient.java
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; | 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.client; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO; | 3 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO; |
| 4 | import cn.iocoder.yudao.module.iot.core.enums.IotModbusFunctionCodeEnum; | 4 | import cn.iocoder.yudao.module.iot.core.enums.IotModbusFunctionCodeEnum; |
| 5 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConnectionManager; | ||
| 5 | import com.ghgande.j2mod.modbus.io.ModbusTCPTransaction; | 6 | import com.ghgande.j2mod.modbus.io.ModbusTCPTransaction; |
| 6 | import com.ghgande.j2mod.modbus.msg.*; | 7 | import com.ghgande.j2mod.modbus.msg.*; |
| 7 | import com.ghgande.j2mod.modbus.procimg.InputRegister; | 8 | import com.ghgande.j2mod.modbus.procimg.InputRegister; |
| @@ -12,7 +13,9 @@ import io.vertx.core.Future; | @@ -12,7 +13,9 @@ import io.vertx.core.Future; | ||
| 12 | import lombok.extern.slf4j.Slf4j; | 13 | import lombok.extern.slf4j.Slf4j; |
| 13 | 14 | ||
| 14 | /** | 15 | /** |
| 15 | - * IoT Modbus TCP 客户端,负责: | 16 | + * IoT Modbus TCP 客户端 |
| 17 | + * <p> | ||
| 18 | + * 封装 Modbus 协议读写操作: | ||
| 16 | * 1. 封装 Modbus 读/写操作 | 19 | * 1. 封装 Modbus 读/写操作 |
| 17 | * 2. 根据功能码,执行对应的 Modbus 请求 | 20 | * 2. 根据功能码,执行对应的 Modbus 请求 |
| 18 | * | 21 | * |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusDataConverter.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/codec/IotModbusDataConverter.java
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; | 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.codec; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.ArrayUtil; | 3 | import cn.hutool.core.util.ArrayUtil; |
| 4 | import cn.hutool.core.util.ObjectUtil; | 4 | import cn.hutool.core.util.ObjectUtil; |
| @@ -14,7 +14,9 @@ import java.nio.ByteBuffer; | @@ -14,7 +14,9 @@ import java.nio.ByteBuffer; | ||
| 14 | import java.nio.ByteOrder; | 14 | import java.nio.ByteOrder; |
| 15 | 15 | ||
| 16 | /** | 16 | /** |
| 17 | - * IoT Modbus 数据转换器,负责: | 17 | + * IoT Modbus 数据转换器 |
| 18 | + * <p> | ||
| 19 | + * 负责 Modbus 原始寄存器值与物模型属性值的相互转换: | ||
| 18 | * 1. 将 Modbus 原始寄存器值转换为物模型属性值 | 20 | * 1. 将 Modbus 原始寄存器值转换为物模型属性值 |
| 19 | * 2. 将物模型属性值转换为 Modbus 原始寄存器值 | 21 | * 2. 将物模型属性值转换为 Modbus 原始寄存器值 |
| 20 | * | 22 | * |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConfigCacheService.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/manager/IotModbusTcpConfigCacheService.java
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; | 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; | 4 | import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConnectionManager.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/manager/IotModbusTcpConnectionManager.java
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; | 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO; | 3 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO; |
| 4 | import com.ghgande.j2mod.modbus.net.TCPMasterConnection; | 4 | import com.ghgande.j2mod.modbus.net.TCPMasterConnection; |
| @@ -16,7 +16,9 @@ import java.util.Map; | @@ -16,7 +16,9 @@ import java.util.Map; | ||
| 16 | import java.util.concurrent.ConcurrentHashMap; | 16 | import java.util.concurrent.ConcurrentHashMap; |
| 17 | 17 | ||
| 18 | /** | 18 | /** |
| 19 | - * IoT Modbus TCP 连接管理器,负责: | 19 | + * IoT Modbus TCP 连接管理器 |
| 20 | + * <p> | ||
| 21 | + * 统一管理 Modbus TCP 连接: | ||
| 20 | * 1. 管理 TCP 连接(相同 ip:port 共用连接) | 22 | * 1. 管理 TCP 连接(相同 ip:port 共用连接) |
| 21 | * 2. 分布式锁管理(连接级别),避免多节点重复创建连接 | 23 | * 2. 分布式锁管理(连接级别),避免多节点重复创建连接 |
| 22 | * 3. 连接重试和故障恢复 | 24 | * 3. 连接重试和故障恢复 |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpPollScheduler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/manager/IotModbusTcpPollScheduler.java
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; | 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager; |
| 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.IotModbusDeviceConfigRespDTO; | 4 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO; |
| 5 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO; | 5 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO; |
| 6 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router.IotModbusTcpUpstreamHandler; | ||
| 7 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.client.IotModbusTcpClient; | ||
| 6 | import io.vertx.core.Vertx; | 8 | import io.vertx.core.Vertx; |
| 7 | import lombok.AllArgsConstructor; | 9 | import lombok.AllArgsConstructor; |
| 8 | import lombok.Data; | 10 | import lombok.Data; |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/package-info.java
0 → 100644
| 1 | +/** | ||
| 2 | + * Modbus TCP 协议实现包 | ||
| 3 | + * <p> | ||
| 4 | + * 提供基于 j2mod 的 Modbus TCP 主站(Master)功能,支持: | ||
| 5 | + * 1. 定时轮询 Modbus 从站设备数据 | ||
| 6 | + * 2. 下发属性设置命令到从站设备 | ||
| 7 | + * 3. 数据格式转换(寄存器值 ↔ 物模型属性值) | ||
| 8 | + */ | ||
| 9 | +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; | ||
| 0 | \ No newline at end of file | 10 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpDownstreamHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/router/IotModbusTcpDownstreamHandler.java
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; | 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router; |
| 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.IotModbusDeviceConfigRespDTO; | 4 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO; |
| 5 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO; | 5 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO; |
| 6 | import cn.iocoder.yudao.module.iot.core.enums.IotModbusFunctionCodeEnum; | 6 | import cn.iocoder.yudao.module.iot.core.enums.IotModbusFunctionCodeEnum; |
| 7 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 7 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 8 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.codec.IotModbusDataConverter; | ||
| 9 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.client.IotModbusTcpClient; | ||
| 10 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConfigCacheService; | ||
| 11 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.manager.IotModbusTcpConnectionManager; | ||
| 8 | import lombok.RequiredArgsConstructor; | 12 | import lombok.RequiredArgsConstructor; |
| 9 | import lombok.extern.slf4j.Slf4j; | 13 | import lombok.extern.slf4j.Slf4j; |
| 10 | 14 | ||
| @@ -34,6 +38,7 @@ public class IotModbusTcpDownstreamHandler { | @@ -34,6 +38,7 @@ public class IotModbusTcpDownstreamHandler { | ||
| 34 | @SuppressWarnings("unchecked") | 38 | @SuppressWarnings("unchecked") |
| 35 | public void handle(IotDeviceMessage message) { | 39 | public void handle(IotDeviceMessage message) { |
| 36 | // 1.1 检查是否是属性设置消息 | 40 | // 1.1 检查是否是属性设置消息 |
| 41 | + // TODO @AI:要使用枚举 | ||
| 37 | if (!"thing.service.property.set".equals(message.getMethod())) { | 42 | if (!"thing.service.property.set".equals(message.getMethod())) { |
| 38 | log.debug("[handle][忽略非属性设置消息: {}]", message.getMethod()); | 43 | log.debug("[handle][忽略非属性设置消息: {}]", message.getMethod()); |
| 39 | return; | 44 | return; |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/router/IotModbusTcpUpstreamHandler.java
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; | 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.router; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.map.MapUtil; | ||
| 3 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO; | 4 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO; |
| 4 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO; | 5 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO; |
| 6 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 7 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 8 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp.codec.IotModbusDataConverter; | ||
| 6 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | 9 | import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; |
| 7 | import lombok.Setter; | 10 | import lombok.Setter; |
| 8 | import lombok.extern.slf4j.Slf4j; | 11 | import lombok.extern.slf4j.Slf4j; |
| 9 | 12 | ||
| 10 | -import java.util.HashMap; | ||
| 11 | import java.util.Map; | 13 | import java.util.Map; |
| 12 | 14 | ||
| 13 | /** | 15 | /** |
| @@ -46,9 +48,8 @@ public class IotModbusTcpUpstreamHandler { | @@ -46,9 +48,8 @@ public class IotModbusTcpUpstreamHandler { | ||
| 46 | log.debug("[handleReadResult][设备={}, 属性={}, 原始值={}, 转换值={}]", | 48 | log.debug("[handleReadResult][设备={}, 属性={}, 原始值={}, 转换值={}]", |
| 47 | config.getDeviceId(), point.getIdentifier(), rawValue, convertedValue); | 49 | config.getDeviceId(), point.getIdentifier(), rawValue, convertedValue); |
| 48 | // 1.2 构造属性上报消息 | 50 | // 1.2 构造属性上报消息 |
| 49 | - Map<String, Object> params = new HashMap<>(); | ||
| 50 | - params.put(point.getIdentifier(), convertedValue); | ||
| 51 | - IotDeviceMessage message = IotDeviceMessage.requestOf("thing.event.property.post", params); | 51 | + Map<String, Object> params = MapUtil.of(point.getIdentifier(), convertedValue); |
| 52 | + IotDeviceMessage message = IotDeviceMessage.requestOf(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(), params); | ||
| 52 | 53 | ||
| 53 | // 2. 发送到消息总线 | 54 | // 2. 发送到消息总线 |
| 54 | messageService.sendDeviceMessage(message, config.getProductKey(), | 55 | messageService.sendDeviceMessage(message, config.getProductKey(), |
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/ModbusTcpSlaveSimulatorTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; | ||
| 2 | + | ||
| 3 | +import com.ghgande.j2mod.modbus.procimg.*; | ||
| 4 | +import com.ghgande.j2mod.modbus.slave.ModbusSlave; | ||
| 5 | +import com.ghgande.j2mod.modbus.slave.ModbusSlaveFactory; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * Modbus TCP 从站模拟器 | ||
| 9 | + * | ||
| 10 | + * 用于测试 Modbus TCP 网关的连接和数据读写功能 | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | +public class ModbusTcpSlaveSimulatorTest { | ||
| 15 | + | ||
| 16 | + private static final int PORT = 5020; | ||
| 17 | + private static final int SLAVE_ID = 1; | ||
| 18 | + | ||
| 19 | + @SuppressWarnings({"InfiniteLoopStatement", "BusyWait", "CommentedOutCode"}) | ||
| 20 | + public static void main(String[] args) throws Exception { | ||
| 21 | + // 1. 创建进程映像(Process Image),存储寄存器数据 | ||
| 22 | + SimpleProcessImage spi = new SimpleProcessImage(SLAVE_ID); | ||
| 23 | + | ||
| 24 | + // 2.1 初始化线圈(Coil,功能码 01/05)- 离散输出,可读写 | ||
| 25 | + // 地址 0-9,共 10 个线圈 | ||
| 26 | + for (int i = 0; i < 10; i++) { | ||
| 27 | + spi.addDigitalOut(new SimpleDigitalOut(i % 2 == 0)); // 交替 true/false | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + // 2.2 初始化离散输入(Discrete Input,功能码 02)- 只读 | ||
| 31 | + // 地址 0-9,共 10 个离散输入 | ||
| 32 | + for (int i = 0; i < 10; i++) { | ||
| 33 | + spi.addDigitalIn(new SimpleDigitalIn(i % 3 == 0)); // 每 3 个一个 true | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + // 2.3 初始化保持寄存器(Holding Register,功能码 03/06/16)- 可读写 | ||
| 37 | + // 地址 0-19,共 20 个寄存器 | ||
| 38 | + for (int i = 0; i < 20; i++) { | ||
| 39 | + spi.addRegister(new SimpleRegister(i * 100)); // 值为 0, 100, 200, ... | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + // 2.4 初始化输入寄存器(Input Register,功能码 04)- 只读 | ||
| 43 | + // 地址 0-19,共 20 个寄存器 | ||
| 44 | + SimpleInputRegister[] inputRegisters = new SimpleInputRegister[20]; | ||
| 45 | + for (int i = 0; i < 20; i++) { | ||
| 46 | + inputRegisters[i] = new SimpleInputRegister(i * 10 + 1); // 值为 1, 11, 21, ... | ||
| 47 | + spi.addInputRegister(inputRegisters[i]); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + // 3.1 创建 Modbus TCP 从站 | ||
| 51 | + ModbusSlave slave = ModbusSlaveFactory.createTCPSlave(PORT, 5); | ||
| 52 | + slave.addProcessImage(SLAVE_ID, spi); | ||
| 53 | + // 3.2 启动从站 | ||
| 54 | + slave.open(); | ||
| 55 | + | ||
| 56 | + System.out.println("==================================================="); | ||
| 57 | + System.out.println("Modbus TCP 从站模拟器已启动"); | ||
| 58 | + System.out.println("端口: " + PORT); | ||
| 59 | + System.out.println("从站地址 (Slave ID): " + SLAVE_ID); | ||
| 60 | + System.out.println("==================================================="); | ||
| 61 | + System.out.println("可用寄存器:"); | ||
| 62 | + System.out.println(" - 线圈 (Coil, 功能码 01/05): 地址 0-9"); | ||
| 63 | + System.out.println(" - 离散输入 (Discrete Input, 功能码 02): 地址 0-9"); | ||
| 64 | + System.out.println(" - 保持寄存器 (Holding Register, 功能码 03/06/16): 地址 0-19"); | ||
| 65 | + System.out.println(" - 输入寄存器 (Input Register, 功能码 04): 地址 0-19"); | ||
| 66 | + System.out.println("==================================================="); | ||
| 67 | + System.out.println("按 Ctrl+C 停止模拟器"); | ||
| 68 | + | ||
| 69 | + // 4. 添加关闭钩子 | ||
| 70 | + Runtime.getRuntime().addShutdownHook(new Thread(() -> { | ||
| 71 | + System.out.println("\n正在关闭模拟器..."); | ||
| 72 | + slave.close(); | ||
| 73 | + System.out.println("模拟器已关闭"); | ||
| 74 | + })); | ||
| 75 | + | ||
| 76 | + // 5. 保持运行,定时更新输入寄存器模拟数据变化 | ||
| 77 | + int counter = 0; | ||
| 78 | + while (true) { | ||
| 79 | + Thread.sleep(5000); // 每 5 秒更新一次 | ||
| 80 | + counter++; | ||
| 81 | + | ||
| 82 | + // 更新输入寄存器的值,模拟传感器数据变化 | ||
| 83 | + for (int i = 0; i < 20; i++) { | ||
| 84 | + int newValue = (i * 10 + 1) + counter; | ||
| 85 | + inputRegisters[i].setValue(newValue); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + // 更新保持寄存器的第一个值 | ||
| 89 | + spi.getRegister(0).setValue(counter * 100); | ||
| 90 | +// System.out.println("[" + java.time.LocalTime.now() + "] 数据已更新, counter=" + counter | ||
| 91 | +// + ", 保持寄存器[0]=" + (counter * 100) | ||
| 92 | +// + ", 输入寄存器[0]=" + (1 + counter)); | ||
| 93 | + } | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | +} |