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