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

This commit is contained in:
YunaiV
2026-01-17 01:13:06 +08:00
parent 9e2c1feae1
commit cece79d04c
22 changed files with 216 additions and 350 deletions

View File

@@ -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);
}

View File

@@ -28,19 +28,11 @@ public class IotDeviceModbusConfigController {
@Resource
private IotDeviceModbusConfigService modbusConfigService;
// TODO @AIcreate 和 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));
}

View File

@@ -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));
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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 字节序枚举
*

View File

@@ -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;
}
}

View File

@@ -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 原始数据类型枚举
*

View File

@@ -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();
}

View File

@@ -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 @AIObjUtil 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());
}

View File

@@ -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 设备点位 Mapkey 为设备编号value 为点位配置列表
*/
List<IotDeviceModbusPointDO> getModbusPointListByDeviceId(Long deviceId);
/**
* 根据设备编号获得启用的点位配置列表
*
* @param deviceId 设备编号
* @return 启用的点位配置列表
*/
List<IotDeviceModbusPointDO> getEnabledModbusPointListByDeviceId(Long deviceId);
Map<Long, List<IotDeviceModbusPointDO>> getEnabledDeviceModbusPointMapByDeviceIds(Collection<Long> deviceIds);
}

View File

@@ -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 @AIObjUtil 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);
}
}

View File

@@ -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;

View File

@@ -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,

View File

@@ -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 @AICollUtil.isEmpty
if (rawValues == null || rawValues.length == 0) {
if (ArrayUtil.isEmpty(rawValues)) {
return null;
}
String rawDataType = point.getRawDataType();
String byteOrder = point.getByteOrder();
// TODO @AIdefaultIfNull
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 @AIStrUtil.equalsnull 要抛出异常;
// TODO @AIStrUtil.equalsnull 要抛出异常;(保留 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;
}

View File

@@ -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 @AI1、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 @AI1、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];

View File

@@ -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 @AIconvertSet 简化;
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);

View File

@@ -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 @AIexecuteBlocking 方法,已经废弃了,看看要不要替换。
// 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);
}
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 @AIlombok 简化
/**
* 设置 serverId
*
* @param serverId 服务器 ID
*/
public void setServerId(String serverId) {
this.serverId = serverId;
}
/**
* 处理 Modbus 读取结果
*

View File

@@ -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;
}
}