From cece79d04cdf58c579fb5ec6c17219bb380c2a34 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 17 Jan 2026 01:13:06 +0800 Subject: [PATCH] feat:【iot】modbus-tcp 协议接入:20% 初始化:基于 humming-beaming-dove.md 规划 --- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java | 46 +++++++++++++++++++++------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusConfigController.java | 49 +++++++++++++++++++------------------------------ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusPointController.java | 32 ++++++++++---------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigSaveReqVO.java | 3 --- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusPointMapper.java | 21 +++++---------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusByteOrderEnum.java | 1 + yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusFunctionCodeEnum.java | 15 +-------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusRawDataTypeEnum.java | 1 + yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigService.java | 24 ++++++++---------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigServiceImpl.java | 79 +++++++++++++++++++++---------------------------------------------------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointService.java | 28 +++++++++++----------------- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointServiceImpl.java | 63 +++++++++++++++++++++++++++------------------------------------ yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusDeviceConfigRespDTO.java | 10 +++------- yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusDataConverter.java | 37 +++++++++++++------------------------ yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpClient.java | 25 +++++++++---------------- yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConfigCacheService.java | 20 +++++--------------- yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConnectionManager.java | 21 ++++----------------- yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpDownstreamHandler.java | 9 ++------- yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpPollScheduler.java | 4 +--- yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamHandler.java | 16 +++------------- yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamProtocol.java | 15 +++------------ 22 files changed, 217 insertions(+), 351 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java index 5a01518..4a18802 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java @@ -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> getEnabledModbusDeviceConfigs() { // 1. 获取所有启用的 Modbus 连接配置 - List configList = modbusConfigService.getEnabledModbusConfigList(); - if (configList.isEmpty()) { + List configList = modbusConfigService.getEnabledDeviceModbusConfigList(); + if (CollUtil.isEmpty(configList)) { return success(new ArrayList<>()); } // 2. 组装返回结果 + Set deviceIds = convertSet(configList, IotDeviceModbusConfigDO::getDeviceId); + Map deviceMap = deviceService.getDeviceMap(deviceIds); + Map> pointMap = modbusPointService.getEnabledDeviceModbusPointMapByDeviceIds(deviceIds); List 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 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 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); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusConfigController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusConfigController.java index 14648ed..cbc973e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusConfigController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusConfigController.java @@ -28,19 +28,11 @@ public class IotDeviceModbusConfigController { @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 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 updateModbusConfig(@Valid @RequestBody IotDeviceModbusConfigSaveReqVO updateReqVO) { - modbusConfigService.updateModbusConfig(updateReqVO); + public CommonResult 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 deleteModbusConfig(@RequestParam("id") Long id) { - modbusConfigService.deleteModbusConfig(id); + public CommonResult 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") - @PreAuthorize("@ss.hasPermission('iot:device-modbus-config:query')") - public CommonResult 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") + @Parameter(name = "id", description = "编号", example = "1024") + @Parameter(name = "deviceId", description = "设备编号", example = "2048") @PreAuthorize("@ss.hasPermission('iot:device-modbus-config:query')") - public CommonResult getModbusConfigByDeviceId(@RequestParam("deviceId") Long deviceId) { - IotDeviceModbusConfigDO modbusConfig = modbusConfigService.getModbusConfigByDeviceId(deviceId); + public CommonResult 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> getModbusConfigPage(@Valid IotDeviceModbusConfigPageReqVO pageReqVO) { - PageResult pageResult = modbusConfigService.getModbusConfigPage(pageReqVO); + public CommonResult> getDeviceModbusConfigPage(@Valid IotDeviceModbusConfigPageReqVO pageReqVO) { + PageResult pageResult = modbusConfigService.getDeviceModbusConfigPage(pageReqVO); return success(BeanUtils.toBean(pageResult, IotDeviceModbusConfigRespVO.class)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusPointController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusPointController.java index 55acc24..22d1743 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusPointController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusPointController.java @@ -17,8 +17,6 @@ import org.springframework.security.access.prepost.PreAuthorize; 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 createModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO createReqVO) { - return success(modbusPointService.createModbusPoint(createReqVO)); + public CommonResult 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 updateModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO updateReqVO) { - modbusPointService.updateModbusPoint(updateReqVO); + public CommonResult 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 deleteModbusPoint(@RequestParam("id") Long id) { - modbusPointService.deleteModbusPoint(id); + public CommonResult 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 getModbusPoint(@RequestParam("id") Long id) { - IotDeviceModbusPointDO modbusPoint = modbusPointService.getModbusPoint(id); + public CommonResult 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> getModbusPointPage(@Valid IotDeviceModbusPointPageReqVO pageReqVO) { - PageResult pageResult = modbusPointService.getModbusPointPage(pageReqVO); + public CommonResult> getDeviceModbusPointPage(@Valid IotDeviceModbusPointPageReqVO pageReqVO) { + PageResult 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> getModbusPointListByDeviceId(@RequestParam("deviceId") Long deviceId) { - List list = modbusPointService.getModbusPointListByDeviceId(deviceId); - return success(BeanUtils.toBean(list, IotDeviceModbusPointRespVO.class)); - } - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigSaveReqVO.java index 155ce5f..01f92b8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigSaveReqVO.java @@ -9,9 +9,6 @@ import lombok.Data; @Data public class IotDeviceModbusConfigSaveReqVO { - @Schema(description = "主键", example = "1") - private Long id; - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") @NotNull(message = "设备编号不能为空") private Long deviceId; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusPointMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusPointMapper.java index 9043dd4..74dc463 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusPointMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusPointMapper.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceMo 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 selectListByDeviceId(Long deviceId) { - return selectList(IotDeviceModbusPointDO::getDeviceId, deviceId); - } - - // TODO @AI:是不是 selectList(f1, v1, f2, v2); - default List selectListByDeviceIdAndStatus(Long deviceId, Integer status) { + default List selectListByDeviceIdsAndStatus(Collection deviceIds, Integer status) { return selectList(new LambdaQueryWrapperX() - .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() - .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); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusByteOrderEnum.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusByteOrderEnum.java index fdd0feb..78e4540 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusByteOrderEnum.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusByteOrderEnum.java @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; 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 字节序枚举 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusFunctionCodeEnum.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusFunctionCodeEnum.java index 9fcc91e..e21223d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusFunctionCodeEnum.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusFunctionCodeEnum.java @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; 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 { .orElse(null); } - // TODO @AI:如果用不到,可以暂时删除哈; - /** - * 获取写功能码 - * - * @param registerCount 寄存器数量 - * @return 写功能码 - */ - public Integer getWriteCode(int registerCount) { - if (!writable) { - return null; - } - return registerCount == 1 ? writeSingleCode : writeMultipleCode; - } - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusRawDataTypeEnum.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusRawDataTypeEnum.java index 97efc14..705ba16 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusRawDataTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotModbusRawDataTypeEnum.java @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; 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 原始数据类型枚举 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigService.java index ded475d..e38da3e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigService.java @@ -16,26 +16,18 @@ import java.util.List; 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 getModbusConfigPage(IotDeviceModbusConfigPageReqVO pageReqVO); + PageResult getDeviceModbusConfigPage(IotDeviceModbusConfigPageReqVO pageReqVO); /** * 获得所有启用的 Modbus 连接配置列表 * * @return 启用的 Modbus 连接配置列表 */ - List getEnabledModbusConfigList(); + List getEnabledDeviceModbusConfigList(); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigServiceImpl.java index 582258a..ea7624f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigServiceImpl.java @@ -14,7 +14,6 @@ import org.springframework.validation.annotation.Validated; 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); - - // 2. 插入 - IotDeviceModbusConfigDO modbusConfig = BeanUtils.toBean(createReqVO, IotDeviceModbusConfigDO.class); - setDefaultValues(modbusConfig); - modbusConfigMapper.insert(modbusConfig); - return modbusConfig.getId(); - } - - @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); + public void saveDeviceModbusConfig(IotDeviceModbusConfigSaveReqVO saveReqVO) { + // 1. 校验设备存在 + deviceService.validateDeviceExists(saveReqVO.getDeviceId()); + + // 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 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 getModbusConfigPage(IotDeviceModbusConfigPageReqVO pageReqVO) { + public PageResult getDeviceModbusConfigPage(IotDeviceModbusConfigPageReqVO pageReqVO) { return modbusConfigMapper.selectPage(pageReqVO); } @Override - public List getEnabledModbusConfigList() { + public List getEnabledDeviceModbusConfigList() { return modbusConfigMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointService.java index daf2b09..aac98c1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointService.java @@ -6,7 +6,9 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceMo 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 getModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO); + PageResult getDeviceModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO); /** - * 根据设备编号获得点位配置列表 + * 根据设备编号批量获得启用的点位配置 Map * - * @param deviceId 设备编号 - * @return 点位配置列表 + * @param deviceIds 设备编号集合 + * @return 设备点位 Map,key 为设备编号,value 为点位配置列表 */ - List getModbusPointListByDeviceId(Long deviceId); - - /** - * 根据设备编号获得启用的点位配置列表 - * - * @param deviceId 设备编号 - * @return 启用的点位配置列表 - */ - List getEnabledModbusPointListByDeviceId(Long deviceId); + Map> getEnabledDeviceModbusPointMapByDeviceIds(Collection deviceIds); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointServiceImpl.java index 0b79be8..242e1c9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointServiceImpl.java @@ -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 getModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO) { + public PageResult getDeviceModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO) { return modbusPointMapper.selectPage(pageReqVO); } @Override - public List getModbusPointListByDeviceId(Long deviceId) { - return modbusPointMapper.selectListByDeviceId(deviceId); - } - - @Override - public List getEnabledModbusPointListByDeviceId(Long deviceId) { - return modbusPointMapper.selectListByDeviceIdAndStatus(deviceId, CommonStatusEnum.ENABLE.getStatus()); + public Map> getEnabledDeviceModbusPointMapByDeviceIds(Collection deviceIds) { + if (CollUtil.isEmpty(deviceIds)) { + return Collections.emptyMap(); + } + List pointList = modbusPointMapper.selectListByDeviceIdsAndStatus( + deviceIds, CommonStatusEnum.ENABLE.getStatus()); + return convertMultiMap(pointList, IotDeviceModbusPointDO::getDeviceId); } } diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusDeviceConfigRespDTO.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusDeviceConfigRespDTO.java index 46d318a..0b382a3 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusDeviceConfigRespDTO.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusDeviceConfigRespDTO.java @@ -24,10 +24,6 @@ public class IotModbusDeviceConfigRespDTO { * 设备名称 */ 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; diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java index 53aafda..6641052 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java @@ -22,6 +22,7 @@ import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; 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; @@ -209,6 +210,54 @@ public class IotGatewayConfiguration { } @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, IotModbusTcpConnectionManager connectionManager, diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusDataConverter.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusDataConverter.java index ddec584..e61811c 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusDataConverter.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusDataConverter.java @@ -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; } diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpClient.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpClient.java index 43a2fbe..436cabc 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpClient.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpClient.java @@ -8,22 +8,15 @@ import com.ghgande.j2mod.modbus.procimg.Register; 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]; diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConfigCacheService.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConfigCacheService.java index 38744bf..611b2f4 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConfigCacheService.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConfigCacheService.java @@ -5,25 +5,18 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; 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 currentConfigs, Consumer cleanupAction) { // 1.1 获取当前有效的设备 ID - // TODO @AI:convertSet 简化; - Set currentDeviceIds = new HashSet<>(); - for (IotModbusDeviceConfigRespDTO config : currentConfigs) { - currentDeviceIds.add(config.getDeviceId()); - } + Set currentDeviceIds = convertSet(currentConfigs, IotModbusDeviceConfigRespDTO::getDeviceId); // 1.2 找出已删除的设备 Set removedDeviceIds = new HashSet<>(knownDeviceIds); removedDeviceIds.removeAll(currentDeviceIds); diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConnectionManager.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConnectionManager.java index 75b98ea..4ac426c 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConnectionManager.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpConnectionManager.java @@ -10,24 +10,19 @@ import lombok.RequiredArgsConstructor; 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 Future executeBlocking(java.util.function.Function 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); } } diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpDownstreamHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpDownstreamHandler.java index eef237f..5b83c33 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpDownstreamHandler.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpDownstreamHandler.java @@ -5,23 +5,18 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO; 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 { diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpPollScheduler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpPollScheduler.java index e4b3192..793c304 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpPollScheduler.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpPollScheduler.java @@ -5,13 +5,13 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusPointRespDTO; 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 { diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamHandler.java index df46228..a999a6b 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamHandler.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamHandler.java @@ -4,12 +4,13 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigRespDTO; 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 读取结果 * diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamProtocol.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamProtocol.java index 1717e61..5764a81 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamProtocol.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbustcp/IotModbusTcpUpstreamProtocol.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessa 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; - } - } -- libgit2 0.21.4