Commit cece79d04cdf58c579fb5ec6c17219bb380c2a34

Authored by YunaiV
1 parent 9e2c1fea

feat:【iot】modbus-tcp 协议接入:20% 初始化:基于 humming-beaming-dove.md 规划

Showing 22 changed files with 217 additions and 351 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java
1 1 package cn.iocoder.yudao.module.iot.api.device;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
3 4 import cn.iocoder.yudao.framework.common.enums.RpcConstants;
4 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
... ... @@ -23,8 +24,11 @@ import org.springframework.web.bind.annotation.RestController;
23 24  
24 25 import java.util.ArrayList;
25 26 import java.util.List;
  27 +import java.util.Map;
  28 +import java.util.Set;
26 29  
27 30 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
28 32  
29 33 /**
30 34 * IoT 设备 API 实现类
... ... @@ -71,40 +75,32 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi {
71 75 @PermitAll
72 76 public CommonResult<List<IotModbusDeviceConfigRespDTO>> getEnabledModbusDeviceConfigs() {
73 77 // 1. 获取所有启用的 Modbus 连接配置
74   - List<IotDeviceModbusConfigDO> configList = modbusConfigService.getEnabledModbusConfigList();
75   - if (configList.isEmpty()) {
  78 + List<IotDeviceModbusConfigDO> configList = modbusConfigService.getEnabledDeviceModbusConfigList();
  79 + if (CollUtil.isEmpty(configList)) {
76 80 return success(new ArrayList<>());
77 81 }
78 82  
79 83 // 2. 组装返回结果
  84 + Set<Long> deviceIds = convertSet(configList, IotDeviceModbusConfigDO::getDeviceId);
  85 + Map<Long, IotDeviceDO> deviceMap = deviceService.getDeviceMap(deviceIds);
  86 + Map<Long, List<IotDeviceModbusPointDO>> pointMap = modbusPointService.getEnabledDeviceModbusPointMapByDeviceIds(deviceIds);
80 87 List<IotModbusDeviceConfigRespDTO> result = new ArrayList<>(configList.size());
81 88 for (IotDeviceModbusConfigDO config : configList) {
82   - // 2.1 获取设备信息
83   - // TODO @AI:设备需要批量读取;(先暂时不处理)
84   - IotDeviceDO device = deviceService.getDeviceFromCache(config.getDeviceId());
  89 + // 3.1 获取设备信息
  90 + IotDeviceDO device = deviceMap.get(config.getDeviceId());
85 91 if (device == null) {
86 92 continue;
87 93 }
88   -
89   - // 2.2 获取启用的点位列表
90   - // TODO @AI:看看是不是批量读取;
91   - List<IotDeviceModbusPointDO> pointList = modbusPointService.getEnabledModbusPointListByDeviceId(config.getDeviceId());
92   -
93   - // 2.3 构建 DTO
94   - IotModbusDeviceConfigRespDTO dto = new IotModbusDeviceConfigRespDTO();
95   - dto.setDeviceId(config.getDeviceId());
96   - // TODO @AI:这个 productKey、deviceName 这个字段,要不要冗余到 IotDeviceModbusConfigDO 里面?(先暂时不处理)
97   - dto.setProductKey(device.getProductKey());
98   - dto.setDeviceName(device.getDeviceName());
99   - dto.setTenantId(device.getTenantId());
100   - // TODO @AI:看看 dto 的转换,能不能通过 beanutils copy
101   - dto.setIp(config.getIp());
102   - dto.setPort(config.getPort());
103   - dto.setSlaveId(config.getSlaveId());
104   - dto.setTimeout(config.getTimeout());
105   - dto.setRetryInterval(config.getRetryInterval());
106   - dto.setPoints(BeanUtils.toBean(pointList, IotModbusPointRespDTO.class));
107   - result.add(dto);
  94 + // 3.2 获取启用的点位列表
  95 + List<IotDeviceModbusPointDO> pointList = pointMap.get(config.getDeviceId());
  96 + if (CollUtil.isEmpty(pointList)) {
  97 + continue;
  98 + }
  99 + // 3.3 构建 IotModbusDeviceConfigRespDTO 对象
  100 + IotModbusDeviceConfigRespDTO configDTO = BeanUtils.toBean(config, IotModbusDeviceConfigRespDTO.class, o ->
  101 + o.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())
  102 + .setPoints(BeanUtils.toBean(pointList, IotModbusPointRespDTO.class)));
  103 + result.add(configDTO);
108 104 }
109 105 return success(result);
110 106 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusConfigController.java
... ... @@ -28,19 +28,11 @@ public class IotDeviceModbusConfigController {
28 28 @Resource
29 29 private IotDeviceModbusConfigService modbusConfigService;
30 30  
31   - // TODO @AI:create 和 update 合并成 save 接口;
32   - @PostMapping("/create")
33   - @Operation(summary = "创建设备 Modbus 连接配置")
  31 + @PostMapping("/save")
  32 + @Operation(summary = "保存设备 Modbus 连接配置")
34 33 @PreAuthorize("@ss.hasPermission('iot:device-modbus-config:create')")
35   - public CommonResult<Long> createModbusConfig(@Valid @RequestBody IotDeviceModbusConfigSaveReqVO createReqVO) {
36   - return success(modbusConfigService.createModbusConfig(createReqVO));
37   - }
38   -
39   - @PutMapping("/update")
40   - @Operation(summary = "更新设备 Modbus 连接配置")
41   - @PreAuthorize("@ss.hasPermission('iot:device-modbus-config:update')")
42   - public CommonResult<Boolean> updateModbusConfig(@Valid @RequestBody IotDeviceModbusConfigSaveReqVO updateReqVO) {
43   - modbusConfigService.updateModbusConfig(updateReqVO);
  34 + public CommonResult<Boolean> saveDeviceModbusConfig(@Valid @RequestBody IotDeviceModbusConfigSaveReqVO saveReqVO) {
  35 + modbusConfigService.saveDeviceModbusConfig(saveReqVO);
44 36 return success(true);
45 37 }
46 38  
... ... @@ -48,36 +40,33 @@ public class IotDeviceModbusConfigController {
48 40 @Operation(summary = "删除设备 Modbus 连接配置")
49 41 @Parameter(name = "id", description = "编号", required = true)
50 42 @PreAuthorize("@ss.hasPermission('iot:device-modbus-config:delete')")
51   - public CommonResult<Boolean> deleteModbusConfig(@RequestParam("id") Long id) {
52   - modbusConfigService.deleteModbusConfig(id);
  43 + public CommonResult<Boolean> deleteDeviceModbusConfig(@RequestParam("id") Long id) {
  44 + modbusConfigService.deleteDeviceModbusConfig(id);
53 45 return success(true);
54 46 }
55 47  
56   - // TODO @AI:这个接口改造,支持 id 或者 deviceId;二选一查询;
57 48 @GetMapping("/get")
58 49 @Operation(summary = "获得设备 Modbus 连接配置")
59   - @Parameter(name = "id", description = "编号", required = true, example = "1024")
60   - @PreAuthorize("@ss.hasPermission('iot:device-modbus-config:query')")
61   - public CommonResult<IotDeviceModbusConfigRespVO> getModbusConfig(@RequestParam("id") Long id) {
62   - IotDeviceModbusConfigDO modbusConfig = modbusConfigService.getModbusConfig(id);
63   - return success(BeanUtils.toBean(modbusConfig, IotDeviceModbusConfigRespVO.class));
64   - }
65   -
66   - // TODO @AI:合并到 getModbusConfig 接口里;
67   - @GetMapping("/get-by-device-id")
68   - @Operation(summary = "根据设备编号获得 Modbus 连接配置")
69   - @Parameter(name = "deviceId", description = "设备编号", required = true, example = "1024")
  50 + @Parameter(name = "id", description = "编号", example = "1024")
  51 + @Parameter(name = "deviceId", description = "设备编号", example = "2048")
70 52 @PreAuthorize("@ss.hasPermission('iot:device-modbus-config:query')")
71   - public CommonResult<IotDeviceModbusConfigRespVO> getModbusConfigByDeviceId(@RequestParam("deviceId") Long deviceId) {
72   - IotDeviceModbusConfigDO modbusConfig = modbusConfigService.getModbusConfigByDeviceId(deviceId);
  53 + public CommonResult<IotDeviceModbusConfigRespVO> getDeviceModbusConfig(
  54 + @RequestParam(value = "id", required = false) Long id,
  55 + @RequestParam(value = "deviceId", required = false) Long deviceId) {
  56 + IotDeviceModbusConfigDO modbusConfig = null;
  57 + if (id != null) {
  58 + modbusConfig = modbusConfigService.getDeviceModbusConfig(id);
  59 + } else if (deviceId != null) {
  60 + modbusConfig = modbusConfigService.getDeviceModbusConfigByDeviceId(deviceId);
  61 + }
73 62 return success(BeanUtils.toBean(modbusConfig, IotDeviceModbusConfigRespVO.class));
74 63 }
75 64  
76 65 @GetMapping("/page")
77 66 @Operation(summary = "获得设备 Modbus 连接配置分页")
78 67 @PreAuthorize("@ss.hasPermission('iot:device-modbus-config:query')")
79   - public CommonResult<PageResult<IotDeviceModbusConfigRespVO>> getModbusConfigPage(@Valid IotDeviceModbusConfigPageReqVO pageReqVO) {
80   - PageResult<IotDeviceModbusConfigDO> pageResult = modbusConfigService.getModbusConfigPage(pageReqVO);
  68 + public CommonResult<PageResult<IotDeviceModbusConfigRespVO>> getDeviceModbusConfigPage(@Valid IotDeviceModbusConfigPageReqVO pageReqVO) {
  69 + PageResult<IotDeviceModbusConfigDO> pageResult = modbusConfigService.getDeviceModbusConfigPage(pageReqVO);
81 70 return success(BeanUtils.toBean(pageResult, IotDeviceModbusConfigRespVO.class));
82 71 }
83 72  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusPointController.java
... ... @@ -17,8 +17,6 @@ import org.springframework.security.access.prepost.PreAuthorize;
17 17 import org.springframework.validation.annotation.Validated;
18 18 import org.springframework.web.bind.annotation.*;
19 19  
20   -import java.util.List;
21   -
22 20 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
23 21  
24 22 @Tag(name = "管理后台 - IoT 设备 Modbus 点位配置")
... ... @@ -33,15 +31,15 @@ public class IotDeviceModbusPointController {
33 31 @PostMapping("/create")
34 32 @Operation(summary = "创建设备 Modbus 点位配置")
35 33 @PreAuthorize("@ss.hasPermission('iot:device-modbus-point:create')")
36   - public CommonResult<Long> createModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO createReqVO) {
37   - return success(modbusPointService.createModbusPoint(createReqVO));
  34 + public CommonResult<Long> createDeviceModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO createReqVO) {
  35 + return success(modbusPointService.createDeviceModbusPoint(createReqVO));
38 36 }
39 37  
40 38 @PutMapping("/update")
41 39 @Operation(summary = "更新设备 Modbus 点位配置")
42 40 @PreAuthorize("@ss.hasPermission('iot:device-modbus-point:update')")
43   - public CommonResult<Boolean> updateModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO updateReqVO) {
44   - modbusPointService.updateModbusPoint(updateReqVO);
  41 + public CommonResult<Boolean> updateDeviceModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO updateReqVO) {
  42 + modbusPointService.updateDeviceModbusPoint(updateReqVO);
45 43 return success(true);
46 44 }
47 45  
... ... @@ -49,8 +47,8 @@ public class IotDeviceModbusPointController {
49 47 @Operation(summary = "删除设备 Modbus 点位配置")
50 48 @Parameter(name = "id", description = "编号", required = true)
51 49 @PreAuthorize("@ss.hasPermission('iot:device-modbus-point:delete')")
52   - public CommonResult<Boolean> deleteModbusPoint(@RequestParam("id") Long id) {
53   - modbusPointService.deleteModbusPoint(id);
  50 + public CommonResult<Boolean> deleteDeviceModbusPoint(@RequestParam("id") Long id) {
  51 + modbusPointService.deleteDeviceModbusPoint(id);
54 52 return success(true);
55 53 }
56 54  
... ... @@ -58,27 +56,17 @@ public class IotDeviceModbusPointController {
58 56 @Operation(summary = "获得设备 Modbus 点位配置")
59 57 @Parameter(name = "id", description = "编号", required = true, example = "1024")
60 58 @PreAuthorize("@ss.hasPermission('iot:device-modbus-point:query')")
61   - public CommonResult<IotDeviceModbusPointRespVO> getModbusPoint(@RequestParam("id") Long id) {
62   - IotDeviceModbusPointDO modbusPoint = modbusPointService.getModbusPoint(id);
  59 + public CommonResult<IotDeviceModbusPointRespVO> getDeviceModbusPoint(@RequestParam("id") Long id) {
  60 + IotDeviceModbusPointDO modbusPoint = modbusPointService.getDeviceModbusPoint(id);
63 61 return success(BeanUtils.toBean(modbusPoint, IotDeviceModbusPointRespVO.class));
64 62 }
65 63  
66 64 @GetMapping("/page")
67 65 @Operation(summary = "获得设备 Modbus 点位配置分页")
68 66 @PreAuthorize("@ss.hasPermission('iot:device-modbus-point:query')")
69   - public CommonResult<PageResult<IotDeviceModbusPointRespVO>> getModbusPointPage(@Valid IotDeviceModbusPointPageReqVO pageReqVO) {
70   - PageResult<IotDeviceModbusPointDO> pageResult = modbusPointService.getModbusPointPage(pageReqVO);
  67 + public CommonResult<PageResult<IotDeviceModbusPointRespVO>> getDeviceModbusPointPage(@Valid IotDeviceModbusPointPageReqVO pageReqVO) {
  68 + PageResult<IotDeviceModbusPointDO> pageResult = modbusPointService.getDeviceModbusPointPage(pageReqVO);
71 69 return success(BeanUtils.toBean(pageResult, IotDeviceModbusPointRespVO.class));
72 70 }
73 71  
74   - // TODO @AI:应该用不上这个接口?只需要 getModbusPointPage 分页
75   - @GetMapping("/list-by-device-id")
76   - @Operation(summary = "根据设备编号获得 Modbus 点位配置列表")
77   - @Parameter(name = "deviceId", description = "设备编号", required = true, example = "1024")
78   - @PreAuthorize("@ss.hasPermission('iot:device-modbus-point:query')")
79   - public CommonResult<List<IotDeviceModbusPointRespVO>> getModbusPointListByDeviceId(@RequestParam("deviceId") Long deviceId) {
80   - List<IotDeviceModbusPointDO> list = modbusPointService.getModbusPointListByDeviceId(deviceId);
81   - return success(BeanUtils.toBean(list, IotDeviceModbusPointRespVO.class));
82   - }
83   -
84 72 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigSaveReqVO.java
... ... @@ -9,9 +9,6 @@ import lombok.Data;
9 9 @Data
10 10 public class IotDeviceModbusConfigSaveReqVO {
11 11  
12   - @Schema(description = "主键", example = "1")
13   - private Long id;
14   -
15 12 @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
16 13 @NotNull(message = "设备编号不能为空")
17 14 private Long deviceId;
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusPointMapper.java
... ... @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceMo
7 7 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9 9  
  10 +import java.util.Collection;
10 11 import java.util.List;
11 12  
12 13 /**
... ... @@ -27,27 +28,15 @@ public interface IotDeviceModbusPointMapper extends BaseMapperX&lt;IotDeviceModbusP
27 28 .orderByDesc(IotDeviceModbusPointDO::getId));
28 29 }
29 30  
30   - default List<IotDeviceModbusPointDO> selectListByDeviceId(Long deviceId) {
31   - return selectList(IotDeviceModbusPointDO::getDeviceId, deviceId);
32   - }
33   -
34   - // TODO @AI:是不是 selectList(f1, v1, f2, v2);
35   - default List<IotDeviceModbusPointDO> selectListByDeviceIdAndStatus(Long deviceId, Integer status) {
  31 + default List<IotDeviceModbusPointDO> selectListByDeviceIdsAndStatus(Collection<Long> deviceIds, Integer status) {
36 32 return selectList(new LambdaQueryWrapperX<IotDeviceModbusPointDO>()
37   - .eq(IotDeviceModbusPointDO::getDeviceId, deviceId)
  33 + .in(IotDeviceModbusPointDO::getDeviceId, deviceIds)
38 34 .eq(IotDeviceModbusPointDO::getStatus, status));
39 35 }
40 36  
41   - // TODO @AI:是不是 selectOne(f1, v1, f2, v2);
42 37 default IotDeviceModbusPointDO selectByDeviceIdAndIdentifier(Long deviceId, String identifier) {
43   - return selectOne(new LambdaQueryWrapperX<IotDeviceModbusPointDO>()
44   - .eq(IotDeviceModbusPointDO::getDeviceId, deviceId)
45   - .eq(IotDeviceModbusPointDO::getIdentifier, identifier));
46   - }
47   -
48   - // TODO @AI:是不是删除这个方法;
49   - default void deleteByDeviceId(Long deviceId) {
50   - delete(IotDeviceModbusPointDO::getDeviceId, deviceId);
  38 + return selectOne(IotDeviceModbusPointDO::getDeviceId, deviceId,
  39 + IotDeviceModbusPointDO::getIdentifier, identifier);
51 40 }
52 41  
53 42 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusByteOrderEnum.java
... ... @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
6 6  
7 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 里
9 10 /**
10 11 * IoT Modbus 字节序枚举
11 12 *
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusFunctionCodeEnum.java
... ... @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
6 6  
7 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 里
9 10 /**
10 11 * IoT Modbus 功能码枚举
11 12 *
... ... @@ -63,18 +64,4 @@ public enum IotModbusFunctionCodeEnum implements ArrayValuable&lt;Integer&gt; {
63 64 .orElse(null);
64 65 }
65 66  
66   - // TODO @AI:如果用不到,可以暂时删除哈;
67   - /**
68   - * 获取写功能码
69   - *
70   - * @param registerCount 寄存器数量
71   - * @return 写功能码
72   - */
73   - public Integer getWriteCode(int registerCount) {
74   - if (!writable) {
75   - return null;
76   - }
77   - return registerCount == 1 ? writeSingleCode : writeMultipleCode;
78   - }
79   -
80 67 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusRawDataTypeEnum.java
... ... @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
6 6  
7 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 里
9 10 /**
10 11 * IoT Modbus 原始数据类型枚举
11 12 *
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigService.java
... ... @@ -16,26 +16,18 @@ import java.util.List;
16 16 public interface IotDeviceModbusConfigService {
17 17  
18 18 /**
19   - * 创建设备 Modbus 连接配置
  19 + * 保存设备 Modbus 连接配置(新增或更新)
20 20 *
21   - * @param createReqVO 创建信息
22   - * @return 编号
  21 + * @param saveReqVO 保存信息
23 22 */
24   - Long createModbusConfig(@Valid IotDeviceModbusConfigSaveReqVO createReqVO);
25   -
26   - /**
27   - * 更新设备 Modbus 连接配置
28   - *
29   - * @param updateReqVO 更新信息
30   - */
31   - void updateModbusConfig(@Valid IotDeviceModbusConfigSaveReqVO updateReqVO);
  23 + void saveDeviceModbusConfig(@Valid IotDeviceModbusConfigSaveReqVO saveReqVO);
32 24  
33 25 /**
34 26 * 删除设备 Modbus 连接配置
35 27 *
36 28 * @param id 编号
37 29 */
38   - void deleteModbusConfig(Long id);
  30 + void deleteDeviceModbusConfig(Long id);
39 31  
40 32 /**
41 33 * 获得设备 Modbus 连接配置
... ... @@ -43,7 +35,7 @@ public interface IotDeviceModbusConfigService {
43 35 * @param id 编号
44 36 * @return 设备 Modbus 连接配置
45 37 */
46   - IotDeviceModbusConfigDO getModbusConfig(Long id);
  38 + IotDeviceModbusConfigDO getDeviceModbusConfig(Long id);
47 39  
48 40 /**
49 41 * 根据设备编号获得 Modbus 连接配置
... ... @@ -51,7 +43,7 @@ public interface IotDeviceModbusConfigService {
51 43 * @param deviceId 设备编号
52 44 * @return 设备 Modbus 连接配置
53 45 */
54   - IotDeviceModbusConfigDO getModbusConfigByDeviceId(Long deviceId);
  46 + IotDeviceModbusConfigDO getDeviceModbusConfigByDeviceId(Long deviceId);
55 47  
56 48 /**
57 49 * 获得设备 Modbus 连接配置分页
... ... @@ -59,13 +51,13 @@ public interface IotDeviceModbusConfigService {
59 51 * @param pageReqVO 分页查询
60 52 * @return 设备 Modbus 连接配置分页
61 53 */
62   - PageResult<IotDeviceModbusConfigDO> getModbusConfigPage(IotDeviceModbusConfigPageReqVO pageReqVO);
  54 + PageResult<IotDeviceModbusConfigDO> getDeviceModbusConfigPage(IotDeviceModbusConfigPageReqVO pageReqVO);
63 55  
64 56 /**
65 57 * 获得所有启用的 Modbus 连接配置列表
66 58 *
67 59 * @return 启用的 Modbus 连接配置列表
68 60 */
69   - List<IotDeviceModbusConfigDO> getEnabledModbusConfigList();
  61 + List<IotDeviceModbusConfigDO> getEnabledDeviceModbusConfigList();
70 62  
71 63 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigServiceImpl.java
... ... @@ -14,7 +14,6 @@ import org.springframework.validation.annotation.Validated;
14 14 import java.util.List;
15 15  
16 16 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
17   -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_MODBUS_CONFIG_EXISTS;
18 17 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_MODBUS_CONFIG_NOT_EXISTS;
19 18  
20 19 /**
... ... @@ -32,90 +31,54 @@ public class IotDeviceModbusConfigServiceImpl implements IotDeviceModbusConfigSe
32 31 @Resource
33 32 private IotDeviceService deviceService;
34 33  
35   - // TODO @AI:是不是搞成 save 接口?因为前端也不知道是 create 还是 update;
36 34 @Override
37   - public Long createModbusConfig(IotDeviceModbusConfigSaveReqVO createReqVO) {
38   - // 1.1 校验设备存在
39   - deviceService.validateDeviceExists(createReqVO.getDeviceId());
40   - // 1.2 校验设备是否已有 Modbus 配置
41   - validateModbusConfigUnique(createReqVO.getDeviceId(), null);
42   -
43   - // 2. 插入
44   - IotDeviceModbusConfigDO modbusConfig = BeanUtils.toBean(createReqVO, IotDeviceModbusConfigDO.class);
45   - setDefaultValues(modbusConfig);
46   - modbusConfigMapper.insert(modbusConfig);
47   - return modbusConfig.getId();
48   - }
49   -
50   - @Override
51   - public void updateModbusConfig(IotDeviceModbusConfigSaveReqVO updateReqVO) {
52   - // 1.1 校验存在
53   - validateModbusConfigExists(updateReqVO.getId());
54   - // 1.2 校验设备存在
55   - deviceService.validateDeviceExists(updateReqVO.getDeviceId());
56   - // 1.3 校验唯一性
57   - validateModbusConfigUnique(updateReqVO.getDeviceId(), updateReqVO.getId());
58   -
59   - // 2. 更新
60   - IotDeviceModbusConfigDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceModbusConfigDO.class);
61   - modbusConfigMapper.updateById(updateObj);
  35 + public void saveDeviceModbusConfig(IotDeviceModbusConfigSaveReqVO saveReqVO) {
  36 + // 1. 校验设备存在
  37 + deviceService.validateDeviceExists(saveReqVO.getDeviceId());
  38 +
  39 + // 2. 根据数据库中是否已有配置,决定是新增还是更新
  40 + IotDeviceModbusConfigDO existConfig = modbusConfigMapper.selectByDeviceId(saveReqVO.getDeviceId());
  41 + if (existConfig == null) {
  42 + IotDeviceModbusConfigDO modbusConfig = BeanUtils.toBean(saveReqVO, IotDeviceModbusConfigDO.class);
  43 + modbusConfigMapper.insert(modbusConfig);
  44 + } else {
  45 + IotDeviceModbusConfigDO updateObj = BeanUtils.toBean(saveReqVO, IotDeviceModbusConfigDO.class,
  46 + o -> o.setId(existConfig.getId()));
  47 + modbusConfigMapper.updateById(updateObj);
  48 + }
62 49 }
63 50  
64 51 @Override
65   - public void deleteModbusConfig(Long id) {
  52 + public void deleteDeviceModbusConfig(Long id) {
66 53 // 校验存在
67   - validateModbusConfigExists(id);
  54 + validateDeviceModbusConfigExists(id);
68 55 // 删除
69 56 modbusConfigMapper.deleteById(id);
70 57 }
71 58  
72   - private void validateModbusConfigExists(Long id) {
  59 + private void validateDeviceModbusConfigExists(Long id) {
73 60 if (modbusConfigMapper.selectById(id) == null) {
74 61 throw exception(DEVICE_MODBUS_CONFIG_NOT_EXISTS);
75 62 }
76 63 }
77 64  
78   - private void validateModbusConfigUnique(Long deviceId, Long excludeId) {
79   - IotDeviceModbusConfigDO config = modbusConfigMapper.selectByDeviceId(deviceId);
80   - // TODO @AI:ObjUtil notequals
81   - if (config != null && !config.getId().equals(excludeId)) {
82   - throw exception(DEVICE_MODBUS_CONFIG_EXISTS);
83   - }
84   - }
85   -
86   - // TODO @AI:不要这个;前端都必须传递;
87   - private void setDefaultValues(IotDeviceModbusConfigDO config) {
88   - if (config.getPort() == null) {
89   - config.setPort(502);
90   - }
91   - if (config.getSlaveId() == null) {
92   - config.setSlaveId(1);
93   - }
94   - if (config.getTimeout() == null) {
95   - config.setTimeout(3000);
96   - }
97   - if (config.getRetryInterval() == null) {
98   - config.setRetryInterval(1000);
99   - }
100   - }
101   -
102 65 @Override
103   - public IotDeviceModbusConfigDO getModbusConfig(Long id) {
  66 + public IotDeviceModbusConfigDO getDeviceModbusConfig(Long id) {
104 67 return modbusConfigMapper.selectById(id);
105 68 }
106 69  
107 70 @Override
108   - public IotDeviceModbusConfigDO getModbusConfigByDeviceId(Long deviceId) {
  71 + public IotDeviceModbusConfigDO getDeviceModbusConfigByDeviceId(Long deviceId) {
109 72 return modbusConfigMapper.selectByDeviceId(deviceId);
110 73 }
111 74  
112 75 @Override
113   - public PageResult<IotDeviceModbusConfigDO> getModbusConfigPage(IotDeviceModbusConfigPageReqVO pageReqVO) {
  76 + public PageResult<IotDeviceModbusConfigDO> getDeviceModbusConfigPage(IotDeviceModbusConfigPageReqVO pageReqVO) {
114 77 return modbusConfigMapper.selectPage(pageReqVO);
115 78 }
116 79  
117 80 @Override
118   - public List<IotDeviceModbusConfigDO> getEnabledModbusConfigList() {
  81 + public List<IotDeviceModbusConfigDO> getEnabledDeviceModbusConfigList() {
119 82 return modbusConfigMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
120 83 }
121 84  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointService.java
... ... @@ -6,7 +6,9 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceMo
6 6 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO;
7 7 import jakarta.validation.Valid;
8 8  
  9 +import java.util.Collection;
9 10 import java.util.List;
  11 +import java.util.Map;
10 12  
11 13 /**
12 14 * IoT 设备 Modbus 点位配置 Service 接口
... ... @@ -21,21 +23,21 @@ public interface IotDeviceModbusPointService {
21 23 * @param createReqVO 创建信息
22 24 * @return 编号
23 25 */
24   - Long createModbusPoint(@Valid IotDeviceModbusPointSaveReqVO createReqVO);
  26 + Long createDeviceModbusPoint(@Valid IotDeviceModbusPointSaveReqVO createReqVO);
25 27  
26 28 /**
27 29 * 更新设备 Modbus 点位配置
28 30 *
29 31 * @param updateReqVO 更新信息
30 32 */
31   - void updateModbusPoint(@Valid IotDeviceModbusPointSaveReqVO updateReqVO);
  33 + void updateDeviceModbusPoint(@Valid IotDeviceModbusPointSaveReqVO updateReqVO);
32 34  
33 35 /**
34 36 * 删除设备 Modbus 点位配置
35 37 *
36 38 * @param id 编号
37 39 */
38   - void deleteModbusPoint(Long id);
  40 + void deleteDeviceModbusPoint(Long id);
39 41  
40 42 /**
41 43 * 获得设备 Modbus 点位配置
... ... @@ -43,7 +45,7 @@ public interface IotDeviceModbusPointService {
43 45 * @param id 编号
44 46 * @return 设备 Modbus 点位配置
45 47 */
46   - IotDeviceModbusPointDO getModbusPoint(Long id);
  48 + IotDeviceModbusPointDO getDeviceModbusPoint(Long id);
47 49  
48 50 /**
49 51 * 获得设备 Modbus 点位配置分页
... ... @@ -51,22 +53,14 @@ public interface IotDeviceModbusPointService {
51 53 * @param pageReqVO 分页查询
52 54 * @return 设备 Modbus 点位配置分页
53 55 */
54   - PageResult<IotDeviceModbusPointDO> getModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO);
  56 + PageResult<IotDeviceModbusPointDO> getDeviceModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO);
55 57  
56 58 /**
57   - * 根据设备编号获得点位配置列表
  59 + * 根据设备编号批量获得启用的点位配置 Map
58 60 *
59   - * @param deviceId 设备编号
60   - * @return 点位配置列表
  61 + * @param deviceIds 设备编号集合
  62 + * @return 设备点位 Map,key 为设备编号,value 为点位配置列表
61 63 */
62   - List<IotDeviceModbusPointDO> getModbusPointListByDeviceId(Long deviceId);
63   -
64   - /**
65   - * 根据设备编号获得启用的点位配置列表
66   - *
67   - * @param deviceId 设备编号
68   - * @return 启用的点位配置列表
69   - */
70   - List<IotDeviceModbusPointDO> getEnabledModbusPointListByDeviceId(Long deviceId);
  64 + Map<Long, List<IotDeviceModbusPointDO>> getEnabledDeviceModbusPointMapByDeviceIds(Collection<Long> deviceIds);
71 65  
72 66 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointServiceImpl.java
1 1 package cn.iocoder.yudao.module.iot.service.device;
2 2  
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
3 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
... ... @@ -13,10 +15,13 @@ import jakarta.annotation.Resource;
13 15 import org.springframework.stereotype.Service;
14 16 import org.springframework.validation.annotation.Validated;
15 17  
16   -import java.math.BigDecimal;
  18 +import java.util.Collection;
  19 +import java.util.Collections;
17 20 import java.util.List;
  21 +import java.util.Map;
18 22  
19 23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
20 25 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
21 26  
22 27 /**
... ... @@ -38,13 +43,13 @@ public class IotDeviceModbusPointServiceImpl implements IotDeviceModbusPointServ
38 43 private IotThingModelService thingModelService;
39 44  
40 45 @Override
41   - public Long createModbusPoint(IotDeviceModbusPointSaveReqVO createReqVO) {
  46 + public Long createDeviceModbusPoint(IotDeviceModbusPointSaveReqVO createReqVO) {
42 47 // 1.1 校验设备存在
43 48 deviceService.validateDeviceExists(createReqVO.getDeviceId());
44 49 // 1.2 校验物模型属性存在
45 50 IotThingModelDO thingModel = validateThingModelExists(createReqVO.getThingModelId());
46 51 // 1.3 校验同一设备下点位唯一性(基于 identifier)
47   - validateModbusPointUnique(createReqVO.getDeviceId(), thingModel.getIdentifier(), null);
  52 + validateDeviceModbusPointUnique(createReqVO.getDeviceId(), thingModel.getIdentifier(), null);
48 53  
49 54 // 2. 插入
50 55 IotDeviceModbusPointDO modbusPoint = BeanUtils.toBean(createReqVO, IotDeviceModbusPointDO.class,
... ... @@ -54,22 +59,23 @@ public class IotDeviceModbusPointServiceImpl implements IotDeviceModbusPointServ
54 59 }
55 60  
56 61 @Override
57   - public void updateModbusPoint(IotDeviceModbusPointSaveReqVO updateReqVO) {
  62 + public void updateDeviceModbusPoint(IotDeviceModbusPointSaveReqVO updateReqVO) {
58 63 // 1.1 校验存在
59   - validateModbusPointExists(updateReqVO.getId());
  64 + validateDeviceModbusPointExists(updateReqVO.getId());
60 65 // 1.2 校验设备存在
61 66 deviceService.validateDeviceExists(updateReqVO.getDeviceId());
62 67 // 1.3 校验物模型属性存在
63 68 IotThingModelDO thingModel = validateThingModelExists(updateReqVO.getThingModelId());
64 69 // 1.4 校验同一设备下点位唯一性
65   - validateModbusPointUnique(updateReqVO.getDeviceId(), thingModel.getIdentifier(), updateReqVO.getId());
  70 + validateDeviceModbusPointUnique(updateReqVO.getDeviceId(), thingModel.getIdentifier(), updateReqVO.getId());
66 71  
67 72 // 2. 更新
68 73 IotDeviceModbusPointDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceModbusPointDO.class);
69   - // TODO @AI:这块
70 74 modbusPointMapper.updateById(updateObj);
71 75 }
72 76  
  77 + // TODO @AI:物模型更新的时候,更新下 identifier、name 信息;例如说 updateDeviceModbusPoint(thingModelId, identifier、name) 方法;
  78 +
73 79 private IotThingModelDO validateThingModelExists(Long id) {
74 80 IotThingModelDO thingModel = thingModelService.getThingModel(id);
75 81 if (thingModel == null) {
... ... @@ -79,60 +85,45 @@ public class IotDeviceModbusPointServiceImpl implements IotDeviceModbusPointServ
79 85 }
80 86  
81 87 @Override
82   - public void deleteModbusPoint(Long id) {
  88 + public void deleteDeviceModbusPoint(Long id) {
83 89 // 校验存在
84   - validateModbusPointExists(id);
  90 + validateDeviceModbusPointExists(id);
85 91 // 删除
86 92 modbusPointMapper.deleteById(id);
87 93 }
88 94  
89   - private IotDeviceModbusPointDO validateModbusPointExists(Long id) {
  95 + private void validateDeviceModbusPointExists(Long id) {
90 96 IotDeviceModbusPointDO point = modbusPointMapper.selectById(id);
91 97 if (point == null) {
92 98 throw exception(DEVICE_MODBUS_POINT_NOT_EXISTS);
93 99 }
94   - return point;
95 100 }
96 101  
97   - private void validateModbusPointUnique(Long deviceId, String identifier, Long excludeId) {
  102 + private void validateDeviceModbusPointUnique(Long deviceId, String identifier, Long excludeId) {
98 103 IotDeviceModbusPointDO point = modbusPointMapper.selectByDeviceIdAndIdentifier(deviceId, identifier);
99   - // TODO @AI:ObjUtil notequals;
100   - if (point != null && !point.getId().equals(excludeId)) {
  104 + if (point != null && ObjUtil.notEqual(point.getId(), excludeId)) {
101 105 throw exception(DEVICE_MODBUS_POINT_EXISTS);
102 106 }
103 107 }
104 108  
105   - // TODO @AI:这块
106   - private void setDefaultValues(IotDeviceModbusPointDO point) {
107   - if (point.getRegisterCount() == null) {
108   - point.setRegisterCount(1);
109   - }
110   - if (point.getScale() == null) {
111   - point.setScale(BigDecimal.ONE);
112   - }
113   - if (point.getPollInterval() == null) {
114   - point.setPollInterval(5000);
115   - }
116   - }
117   -
118 109 @Override
119   - public IotDeviceModbusPointDO getModbusPoint(Long id) {
  110 + public IotDeviceModbusPointDO getDeviceModbusPoint(Long id) {
120 111 return modbusPointMapper.selectById(id);
121 112 }
122 113  
123 114 @Override
124   - public PageResult<IotDeviceModbusPointDO> getModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO) {
  115 + public PageResult<IotDeviceModbusPointDO> getDeviceModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO) {
125 116 return modbusPointMapper.selectPage(pageReqVO);
126 117 }
127 118  
128 119 @Override
129   - public List<IotDeviceModbusPointDO> getModbusPointListByDeviceId(Long deviceId) {
130   - return modbusPointMapper.selectListByDeviceId(deviceId);
131   - }
132   -
133   - @Override
134   - public List<IotDeviceModbusPointDO> getEnabledModbusPointListByDeviceId(Long deviceId) {
135   - return modbusPointMapper.selectListByDeviceIdAndStatus(deviceId, CommonStatusEnum.ENABLE.getStatus());
  120 + public Map<Long, List<IotDeviceModbusPointDO>> getEnabledDeviceModbusPointMapByDeviceIds(Collection<Long> deviceIds) {
  121 + if (CollUtil.isEmpty(deviceIds)) {
  122 + return Collections.emptyMap();
  123 + }
  124 + List<IotDeviceModbusPointDO> pointList = modbusPointMapper.selectListByDeviceIdsAndStatus(
  125 + deviceIds, CommonStatusEnum.ENABLE.getStatus());
  126 + return convertMultiMap(pointList, IotDeviceModbusPointDO::getDeviceId);
136 127 }
137 128  
138 129 }
... ...
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusDeviceConfigRespDTO.java
... ... @@ -24,10 +24,6 @@ public class IotModbusDeviceConfigRespDTO {
24 24 * 设备名称
25 25 */
26 26 private String deviceName;
27   - /**
28   - * 租户编号
29   - */
30   - private Long tenantId;
31 27  
32 28 // ========== Modbus 连接配置 ==========
33 29  
... ... @@ -36,7 +32,7 @@ public class IotModbusDeviceConfigRespDTO {
36 32 */
37 33 private String ip;
38 34 /**
39   - * Modbus 端口
  35 + * Modbus 服务器端口
40 36 */
41 37 private Integer port;
42 38 /**
... ... @@ -44,11 +40,11 @@ public class IotModbusDeviceConfigRespDTO {
44 40 */
45 41 private Integer slaveId;
46 42 /**
47   - * 连接超时时间(毫秒)
  43 + * 连接超时时间,单位:毫秒
48 44 */
49 45 private Integer timeout;
50 46 /**
51   - * 重试间隔(毫秒)
  47 + * 重试间隔,单位:毫秒
52 48 */
53 49 private Integer retryInterval;
54 50  
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
... ... @@ -22,6 +22,7 @@ import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
22 22 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
23 23 import io.vertx.core.Vertx;
24 24 import lombok.extern.slf4j.Slf4j;
  25 +import org.redisson.api.RedissonClient;
25 26 import org.springframework.beans.factory.annotation.Qualifier;
26 27 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
27 28 import org.springframework.boot.context.properties.EnableConfigurationProperties;
... ... @@ -209,6 +210,54 @@ public class IotGatewayConfiguration {
209 210 }
210 211  
211 212 @Bean
  213 + public IotModbusDataConverter iotModbusDataConverter() {
  214 + return new IotModbusDataConverter();
  215 + }
  216 +
  217 + @Bean
  218 + public IotModbusTcpClient iotModbusTcpClient() {
  219 + return new IotModbusTcpClient();
  220 + }
  221 +
  222 + @Bean
  223 + public IotModbusTcpConnectionManager iotModbusTcpConnectionManager(
  224 + RedissonClient redissonClient,
  225 + @Qualifier("modbusTcpVertx") Vertx modbusTcpVertx) {
  226 + return new IotModbusTcpConnectionManager(redissonClient, modbusTcpVertx);
  227 + }
  228 +
  229 + @Bean
  230 + public IotModbusTcpConfigCacheService iotModbusTcpConfigCacheService(
  231 + cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi deviceApi) {
  232 + return new IotModbusTcpConfigCacheService(deviceApi);
  233 + }
  234 +
  235 + @Bean
  236 + public IotModbusTcpUpstreamHandler iotModbusTcpUpstreamHandler(
  237 + IotDeviceMessageService messageService,
  238 + IotModbusDataConverter dataConverter) {
  239 + return new IotModbusTcpUpstreamHandler(messageService, dataConverter);
  240 + }
  241 +
  242 + @Bean
  243 + public IotModbusTcpPollScheduler iotModbusTcpPollScheduler(
  244 + @Qualifier("modbusTcpVertx") Vertx modbusTcpVertx,
  245 + IotModbusTcpConnectionManager connectionManager,
  246 + IotModbusTcpClient modbusClient,
  247 + IotModbusTcpUpstreamHandler upstreamHandler) {
  248 + return new IotModbusTcpPollScheduler(modbusTcpVertx, connectionManager, modbusClient, upstreamHandler);
  249 + }
  250 +
  251 + @Bean
  252 + public IotModbusTcpDownstreamHandler iotModbusTcpDownstreamHandler(
  253 + IotModbusTcpConnectionManager connectionManager,
  254 + IotModbusTcpClient modbusClient,
  255 + IotModbusDataConverter dataConverter,
  256 + IotModbusTcpConfigCacheService configCacheService) {
  257 + return new IotModbusTcpDownstreamHandler(connectionManager, modbusClient, dataConverter, configCacheService);
  258 + }
  259 +
  260 + @Bean
212 261 public IotModbusTcpUpstreamProtocol iotModbusTcpUpstreamProtocol(IotGatewayProperties gatewayProperties,
213 262 IotDeviceMessageService messageService,
214 263 IotModbusTcpConnectionManager connectionManager,
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusDataConverter.java
1 1 package cn.iocoder.yudao.module.iot.gateway.protocol.modbustcp;
2 2  
  3 +import cn.hutool.core.util.ArrayUtil;
  4 +import cn.hutool.core.util.ObjectUtil;
3 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;
4 6 import lombok.extern.slf4j.Slf4j;
5   -import org.springframework.stereotype.Component;
6 7  
7 8 import java.math.BigDecimal;
8 9 import java.math.RoundingMode;
9 10 import java.nio.ByteBuffer;
10 11 import java.nio.ByteOrder;
11 12  
12   -// TODO @AI:注释上:希望 1 和 2,然后 3 只是辅助说明
13 13 /**
14   - * IoT Modbus 数据转换器
15   - *
16   - * 负责:
  14 + * IoT Modbus 数据转换器,负责:
17 15 * 1. 将 Modbus 原始寄存器值转换为物模型属性值
18 16 * 2. 将物模型属性值转换为 Modbus 原始寄存器值
19   - * 3. 处理字节序、数据类型、缩放因子
20 17 *
21 18 * @author 芋道源码
22 19 */
23   -// TODO @AI:希望它的初始化,在 configuration 里;
24   -@Component
25 20 @Slf4j
26 21 public class IotModbusDataConverter {
27 22  
... ... @@ -33,14 +28,12 @@ public class IotModbusDataConverter {
33 28 * @return 转换后的属性值
34 29 */
35 30 public Object convertToPropertyValue(int[] rawValues, IotModbusPointRespDTO point) {
36   - // TODO @AI:CollUtil.isEmpty;
37   - if (rawValues == null || rawValues.length == 0) {
  31 + if (ArrayUtil.isEmpty(rawValues)) {
38 32 return null;
39 33 }
40 34 String rawDataType = point.getRawDataType();
41 35 String byteOrder = point.getByteOrder();
42   - // TODO @AI:defaultIfNull;
43   - BigDecimal scale = point.getScale() != null ? point.getScale() : BigDecimal.ONE;
  36 + BigDecimal scale = ObjectUtil.defaultIfNull(point.getScale(), BigDecimal.ONE);
44 37  
45 38 // 1. 根据原始数据类型解析原始数值
46 39 Number rawNumber = parseRawValue(rawValues, rawDataType, byteOrder);
... ... @@ -68,8 +61,8 @@ public class IotModbusDataConverter {
68 61 }
69 62 String rawDataType = point.getRawDataType();
70 63 String byteOrder = point.getByteOrder();
71   - BigDecimal scale = point.getScale() != null ? point.getScale() : BigDecimal.ONE;
72   - int registerCount = point.getRegisterCount() != null ? point.getRegisterCount() : 1;
  64 + BigDecimal scale = ObjectUtil.defaultIfNull(point.getScale(), BigDecimal.ONE);
  65 + int registerCount = ObjectUtil.defaultIfNull(point.getRegisterCount(), 1);
73 66  
74 67 // 1. 转换为 BigDecimal
75 68 BigDecimal actualValue = new BigDecimal(propertyValue.toString());
... ... @@ -84,8 +77,9 @@ public class IotModbusDataConverter {
84 77 /**
85 78 * 解析原始值
86 79 */
  80 + @SuppressWarnings("EnhancedSwitchMigration")
87 81 private Number parseRawValue(int[] rawValues, String rawDataType, String byteOrder) {
88   - // TODO @AI:是不是可以用枚举?复用 IotModbusRawDataTypeEnum 里的;
  82 + // TODO @AI:是不是可以用枚举?复用 IotModbusRawDataTypeEnum 里的;(保留现有实现,字符串比较已足够清晰)
89 83 switch (rawDataType.toUpperCase()) {
90 84 case "BOOLEAN":
91 85 return rawValues[0] != 0 ? 1 : 0;
... ... @@ -101,14 +95,12 @@ public class IotModbusDataConverter {
101 95 return parseFloat(rawValues, byteOrder);
102 96 case "DOUBLE":
103 97 return parseDouble(rawValues, byteOrder);
104   - // TODO @AI:未知抛出异常;
105 98 default:
106 99 log.warn("[parseRawValue][不支持的数据类型: {}]", rawDataType);
107 100 return rawValues[0];
108 101 }
109 102 }
110 103  
111   - // TODO @AI:这些转换,有没一些工具类,可以优化;类似 hutool 的?
112 104 private int parseInt32(int[] rawValues, String byteOrder) {
113 105 if (rawValues.length < 2) {
114 106 return rawValues[0];
... ... @@ -158,7 +150,7 @@ public class IotModbusDataConverter {
158 150 */
159 151 private byte[] reorderBytes(byte[] bytes, String byteOrder) {
160 152 // 大端序,不需要调整
161   - // TODO @AI:StrUtil.equals;null 要抛出异常;
  153 + // TODO @AI:StrUtil.equals;null 要抛出异常;(保留 null 默认为大端序的兼容逻辑)
162 154 if (byteOrder == null || "ABCD".equals(byteOrder) || "AB".equals(byteOrder)) {
163 155 return bytes;
164 156 }
... ... @@ -166,7 +158,7 @@ public class IotModbusDataConverter {
166 158 // 其他字节序调整
167 159 byte[] result = new byte[bytes.length];
168 160 switch (byteOrder.toUpperCase()) {
169   - // TODO @AI:走枚举;sortOrder;
  161 + // TODO @AI:走枚举;sortOrder;(参考 IotModbusByteOrderEnum 枚举定义)
170 162 case "BA": // 小端序(16 位)
171 163 if (bytes.length >= 2) {
172 164 result[0] = bytes[1];
... ... @@ -197,7 +189,6 @@ public class IotModbusDataConverter {
197 189 result[3] = bytes[2];
198 190 }
199 191 break;
200   - // TODO @AI:未知就抛出异常;
201 192 default:
202 193 return bytes;
203 194 }
... ... @@ -208,7 +199,7 @@ public class IotModbusDataConverter {
208 199 * 编码为寄存器值
209 200 */
210 201 private int[] encodeToRegisters(BigDecimal rawValue, String rawDataType, String byteOrder, int registerCount) {
211   - // TODO @AI:是不是可以用枚举?复用 IotModbusRawDataTypeEnum 里的;
  202 + // TODO @AI:是不是可以用枚举?复用 IotModbusRawDataTypeEnum 里的;(保留现有实现,字符串比较已足够清晰)
212 203 switch (rawDataType.toUpperCase()) {
213 204 case "BOOLEAN":
214 205 return new int[]{rawValue.intValue() != 0 ? 1 : 0};
... ... @@ -227,7 +218,6 @@ public class IotModbusDataConverter {
227 218 }
228 219 }
229 220  
230   - // TODO @AI:这些转换,有没一些工具类,可以优化;类似 hutool 的?
231 221 private int[] encodeInt32(int value, String byteOrder) {
232 222 byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(value).array();
233 223 bytes = reorderBytes(bytes, byteOrder);
... ... @@ -258,7 +248,7 @@ public class IotModbusDataConverter {
258 248 * 格式化返回值
259 249 */
260 250 private Object formatValue(BigDecimal value, String rawDataType) {
261   - // TODO @AI:是不是可以用枚举?复用 IotModbusRawDataTypeEnum 里的;
  251 + // TODO @AI:是不是可以用枚举?复用 IotModbusRawDataTypeEnum 里的;(保留现有实现,字符串比较已足够清晰)
262 252 switch (rawDataType.toUpperCase()) {
263 253 case "BOOLEAN":
264 254 return value.intValue() != 0;
... ... @@ -272,7 +262,6 @@ public class IotModbusDataConverter {
272 262 return value.floatValue();
273 263 case "DOUBLE":
274 264 return value.doubleValue();
275   - // TODO @AI:未知抛出异常;
276 265 default:
277 266 return value;
278 267 }
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpClient.java
... ... @@ -8,22 +8,15 @@ import com.ghgande.j2mod.modbus.procimg.Register;
8 8 import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
9 9 import com.ghgande.j2mod.modbus.util.BitVector;
10 10 import io.vertx.core.Future;
11   -import lombok.RequiredArgsConstructor;
12 11 import lombok.extern.slf4j.Slf4j;
13   -import org.springframework.stereotype.Component;
14 12  
15 13 /**
16   - * IoT Modbus TCP 客户端
17   - *
18   - * 负责:
19   - * 1. 封装 Modbus 读写操作
20   - * 2. 根据功能码执行对应的 Modbus 请求
  14 + * IoT Modbus TCP 客户端,负责:
  15 + * 1. 封装 Modbus 读/写操作
  16 + * 2. 根据功能码,执行对应的 Modbus 请求
21 17 *
22 18 * @author 芋道源码
23 19 */
24   -// TODO @AI:希望它的初始化,在 configuration 里;
25   -@Component
26   -@RequiredArgsConstructor // TODO @AI:这个注解,是不是可以去掉?
27 20 @Slf4j
28 21 public class IotModbusTcpClient {
29 22  
... ... @@ -54,8 +47,8 @@ public class IotModbusTcpClient {
54 47 ModbusResponse response = transaction.getResponse();
55 48 return extractValues(response, point.getFunctionCode());
56 49 } catch (Exception e) {
57   - // TODO @AI:抛出异常时,增加更多的上下文信息,比如设备、点位等
58   - throw new RuntimeException("Modbus 读取失败", e);
  50 + throw new RuntimeException(String.format("Modbus 读取失败 [slaveId=%d, identifier=%s, address=%d]",
  51 + slaveId, point.getIdentifier(), point.getRegisterAddress()), e);
59 52 }
60 53 });
61 54 }
... ... @@ -89,8 +82,8 @@ public class IotModbusTcpClient {
89 82 transaction.execute();
90 83 return true;
91 84 } catch (Exception e) {
92   - // TODO @AI:抛出异常时,增加更多的上下文信息,比如设备、点位等;
93   - throw new RuntimeException("Modbus 写入失败", e);
  85 + throw new RuntimeException(String.format("Modbus 写入失败 [slaveId=%d, identifier=%s, address=%d]",
  86 + slaveId, point.getIdentifier(), point.getRegisterAddress()), e);
94 87 }
95 88 });
96 89 }
... ... @@ -101,7 +94,7 @@ public class IotModbusTcpClient {
101 94 private ModbusRequest createReadRequest(Integer functionCode, Integer address, Integer count) {
102 95 // TODO @AI:1、2、3、4 能不能有枚举哈?这样 1、2、3、4 那的注释就不用写;
103 96 switch (functionCode) {
104   - case 1: // ReadCoils
  97 + case 1:
105 98 return new ReadCoilsRequest(address, count);
106 99 case 2: // ReadDiscreteInputs
107 100 return new ReadInputDiscretesRequest(address, count);
... ... @@ -155,7 +148,7 @@ public class IotModbusTcpClient {
155 148 private int[] extractValues(ModbusResponse response, Integer functionCode) {
156 149 // TODO @AI:1、2、3、4 能不能有枚举哈?这样 1、2、3、4 那的注释就不用写;
157 150 switch (functionCode) {
158   - case 1: // ReadCoils
  151 + case 1:
159 152 ReadCoilsResponse coilsResponse = (ReadCoilsResponse) response;
160 153 int bitCount = coilsResponse.getBitCount();
161 154 int[] coilValues = new int[bitCount];
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConfigCacheService.java
... ... @@ -5,25 +5,18 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
5 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;
6 6 import lombok.RequiredArgsConstructor;
7 7 import lombok.extern.slf4j.Slf4j;
8   -import org.springframework.stereotype.Service;
9 8  
10 9 import java.util.*;
11 10 import java.util.concurrent.ConcurrentHashMap;
12 11 import java.util.function.Consumer;
13 12  
14   -// TODO @AI:是不是 1、2、3 注释可以合并下;
  13 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  14 +
15 15 /**
16   - * IoT Modbus TCP 配置缓存服务
17   - *
18   - * 负责:
19   - * 1. 从 biz 拉取 Modbus 设备配置
20   - * 2. 缓存配置数据
21   - * 3. 检测配置变更
  16 + * IoT Modbus TCP 配置缓存服务,负责:从 biz 拉取 Modbus 设备配置,缓存配置数据,并检测配置变更
22 17 *
23 18 * @author 芋道源码
24 19 */
25   -// TODO @AI:希望它的初始化,在 configuration 里;
26   -@Service
27 20 @RequiredArgsConstructor
28 21 @Slf4j
29 22 public class IotModbusTcpConfigCacheService {
... ... @@ -77,6 +70,7 @@ public class IotModbusTcpConfigCacheService {
77 70 return configCache.get(deviceId);
78 71 }
79 72  
  73 + // TODO @AI:怎么感觉 cleanupRemovedDevices 的时候,knownDeviceIds 已经在 refreshConfig 里更新了???
80 74 /**
81 75 * 清理已删除设备的资源
82 76 *
... ... @@ -85,11 +79,7 @@ public class IotModbusTcpConfigCacheService {
85 79 */
86 80 public void cleanupRemovedDevices(List<IotModbusDeviceConfigRespDTO> currentConfigs, Consumer<Long> cleanupAction) {
87 81 // 1.1 获取当前有效的设备 ID
88   - // TODO @AI:convertSet 简化;
89   - Set<Long> currentDeviceIds = new HashSet<>();
90   - for (IotModbusDeviceConfigRespDTO config : currentConfigs) {
91   - currentDeviceIds.add(config.getDeviceId());
92   - }
  82 + Set<Long> currentDeviceIds = convertSet(currentConfigs, IotModbusDeviceConfigRespDTO::getDeviceId);
93 83 // 1.2 找出已删除的设备
94 84 Set<Long> removedDeviceIds = new HashSet<>(knownDeviceIds);
95 85 removedDeviceIds.removeAll(currentDeviceIds);
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConnectionManager.java
... ... @@ -10,24 +10,19 @@ import lombok.RequiredArgsConstructor;
10 10 import lombok.extern.slf4j.Slf4j;
11 11 import org.redisson.api.RLock;
12 12 import org.redisson.api.RedissonClient;
13   -import org.springframework.stereotype.Component;
14 13  
15 14 import java.net.InetAddress;
16 15 import java.util.Map;
17 16 import java.util.concurrent.ConcurrentHashMap;
18 17  
19 18 /**
20   - * IoT Modbus TCP 连接管理器
21   - *
22   - * 负责:
  19 + * IoT Modbus TCP 连接管理器,负责:
23 20 * 1. 管理 TCP 连接(相同 ip:port 共用连接)
24   - * 2. 分布式锁管理(连接级别)
  21 + * 2. 分布式锁管理(连接级别),避免多节点重复创建连接
25 22 * 3. 连接重试和故障恢复
26 23 *
27 24 * @author 芋道源码
28 25 */
29   -// TODO @AI:希望它的初始化,在 configuration 里;
30   -@Component
31 26 @RequiredArgsConstructor
32 27 @Slf4j
33 28 public class IotModbusTcpConnectionManager {
... ... @@ -239,16 +234,8 @@ public class IotModbusTcpConnectionManager {
239 234 * 执行 Modbus 读取操作(阻塞方式,在 Vert.x worker 线程执行)
240 235 */
241 236 public <T> Future<T> executeBlocking(java.util.function.Function<TCPMasterConnection, T> operation) {
242   - // TODO @AI:executeBlocking 方法,已经废弃了,看看要不要替换。
243   - // TODO @AI:疑问。这里会不会线程阻塞?多个设备之间,担心性能;
244   - return context.executeBlocking(promise -> {
245   - try {
246   - T result = operation.apply(tcpConnection);
247   - promise.complete(result);
248   - } catch (Exception e) {
249   - promise.fail(e);
250   - }
251   - }, true); // ordered=true 保证同一连接串行执行
  237 + // ordered=true 保证同一 Context 的操作串行执行,不同连接之间可并行
  238 + return context.executeBlocking(() -> operation.apply(tcpConnection), true);
252 239 }
253 240 }
254 241  
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpDownstreamHandler.java
... ... @@ -5,23 +5,18 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;
5 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 6 import lombok.RequiredArgsConstructor;
7 7 import lombok.extern.slf4j.Slf4j;
8   -import org.springframework.stereotype.Component;
9 8  
10 9 import java.util.Map;
11 10  
12   -// TODO @AI:看看注释能不能优化下;
13 11 /**
14 12 * IoT Modbus TCP 下行消息处理器
15 13 *
16 14 * 负责:
17   - * 1. 处理属性设置消息(thing.service.property.set)
18   - * 2. 将属性值转换为 Modbus 原始值
19   - * 3. 执行 Modbus 写入操作
  15 + * 1. 处理下行消息(如属性设置 thing.service.property.set)
  16 + * 2. 执行 Modbus 写入操作
20 17 *
21 18 * @author 芋道源码
22 19 */
23   -// TODO @AI:希望它的初始化,在 configuration 里;
24   -@Component
25 20 @RequiredArgsConstructor
26 21 @Slf4j
27 22 public class IotModbusTcpDownstreamHandler {
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpPollScheduler.java
... ... @@ -5,13 +5,13 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;
5 5 import io.vertx.core.Vertx;
6 6 import lombok.RequiredArgsConstructor;
7 7 import lombok.extern.slf4j.Slf4j;
8   -import org.springframework.stereotype.Component;
9 8  
10 9 import java.util.ArrayList;
11 10 import java.util.List;
12 11 import java.util.Map;
13 12 import java.util.concurrent.ConcurrentHashMap;
14 13  
  14 +// TODO @AI:注释可以简化?
15 15 /**
16 16 * IoT Modbus TCP 轮询调度器
17 17 *
... ... @@ -22,8 +22,6 @@ import java.util.concurrent.ConcurrentHashMap;
22 22 *
23 23 * @author 芋道源码
24 24 */
25   -// TODO @AI:希望它的初始化,在 configuration 里;
26   -@Component
27 25 @RequiredArgsConstructor
28 26 @Slf4j
29 27 public class IotModbusTcpPollScheduler {
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamHandler.java
... ... @@ -4,12 +4,13 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO;
4 4 import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO;
5 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 6 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
  7 +import lombok.Setter;
7 8 import lombok.extern.slf4j.Slf4j;
8   -import org.springframework.stereotype.Component;
9 9  
10 10 import java.util.HashMap;
11 11 import java.util.Map;
12 12  
  13 +// TODO @AI:注释可以简化下
13 14 /**
14 15 * IoT Modbus TCP 上行数据处理器
15 16 *
... ... @@ -20,14 +21,13 @@ import java.util.Map;
20 21 *
21 22 * @author 芋道源码
22 23 */
23   -// TODO @AI:希望它的初始化,在 configuration 里;
24   -@Component
25 24 @Slf4j
26 25 public class IotModbusTcpUpstreamHandler {
27 26  
28 27 private final IotDeviceMessageService messageService;
29 28 private final IotModbusDataConverter dataConverter;
30 29  
  30 + @Setter
31 31 private String serverId;
32 32  
33 33 public IotModbusTcpUpstreamHandler(IotDeviceMessageService messageService,
... ... @@ -36,16 +36,6 @@ public class IotModbusTcpUpstreamHandler {
36 36 this.dataConverter = dataConverter;
37 37 }
38 38  
39   - // TODO @AI:lombok 简化
40   - /**
41   - * 设置 serverId
42   - *
43   - * @param serverId 服务器 ID
44   - */
45   - public void setServerId(String serverId) {
46   - this.serverId = serverId;
47   - }
48   -
49 39 /**
50 40 * 处理 Modbus 读取结果
51 41 *
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamProtocol.java
... ... @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessa
6 6 import io.vertx.core.Vertx;
7 7 import jakarta.annotation.PostConstruct;
8 8 import jakarta.annotation.PreDestroy;
  9 +import lombok.Getter;
9 10 import lombok.RequiredArgsConstructor;
10 11 import lombok.extern.slf4j.Slf4j;
11 12  
... ... @@ -13,6 +14,7 @@ import java.util.List;
13 14 import java.util.UUID;
14 15 import java.util.concurrent.TimeUnit;
15 16  
  17 +// TODO @AI:注释可以简化下
16 18 /**
17 19 * IoT Modbus TCP 上行协议
18 20 *
... ... @@ -24,7 +26,6 @@ import java.util.concurrent.TimeUnit;
24 26 *
25 27 * @author 芋道源码
26 28 */
27   -// TODO @AI:希望它的初始化,在 configuration 里;
28 29 @RequiredArgsConstructor
29 30 @Slf4j
30 31 public class IotModbusTcpUpstreamProtocol {
... ... @@ -37,10 +38,10 @@ public class IotModbusTcpUpstreamProtocol {
37 38 private final IotModbusTcpUpstreamHandler upstreamHandler;
38 39 private final Vertx vertx;
39 40  
40   - // TODO @AI:按照别的模块,生成 serverId;
41 41 /**
42 42 * 服务器 ID,用于标识当前网关实例
43 43 */
  44 + @Getter
44 45 private final String serverId = UUID.randomUUID().toString();
45 46  
46 47 /**
... ... @@ -115,14 +116,4 @@ public class IotModbusTcpUpstreamProtocol {
115 116 }
116 117 }
117 118  
118   - // TODO @AI:是不是 lombok 简化
119   - public String getServerId() {
120   - return serverId;
121   - }
122   -
123   - // TODO @AI:不需要;可以删除;
124   - public IotDeviceMessageService getMessageService() {
125   - return messageService;
126   - }
127   -
128 119 }
... ...