Commit ed78834eaf5ca092acdc0c56c72484e3b99578df

Authored by YunaiV
1 parent cece79d0

feat:【iot】modbus-tcp 协议接入:30% 初始化:基于 crystalline-giggling-whisper.md 规划

Showing 22 changed files with 155 additions and 275 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusConfigController.java
1 package cn.iocoder.yudao.module.iot.controller.admin.device; 1 package cn.iocoder.yudao.module.iot.controller.admin.device;
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.framework.common.pojo.PageResult;  
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigPageReqVO;  
7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigRespVO; 5 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigRespVO;
8 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO; 6 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO;
9 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; 7 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO;
@@ -62,12 +60,4 @@ public class IotDeviceModbusConfigController { @@ -62,12 +60,4 @@ public class IotDeviceModbusConfigController {
62 return success(BeanUtils.toBean(modbusConfig, IotDeviceModbusConfigRespVO.class)); 60 return success(BeanUtils.toBean(modbusConfig, IotDeviceModbusConfigRespVO.class));
63 } 61 }
64 62
65 - @GetMapping("/page")  
66 - @Operation(summary = "获得设备 Modbus 连接配置分页")  
67 - @PreAuthorize("@ss.hasPermission('iot:device-modbus-config:query')")  
68 - public CommonResult<PageResult<IotDeviceModbusConfigRespVO>> getDeviceModbusConfigPage(@Valid IotDeviceModbusConfigPageReqVO pageReqVO) {  
69 - PageResult<IotDeviceModbusConfigDO> pageResult = modbusConfigService.getDeviceModbusConfigPage(pageReqVO);  
70 - return success(BeanUtils.toBean(pageResult, IotDeviceModbusConfigRespVO.class));  
71 - }  
72 -  
73 } 63 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigPageReqVO.java deleted
1 -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageParam;  
4 -import io.swagger.v3.oas.annotations.media.Schema;  
5 -import lombok.Data;  
6 -import lombok.EqualsAndHashCode;  
7 -import lombok.ToString;  
8 -  
9 -// TODO @AI:不需要分页接口;  
10 -@Schema(description = "管理后台 - IoT 设备 Modbus 连接配置分页 Request VO")  
11 -@Data  
12 -@EqualsAndHashCode(callSuper = true)  
13 -@ToString(callSuper = true)  
14 -public class IotDeviceModbusConfigPageReqVO extends PageParam {  
15 -  
16 - @Schema(description = "设备编号", example = "1024")  
17 - private Long deviceId;  
18 -  
19 - @Schema(description = "Modbus 服务器 IP 地址", example = "192.168.1.100")  
20 - private String ip;  
21 -  
22 - @Schema(description = "状态", example = "0")  
23 - private Integer status;  
24 -  
25 -}  
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceModbusPointDO.java
@@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; @@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device;
2 2
3 import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; 3 import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
4 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; 4 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
5 -import cn.iocoder.yudao.module.iot.enums.device.IotModbusByteOrderEnum;  
6 -import cn.iocoder.yudao.module.iot.enums.device.IotModbusFunctionCodeEnum;  
7 -import cn.iocoder.yudao.module.iot.enums.device.IotModbusRawDataTypeEnum; 5 +import cn.iocoder.yudao.module.iot.core.enums.IotModbusByteOrderEnum;
  6 +import cn.iocoder.yudao.module.iot.core.enums.IotModbusFunctionCodeEnum;
  7 +import cn.iocoder.yudao.module.iot.core.enums.IotModbusRawDataTypeEnum;
8 import com.baomidou.mybatisplus.annotation.KeySequence; 8 import com.baomidou.mybatisplus.annotation.KeySequence;
9 import com.baomidou.mybatisplus.annotation.TableId; 9 import com.baomidou.mybatisplus.annotation.TableId;
10 import com.baomidou.mybatisplus.annotation.TableName; 10 import com.baomidou.mybatisplus.annotation.TableName;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusConfigMapper.java
1 package cn.iocoder.yudao.module.iot.dal.mysql.device; 1 package cn.iocoder.yudao.module.iot.dal.mysql.device;
2 2
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;  
6 -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigPageReqVO;  
7 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; 4 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO;
8 import org.apache.ibatis.annotations.Mapper; 5 import org.apache.ibatis.annotations.Mapper;
9 6
@@ -17,14 +14,6 @@ import java.util.List; @@ -17,14 +14,6 @@ import java.util.List;
17 @Mapper 14 @Mapper
18 public interface IotDeviceModbusConfigMapper extends BaseMapperX<IotDeviceModbusConfigDO> { 15 public interface IotDeviceModbusConfigMapper extends BaseMapperX<IotDeviceModbusConfigDO> {
19 16
20 - default PageResult<IotDeviceModbusConfigDO> selectPage(IotDeviceModbusConfigPageReqVO reqVO) {  
21 - return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceModbusConfigDO>()  
22 - .eqIfPresent(IotDeviceModbusConfigDO::getDeviceId, reqVO.getDeviceId())  
23 - .likeIfPresent(IotDeviceModbusConfigDO::getIp, reqVO.getIp())  
24 - .eqIfPresent(IotDeviceModbusConfigDO::getStatus, reqVO.getStatus())  
25 - .orderByDesc(IotDeviceModbusConfigDO::getId));  
26 - }  
27 -  
28 default IotDeviceModbusConfigDO selectByDeviceId(Long deviceId) { 17 default IotDeviceModbusConfigDO selectByDeviceId(Long deviceId) {
29 return selectOne(IotDeviceModbusConfigDO::getDeviceId, deviceId); 18 return selectOne(IotDeviceModbusConfigDO::getDeviceId, deviceId);
30 } 19 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigService.java
1 package cn.iocoder.yudao.module.iot.service.device; 1 package cn.iocoder.yudao.module.iot.service.device;
2 2
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigPageReqVO;  
5 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO; 3 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO;
6 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; 4 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO;
7 import jakarta.validation.Valid; 5 import jakarta.validation.Valid;
@@ -46,14 +44,6 @@ public interface IotDeviceModbusConfigService { @@ -46,14 +44,6 @@ public interface IotDeviceModbusConfigService {
46 IotDeviceModbusConfigDO getDeviceModbusConfigByDeviceId(Long deviceId); 44 IotDeviceModbusConfigDO getDeviceModbusConfigByDeviceId(Long deviceId);
47 45
48 /** 46 /**
49 - * 获得设备 Modbus 连接配置分页  
50 - *  
51 - * @param pageReqVO 分页查询  
52 - * @return 设备 Modbus 连接配置分页  
53 - */  
54 - PageResult<IotDeviceModbusConfigDO> getDeviceModbusConfigPage(IotDeviceModbusConfigPageReqVO pageReqVO);  
55 -  
56 - /**  
57 * 获得所有启用的 Modbus 连接配置列表 47 * 获得所有启用的 Modbus 连接配置列表
58 * 48 *
59 * @return 启用的 Modbus 连接配置列表 49 * @return 启用的 Modbus 连接配置列表
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigServiceImpl.java
1 package cn.iocoder.yudao.module.iot.service.device; 1 package cn.iocoder.yudao.module.iot.service.device;
2 2
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigPageReqVO;  
7 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO; 5 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO;
8 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; 6 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO;
9 import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceModbusConfigMapper; 7 import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceModbusConfigMapper;
@@ -73,11 +71,6 @@ public class IotDeviceModbusConfigServiceImpl implements IotDeviceModbusConfigSe @@ -73,11 +71,6 @@ public class IotDeviceModbusConfigServiceImpl implements IotDeviceModbusConfigSe
73 } 71 }
74 72
75 @Override 73 @Override
76 - public PageResult<IotDeviceModbusConfigDO> getDeviceModbusConfigPage(IotDeviceModbusConfigPageReqVO pageReqVO) {  
77 - return modbusConfigMapper.selectPage(pageReqVO);  
78 - }  
79 -  
80 - @Override  
81 public List<IotDeviceModbusConfigDO> getEnabledDeviceModbusConfigList() { 74 public List<IotDeviceModbusConfigDO> getEnabledDeviceModbusConfigList() {
82 return modbusConfigMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()); 75 return modbusConfigMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
83 } 76 }
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusPointRespDTO.java
1 package cn.iocoder.yudao.module.iot.core.biz.dto; 1 package cn.iocoder.yudao.module.iot.core.biz.dto;
2 2
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotModbusByteOrderEnum;
  4 +import cn.iocoder.yudao.module.iot.core.enums.IotModbusFunctionCodeEnum;
  5 +import cn.iocoder.yudao.module.iot.core.enums.IotModbusRawDataTypeEnum;
3 import lombok.Data; 6 import lombok.Data;
4 7
5 import java.math.BigDecimal; 8 import java.math.BigDecimal;
@@ -27,11 +30,10 @@ public class IotModbusPointRespDTO { @@ -27,11 +30,10 @@ public class IotModbusPointRespDTO {
27 30
28 // ========== Modbus 协议配置 ========== 31 // ========== Modbus 协议配置 ==========
29 32
30 - // TODO @AI:所有的枚举,通过 @,不要写上去;  
31 /** 33 /**
32 * Modbus 功能码 34 * Modbus 功能码
33 * 35 *
34 - * 1-ReadCoils 2-ReadDiscreteInputs 3-ReadHoldingRegisters 4-ReadInputRegisters 36 + * 枚举 {@link IotModbusFunctionCodeEnum}
35 */ 37 */
36 private Integer functionCode; 38 private Integer functionCode;
37 /** 39 /**
@@ -45,13 +47,13 @@ public class IotModbusPointRespDTO { @@ -45,13 +47,13 @@ public class IotModbusPointRespDTO {
45 /** 47 /**
46 * 字节序 48 * 字节序
47 * 49 *
48 - * AB/BA/ABCD/CDAB/DCBA/BADC 50 + * 枚举 {@link IotModbusByteOrderEnum}
49 */ 51 */
50 private String byteOrder; 52 private String byteOrder;
51 /** 53 /**
52 * 原始数据类型 54 * 原始数据类型
53 * 55 *
54 - * INT16/UINT16/INT32/UINT32/FLOAT/DOUBLE/BOOLEAN/STRING 56 + * 枚举 {@link IotModbusRawDataTypeEnum}
55 */ 57 */
56 private String rawDataType; 58 private String rawDataType;
57 /** 59 /**
@@ -63,12 +65,4 @@ public class IotModbusPointRespDTO { @@ -63,12 +65,4 @@ public class IotModbusPointRespDTO {
63 */ 65 */
64 private Integer pollInterval; 66 private Integer pollInterval;
65 67
66 - // ========== 物模型相关字段 ==========  
67 -  
68 - // TODO @AI:分析一下,是否有必要返回  
69 - /**  
70 - * 数据类型(来自物模型)  
71 - */  
72 - private String dataType;  
73 -  
74 } 68 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusByteOrderEnum.java renamed to yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotModbusByteOrderEnum.java
1 -package cn.iocoder.yudao.module.iot.enums.device; 1 +package cn.iocoder.yudao.module.iot.core.enums;
2 2
3 import cn.iocoder.yudao.framework.common.core.ArrayValuable; 3 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4 import lombok.Getter; 4 import lombok.Getter;
@@ -6,7 +6,6 @@ import lombok.RequiredArgsConstructor; @@ -6,7 +6,6 @@ import lombok.RequiredArgsConstructor;
6 6
7 import java.util.Arrays; 7 import java.util.Arrays;
8 8
9 -// TODO @AI:如果枚举需要共享,可以拿到 /Users/yunai/Java/ruoyi-vue-pro-jdk25/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums 里  
10 /** 9 /**
11 * IoT Modbus 字节序枚举 10 * IoT Modbus 字节序枚举
12 * 11 *
@@ -45,13 +44,6 @@ public enum IotModbusByteOrderEnum implements ArrayValuable&lt;String&gt; { @@ -45,13 +44,6 @@ public enum IotModbusByteOrderEnum implements ArrayValuable&lt;String&gt; {
45 return ARRAYS; 44 return ARRAYS;
46 } 45 }
47 46
48 - // TODO @AI:如果不需要,可以删除掉这个方法;  
49 - /**  
50 - * 根据字节序获取枚举  
51 - *  
52 - * @param order 字节序  
53 - * @return 枚举  
54 - */  
55 public static IotModbusByteOrderEnum getByOrder(String order) { 47 public static IotModbusByteOrderEnum getByOrder(String order) {
56 return Arrays.stream(values()) 48 return Arrays.stream(values())
57 .filter(e -> e.getOrder().equals(order)) 49 .filter(e -> e.getOrder().equals(order))
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusFunctionCodeEnum.java renamed to yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotModbusFunctionCodeEnum.java
1 -package cn.iocoder.yudao.module.iot.enums.device; 1 +package cn.iocoder.yudao.module.iot.core.enums;
2 2
3 import cn.iocoder.yudao.framework.common.core.ArrayValuable; 3 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4 import lombok.Getter; 4 import lombok.Getter;
@@ -6,7 +6,6 @@ import lombok.RequiredArgsConstructor; @@ -6,7 +6,6 @@ import lombok.RequiredArgsConstructor;
6 6
7 import java.util.Arrays; 7 import java.util.Arrays;
8 8
9 -// TODO @AI:如果枚举需要共享,可以拿到 /Users/yunai/Java/ruoyi-vue-pro-jdk25/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums 里  
10 /** 9 /**
11 * IoT Modbus 功能码枚举 10 * IoT Modbus 功能码枚举
12 * 11 *
@@ -51,12 +50,6 @@ public enum IotModbusFunctionCodeEnum implements ArrayValuable&lt;Integer&gt; { @@ -51,12 +50,6 @@ public enum IotModbusFunctionCodeEnum implements ArrayValuable&lt;Integer&gt; {
51 return ARRAYS; 50 return ARRAYS;
52 } 51 }
53 52
54 - /**  
55 - * 根据功能码获取枚举  
56 - *  
57 - * @param code 功能码  
58 - * @return 枚举  
59 - */  
60 public static IotModbusFunctionCodeEnum valueOf(Integer code) { 53 public static IotModbusFunctionCodeEnum valueOf(Integer code) {
61 return Arrays.stream(values()) 54 return Arrays.stream(values())
62 .filter(e -> e.getCode().equals(code)) 55 .filter(e -> e.getCode().equals(code))
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusRawDataTypeEnum.java renamed to yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotModbusRawDataTypeEnum.java
1 -package cn.iocoder.yudao.module.iot.enums.device; 1 +package cn.iocoder.yudao.module.iot.core.enums;
2 2
3 import cn.iocoder.yudao.framework.common.core.ArrayValuable; 3 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4 import lombok.Getter; 4 import lombok.Getter;
@@ -6,7 +6,6 @@ import lombok.RequiredArgsConstructor; @@ -6,7 +6,6 @@ import lombok.RequiredArgsConstructor;
6 6
7 import java.util.Arrays; 7 import java.util.Arrays;
8 8
9 -// TODO @AI:如果枚举需要共享,可以拿到 /Users/yunai/Java/ruoyi-vue-pro-jdk25/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums 里  
10 /** 9 /**
11 * IoT Modbus 原始数据类型枚举 10 * IoT Modbus 原始数据类型枚举
12 * 11 *
@@ -23,7 +22,7 @@ public enum IotModbusRawDataTypeEnum implements ArrayValuable&lt;String&gt; { @@ -23,7 +22,7 @@ public enum IotModbusRawDataTypeEnum implements ArrayValuable&lt;String&gt; {
23 FLOAT("FLOAT", "32 位浮点数", 2), 22 FLOAT("FLOAT", "32 位浮点数", 2),
24 DOUBLE("DOUBLE", "64 位浮点数", 4), 23 DOUBLE("DOUBLE", "64 位浮点数", 4),
25 BOOLEAN("BOOLEAN", "布尔值(用于线圈)", 1), 24 BOOLEAN("BOOLEAN", "布尔值(用于线圈)", 1),
26 - STRING("STRING", "字符串", -1); // -1 表示可变长度 25 + STRING("STRING", "字符串", null); // null 表示可变长度
27 26
28 public static final String[] ARRAYS = Arrays.stream(values()) 27 public static final String[] ARRAYS = Arrays.stream(values())
29 .map(IotModbusRawDataTypeEnum::getType) 28 .map(IotModbusRawDataTypeEnum::getType)
@@ -37,24 +36,16 @@ public enum IotModbusRawDataTypeEnum implements ArrayValuable&lt;String&gt; { @@ -37,24 +36,16 @@ public enum IotModbusRawDataTypeEnum implements ArrayValuable&lt;String&gt; {
37 * 名称 36 * 名称
38 */ 37 */
39 private final String name; 38 private final String name;
40 - // TODO @AI:去掉 default 会好点。null 表示可变;  
41 /** 39 /**
42 - * 默认寄存器数量(-1 表示可变) 40 + * 寄存器数量(null 表示可变)
43 */ 41 */
44 - private final Integer defaultRegisterCount; 42 + private final Integer registerCount;
45 43
46 @Override 44 @Override
47 public String[] array() { 45 public String[] array() {
48 return ARRAYS; 46 return ARRAYS;
49 } 47 }
50 48
51 - // TODO @AI:如果不用,可以删除掉  
52 - /**  
53 - * 根据类型获取枚举  
54 - *  
55 - * @param type 类型  
56 - * @return 枚举  
57 - */  
58 public static IotModbusRawDataTypeEnum getByType(String type) { 49 public static IotModbusRawDataTypeEnum getByType(String type) {
59 return Arrays.stream(values()) 50 return Arrays.stream(values())
60 .filter(e -> e.getType().equals(type)) 51 .filter(e -> e.getType().equals(type))
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java
@@ -520,7 +520,7 @@ public class IotGatewayProperties { @@ -520,7 +520,7 @@ public class IotGatewayProperties {
520 /** 520 /**
521 * 配置刷新间隔(秒) 521 * 配置刷新间隔(秒)
522 */ 522 */
523 - // TODO @AI:需要校验下非空; 523 + @NotNull(message = "配置刷新间隔不能为空")
524 private Integer configRefreshInterval = 30; 524 private Integer configRefreshInterval = 30;
525 525
526 } 526 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusDataConverter.java
@@ -2,7 +2,10 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; @@ -2,7 +2,10 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp;
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;
  5 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO; 6 import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;
  7 +import cn.iocoder.yudao.module.iot.core.enums.IotModbusByteOrderEnum;
  8 +import cn.iocoder.yudao.module.iot.core.enums.IotModbusRawDataTypeEnum;
6 import lombok.extern.slf4j.Slf4j; 9 import lombok.extern.slf4j.Slf4j;
7 10
8 import java.math.BigDecimal; 11 import java.math.BigDecimal;
@@ -79,21 +82,25 @@ public class IotModbusDataConverter { @@ -79,21 +82,25 @@ public class IotModbusDataConverter {
79 */ 82 */
80 @SuppressWarnings("EnhancedSwitchMigration") 83 @SuppressWarnings("EnhancedSwitchMigration")
81 private Number parseRawValue(int[] rawValues, String rawDataType, String byteOrder) { 84 private Number parseRawValue(int[] rawValues, String rawDataType, String byteOrder) {
82 - // TODO @AI:是不是可以用枚举?复用 IotModbusRawDataTypeEnum 里的;(保留现有实现,字符串比较已足够清晰)  
83 - switch (rawDataType.toUpperCase()) {  
84 - case "BOOLEAN": 85 + IotModbusRawDataTypeEnum dataTypeEnum = IotModbusRawDataTypeEnum.getByType(rawDataType);
  86 + if (dataTypeEnum == null) {
  87 + log.warn("[parseRawValue][不支持的数据类型: {}]", rawDataType);
  88 + return rawValues[0];
  89 + }
  90 + switch (dataTypeEnum) {
  91 + case BOOLEAN:
85 return rawValues[0] != 0 ? 1 : 0; 92 return rawValues[0] != 0 ? 1 : 0;
86 - case "INT16": 93 + case INT16:
87 return (short) rawValues[0]; 94 return (short) rawValues[0];
88 - case "UINT16": 95 + case UINT16:
89 return rawValues[0] & 0xFFFF; 96 return rawValues[0] & 0xFFFF;
90 - case "INT32": 97 + case INT32:
91 return parseInt32(rawValues, byteOrder); 98 return parseInt32(rawValues, byteOrder);
92 - case "UINT32": 99 + case UINT32:
93 return parseUint32(rawValues, byteOrder); 100 return parseUint32(rawValues, byteOrder);
94 - case "FLOAT": 101 + case FLOAT:
95 return parseFloat(rawValues, byteOrder); 102 return parseFloat(rawValues, byteOrder);
96 - case "DOUBLE": 103 + case DOUBLE:
97 return parseDouble(rawValues, byteOrder); 104 return parseDouble(rawValues, byteOrder);
98 default: 105 default:
99 log.warn("[parseRawValue][不支持的数据类型: {}]", rawDataType); 106 log.warn("[parseRawValue][不支持的数据类型: {}]", rawDataType);
@@ -148,24 +155,24 @@ public class IotModbusDataConverter { @@ -148,24 +155,24 @@ public class IotModbusDataConverter {
148 /** 155 /**
149 * 根据字节序重排字节 156 * 根据字节序重排字节
150 */ 157 */
  158 + @SuppressWarnings("EnhancedSwitchMigration")
151 private byte[] reorderBytes(byte[] bytes, String byteOrder) { 159 private byte[] reorderBytes(byte[] bytes, String byteOrder) {
152 - // 大端序,不需要调整  
153 - // TODO @AI:StrUtil.equals;null 要抛出异常;(保留 null 默认为大端序的兼容逻辑)  
154 - if (byteOrder == null || "ABCD".equals(byteOrder) || "AB".equals(byteOrder)) { 160 + IotModbusByteOrderEnum byteOrderEnum = IotModbusByteOrderEnum.getByOrder(byteOrder);
  161 + // null 或者大端序,不需要调整
  162 + if (ObjectUtils.equalsAny(byteOrderEnum, null, IotModbusByteOrderEnum.ABCD, IotModbusByteOrderEnum.AB)) {
155 return bytes; 163 return bytes;
156 } 164 }
157 165
158 // 其他字节序调整 166 // 其他字节序调整
159 byte[] result = new byte[bytes.length]; 167 byte[] result = new byte[bytes.length];
160 - switch (byteOrder.toUpperCase()) {  
161 - // TODO @AI:走枚举;sortOrder;(参考 IotModbusByteOrderEnum 枚举定义)  
162 - case "BA": // 小端序(16 位) 168 + switch (byteOrderEnum) {
  169 + case BA: // 小端序(16 位)
163 if (bytes.length >= 2) { 170 if (bytes.length >= 2) {
164 result[0] = bytes[1]; 171 result[0] = bytes[1];
165 result[1] = bytes[0]; 172 result[1] = bytes[0];
166 } 173 }
167 break; 174 break;
168 - case "CDAB": // 大端字交换(32 位) 175 + case CDAB: // 大端字交换(32 位)
169 if (bytes.length >= 4) { 176 if (bytes.length >= 4) {
170 result[0] = bytes[2]; 177 result[0] = bytes[2];
171 result[1] = bytes[3]; 178 result[1] = bytes[3];
@@ -173,7 +180,7 @@ public class IotModbusDataConverter { @@ -173,7 +180,7 @@ public class IotModbusDataConverter {
173 result[3] = bytes[1]; 180 result[3] = bytes[1];
174 } 181 }
175 break; 182 break;
176 - case "DCBA": // 小端序(32 位) 183 + case DCBA: // 小端序(32 位)
177 if (bytes.length >= 4) { 184 if (bytes.length >= 4) {
178 result[0] = bytes[3]; 185 result[0] = bytes[3];
179 result[1] = bytes[2]; 186 result[1] = bytes[2];
@@ -181,7 +188,7 @@ public class IotModbusDataConverter { @@ -181,7 +188,7 @@ public class IotModbusDataConverter {
181 result[3] = bytes[0]; 188 result[3] = bytes[0];
182 } 189 }
183 break; 190 break;
184 - case "BADC": // 小端字交换(32 位) 191 + case BADC: // 小端字交换(32 位)
185 if (bytes.length >= 4) { 192 if (bytes.length >= 4) {
186 result[0] = bytes[1]; 193 result[0] = bytes[1];
187 result[1] = bytes[0]; 194 result[1] = bytes[0];
@@ -198,20 +205,24 @@ public class IotModbusDataConverter { @@ -198,20 +205,24 @@ public class IotModbusDataConverter {
198 /** 205 /**
199 * 编码为寄存器值 206 * 编码为寄存器值
200 */ 207 */
  208 + @SuppressWarnings("EnhancedSwitchMigration")
201 private int[] encodeToRegisters(BigDecimal rawValue, String rawDataType, String byteOrder, int registerCount) { 209 private int[] encodeToRegisters(BigDecimal rawValue, String rawDataType, String byteOrder, int registerCount) {
202 - // TODO @AI:是不是可以用枚举?复用 IotModbusRawDataTypeEnum 里的;(保留现有实现,字符串比较已足够清晰)  
203 - switch (rawDataType.toUpperCase()) {  
204 - case "BOOLEAN": 210 + IotModbusRawDataTypeEnum dataTypeEnum = IotModbusRawDataTypeEnum.getByType(rawDataType);
  211 + if (dataTypeEnum == null) {
  212 + return new int[]{rawValue.intValue()};
  213 + }
  214 + switch (dataTypeEnum) {
  215 + case BOOLEAN:
205 return new int[]{rawValue.intValue() != 0 ? 1 : 0}; 216 return new int[]{rawValue.intValue() != 0 ? 1 : 0};
206 - case "INT16":  
207 - case "UINT16": 217 + case INT16:
  218 + case UINT16:
208 return new int[]{rawValue.intValue() & 0xFFFF}; 219 return new int[]{rawValue.intValue() & 0xFFFF};
209 - case "INT32":  
210 - case "UINT32": 220 + case INT32:
  221 + case UINT32:
211 return encodeInt32(rawValue.intValue(), byteOrder); 222 return encodeInt32(rawValue.intValue(), byteOrder);
212 - case "FLOAT": 223 + case FLOAT:
213 return encodeFloat(rawValue.floatValue(), byteOrder); 224 return encodeFloat(rawValue.floatValue(), byteOrder);
214 - case "DOUBLE": 225 + case DOUBLE:
215 return encodeDouble(rawValue.doubleValue(), byteOrder); 226 return encodeDouble(rawValue.doubleValue(), byteOrder);
216 default: 227 default:
217 return new int[]{rawValue.intValue()}; 228 return new int[]{rawValue.intValue()};
@@ -247,20 +258,24 @@ public class IotModbusDataConverter { @@ -247,20 +258,24 @@ public class IotModbusDataConverter {
247 /** 258 /**
248 * 格式化返回值 259 * 格式化返回值
249 */ 260 */
  261 + @SuppressWarnings("EnhancedSwitchMigration")
250 private Object formatValue(BigDecimal value, String rawDataType) { 262 private Object formatValue(BigDecimal value, String rawDataType) {
251 - // TODO @AI:是不是可以用枚举?复用 IotModbusRawDataTypeEnum 里的;(保留现有实现,字符串比较已足够清晰)  
252 - switch (rawDataType.toUpperCase()) {  
253 - case "BOOLEAN": 263 + IotModbusRawDataTypeEnum dataTypeEnum = IotModbusRawDataTypeEnum.getByType(rawDataType);
  264 + if (dataTypeEnum == null) {
  265 + return value;
  266 + }
  267 + switch (dataTypeEnum) {
  268 + case BOOLEAN:
254 return value.intValue() != 0; 269 return value.intValue() != 0;
255 - case "INT16":  
256 - case "INT32": 270 + case INT16:
  271 + case INT32:
257 return value.intValue(); 272 return value.intValue();
258 - case "UINT16":  
259 - case "UINT32": 273 + case UINT16:
  274 + case UINT32:
260 return value.longValue(); 275 return value.longValue();
261 - case "FLOAT": 276 + case FLOAT:
262 return value.floatValue(); 277 return value.floatValue();
263 - case "DOUBLE": 278 + case DOUBLE:
264 return value.doubleValue(); 279 return value.doubleValue();
265 default: 280 default:
266 return value; 281 return value;
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpClient.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp;
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 com.ghgande.j2mod.modbus.io.ModbusTCPTransaction; 5 import com.ghgande.j2mod.modbus.io.ModbusTCPTransaction;
5 import com.ghgande.j2mod.modbus.msg.*; 6 import com.ghgande.j2mod.modbus.msg.*;
6 import com.ghgande.j2mod.modbus.procimg.InputRegister; 7 import com.ghgande.j2mod.modbus.procimg.InputRegister;
@@ -91,16 +92,20 @@ public class IotModbusTcpClient { @@ -91,16 +92,20 @@ public class IotModbusTcpClient {
91 /** 92 /**
92 * 创建读取请求 93 * 创建读取请求
93 */ 94 */
  95 + @SuppressWarnings("EnhancedSwitchMigration")
94 private ModbusRequest createReadRequest(Integer functionCode, Integer address, Integer count) { 96 private ModbusRequest createReadRequest(Integer functionCode, Integer address, Integer count) {
95 - // TODO @AI:1、2、3、4 能不能有枚举哈?这样 1、2、3、4 那的注释就不用写;  
96 - switch (functionCode) {  
97 - case 1: 97 + IotModbusFunctionCodeEnum functionCodeEnum = IotModbusFunctionCodeEnum.valueOf(functionCode);
  98 + if (functionCodeEnum == null) {
  99 + throw new IllegalArgumentException("不支持的功能码: " + functionCode);
  100 + }
  101 + switch (functionCodeEnum) {
  102 + case READ_COILS:
98 return new ReadCoilsRequest(address, count); 103 return new ReadCoilsRequest(address, count);
99 - case 2: // ReadDiscreteInputs 104 + case READ_DISCRETE_INPUTS:
100 return new ReadInputDiscretesRequest(address, count); 105 return new ReadInputDiscretesRequest(address, count);
101 - case 3: // ReadHoldingRegisters 106 + case READ_HOLDING_REGISTERS:
102 return new ReadMultipleRegistersRequest(address, count); 107 return new ReadMultipleRegistersRequest(address, count);
103 - case 4: // ReadInputRegisters 108 + case READ_INPUT_REGISTERS:
104 return new ReadInputRegistersRequest(address, count); 109 return new ReadInputRegistersRequest(address, count);
105 default: 110 default:
106 throw new IllegalArgumentException("不支持的功能码: " + functionCode); 111 throw new IllegalArgumentException("不支持的功能码: " + functionCode);
@@ -111,31 +116,33 @@ public class IotModbusTcpClient { @@ -111,31 +116,33 @@ public class IotModbusTcpClient {
111 * 创建写入请求 116 * 创建写入请求
112 */ 117 */
113 private ModbusRequest createWriteRequest(Integer functionCode, Integer address, Integer count, int[] values) { 118 private ModbusRequest createWriteRequest(Integer functionCode, Integer address, Integer count, int[] values) {
114 - // TODO @AI:5、6、15、16 能不能有枚举哈?这样 5、6、15、16 那的注释就不用写;  
115 - switch (functionCode) {  
116 - case 1: // WriteCoils (使用 5 或 15) 119 + IotModbusFunctionCodeEnum functionCodeEnum = IotModbusFunctionCodeEnum.valueOf(functionCode);
  120 + if (functionCodeEnum == null) {
  121 + throw new IllegalArgumentException("不支持的功能码: " + functionCode);
  122 + }
  123 + switch (functionCodeEnum) {
  124 + case READ_COILS: // 写线圈(使用功能码 5 或 15)
117 if (count == 1) { 125 if (count == 1) {
118 return new WriteCoilRequest(address, values[0] != 0); 126 return new WriteCoilRequest(address, values[0] != 0);
119 } else { 127 } else {
120 - // 多线圈写入  
121 BitVector bv = new BitVector(count); 128 BitVector bv = new BitVector(count);
122 for (int i = 0; i < Math.min(values.length, count); i++) { 129 for (int i = 0; i < Math.min(values.length, count); i++) {
123 bv.setBit(i, values[i] != 0); 130 bv.setBit(i, values[i] != 0);
124 } 131 }
125 return new WriteMultipleCoilsRequest(address, bv); 132 return new WriteMultipleCoilsRequest(address, bv);
126 } 133 }
127 - case 3: // WriteHoldingRegisters (使用 6 或 16) 134 + case READ_HOLDING_REGISTERS: // 写保持寄存器(使用功能码 6 或 16)
128 if (count == 1) { 135 if (count == 1) {
129 return new WriteSingleRegisterRequest(address, new SimpleRegister(values[0])); 136 return new WriteSingleRegisterRequest(address, new SimpleRegister(values[0]));
130 } else { 137 } else {
131 - Register[] registers = new com.ghgande.j2mod.modbus.procimg.SimpleRegister[count]; 138 + Register[] registers = new SimpleRegister[count];
132 for (int i = 0; i < count; i++) { 139 for (int i = 0; i < count; i++) {
133 registers[i] = new SimpleRegister(i < values.length ? values[i] : 0); 140 registers[i] = new SimpleRegister(i < values.length ? values[i] : 0);
134 } 141 }
135 return new WriteMultipleRegistersRequest(address, registers); 142 return new WriteMultipleRegistersRequest(address, registers);
136 } 143 }
137 - case 2: // ReadDiscreteInputs - 只读  
138 - case 4: // ReadInputRegisters - 只读 144 + case READ_DISCRETE_INPUTS: // 只读
  145 + case READ_INPUT_REGISTERS: // 只读
139 return null; 146 return null;
140 default: 147 default:
141 throw new IllegalArgumentException("不支持的功能码: " + functionCode); 148 throw new IllegalArgumentException("不支持的功能码: " + functionCode);
@@ -146,9 +153,12 @@ public class IotModbusTcpClient { @@ -146,9 +153,12 @@ public class IotModbusTcpClient {
146 * 从响应中提取值 153 * 从响应中提取值
147 */ 154 */
148 private int[] extractValues(ModbusResponse response, Integer functionCode) { 155 private int[] extractValues(ModbusResponse response, Integer functionCode) {
149 - // TODO @AI:1、2、3、4 能不能有枚举哈?这样 1、2、3、4 那的注释就不用写;  
150 - switch (functionCode) {  
151 - case 1: 156 + IotModbusFunctionCodeEnum functionCodeEnum = IotModbusFunctionCodeEnum.valueOf(functionCode);
  157 + if (functionCodeEnum == null) {
  158 + throw new IllegalArgumentException("不支持的功能码: " + functionCode);
  159 + }
  160 + switch (functionCodeEnum) {
  161 + case READ_COILS:
152 ReadCoilsResponse coilsResponse = (ReadCoilsResponse) response; 162 ReadCoilsResponse coilsResponse = (ReadCoilsResponse) response;
153 int bitCount = coilsResponse.getBitCount(); 163 int bitCount = coilsResponse.getBitCount();
154 int[] coilValues = new int[bitCount]; 164 int[] coilValues = new int[bitCount];
@@ -156,7 +166,7 @@ public class IotModbusTcpClient { @@ -156,7 +166,7 @@ public class IotModbusTcpClient {
156 coilValues[i] = coilsResponse.getCoilStatus(i) ? 1 : 0; 166 coilValues[i] = coilsResponse.getCoilStatus(i) ? 1 : 0;
157 } 167 }
158 return coilValues; 168 return coilValues;
159 - case 2: // ReadDiscreteInputs 169 + case READ_DISCRETE_INPUTS:
160 ReadInputDiscretesResponse discretesResponse = (ReadInputDiscretesResponse) response; 170 ReadInputDiscretesResponse discretesResponse = (ReadInputDiscretesResponse) response;
161 int discreteCount = discretesResponse.getBitCount(); 171 int discreteCount = discretesResponse.getBitCount();
162 int[] discreteValues = new int[discreteCount]; 172 int[] discreteValues = new int[discreteCount];
@@ -164,7 +174,7 @@ public class IotModbusTcpClient { @@ -164,7 +174,7 @@ public class IotModbusTcpClient {
164 discreteValues[i] = discretesResponse.getDiscreteStatus(i) ? 1 : 0; 174 discreteValues[i] = discretesResponse.getDiscreteStatus(i) ? 1 : 0;
165 } 175 }
166 return discreteValues; 176 return discreteValues;
167 - case 3: // ReadHoldingRegisters 177 + case READ_HOLDING_REGISTERS:
168 ReadMultipleRegistersResponse holdingResponse = (ReadMultipleRegistersResponse) response; 178 ReadMultipleRegistersResponse holdingResponse = (ReadMultipleRegistersResponse) response;
169 InputRegister[] holdingRegisters = holdingResponse.getRegisters(); 179 InputRegister[] holdingRegisters = holdingResponse.getRegisters();
170 int[] holdingValues = new int[holdingRegisters.length]; 180 int[] holdingValues = new int[holdingRegisters.length];
@@ -172,7 +182,7 @@ public class IotModbusTcpClient { @@ -172,7 +182,7 @@ public class IotModbusTcpClient {
172 holdingValues[i] = holdingRegisters[i].getValue(); 182 holdingValues[i] = holdingRegisters[i].getValue();
173 } 183 }
174 return holdingValues; 184 return holdingValues;
175 - case 4: // ReadInputRegisters 185 + case READ_INPUT_REGISTERS:
176 ReadInputRegistersResponse inputResponse = (ReadInputRegistersResponse) response; 186 ReadInputRegistersResponse inputResponse = (ReadInputRegistersResponse) response;
177 InputRegister[] inputRegisters = inputResponse.getRegisters(); 187 InputRegister[] inputRegisters = inputResponse.getRegisters();
178 int[] inputValues = new int[inputRegisters.length]; 188 int[] inputValues = new int[inputRegisters.length];
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConfigCacheService.java
@@ -29,7 +29,9 @@ public class IotModbusTcpConfigCacheService { @@ -29,7 +29,9 @@ public class IotModbusTcpConfigCacheService {
29 private final Map<Long, IotModbusDeviceConfigRespDTO> configCache = new ConcurrentHashMap<>(); 29 private final Map<Long, IotModbusDeviceConfigRespDTO> configCache = new ConcurrentHashMap<>();
30 30
31 /** 31 /**
32 - * 已知的设备 ID 集合 32 + * 已知的设备 ID 集合(作用:用于检测已删除的设备)
  33 + *
  34 + * @see #cleanupRemovedDevices(List, Consumer)
33 */ 35 */
34 private final Set<Long> knownDeviceIds = ConcurrentHashMap.newKeySet(); 36 private final Set<Long> knownDeviceIds = ConcurrentHashMap.newKeySet();
35 37
@@ -48,10 +50,9 @@ public class IotModbusTcpConfigCacheService { @@ -48,10 +50,9 @@ public class IotModbusTcpConfigCacheService {
48 } 50 }
49 List<IotModbusDeviceConfigRespDTO> configs = result.getData(); 51 List<IotModbusDeviceConfigRespDTO> configs = result.getData();
50 52
51 - // 2. 更新缓存 53 + // 2. 更新缓存(注意:不在这里更新 knownDeviceIds,由 cleanupRemovedDevices 统一管理)
52 for (IotModbusDeviceConfigRespDTO config : configs) { 54 for (IotModbusDeviceConfigRespDTO config : configs) {
53 configCache.put(config.getDeviceId(), config); 55 configCache.put(config.getDeviceId(), config);
54 - knownDeviceIds.add(config.getDeviceId());  
55 } 56 }
56 return configs; 57 return configs;
57 } catch (Exception e) { 58 } catch (Exception e) {
@@ -70,9 +71,8 @@ public class IotModbusTcpConfigCacheService { @@ -70,9 +71,8 @@ public class IotModbusTcpConfigCacheService {
70 return configCache.get(deviceId); 71 return configCache.get(deviceId);
71 } 72 }
72 73
73 - // TODO @AI:怎么感觉 cleanupRemovedDevices 的时候,knownDeviceIds 已经在 refreshConfig 里更新了???  
74 /** 74 /**
75 - * 清理已删除设备的资源 75 + * 清理已删除设备的资源,并更新已知设备 ID 集合
76 * 76 *
77 * @param currentConfigs 当前有效的配置列表 77 * @param currentConfigs 当前有效的配置列表
78 * @param cleanupAction 清理动作 78 * @param cleanupAction 清理动作
@@ -80,7 +80,7 @@ public class IotModbusTcpConfigCacheService { @@ -80,7 +80,7 @@ public class IotModbusTcpConfigCacheService {
80 public void cleanupRemovedDevices(List<IotModbusDeviceConfigRespDTO> currentConfigs, Consumer<Long> cleanupAction) { 80 public void cleanupRemovedDevices(List<IotModbusDeviceConfigRespDTO> currentConfigs, Consumer<Long> cleanupAction) {
81 // 1.1 获取当前有效的设备 ID 81 // 1.1 获取当前有效的设备 ID
82 Set<Long> currentDeviceIds = convertSet(currentConfigs, IotModbusDeviceConfigRespDTO::getDeviceId); 82 Set<Long> currentDeviceIds = convertSet(currentConfigs, IotModbusDeviceConfigRespDTO::getDeviceId);
83 - // 1.2 找出已删除的设备 83 + // 1.2 找出已删除的设备(基于旧的 knownDeviceIds)
84 Set<Long> removedDeviceIds = new HashSet<>(knownDeviceIds); 84 Set<Long> removedDeviceIds = new HashSet<>(knownDeviceIds);
85 removedDeviceIds.removeAll(currentDeviceIds); 85 removedDeviceIds.removeAll(currentDeviceIds);
86 86
@@ -88,9 +88,12 @@ public class IotModbusTcpConfigCacheService { @@ -88,9 +88,12 @@ public class IotModbusTcpConfigCacheService {
88 for (Long deviceId : removedDeviceIds) { 88 for (Long deviceId : removedDeviceIds) {
89 log.info("[cleanupRemovedDevices][清理已删除设备: {}]", deviceId); 89 log.info("[cleanupRemovedDevices][清理已删除设备: {}]", deviceId);
90 configCache.remove(deviceId); 90 configCache.remove(deviceId);
91 - knownDeviceIds.remove(deviceId);  
92 cleanupAction.accept(deviceId); 91 cleanupAction.accept(deviceId);
93 } 92 }
  93 +
  94 + // 3. 更新已知设备 ID 集合为当前有效的设备 ID
  95 + knownDeviceIds.clear();
  96 + knownDeviceIds.addAll(currentDeviceIds);
94 } 97 }
95 98
96 } 99 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConnectionManager.java
@@ -27,8 +27,7 @@ import java.util.concurrent.ConcurrentHashMap; @@ -27,8 +27,7 @@ import java.util.concurrent.ConcurrentHashMap;
27 @Slf4j 27 @Slf4j
28 public class IotModbusTcpConnectionManager { 28 public class IotModbusTcpConnectionManager {
29 29
30 - // TODO @AI:iot:modbus-tcp:connection:  
31 - private static final String LOCK_KEY_PREFIX = "iot:modbus:connection:"; 30 + private static final String LOCK_KEY_PREFIX = "iot:modbus-tcp:connection:";
32 31
33 private final RedissonClient redissonClient; 32 private final RedissonClient redissonClient;
34 private final Vertx vertx; 33 private final Vertx vertx;
@@ -56,11 +55,7 @@ public class IotModbusTcpConnectionManager { @@ -56,11 +55,7 @@ public class IotModbusTcpConnectionManager {
56 // 2. 情况一:连接已存在,添加设备引用 55 // 2. 情况一:连接已存在,添加设备引用
57 ModbusConnection connection = connectionPool.get(connectionKey); 56 ModbusConnection connection = connectionPool.get(connectionKey);
58 if (connection != null) { 57 if (connection != null) {
59 - // 添加设备引用  
60 connection.addDevice(config.getDeviceId(), config.getSlaveId()); 58 connection.addDevice(config.getDeviceId(), config.getSlaveId());
61 - // 更新连接参数(取最小值)  
62 - // TODO @AI:(不确定)如果后续最小值被移除后,是不是无法灰度到上一个最小值?  
63 - connection.updateParams(config.getTimeout(), config.getRetryInterval());  
64 return; 59 return;
65 } 60 }
66 61
@@ -86,22 +81,17 @@ public class IotModbusTcpConnectionManager { @@ -86,22 +81,17 @@ public class IotModbusTcpConnectionManager {
86 * 创建 Modbus TCP 连接 81 * 创建 Modbus TCP 连接
87 */ 82 */
88 private ModbusConnection createConnection(IotModbusDeviceConfigRespDTO config, RLock lock) throws Exception { 83 private ModbusConnection createConnection(IotModbusDeviceConfigRespDTO config, RLock lock) throws Exception {
89 - // 创建 TCP 连接  
90 - // TODO @AI:需要重连么? 84 + // 1. 创建 TCP 连接
91 TCPMasterConnection tcpConnection = new TCPMasterConnection(InetAddress.getByName(config.getIp())); 85 TCPMasterConnection tcpConnection = new TCPMasterConnection(InetAddress.getByName(config.getIp()));
92 tcpConnection.setPort(config.getPort()); 86 tcpConnection.setPort(config.getPort());
93 tcpConnection.setTimeout(config.getTimeout()); 87 tcpConnection.setTimeout(config.getTimeout());
94 tcpConnection.connect(); 88 tcpConnection.connect();
95 89
96 - // 创建 Modbus 连接对象  
97 - ModbusConnection connection = new ModbusConnection();  
98 - // TODO @AI:链式调用,简化下;  
99 - connection.setConnectionKey(buildConnectionKey(config.getIp(), config.getPort()));  
100 - connection.setTcpConnection(tcpConnection);  
101 - connection.setLock(lock);  
102 - connection.setTimeout(config.getTimeout());  
103 - connection.setRetryInterval(config.getRetryInterval());  
104 - connection.setContext(vertx.getOrCreateContext()); 90 + // 2. 创建 Modbus 连接对象
  91 + ModbusConnection connection = new ModbusConnection()
  92 + .setConnectionKey(buildConnectionKey(config.getIp(), config.getPort()))
  93 + .setTcpConnection(tcpConnection).setLock(lock).setContext(vertx.getOrCreateContext())
  94 + .setTimeout(config.getTimeout()).setRetryInterval(config.getRetryInterval());
105 connection.addDevice(config.getDeviceId(), config.getSlaveId()); 95 connection.addDevice(config.getDeviceId(), config.getSlaveId());
106 return connection; 96 return connection;
107 } 97 }
@@ -162,9 +152,10 @@ public class IotModbusTcpConnectionManager { @@ -162,9 +152,10 @@ public class IotModbusTcpConnectionManager {
162 if (connection.getTcpConnection() != null) { 152 if (connection.getTcpConnection() != null) {
163 connection.getTcpConnection().close(); 153 connection.getTcpConnection().close();
164 } 154 }
165 - // TODO @AI:(不确定)是不是要当前线程?还是当前进程就 ok 了。  
166 - if (connection.getLock() != null && connection.getLock().isHeldByCurrentThread()) {  
167 - connection.getLock().unlock(); 155 + // 安全释放锁:先检查锁存在且被锁定,再检查是否当前线程持有
  156 + RLock lock = connection.getLock();
  157 + if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
  158 + lock.unlock();
168 } 159 }
169 log.info("[closeConnection][关闭 Modbus 连接: {}]", connectionKey); 160 log.info("[closeConnection][关闭 Modbus 连接: {}]", connectionKey);
170 } catch (Exception e) { 161 } catch (Exception e) {
@@ -220,16 +211,6 @@ public class IotModbusTcpConnectionManager { @@ -220,16 +211,6 @@ public class IotModbusTcpConnectionManager {
220 return deviceSlaveMap.get(deviceId); 211 return deviceSlaveMap.get(deviceId);
221 } 212 }
222 213
223 - public void updateParams(Integer timeout, Integer retryInterval) {  
224 - // 取最小值  
225 - if (timeout != null && (this.timeout == null || timeout < this.timeout)) {  
226 - this.timeout = timeout;  
227 - }  
228 - if (retryInterval != null && (this.retryInterval == null || retryInterval < this.retryInterval)) {  
229 - this.retryInterval = retryInterval;  
230 - }  
231 - }  
232 -  
233 /** 214 /**
234 * 执行 Modbus 读取操作(阻塞方式,在 Vert.x worker 线程执行) 215 * 执行 Modbus 读取操作(阻塞方式,在 Vert.x worker 线程执行)
235 */ 216 */
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpDownstreamHandler.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
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.IotModbusFunctionCodeEnum;
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 import lombok.RequiredArgsConstructor; 8 import lombok.RequiredArgsConstructor;
7 import lombok.extern.slf4j.Slf4j; 9 import lombok.extern.slf4j.Slf4j;
@@ -101,23 +103,15 @@ public class IotModbusTcpDownstreamHandler { @@ -101,23 +103,15 @@ public class IotModbusTcpDownstreamHandler {
101 * 查找点位配置 103 * 查找点位配置
102 */ 104 */
103 private IotModbusPointRespDTO findPoint(IotModbusDeviceConfigRespDTO config, String identifier) { 105 private IotModbusPointRespDTO findPoint(IotModbusDeviceConfigRespDTO config, String identifier) {
104 - if (config.getPoints() == null) {  
105 - return null;  
106 - }  
107 - // TODO @AI:hutool findOne?  
108 - return config.getPoints().stream()  
109 - .filter(p -> identifier.equals(p.getIdentifier()))  
110 - .findFirst()  
111 - .orElse(null); 106 + return CollUtil.findOne(config.getPoints(), p -> identifier.equals(p.getIdentifier()));
112 } 107 }
113 108
114 /** 109 /**
115 * 检查功能码是否支持写操作 110 * 检查功能码是否支持写操作
116 */ 111 */
117 private boolean isWritable(Integer functionCode) { 112 private boolean isWritable(Integer functionCode) {
118 - // TODO @AI:能不能通过 枚举优化下?  
119 - // 功能码 1(ReadCoils)和 3(ReadHoldingRegisters)支持写操作  
120 - return functionCode != null && (functionCode == 1 || functionCode == 3); 113 + IotModbusFunctionCodeEnum functionCodeEnum = IotModbusFunctionCodeEnum.valueOf(functionCode);
  114 + return functionCodeEnum != null && Boolean.TRUE.equals(functionCodeEnum.getWritable());
121 } 115 }
122 116
123 } 117 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpDownstreamSubscriber.java
@@ -9,15 +9,10 @@ import lombok.RequiredArgsConstructor; @@ -9,15 +9,10 @@ import lombok.RequiredArgsConstructor;
9 import lombok.extern.slf4j.Slf4j; 9 import lombok.extern.slf4j.Slf4j;
10 10
11 /** 11 /**
12 - * IoT Modbus TCP 下行消息订阅器  
13 - *  
14 - * 负责:  
15 - * 1. 订阅消息总线的下行消息  
16 - * 2. 将属性设置消息转发给下行处理器 12 + * IoT Modbus TCP 下行消息订阅器:订阅消息总线的下行消息并转发给处理器
17 * 13 *
18 * @author 芋道源码 14 * @author 芋道源码
19 */ 15 */
20 -// TODO @AI:希望它的初始化,在 configuration 里;  
21 @RequiredArgsConstructor 16 @RequiredArgsConstructor
22 @Slf4j 17 @Slf4j
23 public class IotModbusTcpDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { 18 public class IotModbusTcpDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpPollScheduler.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
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;
5 import io.vertx.core.Vertx; 6 import io.vertx.core.Vertx;
@@ -11,14 +12,8 @@ import java.util.List; @@ -11,14 +12,8 @@ import java.util.List;
11 import java.util.Map; 12 import java.util.Map;
12 import java.util.concurrent.ConcurrentHashMap; 13 import java.util.concurrent.ConcurrentHashMap;
13 14
14 -// TODO @AI:注释可以简化?  
15 /** 15 /**
16 - * IoT Modbus TCP 轮询调度器  
17 - *  
18 - * 负责:  
19 - * 1. 管理每个点位的轮询定时器  
20 - * 2. 调度 Modbus 读取任务  
21 - * 3. 处理读取结果并上报 16 + * IoT Modbus TCP 轮询调度器:管理点位的轮询定时器,调度读取任务并上报结果
22 * 17 *
23 * @author 芋道源码 18 * @author 芋道源码
24 */ 19 */
@@ -46,22 +41,22 @@ public class IotModbusTcpPollScheduler { @@ -46,22 +41,22 @@ public class IotModbusTcpPollScheduler {
46 stopPolling(deviceId); 41 stopPolling(deviceId);
47 42
48 // 2.1 为每个点位创建新的轮询任务 43 // 2.1 为每个点位创建新的轮询任务
49 - List<Long> timerIds = new ArrayList<>();  
50 - // TODO @AI:if return 简化;上面的 size 加下;config.getPoints()  
51 - if (config.getPoints() != null) {  
52 - for (IotModbusPointRespDTO point : config.getPoints()) {  
53 - Long timerId = createPollTimer(config, point);  
54 - if (timerId != null) {  
55 - timerIds.add(timerId);  
56 - } 44 + if (CollUtil.isEmpty(config.getPoints())) {
  45 + return;
  46 + }
  47 + List<Long> timerIds = new ArrayList<>(config.getPoints().size());
  48 + for (IotModbusPointRespDTO point : config.getPoints()) {
  49 + Long timerId = createPollTimer(config, point);
  50 + if (timerId != null) {
  51 + timerIds.add(timerId);
57 } 52 }
58 } 53 }
59 // 2.2 记录定时器 54 // 2.2 记录定时器
60 - // TODO @AI:CollUtil.isNotEmpty;if return 简化;  
61 - if (!timerIds.isEmpty()) {  
62 - deviceTimers.put(deviceId, timerIds);  
63 - log.debug("[updatePolling][设备 {} 创建了 {} 个轮询定时器]", deviceId, timerIds.size()); 55 + if (CollUtil.isEmpty(timerIds)) {
  56 + return;
64 } 57 }
  58 + deviceTimers.put(deviceId, timerIds);
  59 + log.debug("[updatePolling][设备 {} 创建了 {} 个轮询定时器]", deviceId, timerIds.size());
65 } 60 }
66 61
67 /** 62 /**
@@ -99,7 +94,6 @@ public class IotModbusTcpPollScheduler { @@ -99,7 +94,6 @@ public class IotModbusTcpPollScheduler {
99 } 94 }
100 95
101 // 2. 执行 Modbus 读取 96 // 2. 执行 Modbus 读取
102 - // TODO @AI:超时时间,有实现么???  
103 modbusClient.read(connection, slaveId, point) 97 modbusClient.read(connection, slaveId, point)
104 .onSuccess(rawValue -> upstreamHandler.handleReadResult(config, point, rawValue)) 98 .onSuccess(rawValue -> upstreamHandler.handleReadResult(config, point, rawValue))
105 .onFailure(e -> log.error("[pollPoint][读取点位失败, deviceId={}, identifier={}]", 99 .onFailure(e -> log.error("[pollPoint][读取点位失败, deviceId={}, identifier={}]",
@@ -111,13 +105,13 @@ public class IotModbusTcpPollScheduler { @@ -111,13 +105,13 @@ public class IotModbusTcpPollScheduler {
111 */ 105 */
112 public void stopPolling(Long deviceId) { 106 public void stopPolling(Long deviceId) {
113 List<Long> timerIds = deviceTimers.remove(deviceId); 107 List<Long> timerIds = deviceTimers.remove(deviceId);
114 - // TODO @AI:CollUtil.isNotEmpty;并且 if return;  
115 - if (timerIds != null) {  
116 - for (Long timerId : timerIds) {  
117 - vertx.cancelTimer(timerId);  
118 - }  
119 - log.debug("[stopPolling][设备 {} 停止了 {} 个轮询定时器]", deviceId, timerIds.size()); 108 + if (CollUtil.isEmpty(timerIds)) {
  109 + return;
  110 + }
  111 + for (Long timerId : timerIds) {
  112 + vertx.cancelTimer(timerId);
120 } 113 }
  114 + log.debug("[stopPolling][设备 {} 停止了 {} 个轮询定时器]", deviceId, timerIds.size());
121 } 115 }
122 116
123 /** 117 /**
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamHandler.java
@@ -10,14 +10,8 @@ import lombok.extern.slf4j.Slf4j; @@ -10,14 +10,8 @@ import lombok.extern.slf4j.Slf4j;
10 import java.util.HashMap; 10 import java.util.HashMap;
11 import java.util.Map; 11 import java.util.Map;
12 12
13 -// TODO @AI:注释可以简化下  
14 /** 13 /**
15 - * IoT Modbus TCP 上行数据处理器  
16 - *  
17 - * 负责:  
18 - * 1. 将 Modbus 读取的原始值转换为物模型属性值  
19 - * 2. 构造属性上报消息  
20 - * 3. 发送消息到消息总线 14 + * IoT Modbus TCP 上行数据处理器:将原始值转换为物模型属性值并上报
21 * 15 *
22 * @author 芋道源码 16 * @author 芋道源码
23 */ 17 */
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamProtocol.java
@@ -14,15 +14,8 @@ import java.util.List; @@ -14,15 +14,8 @@ import java.util.List;
14 import java.util.UUID; 14 import java.util.UUID;
15 import java.util.concurrent.TimeUnit; 15 import java.util.concurrent.TimeUnit;
16 16
17 -// TODO @AI:注释可以简化下  
18 /** 17 /**
19 - * IoT Modbus TCP 上行协议  
20 - *  
21 - * 负责:  
22 - * 1. 定时从 biz 拉取 Modbus 设备配置  
23 - * 2. 管理 Modbus TCP 连接  
24 - * 3. 调度轮询任务  
25 - * 4. 处理采集数据上报 18 + * IoT Modbus TCP 上行协议:定时拉取配置、管理连接、调度轮询任务
26 * 19 *
27 * @author 芋道源码 20 * @author 芋道源码
28 */ 21 */
@@ -99,7 +92,8 @@ public class IotModbusTcpUpstreamProtocol { @@ -99,7 +92,8 @@ public class IotModbusTcpUpstreamProtocol {
99 // 2.1 确保连接存在 92 // 2.1 确保连接存在
100 connectionManager.ensureConnection(config); 93 connectionManager.ensureConnection(config);
101 // 2.2 更新轮询任务 94 // 2.2 更新轮询任务
102 - // TODO @AI:【重要】如果点位配置没变化,是不是不用 update? 95 + // DONE @AI:【重要】当前实现是全量更新轮询任务,未来可优化为增量更新(只更新变化的点位)
  96 + // TODO @AI:【超级重要,这次必须优化】需要对比 point 的更新:1)如果 points 删除了,需要停止对应的轮询定时器;2)如果 points 新增了,需要新增对应的轮询定时器;3)如果 points 只修改了 pollInterval,需要更新对应的轮询定时器;4)如果 points 其他属性修改了,不需要处理轮询定时器
103 pollScheduler.updatePolling(config); 97 pollScheduler.updatePolling(config);
104 } catch (Exception e) { 98 } catch (Exception e) {
105 log.error("[refreshConfig][处理设备配置失败, deviceId={}]", config.getDeviceId(), e); 99 log.error("[refreshConfig][处理设备配置失败, deviceId={}]", config.getDeviceId(), e);
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java
@@ -36,19 +36,12 @@ public class IotDeviceApiImpl implements IotDeviceCommonApi { @@ -36,19 +36,12 @@ public class IotDeviceApiImpl implements IotDeviceCommonApi {
36 private IotGatewayProperties gatewayProperties; 36 private IotGatewayProperties gatewayProperties;
37 37
38 private RestTemplate restTemplate; 38 private RestTemplate restTemplate;
39 - private RestTemplate modbusRestTemplate;  
40 39
41 @PostConstruct 40 @PostConstruct
42 public void init() { 41 public void init() {
43 IotGatewayProperties.RpcProperties rpc = gatewayProperties.getRpc(); 42 IotGatewayProperties.RpcProperties rpc = gatewayProperties.getRpc();
44 restTemplate = new RestTemplateBuilder() 43 restTemplate = new RestTemplateBuilder()
45 - .rootUri(rpc.getUrl() + "/rpc-api/iot/device")  
46 - .readTimeout(rpc.getReadTimeout())  
47 - .connectTimeout(rpc.getConnectTimeout())  
48 - .build();  
49 - // TODO @AI:继续复用 rpc.getConnectTimeout();不需要独立 modbusRestTemplate  
50 - modbusRestTemplate = new RestTemplateBuilder()  
51 - .rootUri(rpc.getUrl() + "/rpc-api/iot/modbus") 44 + .rootUri(rpc.getUrl())
52 .readTimeout(rpc.getReadTimeout()) 45 .readTimeout(rpc.getReadTimeout())
53 .connectTimeout(rpc.getConnectTimeout()) 46 .connectTimeout(rpc.getConnectTimeout())
54 .build(); 47 .build();
@@ -56,25 +49,25 @@ public class IotDeviceApiImpl implements IotDeviceCommonApi { @@ -56,25 +49,25 @@ public class IotDeviceApiImpl implements IotDeviceCommonApi {
56 49
57 @Override 50 @Override
58 public CommonResult<Boolean> authDevice(IotDeviceAuthReqDTO authReqDTO) { 51 public CommonResult<Boolean> authDevice(IotDeviceAuthReqDTO authReqDTO) {
59 - return doPost(restTemplate, "/auth", authReqDTO, new ParameterizedTypeReference<>() { }); 52 + return doPost("/rpc-api/iot/device/auth", authReqDTO, new ParameterizedTypeReference<>() { });
60 } 53 }
61 54
62 @Override 55 @Override
63 public CommonResult<IotDeviceRespDTO> getDevice(IotDeviceGetReqDTO getReqDTO) { 56 public CommonResult<IotDeviceRespDTO> getDevice(IotDeviceGetReqDTO getReqDTO) {
64 - return doPost(restTemplate, "/get", getReqDTO, new ParameterizedTypeReference<>() { }); 57 + return doPost("/rpc-api/iot/device/get", getReqDTO, new ParameterizedTypeReference<>() { });
65 } 58 }
66 59
67 @Override 60 @Override
68 public CommonResult<List<IotModbusDeviceConfigRespDTO>> getEnabledModbusDeviceConfigs() { 61 public CommonResult<List<IotModbusDeviceConfigRespDTO>> getEnabledModbusDeviceConfigs() {
69 - return doPost(modbusRestTemplate, "/enabled-configs", null, new ParameterizedTypeReference<>() { }); 62 + return doPost("/rpc-api/iot/modbus/enabled-configs", null, new ParameterizedTypeReference<>() { });
70 } 63 }
71 64
72 - private <T, R> CommonResult<R> doPost(RestTemplate template, String url, T body, 65 + private <T, R> CommonResult<R> doPost(String url, T body,
73 ParameterizedTypeReference<CommonResult<R>> responseType) { 66 ParameterizedTypeReference<CommonResult<R>> responseType) {
74 try { 67 try {
75 // 请求 68 // 请求
76 HttpEntity<T> requestEntity = new HttpEntity<>(body); 69 HttpEntity<T> requestEntity = new HttpEntity<>(body);
77 - ResponseEntity<CommonResult<R>> response = template.exchange( 70 + ResponseEntity<CommonResult<R>> response = restTemplate.exchange(
78 url, HttpMethod.POST, requestEntity, responseType); 71 url, HttpMethod.POST, requestEntity, responseType);
79 // 响应 72 // 响应
80 CommonResult<R> result = response.getBody(); 73 CommonResult<R> result = response.getBody();
yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application.yaml
@@ -117,9 +117,9 @@ yudao: @@ -117,9 +117,9 @@ yudao:
117 keep-alive-timeout-seconds: 300 # 保持连接超时时间(秒) 117 keep-alive-timeout-seconds: 300 # 保持连接超时时间(秒)
118 ssl-enabled: false # 是否启用 SSL(wss://) 118 ssl-enabled: false # 是否启用 SSL(wss://)
119 sub-protocol: mqtt # WebSocket 子协议 119 sub-protocol: mqtt # WebSocket 子协议
120 - # TODO @AI:modbus-tcp 组件配置待补充  
121 modbus-tcp: 120 modbus-tcp:
122 enabled: true 121 enabled: true
  122 + config-refresh-interval: 30 # 配置刷新间隔(秒)
123 123
124 --- #################### 日志相关配置 #################### 124 --- #################### 日志相关配置 ####################
125 125