Commit 78bcd54e34c1e55b8f92d2e515e383bc1d3060ac
Committed by
Gitee
!1518 接入 modbus 协议,重构所有协议的配置
Merge pull request !1518 from 芋道源码/feature/iot
Showing
238 changed files
with
14808 additions
and
7751 deletions
Too many changes.
To preserve performance only 100 of 238 files are displayed.
yudao-module-iot/yudao-module-iot-biz/pom.xml
| @@ -101,18 +101,6 @@ | @@ -101,18 +101,6 @@ | ||
| 101 | <artifactId>spring-boot-starter-amqp</artifactId> | 101 | <artifactId>spring-boot-starter-amqp</artifactId> |
| 102 | <optional>true</optional> | 102 | <optional>true</optional> |
| 103 | </dependency> | 103 | </dependency> |
| 104 | - | ||
| 105 | - <!-- IoT 网络组件:接收来自设备的上行数据 --> | ||
| 106 | - <!-- <dependency>--> | ||
| 107 | - <!-- <groupId>cn.iocoder.boot</groupId>--> | ||
| 108 | - <!-- <artifactId>yudao-module-iot-net-component-http</artifactId>--> | ||
| 109 | - <!-- <version>${revision}</version>--> | ||
| 110 | - <!-- </dependency>--> | ||
| 111 | - <!-- <dependency>--> | ||
| 112 | - <!-- <groupId>cn.iocoder.boot</groupId>--> | ||
| 113 | - <!-- <artifactId>yudao-module-iot-net-component-emqx</artifactId>--> | ||
| 114 | - <!-- <version>${revision}</version>--> | ||
| 115 | - <!-- </dependency>--> | ||
| 116 | </dependencies> | 104 | </dependencies> |
| 117 | 105 | ||
| 118 | </project> | 106 | </project> |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java
| 1 | package cn.iocoder.yudao.module.iot.api.device; | 1 | package cn.iocoder.yudao.module.iot.api.device; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.hutool.core.util.ObjUtil; | ||
| 5 | +import cn.hutool.core.util.StrUtil; | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.RpcConstants; | 6 | import cn.iocoder.yudao.framework.common.enums.RpcConstants; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 7 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 8 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 9 | +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; | ||
| 6 | import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; | 10 | import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; |
| 7 | -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; | ||
| 8 | -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO; | ||
| 9 | -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; | ||
| 10 | -import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; | 11 | +import cn.iocoder.yudao.module.iot.core.biz.dto.*; |
| 11 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; | 12 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; |
| 12 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; | 13 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; |
| 13 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; | 14 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; |
| 14 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | 15 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 16 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; | ||
| 17 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO; | ||
| 15 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | 18 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| 19 | +import cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusConfigService; | ||
| 20 | +import cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusPointService; | ||
| 16 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; | 21 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; |
| 17 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; | 22 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; |
| 18 | import jakarta.annotation.Resource; | 23 | import jakarta.annotation.Resource; |
| 19 | import jakarta.annotation.security.PermitAll; | 24 | import jakarta.annotation.security.PermitAll; |
| 25 | +import org.springframework.context.annotation.Lazy; | ||
| 20 | import org.springframework.context.annotation.Primary; | 26 | import org.springframework.context.annotation.Primary; |
| 21 | import org.springframework.validation.annotation.Validated; | 27 | import org.springframework.validation.annotation.Validated; |
| 22 | import org.springframework.web.bind.annotation.PostMapping; | 28 | import org.springframework.web.bind.annotation.PostMapping; |
| 23 | import org.springframework.web.bind.annotation.RequestBody; | 29 | import org.springframework.web.bind.annotation.RequestBody; |
| 24 | import org.springframework.web.bind.annotation.RestController; | 30 | import org.springframework.web.bind.annotation.RestController; |
| 25 | 31 | ||
| 32 | +import java.util.ArrayList; | ||
| 26 | import java.util.List; | 33 | import java.util.List; |
| 34 | +import java.util.Map; | ||
| 35 | +import java.util.Set; | ||
| 27 | 36 | ||
| 28 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 37 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 38 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||
| 29 | 39 | ||
| 30 | /** | 40 | /** |
| 31 | * IoT 设备 API 实现类 | 41 | * IoT 设备 API 实现类 |
| @@ -41,6 +51,12 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { | @@ -41,6 +51,12 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { | ||
| 41 | private IotDeviceService deviceService; | 51 | private IotDeviceService deviceService; |
| 42 | @Resource | 52 | @Resource |
| 43 | private IotProductService productService; | 53 | private IotProductService productService; |
| 54 | + @Resource | ||
| 55 | + @Lazy // 延迟加载,解决循环依赖 | ||
| 56 | + private IotDeviceModbusConfigService modbusConfigService; | ||
| 57 | + @Resource | ||
| 58 | + @Lazy // 延迟加载,解决循环依赖 | ||
| 59 | + private IotDeviceModbusPointService modbusPointService; | ||
| 44 | 60 | ||
| 45 | @Override | 61 | @Override |
| 46 | @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/auth") | 62 | @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/auth") |
| @@ -58,12 +74,58 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { | @@ -58,12 +74,58 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { | ||
| 58 | return success(BeanUtils.toBean(device, IotDeviceRespDTO.class, deviceDTO -> { | 74 | return success(BeanUtils.toBean(device, IotDeviceRespDTO.class, deviceDTO -> { |
| 59 | IotProductDO product = productService.getProductFromCache(deviceDTO.getProductId()); | 75 | IotProductDO product = productService.getProductFromCache(deviceDTO.getProductId()); |
| 60 | if (product != null) { | 76 | if (product != null) { |
| 61 | - deviceDTO.setCodecType(product.getCodecType()); | 77 | + deviceDTO.setProtocolType(product.getProtocolType()).setSerializeType(product.getSerializeType()); |
| 62 | } | 78 | } |
| 63 | })); | 79 | })); |
| 64 | } | 80 | } |
| 65 | 81 | ||
| 66 | @Override | 82 | @Override |
| 83 | + @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/modbus/config-list") | ||
| 84 | + @PermitAll | ||
| 85 | + @TenantIgnore | ||
| 86 | + public CommonResult<List<IotModbusDeviceConfigRespDTO>> getModbusDeviceConfigList( | ||
| 87 | + @RequestBody IotModbusDeviceConfigListReqDTO listReqDTO) { | ||
| 88 | + // 1. 获取 Modbus 连接配置 | ||
| 89 | + List<IotDeviceModbusConfigDO> configList = modbusConfigService.getDeviceModbusConfigList(listReqDTO); | ||
| 90 | + if (CollUtil.isEmpty(configList)) { | ||
| 91 | + return success(new ArrayList<>()); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + // 2. 组装返回结果 | ||
| 95 | + Set<Long> deviceIds = convertSet(configList, IotDeviceModbusConfigDO::getDeviceId); | ||
| 96 | + Map<Long, IotDeviceDO> deviceMap = deviceService.getDeviceMap(deviceIds); | ||
| 97 | + Map<Long, List<IotDeviceModbusPointDO>> pointMap = modbusPointService.getEnabledDeviceModbusPointMapByDeviceIds(deviceIds); | ||
| 98 | + Map<Long, IotProductDO> productMap = productService.getProductMap(convertSet(deviceMap.values(), IotDeviceDO::getProductId)); | ||
| 99 | + List<IotModbusDeviceConfigRespDTO> result = new ArrayList<>(configList.size()); | ||
| 100 | + for (IotDeviceModbusConfigDO config : configList) { | ||
| 101 | + // 3.1 获取设备信息 | ||
| 102 | + IotDeviceDO device = deviceMap.get(config.getDeviceId()); | ||
| 103 | + if (device == null) { | ||
| 104 | + continue; | ||
| 105 | + } | ||
| 106 | + // 3.2 按 protocolType 筛选(如果非空) | ||
| 107 | + if (StrUtil.isNotEmpty(listReqDTO.getProtocolType())) { | ||
| 108 | + IotProductDO product = productMap.get(device.getProductId()); | ||
| 109 | + if (product == null || ObjUtil.notEqual(listReqDTO.getProtocolType(), product.getProtocolType())) { | ||
| 110 | + continue; | ||
| 111 | + } | ||
| 112 | + } | ||
| 113 | + // 3.3 获取启用的点位列表 | ||
| 114 | + List<IotDeviceModbusPointDO> pointList = pointMap.get(config.getDeviceId()); | ||
| 115 | + if (CollUtil.isEmpty(pointList)) { | ||
| 116 | + continue; | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + // 3.4 构建 IotModbusDeviceConfigRespDTO 对象 | ||
| 120 | + IotModbusDeviceConfigRespDTO configDTO = BeanUtils.toBean(config, IotModbusDeviceConfigRespDTO.class, o -> | ||
| 121 | + o.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName()) | ||
| 122 | + .setPoints(BeanUtils.toBean(pointList, IotModbusPointRespDTO.class))); | ||
| 123 | + result.add(configDTO); | ||
| 124 | + } | ||
| 125 | + return success(result); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + @Override | ||
| 67 | @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/register") | 129 | @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/register") |
| 68 | @PermitAll | 130 | @PermitAll |
| 69 | public CommonResult<IotDeviceRegisterRespDTO> registerDevice(@RequestBody IotDeviceRegisterReqDTO reqDTO) { | 131 | public CommonResult<IotDeviceRegisterRespDTO> registerDevice(@RequestBody IotDeviceRegisterReqDTO reqDTO) { |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusConfigController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.device; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 4 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 5 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigRespVO; | ||
| 6 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; | ||
| 8 | +import cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusConfigService; | ||
| 9 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 10 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 11 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 12 | +import jakarta.annotation.Resource; | ||
| 13 | +import jakarta.validation.Valid; | ||
| 14 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 15 | +import org.springframework.validation.annotation.Validated; | ||
| 16 | +import org.springframework.web.bind.annotation.*; | ||
| 17 | + | ||
| 18 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 19 | + | ||
| 20 | +@Tag(name = "管理后台 - IoT 设备 Modbus 连接配置") | ||
| 21 | +@RestController | ||
| 22 | +@RequestMapping("/iot/device-modbus-config") | ||
| 23 | +@Validated | ||
| 24 | +public class IotDeviceModbusConfigController { | ||
| 25 | + | ||
| 26 | + @Resource | ||
| 27 | + private IotDeviceModbusConfigService modbusConfigService; | ||
| 28 | + | ||
| 29 | + @PostMapping("/save") | ||
| 30 | + @Operation(summary = "保存设备 Modbus 连接配置") | ||
| 31 | + @PreAuthorize("@ss.hasPermission('iot:device:update')") | ||
| 32 | + public CommonResult<Boolean> saveDeviceModbusConfig(@Valid @RequestBody IotDeviceModbusConfigSaveReqVO saveReqVO) { | ||
| 33 | + modbusConfigService.saveDeviceModbusConfig(saveReqVO); | ||
| 34 | + return success(true); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + @GetMapping("/get") | ||
| 38 | + @Operation(summary = "获得设备 Modbus 连接配置") | ||
| 39 | + @Parameter(name = "id", description = "编号", example = "1024") | ||
| 40 | + @Parameter(name = "deviceId", description = "设备编号", example = "2048") | ||
| 41 | + @PreAuthorize("@ss.hasPermission('iot:device:query')") | ||
| 42 | + public CommonResult<IotDeviceModbusConfigRespVO> getDeviceModbusConfig( | ||
| 43 | + @RequestParam(value = "id", required = false) Long id, | ||
| 44 | + @RequestParam(value = "deviceId", required = false) Long deviceId) { | ||
| 45 | + IotDeviceModbusConfigDO modbusConfig = null; | ||
| 46 | + if (id != null) { | ||
| 47 | + modbusConfig = modbusConfigService.getDeviceModbusConfig(id); | ||
| 48 | + } else if (deviceId != null) { | ||
| 49 | + modbusConfig = modbusConfigService.getDeviceModbusConfigByDeviceId(deviceId); | ||
| 50 | + } | ||
| 51 | + return success(BeanUtils.toBean(modbusConfig, IotDeviceModbusConfigRespVO.class)); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceModbusPointController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.device; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 5 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 6 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointPageReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointRespVO; | ||
| 8 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointSaveReqVO; | ||
| 9 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO; | ||
| 10 | +import cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusPointService; | ||
| 11 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 12 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 13 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 14 | +import jakarta.annotation.Resource; | ||
| 15 | +import jakarta.validation.Valid; | ||
| 16 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 17 | +import org.springframework.validation.annotation.Validated; | ||
| 18 | +import org.springframework.web.bind.annotation.*; | ||
| 19 | + | ||
| 20 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 21 | + | ||
| 22 | +@Tag(name = "管理后台 - IoT 设备 Modbus 点位配置") | ||
| 23 | +@RestController | ||
| 24 | +@RequestMapping("/iot/device-modbus-point") | ||
| 25 | +@Validated | ||
| 26 | +public class IotDeviceModbusPointController { | ||
| 27 | + | ||
| 28 | + @Resource | ||
| 29 | + private IotDeviceModbusPointService modbusPointService; | ||
| 30 | + | ||
| 31 | + @PostMapping("/create") | ||
| 32 | + @Operation(summary = "创建设备 Modbus 点位配置") | ||
| 33 | + @PreAuthorize("@ss.hasPermission('iot:device:update')") | ||
| 34 | + public CommonResult<Long> createDeviceModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO createReqVO) { | ||
| 35 | + return success(modbusPointService.createDeviceModbusPoint(createReqVO)); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + @PutMapping("/update") | ||
| 39 | + @Operation(summary = "更新设备 Modbus 点位配置") | ||
| 40 | + @PreAuthorize("@ss.hasPermission('iot:device:update')") | ||
| 41 | + public CommonResult<Boolean> updateDeviceModbusPoint(@Valid @RequestBody IotDeviceModbusPointSaveReqVO updateReqVO) { | ||
| 42 | + modbusPointService.updateDeviceModbusPoint(updateReqVO); | ||
| 43 | + return success(true); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + @DeleteMapping("/delete") | ||
| 47 | + @Operation(summary = "删除设备 Modbus 点位配置") | ||
| 48 | + @Parameter(name = "id", description = "编号", required = true) | ||
| 49 | + @PreAuthorize("@ss.hasPermission('iot:device:update')") | ||
| 50 | + public CommonResult<Boolean> deleteDeviceModbusPoint(@RequestParam("id") Long id) { | ||
| 51 | + modbusPointService.deleteDeviceModbusPoint(id); | ||
| 52 | + return success(true); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + @GetMapping("/get") | ||
| 56 | + @Operation(summary = "获得设备 Modbus 点位配置") | ||
| 57 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | ||
| 58 | + @PreAuthorize("@ss.hasPermission('iot:device:query')") | ||
| 59 | + public CommonResult<IotDeviceModbusPointRespVO> getDeviceModbusPoint(@RequestParam("id") Long id) { | ||
| 60 | + IotDeviceModbusPointDO modbusPoint = modbusPointService.getDeviceModbusPoint(id); | ||
| 61 | + return success(BeanUtils.toBean(modbusPoint, IotDeviceModbusPointRespVO.class)); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + @GetMapping("/page") | ||
| 65 | + @Operation(summary = "获得设备 Modbus 点位配置分页") | ||
| 66 | + @PreAuthorize("@ss.hasPermission('iot:device:query')") | ||
| 67 | + public CommonResult<PageResult<IotDeviceModbusPointRespVO>> getDeviceModbusPointPage(@Valid IotDeviceModbusPointPageReqVO pageReqVO) { | ||
| 68 | + PageResult<IotDeviceModbusPointDO> pageResult = modbusPointService.getDeviceModbusPointPage(pageReqVO); | ||
| 69 | + return success(BeanUtils.toBean(pageResult, IotDeviceModbusPointRespVO.class)); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java
| @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; | @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 4 | import cn.iocoder.yudao.framework.common.validation.InEnum; | 4 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 5 | -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | 5 | +import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; |
| 6 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; | 6 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; |
| 7 | import io.swagger.v3.oas.annotations.media.Schema; | 7 | import io.swagger.v3.oas.annotations.media.Schema; |
| 8 | import lombok.Data; | 8 | import lombok.Data; |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/message/IotDeviceMessageRespVO.java
| @@ -38,7 +38,7 @@ public class IotDeviceMessageRespVO { | @@ -38,7 +38,7 @@ public class IotDeviceMessageRespVO { | ||
| 38 | @Schema(description = "请求编号", example = "req_123") | 38 | @Schema(description = "请求编号", example = "req_123") |
| 39 | private String requestId; | 39 | private String requestId; |
| 40 | 40 | ||
| 41 | - @Schema(description = "请求方法", requiredMode = Schema.RequiredMode.REQUIRED, example = "thing.property.report") | 41 | + @Schema(description = "请求方法", requiredMode = Schema.RequiredMode.REQUIRED, example = "thing.property.post") |
| 42 | private String method; | 42 | private String method; |
| 43 | 43 | ||
| 44 | @Schema(description = "请求参数") | 44 | @Schema(description = "请求参数") |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import java.time.LocalDateTime; | ||
| 7 | + | ||
| 8 | +@Schema(description = "管理后台 - IoT 设备 Modbus 连接配置 Response VO") | ||
| 9 | +@Data | ||
| 10 | +public class IotDeviceModbusConfigRespVO { | ||
| 11 | + | ||
| 12 | + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 13 | + private Long id; | ||
| 14 | + | ||
| 15 | + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | ||
| 16 | + private Long deviceId; | ||
| 17 | + | ||
| 18 | + @Schema(description = "设备名称", example = "温湿度传感器") | ||
| 19 | + private String deviceName; | ||
| 20 | + | ||
| 21 | + @Schema(description = "Modbus 服务器 IP 地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "192.168.1.100") | ||
| 22 | + private String ip; | ||
| 23 | + | ||
| 24 | + @Schema(description = "Modbus 端口", requiredMode = Schema.RequiredMode.REQUIRED, example = "502") | ||
| 25 | + private Integer port; | ||
| 26 | + | ||
| 27 | + @Schema(description = "从站地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 28 | + private Integer slaveId; | ||
| 29 | + | ||
| 30 | + @Schema(description = "连接超时时间(毫秒)", example = "3000") | ||
| 31 | + private Integer timeout; | ||
| 32 | + | ||
| 33 | + @Schema(description = "重试间隔(毫秒)", example = "1000") | ||
| 34 | + private Integer retryInterval; | ||
| 35 | + | ||
| 36 | + @Schema(description = "工作模式", example = "1") | ||
| 37 | + private Integer mode; | ||
| 38 | + | ||
| 39 | + @Schema(description = "数据帧格式", example = "1") | ||
| 40 | + private Integer frameFormat; | ||
| 41 | + | ||
| 42 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | ||
| 43 | + private Integer status; | ||
| 44 | + | ||
| 45 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | ||
| 46 | + private LocalDateTime createTime; | ||
| 47 | + | ||
| 48 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusConfigSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusModeEnum; | ||
| 6 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 7 | +import jakarta.validation.constraints.NotNull; | ||
| 8 | +import lombok.Data; | ||
| 9 | + | ||
| 10 | +@Schema(description = "管理后台 - IoT 设备 Modbus 连接配置新增/修改 Request VO") | ||
| 11 | +@Data | ||
| 12 | +public class IotDeviceModbusConfigSaveReqVO { | ||
| 13 | + | ||
| 14 | + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | ||
| 15 | + @NotNull(message = "设备编号不能为空") | ||
| 16 | + private Long deviceId; | ||
| 17 | + | ||
| 18 | + @Schema(description = "Modbus 服务器 IP 地址", example = "192.168.1.100") | ||
| 19 | + private String ip; | ||
| 20 | + | ||
| 21 | + @Schema(description = "Modbus 端口", example = "502") | ||
| 22 | + private Integer port; | ||
| 23 | + | ||
| 24 | + @Schema(description = "从站地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 25 | + @NotNull(message = "从站地址不能为空") | ||
| 26 | + private Integer slaveId; | ||
| 27 | + | ||
| 28 | + @Schema(description = "连接超时时间(毫秒)", example = "3000") | ||
| 29 | + private Integer timeout; | ||
| 30 | + | ||
| 31 | + @Schema(description = "重试间隔(毫秒)", example = "1000") | ||
| 32 | + private Integer retryInterval; | ||
| 33 | + | ||
| 34 | + @Schema(description = "工作模式", example = "1") | ||
| 35 | + @InEnum(IotModbusModeEnum.class) | ||
| 36 | + private Integer mode; | ||
| 37 | + | ||
| 38 | + @Schema(description = "数据帧格式", example = "1") | ||
| 39 | + @InEnum(IotModbusFrameFormatEnum.class) | ||
| 40 | + private Integer frameFormat; | ||
| 41 | + | ||
| 42 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | ||
| 43 | + @NotNull(message = "状态不能为空") | ||
| 44 | + private Integer status; | ||
| 45 | + | ||
| 46 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.EqualsAndHashCode; | ||
| 7 | +import lombok.ToString; | ||
| 8 | + | ||
| 9 | +@Schema(description = "管理后台 - IoT 设备 Modbus 点位配置分页 Request VO") | ||
| 10 | +@Data | ||
| 11 | +@EqualsAndHashCode(callSuper = true) | ||
| 12 | +@ToString(callSuper = true) | ||
| 13 | +public class IotDeviceModbusPointPageReqVO extends PageParam { | ||
| 14 | + | ||
| 15 | + @Schema(description = "设备编号", example = "1024") | ||
| 16 | + private Long deviceId; | ||
| 17 | + | ||
| 18 | + @Schema(description = "属性标识符", example = "temperature") | ||
| 19 | + private String identifier; | ||
| 20 | + | ||
| 21 | + @Schema(description = "属性名称", example = "温度") | ||
| 22 | + private String name; | ||
| 23 | + | ||
| 24 | + @Schema(description = "Modbus 功能码", example = "3") | ||
| 25 | + private Integer functionCode; | ||
| 26 | + | ||
| 27 | + @Schema(description = "状态", example = "0") | ||
| 28 | + private Integer status; | ||
| 29 | + | ||
| 30 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import java.math.BigDecimal; | ||
| 7 | +import java.time.LocalDateTime; | ||
| 8 | + | ||
| 9 | +@Schema(description = "管理后台 - IoT 设备 Modbus 点位配置 Response VO") | ||
| 10 | +@Data | ||
| 11 | +public class IotDeviceModbusPointRespVO { | ||
| 12 | + | ||
| 13 | + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 14 | + private Long id; | ||
| 15 | + | ||
| 16 | + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | ||
| 17 | + private Long deviceId; | ||
| 18 | + | ||
| 19 | + @Schema(description = "物模型属性编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") | ||
| 20 | + private Long thingModelId; | ||
| 21 | + | ||
| 22 | + @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature") | ||
| 23 | + private String identifier; | ||
| 24 | + | ||
| 25 | + @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "温度") | ||
| 26 | + private String name; | ||
| 27 | + | ||
| 28 | + @Schema(description = "Modbus 功能码", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") | ||
| 29 | + private Integer functionCode; | ||
| 30 | + | ||
| 31 | + @Schema(description = "寄存器起始地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | ||
| 32 | + private Integer registerAddress; | ||
| 33 | + | ||
| 34 | + @Schema(description = "寄存器数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 35 | + private Integer registerCount; | ||
| 36 | + | ||
| 37 | + @Schema(description = "字节序", requiredMode = Schema.RequiredMode.REQUIRED, example = "AB") | ||
| 38 | + private String byteOrder; | ||
| 39 | + | ||
| 40 | + @Schema(description = "原始数据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "INT16") | ||
| 41 | + private String rawDataType; | ||
| 42 | + | ||
| 43 | + @Schema(description = "缩放因子", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") | ||
| 44 | + private BigDecimal scale; | ||
| 45 | + | ||
| 46 | + @Schema(description = "轮询间隔(毫秒)", requiredMode = Schema.RequiredMode.REQUIRED, example = "5000") | ||
| 47 | + private Integer pollInterval; | ||
| 48 | + | ||
| 49 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | ||
| 50 | + private Integer status; | ||
| 51 | + | ||
| 52 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | ||
| 53 | + private LocalDateTime createTime; | ||
| 54 | + | ||
| 55 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/modbus/IotDeviceModbusPointSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import jakarta.validation.constraints.NotEmpty; | ||
| 5 | +import jakarta.validation.constraints.NotNull; | ||
| 6 | +import lombok.Data; | ||
| 7 | + | ||
| 8 | +import java.math.BigDecimal; | ||
| 9 | + | ||
| 10 | +@Schema(description = "管理后台 - IoT 设备 Modbus 点位配置新增/修改 Request VO") | ||
| 11 | +@Data | ||
| 12 | +public class IotDeviceModbusPointSaveReqVO { | ||
| 13 | + | ||
| 14 | + @Schema(description = "主键", example = "1") | ||
| 15 | + private Long id; | ||
| 16 | + | ||
| 17 | + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | ||
| 18 | + @NotNull(message = "设备编号不能为空") | ||
| 19 | + private Long deviceId; | ||
| 20 | + | ||
| 21 | + @Schema(description = "物模型属性编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") | ||
| 22 | + @NotNull(message = "物模型属性编号不能为空") | ||
| 23 | + private Long thingModelId; | ||
| 24 | + | ||
| 25 | + @Schema(description = "Modbus 功能码", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") | ||
| 26 | + @NotNull(message = "Modbus 功能码不能为空") | ||
| 27 | + private Integer functionCode; | ||
| 28 | + | ||
| 29 | + @Schema(description = "寄存器起始地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | ||
| 30 | + @NotNull(message = "寄存器起始地址不能为空") | ||
| 31 | + private Integer registerAddress; | ||
| 32 | + | ||
| 33 | + @Schema(description = "寄存器数量", example = "1") | ||
| 34 | + private Integer registerCount; | ||
| 35 | + | ||
| 36 | + @Schema(description = "字节序", requiredMode = Schema.RequiredMode.REQUIRED, example = "AB") | ||
| 37 | + @NotEmpty(message = "字节序不能为空") | ||
| 38 | + private String byteOrder; | ||
| 39 | + | ||
| 40 | + @Schema(description = "原始数据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "INT16") | ||
| 41 | + @NotEmpty(message = "原始数据类型不能为空") | ||
| 42 | + private String rawDataType; | ||
| 43 | + | ||
| 44 | + @Schema(description = "缩放因子", example = "1.0") | ||
| 45 | + private BigDecimal scale; | ||
| 46 | + | ||
| 47 | + @Schema(description = "轮询间隔(毫秒)", example = "5000") | ||
| 48 | + private Integer pollInterval; | ||
| 49 | + | ||
| 50 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | ||
| 51 | + @NotNull(message = "状态不能为空") | ||
| 52 | + private Integer status; | ||
| 53 | + | ||
| 54 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.http
0 → 100644
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java
| @@ -141,6 +141,14 @@ public class IotProductController { | @@ -141,6 +141,14 @@ public class IotProductController { | ||
| 141 | result.getData().getList()); | 141 | result.getData().getList()); |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | + @PostMapping("/sync-property-table") | ||
| 145 | + @Operation(summary = "同步产品属性表结构到 TDengine") | ||
| 146 | + @PreAuthorize("@ss.hasPermission('iot:product:update')") | ||
| 147 | + public CommonResult<Boolean> syncProductPropertyTable() { | ||
| 148 | + productService.syncProductPropertyTable(); | ||
| 149 | + return success(true); | ||
| 150 | + } | ||
| 151 | + | ||
| 144 | @GetMapping("/simple-list") | 152 | @GetMapping("/simple-list") |
| 145 | @Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项") | 153 | @Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项") |
| 146 | @Parameter(name = "deviceType", description = "设备类型", example = "1") | 154 | @Parameter(name = "deviceType", description = "设备类型", example = "1") |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java
| @@ -67,10 +67,15 @@ public class IotProductRespVO { | @@ -67,10 +67,15 @@ public class IotProductRespVO { | ||
| 67 | @DictFormat(DictTypeConstants.NET_TYPE) | 67 | @DictFormat(DictTypeConstants.NET_TYPE) |
| 68 | private Integer netType; | 68 | private Integer netType; |
| 69 | 69 | ||
| 70 | - @Schema(description = "数据格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | ||
| 71 | - @ExcelProperty(value = "数据格式", converter = DictConvert.class) | ||
| 72 | - @DictFormat(DictTypeConstants.CODEC_TYPE) | ||
| 73 | - private String codecType; | 70 | + @Schema(description = "协议类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "mqtt") |
| 71 | + @ExcelProperty(value = "协议类型", converter = DictConvert.class) | ||
| 72 | + @DictFormat(DictTypeConstants.PROTOCOL_TYPE) | ||
| 73 | + private String protocolType; | ||
| 74 | + | ||
| 75 | + @Schema(description = "序列化类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "json") | ||
| 76 | + @ExcelProperty(value = "序列化类型", converter = DictConvert.class) | ||
| 77 | + @DictFormat(DictTypeConstants.SERIALIZE_TYPE) | ||
| 78 | + private String serializeType; | ||
| 74 | 79 | ||
| 75 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | 80 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 76 | @ExcelProperty("创建时间") | 81 | @ExcelProperty("创建时间") |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java
| 1 | package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; | 1 | package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.validation.InEnum; | 3 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 4 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum; | ||
| 4 | import cn.iocoder.yudao.module.iot.enums.product.IotNetTypeEnum; | 6 | import cn.iocoder.yudao.module.iot.enums.product.IotNetTypeEnum; |
| 5 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; | 7 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; |
| 6 | import io.swagger.v3.oas.annotations.media.Schema; | 8 | import io.swagger.v3.oas.annotations.media.Schema; |
| @@ -44,9 +46,15 @@ public class IotProductSaveReqVO { | @@ -44,9 +46,15 @@ public class IotProductSaveReqVO { | ||
| 44 | @InEnum(value = IotNetTypeEnum.class, message = "联网方式必须是 {value}") | 46 | @InEnum(value = IotNetTypeEnum.class, message = "联网方式必须是 {value}") |
| 45 | private Integer netType; | 47 | private Integer netType; |
| 46 | 48 | ||
| 47 | - @Schema(description = "数据格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | ||
| 48 | - @NotEmpty(message = "数据格式不能为空") | ||
| 49 | - private String codecType; | 49 | + @Schema(description = "协议类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "mqtt") |
| 50 | + @InEnum(value = IotProtocolTypeEnum.class, message = "协议类型必须是 {value}") | ||
| 51 | + @NotEmpty(message = "协议类型不能为空") | ||
| 52 | + private String protocolType; | ||
| 53 | + | ||
| 54 | + @Schema(description = "序列化类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "json") | ||
| 55 | + @InEnum(value = IotSerializeTypeEnum.class, message = "序列化类型必须是 {value}") | ||
| 56 | + @NotEmpty(message = "序列化类型不能为空") | ||
| 57 | + private String serializeType; | ||
| 50 | 58 | ||
| 51 | @Schema(description = "是否开启动态注册", example = "false") | 59 | @Schema(description = "是否开启动态注册", example = "false") |
| 52 | @NotNull(message = "是否开启动态注册不能为空") | 60 | @NotNull(message = "是否开启动态注册不能为空") |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java
| @@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | @@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | ||
| 5 | import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; | 5 | import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; |
| 6 | import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; | 6 | import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; |
| 7 | import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO; | 7 | import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO; |
| 8 | -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | 8 | +import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; |
| 9 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; | 9 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; |
| 10 | import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; | 10 | import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; |
| 11 | import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; | 11 | import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java
| @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler; | @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler; | ||
| 4 | import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; | 4 | import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; |
| 5 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; | 5 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; |
| 6 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | 6 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| 7 | -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | 7 | +import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; |
| 8 | import com.baomidou.mybatisplus.annotation.KeySequence; | 8 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 9 | import com.baomidou.mybatisplus.annotation.TableField; | 9 | import com.baomidou.mybatisplus.annotation.TableField; |
| 10 | import com.baomidou.mybatisplus.annotation.TableId; | 10 | import com.baomidou.mybatisplus.annotation.TableId; |
| @@ -109,10 +109,6 @@ public class IotDeviceDO extends TenantBaseDO { | @@ -109,10 +109,6 @@ public class IotDeviceDO extends TenantBaseDO { | ||
| 109 | private LocalDateTime activeTime; | 109 | private LocalDateTime activeTime; |
| 110 | 110 | ||
| 111 | /** | 111 | /** |
| 112 | - * 设备的 IP 地址 | ||
| 113 | - */ | ||
| 114 | - private String ip; | ||
| 115 | - /** | ||
| 116 | * 固件编号 | 112 | * 固件编号 |
| 117 | * | 113 | * |
| 118 | * 关联 {@link IotOtaFirmwareDO#getId()} | 114 | * 关联 {@link IotOtaFirmwareDO#getId()} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceMessageDO.java
| @@ -84,7 +84,7 @@ public class IotDeviceMessageDO { | @@ -84,7 +84,7 @@ public class IotDeviceMessageDO { | ||
| 84 | * 请求方法 | 84 | * 请求方法 |
| 85 | * | 85 | * |
| 86 | * 枚举 {@link IotDeviceMessageMethodEnum} | 86 | * 枚举 {@link IotDeviceMessageMethodEnum} |
| 87 | - * 例如说:thing.property.report 属性上报 | 87 | + * 例如说:thing.property.post 属性上报 |
| 88 | */ | 88 | */ |
| 89 | private String method; | 89 | private String method; |
| 90 | /** | 90 | /** |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceModbusConfigDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.dal.dataobject.device; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusFrameFormatEnum; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusModeEnum; | ||
| 6 | +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | ||
| 7 | +import com.baomidou.mybatisplus.annotation.KeySequence; | ||
| 8 | +import com.baomidou.mybatisplus.annotation.TableId; | ||
| 9 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 10 | +import lombok.*; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * IoT 设备 Modbus 连接配置 DO | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +@TableName("iot_device_modbus_config") | ||
| 18 | +@KeySequence("iot_device_modbus_config_seq") | ||
| 19 | +@Data | ||
| 20 | +@Builder | ||
| 21 | +@NoArgsConstructor | ||
| 22 | +@AllArgsConstructor | ||
| 23 | +public class IotDeviceModbusConfigDO extends TenantBaseDO { | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 主键 | ||
| 27 | + */ | ||
| 28 | + @TableId | ||
| 29 | + private Long id; | ||
| 30 | + /** | ||
| 31 | + * 产品编号 | ||
| 32 | + * | ||
| 33 | + * 关联 {@link IotProductDO#getId()} | ||
| 34 | + */ | ||
| 35 | + private Long productId; | ||
| 36 | + /** | ||
| 37 | + * 设备编号 | ||
| 38 | + * | ||
| 39 | + * 关联 {@link IotDeviceDO#getId()} | ||
| 40 | + */ | ||
| 41 | + private Long deviceId; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * Modbus 服务器 IP 地址 | ||
| 45 | + */ | ||
| 46 | + private String ip; | ||
| 47 | + /** | ||
| 48 | + * Modbus 服务器端口 | ||
| 49 | + */ | ||
| 50 | + private Integer port; | ||
| 51 | + /** | ||
| 52 | + * 从站地址 | ||
| 53 | + */ | ||
| 54 | + private Integer slaveId; | ||
| 55 | + /** | ||
| 56 | + * 连接超时时间,单位:毫秒 | ||
| 57 | + */ | ||
| 58 | + private Integer timeout; | ||
| 59 | + /** | ||
| 60 | + * 重试间隔,单位:毫秒 | ||
| 61 | + */ | ||
| 62 | + private Integer retryInterval; | ||
| 63 | + /** | ||
| 64 | + * 模式 | ||
| 65 | + * | ||
| 66 | + * @see IotModbusModeEnum | ||
| 67 | + */ | ||
| 68 | + private Integer mode; | ||
| 69 | + /** | ||
| 70 | + * 数据帧格式 | ||
| 71 | + * | ||
| 72 | + * @see IotModbusFrameFormatEnum | ||
| 73 | + */ | ||
| 74 | + private Integer frameFormat; | ||
| 75 | + /** | ||
| 76 | + * 状态 | ||
| 77 | + * | ||
| 78 | + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} | ||
| 79 | + */ | ||
| 80 | + private Integer status; | ||
| 81 | + | ||
| 82 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceModbusPointDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.dal.dataobject.device; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; | ||
| 4 | +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusByteOrderEnum; | ||
| 6 | +import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusRawDataTypeEnum; | ||
| 7 | +import com.baomidou.mybatisplus.annotation.KeySequence; | ||
| 8 | +import com.baomidou.mybatisplus.annotation.TableId; | ||
| 9 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 10 | +import lombok.*; | ||
| 11 | + | ||
| 12 | +import java.math.BigDecimal; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * IoT 设备 Modbus 点位配置 DO | ||
| 16 | + * | ||
| 17 | + * @author 芋道源码 | ||
| 18 | + */ | ||
| 19 | +@TableName("iot_device_modbus_point") | ||
| 20 | +@KeySequence("iot_device_modbus_point_seq") | ||
| 21 | +@Data | ||
| 22 | +@Builder | ||
| 23 | +@NoArgsConstructor | ||
| 24 | +@AllArgsConstructor | ||
| 25 | +public class IotDeviceModbusPointDO extends TenantBaseDO { | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 主键 | ||
| 29 | + */ | ||
| 30 | + @TableId | ||
| 31 | + private Long id; | ||
| 32 | + /** | ||
| 33 | + * 设备编号 | ||
| 34 | + * | ||
| 35 | + * 关联 {@link IotDeviceDO#getId()} | ||
| 36 | + */ | ||
| 37 | + private Long deviceId; | ||
| 38 | + /** | ||
| 39 | + * 物模型属性编号 | ||
| 40 | + * | ||
| 41 | + * 关联 {@link IotThingModelDO#getId()} | ||
| 42 | + */ | ||
| 43 | + private Long thingModelId; | ||
| 44 | + /** | ||
| 45 | + * 属性标识符 | ||
| 46 | + * | ||
| 47 | + * 冗余 {@link IotThingModelDO#getIdentifier()} | ||
| 48 | + */ | ||
| 49 | + private String identifier; | ||
| 50 | + /** | ||
| 51 | + * 属性名称 | ||
| 52 | + * | ||
| 53 | + * 冗余 {@link IotThingModelDO#getName()} | ||
| 54 | + */ | ||
| 55 | + private String name; | ||
| 56 | + | ||
| 57 | + // ========== Modbus 协议配置 ========== | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * Modbus 功能码 | ||
| 61 | + * | ||
| 62 | + * 取值范围:FC01-04(读线圈、读离散输入、读保持寄存器、读输入寄存器) | ||
| 63 | + */ | ||
| 64 | + private Integer functionCode; | ||
| 65 | + /** | ||
| 66 | + * 寄存器起始地址 | ||
| 67 | + */ | ||
| 68 | + private Integer registerAddress; | ||
| 69 | + /** | ||
| 70 | + * 寄存器数量 | ||
| 71 | + */ | ||
| 72 | + private Integer registerCount; | ||
| 73 | + /** | ||
| 74 | + * 字节序 | ||
| 75 | + * | ||
| 76 | + * 枚举 {@link IotModbusByteOrderEnum} | ||
| 77 | + */ | ||
| 78 | + private String byteOrder; | ||
| 79 | + /** | ||
| 80 | + * 原始数据类型 | ||
| 81 | + * | ||
| 82 | + * 枚举 {@link IotModbusRawDataTypeEnum} | ||
| 83 | + */ | ||
| 84 | + private String rawDataType; | ||
| 85 | + /** | ||
| 86 | + * 缩放因子 | ||
| 87 | + */ | ||
| 88 | + private BigDecimal scale; | ||
| 89 | + /** | ||
| 90 | + * 轮询间隔(毫秒) | ||
| 91 | + */ | ||
| 92 | + private Integer pollInterval; | ||
| 93 | + /** | ||
| 94 | + * 状态 | ||
| 95 | + * | ||
| 96 | + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} | ||
| 97 | + */ | ||
| 98 | + private Integer status; | ||
| 99 | + | ||
| 100 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java
| @@ -78,12 +78,16 @@ public class IotProductDO extends TenantBaseDO { | @@ -78,12 +78,16 @@ public class IotProductDO extends TenantBaseDO { | ||
| 78 | */ | 78 | */ |
| 79 | private Integer netType; | 79 | private Integer netType; |
| 80 | /** | 80 | /** |
| 81 | - * 数据格式(编解码器类型) | 81 | + * 协议类型 |
| 82 | * <p> | 82 | * <p> |
| 83 | - * 字典 {@link cn.iocoder.yudao.module.iot.enums.DictTypeConstants#CODEC_TYPE} | ||
| 84 | - * | ||
| 85 | - * 目的:用于 gateway-server 解析消息格式 | 83 | + * 枚举 {@link cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum} |
| 86 | */ | 84 | */ |
| 87 | - private String codecType; | 85 | + private String protocolType; |
| 86 | + /** | ||
| 87 | + * 序列化类型 | ||
| 88 | + * <p> | ||
| 89 | + * 枚举 {@link cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum} | ||
| 90 | + */ | ||
| 91 | + private String serializeType; | ||
| 88 | 92 | ||
| 89 | } | 93 | } |
| 90 | \ No newline at end of file | 94 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusConfigMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.dal.mysql.device; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigListReqDTO; | ||
| 6 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; | ||
| 7 | +import org.apache.ibatis.annotations.Mapper; | ||
| 8 | + | ||
| 9 | +import java.util.List; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * IoT 设备 Modbus 连接配置 Mapper | ||
| 13 | + * | ||
| 14 | + * @author 芋道源码 | ||
| 15 | + */ | ||
| 16 | +@Mapper | ||
| 17 | +public interface IotDeviceModbusConfigMapper extends BaseMapperX<IotDeviceModbusConfigDO> { | ||
| 18 | + | ||
| 19 | + default IotDeviceModbusConfigDO selectByDeviceId(Long deviceId) { | ||
| 20 | + return selectOne(IotDeviceModbusConfigDO::getDeviceId, deviceId); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + default List<IotDeviceModbusConfigDO> selectList(IotModbusDeviceConfigListReqDTO reqDTO) { | ||
| 24 | + return selectList(new LambdaQueryWrapperX<IotDeviceModbusConfigDO>() | ||
| 25 | + .eqIfPresent(IotDeviceModbusConfigDO::getStatus, reqDTO.getStatus()) | ||
| 26 | + .eqIfPresent(IotDeviceModbusConfigDO::getMode, reqDTO.getMode()) | ||
| 27 | + .inIfPresent(IotDeviceModbusConfigDO::getDeviceId, reqDTO.getDeviceIds())); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceModbusPointMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.dal.mysql.device; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 6 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointPageReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO; | ||
| 8 | +import org.apache.ibatis.annotations.Mapper; | ||
| 9 | + | ||
| 10 | +import java.util.Collection; | ||
| 11 | +import java.util.List; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * IoT 设备 Modbus 点位配置 Mapper | ||
| 15 | + * | ||
| 16 | + * @author 芋道源码 | ||
| 17 | + */ | ||
| 18 | +@Mapper | ||
| 19 | +public interface IotDeviceModbusPointMapper extends BaseMapperX<IotDeviceModbusPointDO> { | ||
| 20 | + | ||
| 21 | + default PageResult<IotDeviceModbusPointDO> selectPage(IotDeviceModbusPointPageReqVO reqVO) { | ||
| 22 | + return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceModbusPointDO>() | ||
| 23 | + .eqIfPresent(IotDeviceModbusPointDO::getDeviceId, reqVO.getDeviceId()) | ||
| 24 | + .likeIfPresent(IotDeviceModbusPointDO::getIdentifier, reqVO.getIdentifier()) | ||
| 25 | + .likeIfPresent(IotDeviceModbusPointDO::getName, reqVO.getName()) | ||
| 26 | + .eqIfPresent(IotDeviceModbusPointDO::getFunctionCode, reqVO.getFunctionCode()) | ||
| 27 | + .eqIfPresent(IotDeviceModbusPointDO::getStatus, reqVO.getStatus()) | ||
| 28 | + .orderByDesc(IotDeviceModbusPointDO::getId)); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + default List<IotDeviceModbusPointDO> selectListByDeviceIdsAndStatus(Collection<Long> deviceIds, Integer status) { | ||
| 32 | + return selectList(new LambdaQueryWrapperX<IotDeviceModbusPointDO>() | ||
| 33 | + .in(IotDeviceModbusPointDO::getDeviceId, deviceIds) | ||
| 34 | + .eq(IotDeviceModbusPointDO::getStatus, status)); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + default IotDeviceModbusPointDO selectByDeviceIdAndIdentifier(Long deviceId, String identifier) { | ||
| 38 | + return selectOne(IotDeviceModbusPointDO::getDeviceId, deviceId, | ||
| 39 | + IotDeviceModbusPointDO::getIdentifier, identifier); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + default void updateByThingModelId(Long thingModelId, IotDeviceModbusPointDO updateObj) { | ||
| 43 | + update(updateObj, new LambdaQueryWrapperX<IotDeviceModbusPointDO>() | ||
| 44 | + .eq(IotDeviceModbusPointDO::getThingModelId, thingModelId)); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java
| @@ -38,6 +38,10 @@ public interface IotProductMapper extends BaseMapperX<IotProductDO> { | @@ -38,6 +38,10 @@ public interface IotProductMapper extends BaseMapperX<IotProductDO> { | ||
| 38 | .apply("LOWER(product_key) = {0}", productKey.toLowerCase())); | 38 | .apply("LOWER(product_key) = {0}", productKey.toLowerCase())); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | + default List<IotProductDO> selectListByStatus(Integer status) { | ||
| 42 | + return selectList(IotProductDO::getStatus, status); | ||
| 43 | + } | ||
| 44 | + | ||
| 41 | default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) { | 45 | default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) { |
| 42 | return selectCount(new LambdaQueryWrapperX<IotProductDO>() | 46 | return selectCount(new LambdaQueryWrapperX<IotProductDO>() |
| 43 | .geIfPresent(IotProductDO::getCreateTime, createTime)); | 47 | .geIfPresent(IotProductDO::getCreateTime, createTime)); |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java
| @@ -8,8 +8,8 @@ package cn.iocoder.yudao.module.iot.enums; | @@ -8,8 +8,8 @@ package cn.iocoder.yudao.module.iot.enums; | ||
| 8 | public class DictTypeConstants { | 8 | public class DictTypeConstants { |
| 9 | 9 | ||
| 10 | public static final String NET_TYPE = "iot_net_type"; | 10 | public static final String NET_TYPE = "iot_net_type"; |
| 11 | - public static final String LOCATION_TYPE = "iot_location_type"; | ||
| 12 | - public static final String CODEC_TYPE = "iot_codec_type"; | 11 | + public static final String PROTOCOL_TYPE = "iot_protocol_type"; |
| 12 | + public static final String SERIALIZE_TYPE = "iot_serialize_type"; | ||
| 13 | 13 | ||
| 14 | public static final String PRODUCT_STATUS = "iot_product_status"; | 14 | public static final String PRODUCT_STATUS = "iot_product_status"; |
| 15 | public static final String PRODUCT_DEVICE_TYPE = "iot_product_device_type"; | 15 | public static final String PRODUCT_DEVICE_TYPE = "iot_product_device_type"; |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
| @@ -54,6 +54,14 @@ public interface ErrorCodeConstants { | @@ -54,6 +54,14 @@ public interface ErrorCodeConstants { | ||
| 54 | ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在"); | 54 | ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在"); |
| 55 | ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除"); | 55 | ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除"); |
| 56 | 56 | ||
| 57 | + // ========== 设备 Modbus 配置 1-050-006-000 ========== | ||
| 58 | + ErrorCode DEVICE_MODBUS_CONFIG_NOT_EXISTS = new ErrorCode(1_050_006_000, "设备 Modbus 连接配置不存在"); | ||
| 59 | + ErrorCode DEVICE_MODBUS_CONFIG_EXISTS = new ErrorCode(1_050_006_001, "设备 Modbus 连接配置已存在"); | ||
| 60 | + | ||
| 61 | + // ========== 设备 Modbus 点位 1-050-007-000 ========== | ||
| 62 | + ErrorCode DEVICE_MODBUS_POINT_NOT_EXISTS = new ErrorCode(1_050_007_000, "设备 Modbus 点位配置不存在"); | ||
| 63 | + ErrorCode DEVICE_MODBUS_POINT_EXISTS = new ErrorCode(1_050_007_001, "设备 Modbus 点位配置已存在"); | ||
| 64 | + | ||
| 57 | // ========== OTA 固件相关 1-050-008-000 ========== | 65 | // ========== OTA 固件相关 1-050-008-000 ========== |
| 58 | 66 | ||
| 59 | ErrorCode OTA_FIRMWARE_NOT_EXISTS = new ErrorCode(1_050_008_000, "固件信息不存在"); | 67 | ErrorCode OTA_FIRMWARE_NOT_EXISTS = new ErrorCode(1_050_008_000, "固件信息不存在"); |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataSinkTypeEnum.java
| @@ -19,9 +19,9 @@ public enum IotDataSinkTypeEnum implements ArrayValuable<Integer> { | @@ -19,9 +19,9 @@ public enum IotDataSinkTypeEnum implements ArrayValuable<Integer> { | ||
| 19 | TCP(2, "TCP"), | 19 | TCP(2, "TCP"), |
| 20 | WEBSOCKET(3, "WebSocket"), | 20 | WEBSOCKET(3, "WebSocket"), |
| 21 | 21 | ||
| 22 | - MQTT(10, "MQTT"), // TODO 待实现; | 22 | + MQTT(10, "MQTT"), // TODO @puhui999:待实现; |
| 23 | 23 | ||
| 24 | - DATABASE(20, "Database"), // TODO @puhui999:待实现;可以简单点,对应的表名是什么,字段先固定了。 | 24 | + DATABASE(20, "Database"), // TODO @puhui999:待实现; |
| 25 | REDIS(21, "Redis"), | 25 | REDIS(21, "Redis"), |
| 26 | 26 | ||
| 27 | ROCKETMQ(30, "RocketMQ"), | 27 | ROCKETMQ(30, "RocketMQ"), |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java
| @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil; | @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil; | ||
| 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 5 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | 5 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; |
| 6 | import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; | 6 | import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; |
| 7 | -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | 7 | +import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; |
| 8 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 8 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 9 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | 9 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 10 | import cn.iocoder.yudao.module.iot.framework.iot.config.YudaoIotProperties; | 10 | import cn.iocoder.yudao.module.iot.framework.iot.config.YudaoIotProperties; |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeJob.java
| @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil; | @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil; | ||
| 4 | import cn.hutool.core.util.StrUtil; | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | 5 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; |
| 6 | import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; | 6 | import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; |
| 7 | -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | 7 | +import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; |
| 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 9 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; | 9 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; |
| 10 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO; | 10 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO; |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceMessageSubscriber.java
| @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.mq.consumer.device; | @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.mq.consumer.device; | ||
| 3 | import cn.hutool.core.util.ObjectUtil; | 3 | import cn.hutool.core.util.ObjectUtil; |
| 4 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | 4 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| 5 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | 5 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; |
| 6 | -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | 6 | +import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; |
| 7 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | 7 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 8 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | 8 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; |
| 9 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 9 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| @@ -67,7 +67,6 @@ public class IotDeviceMessageSubscriber implements IotMessageSubscriber<IotDevic | @@ -67,7 +67,6 @@ public class IotDeviceMessageSubscriber implements IotMessageSubscriber<IotDevic | ||
| 67 | IotDeviceDO device = deviceService.validateDeviceExistsFromCache(message.getDeviceId()); | 67 | IotDeviceDO device = deviceService.validateDeviceExistsFromCache(message.getDeviceId()); |
| 68 | devicePropertyService.updateDeviceReportTimeAsync(device.getId(), LocalDateTime.now()); | 68 | devicePropertyService.updateDeviceReportTimeAsync(device.getId(), LocalDateTime.now()); |
| 69 | // 1.2 更新设备的连接 server | 69 | // 1.2 更新设备的连接 server |
| 70 | - // TODO 芋艿:HTTP 网关的上行消息,不应该更新 serverId,会覆盖掉 MQTT 等长连接的 serverId,导致下行消息无法发送。 | ||
| 71 | devicePropertyService.updateDeviceServerIdAsync(device.getId(), message.getServerId()); | 70 | devicePropertyService.updateDeviceServerIdAsync(device.getId(), message.getServerId()); |
| 72 | 71 | ||
| 73 | // 2. 未上线的设备,强制上线 | 72 | // 2. 未上线的设备,强制上线 |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotDataRuleMessageHandler.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotDataRuleMessageSubscriber.java
| @@ -17,7 +17,7 @@ import org.springframework.stereotype.Component; | @@ -17,7 +17,7 @@ import org.springframework.stereotype.Component; | ||
| 17 | */ | 17 | */ |
| 18 | @Component | 18 | @Component |
| 19 | @Slf4j | 19 | @Slf4j |
| 20 | -public class IotDataRuleMessageHandler implements IotMessageSubscriber<IotDeviceMessage> { | 20 | +public class IotDataRuleMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> { |
| 21 | 21 | ||
| 22 | @Resource | 22 | @Resource |
| 23 | private IotDataRuleService dataRuleService; | 23 | private IotDataRuleService dataRuleService; |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotSceneRuleMessageHandler.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotSceneRuleMessageSubscriber.java
| @@ -9,7 +9,6 @@ import jakarta.annotation.Resource; | @@ -9,7 +9,6 @@ import jakarta.annotation.Resource; | ||
| 9 | import lombok.extern.slf4j.Slf4j; | 9 | import lombok.extern.slf4j.Slf4j; |
| 10 | import org.springframework.stereotype.Component; | 10 | import org.springframework.stereotype.Component; |
| 11 | 11 | ||
| 12 | -// TODO @puhui999:后面重构哈 | ||
| 13 | /** | 12 | /** |
| 14 | * 针对 {@link IotDeviceMessage} 的消费者,处理规则场景 | 13 | * 针对 {@link IotDeviceMessage} 的消费者,处理规则场景 |
| 15 | * | 14 | * |
| @@ -17,7 +16,7 @@ import org.springframework.stereotype.Component; | @@ -17,7 +16,7 @@ import org.springframework.stereotype.Component; | ||
| 17 | */ | 16 | */ |
| 18 | @Component | 17 | @Component |
| 19 | @Slf4j | 18 | @Slf4j |
| 20 | -public class IotSceneRuleMessageHandler implements IotMessageSubscriber<IotDeviceMessage> { | 19 | +public class IotSceneRuleMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> { |
| 21 | 20 | ||
| 22 | @Resource | 21 | @Resource |
| 23 | private IotSceneRuleService sceneRuleService; | 22 | private IotSceneRuleService sceneRuleService; |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.device; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigListReqDTO; | ||
| 5 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; | ||
| 6 | +import jakarta.validation.Valid; | ||
| 7 | + | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * IoT 设备 Modbus 连接配置 Service 接口 | ||
| 12 | + * | ||
| 13 | + * @author 芋道源码 | ||
| 14 | + */ | ||
| 15 | +public interface IotDeviceModbusConfigService { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 保存设备 Modbus 连接配置(新增或更新) | ||
| 19 | + * | ||
| 20 | + * @param saveReqVO 保存信息 | ||
| 21 | + */ | ||
| 22 | + void saveDeviceModbusConfig(@Valid IotDeviceModbusConfigSaveReqVO saveReqVO); | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 获得设备 Modbus 连接配置 | ||
| 26 | + * | ||
| 27 | + * @param id 编号 | ||
| 28 | + * @return 设备 Modbus 连接配置 | ||
| 29 | + */ | ||
| 30 | + IotDeviceModbusConfigDO getDeviceModbusConfig(Long id); | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 根据设备编号获得 Modbus 连接配置 | ||
| 34 | + * | ||
| 35 | + * @param deviceId 设备编号 | ||
| 36 | + * @return 设备 Modbus 连接配置 | ||
| 37 | + */ | ||
| 38 | + IotDeviceModbusConfigDO getDeviceModbusConfigByDeviceId(Long deviceId); | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 获得 Modbus 连接配置列表 | ||
| 42 | + * | ||
| 43 | + * @param listReqDTO 查询参数 | ||
| 44 | + * @return Modbus 连接配置列表 | ||
| 45 | + */ | ||
| 46 | + List<IotDeviceModbusConfigDO> getDeviceModbusConfigList(IotModbusDeviceConfigListReqDTO listReqDTO); | ||
| 47 | + | ||
| 48 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusConfigServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.device; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 4 | +import cn.hutool.core.util.StrUtil; | ||
| 5 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 6 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusConfigSaveReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.iot.core.biz.dto.IotModbusDeviceConfigListReqDTO; | ||
| 8 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 9 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusConfigDO; | ||
| 10 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | ||
| 11 | +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | ||
| 12 | +import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceModbusConfigMapper; | ||
| 13 | +import cn.iocoder.yudao.module.iot.service.product.IotProductService; | ||
| 14 | +import jakarta.annotation.Resource; | ||
| 15 | +import org.springframework.stereotype.Service; | ||
| 16 | +import org.springframework.validation.annotation.Validated; | ||
| 17 | + | ||
| 18 | +import java.util.List; | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * IoT 设备 Modbus 连接配置 Service 实现类 | ||
| 22 | + * | ||
| 23 | + * @author 芋道源码 | ||
| 24 | + */ | ||
| 25 | +@Service | ||
| 26 | +@Validated | ||
| 27 | +public class IotDeviceModbusConfigServiceImpl implements IotDeviceModbusConfigService { | ||
| 28 | + | ||
| 29 | + @Resource | ||
| 30 | + private IotDeviceModbusConfigMapper modbusConfigMapper; | ||
| 31 | + | ||
| 32 | + @Resource | ||
| 33 | + private IotDeviceService deviceService; | ||
| 34 | + @Resource | ||
| 35 | + private IotProductService productService; | ||
| 36 | + | ||
| 37 | + @Override | ||
| 38 | + public void saveDeviceModbusConfig(IotDeviceModbusConfigSaveReqVO saveReqVO) { | ||
| 39 | + // 1.1 校验设备存在 | ||
| 40 | + IotDeviceDO device = deviceService.validateDeviceExists(saveReqVO.getDeviceId()); | ||
| 41 | + // 1.2 根据产品 protocolType 条件校验 | ||
| 42 | + IotProductDO product = productService.getProduct(device.getProductId()); | ||
| 43 | + Assert.notNull(product, "产品不存在"); | ||
| 44 | + validateModbusConfigByProtocolType(saveReqVO, product.getProtocolType()); | ||
| 45 | + | ||
| 46 | + // 2. 根据数据库中是否已有配置,决定是新增还是更新 | ||
| 47 | + IotDeviceModbusConfigDO existConfig = modbusConfigMapper.selectByDeviceId(saveReqVO.getDeviceId()); | ||
| 48 | + if (existConfig == null) { | ||
| 49 | + IotDeviceModbusConfigDO modbusConfig = BeanUtils.toBean(saveReqVO, IotDeviceModbusConfigDO.class); | ||
| 50 | + modbusConfigMapper.insert(modbusConfig); | ||
| 51 | + } else { | ||
| 52 | + IotDeviceModbusConfigDO updateObj = BeanUtils.toBean(saveReqVO, IotDeviceModbusConfigDO.class, | ||
| 53 | + o -> o.setId(existConfig.getId())); | ||
| 54 | + modbusConfigMapper.updateById(updateObj); | ||
| 55 | + } | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + @Override | ||
| 59 | + public IotDeviceModbusConfigDO getDeviceModbusConfig(Long id) { | ||
| 60 | + return modbusConfigMapper.selectById(id); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + @Override | ||
| 64 | + public IotDeviceModbusConfigDO getDeviceModbusConfigByDeviceId(Long deviceId) { | ||
| 65 | + return modbusConfigMapper.selectByDeviceId(deviceId); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + @Override | ||
| 69 | + public List<IotDeviceModbusConfigDO> getDeviceModbusConfigList(IotModbusDeviceConfigListReqDTO listReqDTO) { | ||
| 70 | + return modbusConfigMapper.selectList(listReqDTO); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + private void validateModbusConfigByProtocolType(IotDeviceModbusConfigSaveReqVO saveReqVO, String protocolType) { | ||
| 74 | + IotProtocolTypeEnum protocolTypeEnum = IotProtocolTypeEnum.of(protocolType); | ||
| 75 | + if (protocolTypeEnum == null) { | ||
| 76 | + return; | ||
| 77 | + } | ||
| 78 | + if (protocolTypeEnum == IotProtocolTypeEnum.MODBUS_TCP_CLIENT) { | ||
| 79 | + Assert.isTrue(StrUtil.isNotEmpty(saveReqVO.getIp()), "Client 模式下,IP 地址不能为空"); | ||
| 80 | + Assert.notNull(saveReqVO.getPort(), "Client 模式下,端口不能为空"); | ||
| 81 | + Assert.notNull(saveReqVO.getTimeout(), "Client 模式下,连接超时时间不能为空"); | ||
| 82 | + Assert.notNull(saveReqVO.getRetryInterval(), "Client 模式下,重试间隔不能为空"); | ||
| 83 | + } else if (protocolTypeEnum == IotProtocolTypeEnum.MODBUS_TCP_SERVER) { | ||
| 84 | + Assert.notNull(saveReqVO.getMode(), "Server 模式下,工作模式不能为空"); | ||
| 85 | + Assert.notNull(saveReqVO.getFrameFormat(), "Server 模式下,数据帧格式不能为空"); | ||
| 86 | + } | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.device; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointPageReqVO; | ||
| 5 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointSaveReqVO; | ||
| 6 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO; | ||
| 7 | +import jakarta.validation.Valid; | ||
| 8 | + | ||
| 9 | +import java.util.Collection; | ||
| 10 | +import java.util.List; | ||
| 11 | +import java.util.Map; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * IoT 设备 Modbus 点位配置 Service 接口 | ||
| 15 | + * | ||
| 16 | + * @author 芋道源码 | ||
| 17 | + */ | ||
| 18 | +public interface IotDeviceModbusPointService { | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 创建设备 Modbus 点位配置 | ||
| 22 | + * | ||
| 23 | + * @param createReqVO 创建信息 | ||
| 24 | + * @return 编号 | ||
| 25 | + */ | ||
| 26 | + Long createDeviceModbusPoint(@Valid IotDeviceModbusPointSaveReqVO createReqVO); | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 更新设备 Modbus 点位配置 | ||
| 30 | + * | ||
| 31 | + * @param updateReqVO 更新信息 | ||
| 32 | + */ | ||
| 33 | + void updateDeviceModbusPoint(@Valid IotDeviceModbusPointSaveReqVO updateReqVO); | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 删除设备 Modbus 点位配置 | ||
| 37 | + * | ||
| 38 | + * @param id 编号 | ||
| 39 | + */ | ||
| 40 | + void deleteDeviceModbusPoint(Long id); | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 获得设备 Modbus 点位配置 | ||
| 44 | + * | ||
| 45 | + * @param id 编号 | ||
| 46 | + * @return 设备 Modbus 点位配置 | ||
| 47 | + */ | ||
| 48 | + IotDeviceModbusPointDO getDeviceModbusPoint(Long id); | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * 获得设备 Modbus 点位配置分页 | ||
| 52 | + * | ||
| 53 | + * @param pageReqVO 分页查询 | ||
| 54 | + * @return 设备 Modbus 点位配置分页 | ||
| 55 | + */ | ||
| 56 | + PageResult<IotDeviceModbusPointDO> getDeviceModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO); | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * 物模型变更时,更新关联点位的冗余字段(identifier、name) | ||
| 60 | + * | ||
| 61 | + * @param thingModelId 物模型编号 | ||
| 62 | + * @param identifier 物模型标识符 | ||
| 63 | + * @param name 物模型名称 | ||
| 64 | + */ | ||
| 65 | + void updateDeviceModbusPointByThingModel(Long thingModelId, String identifier, String name); | ||
| 66 | + | ||
| 67 | + /** | ||
| 68 | + * 根据设备编号批量获得启用的点位配置 Map | ||
| 69 | + * | ||
| 70 | + * @param deviceIds 设备编号集合 | ||
| 71 | + * @return 设备点位 Map,key 为设备编号,value 为点位配置列表 | ||
| 72 | + */ | ||
| 73 | + Map<Long, List<IotDeviceModbusPointDO>> getEnabledDeviceModbusPointMapByDeviceIds(Collection<Long> deviceIds); | ||
| 74 | + | ||
| 75 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceModbusPointServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.device; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.hutool.core.util.ObjUtil; | ||
| 5 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 7 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 8 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointPageReqVO; | ||
| 9 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.modbus.IotDeviceModbusPointSaveReqVO; | ||
| 10 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceModbusPointDO; | ||
| 11 | +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | ||
| 12 | +import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceModbusPointMapper; | ||
| 13 | +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; | ||
| 14 | +import jakarta.annotation.Resource; | ||
| 15 | +import org.springframework.stereotype.Service; | ||
| 16 | +import org.springframework.validation.annotation.Validated; | ||
| 17 | + | ||
| 18 | +import java.util.Collection; | ||
| 19 | +import java.util.Collections; | ||
| 20 | +import java.util.List; | ||
| 21 | +import java.util.Map; | ||
| 22 | + | ||
| 23 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 24 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; | ||
| 25 | +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * IoT 设备 Modbus 点位配置 Service 实现类 | ||
| 29 | + * | ||
| 30 | + * @author 芋道源码 | ||
| 31 | + */ | ||
| 32 | +@Service | ||
| 33 | +@Validated | ||
| 34 | +public class IotDeviceModbusPointServiceImpl implements IotDeviceModbusPointService { | ||
| 35 | + | ||
| 36 | + @Resource | ||
| 37 | + private IotDeviceModbusPointMapper modbusPointMapper; | ||
| 38 | + | ||
| 39 | + @Resource | ||
| 40 | + private IotDeviceService deviceService; | ||
| 41 | + | ||
| 42 | + @Resource | ||
| 43 | + private IotThingModelService thingModelService; | ||
| 44 | + | ||
| 45 | + @Override | ||
| 46 | + public Long createDeviceModbusPoint(IotDeviceModbusPointSaveReqVO createReqVO) { | ||
| 47 | + // 1.1 校验设备存在 | ||
| 48 | + deviceService.validateDeviceExists(createReqVO.getDeviceId()); | ||
| 49 | + // 1.2 校验物模型属性存在 | ||
| 50 | + IotThingModelDO thingModel = validateThingModelExists(createReqVO.getThingModelId()); | ||
| 51 | + // 1.3 校验同一设备下点位唯一性(基于 identifier) | ||
| 52 | + validateDeviceModbusPointUnique(createReqVO.getDeviceId(), thingModel.getIdentifier(), null); | ||
| 53 | + | ||
| 54 | + // 2. 插入 | ||
| 55 | + IotDeviceModbusPointDO modbusPoint = BeanUtils.toBean(createReqVO, IotDeviceModbusPointDO.class, | ||
| 56 | + o -> o.setIdentifier(thingModel.getIdentifier()).setName(thingModel.getName())); | ||
| 57 | + modbusPointMapper.insert(modbusPoint); | ||
| 58 | + return modbusPoint.getId(); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + @Override | ||
| 62 | + public void updateDeviceModbusPoint(IotDeviceModbusPointSaveReqVO updateReqVO) { | ||
| 63 | + // 1.1 校验存在 | ||
| 64 | + validateDeviceModbusPointExists(updateReqVO.getId()); | ||
| 65 | + // 1.2 校验设备存在 | ||
| 66 | + deviceService.validateDeviceExists(updateReqVO.getDeviceId()); | ||
| 67 | + // 1.3 校验物模型属性存在 | ||
| 68 | + IotThingModelDO thingModel = validateThingModelExists(updateReqVO.getThingModelId()); | ||
| 69 | + // 1.4 校验同一设备下点位唯一性 | ||
| 70 | + validateDeviceModbusPointUnique(updateReqVO.getDeviceId(), thingModel.getIdentifier(), updateReqVO.getId()); | ||
| 71 | + | ||
| 72 | + // 2. 更新 | ||
| 73 | + IotDeviceModbusPointDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceModbusPointDO.class, | ||
| 74 | + o -> o.setIdentifier(thingModel.getIdentifier()).setName(thingModel.getName())); | ||
| 75 | + modbusPointMapper.updateById(updateObj); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + @Override | ||
| 79 | + public void updateDeviceModbusPointByThingModel(Long thingModelId, String identifier, String name) { | ||
| 80 | + IotDeviceModbusPointDO updateObj = new IotDeviceModbusPointDO() | ||
| 81 | + .setIdentifier(identifier).setName(name); | ||
| 82 | + modbusPointMapper.updateByThingModelId(thingModelId, updateObj); | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + private IotThingModelDO validateThingModelExists(Long id) { | ||
| 86 | + IotThingModelDO thingModel = thingModelService.getThingModel(id); | ||
| 87 | + if (thingModel == null) { | ||
| 88 | + throw exception(THING_MODEL_NOT_EXISTS); | ||
| 89 | + } | ||
| 90 | + return thingModel; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + @Override | ||
| 94 | + public void deleteDeviceModbusPoint(Long id) { | ||
| 95 | + // 校验存在 | ||
| 96 | + validateDeviceModbusPointExists(id); | ||
| 97 | + // 删除 | ||
| 98 | + modbusPointMapper.deleteById(id); | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + private void validateDeviceModbusPointExists(Long id) { | ||
| 102 | + IotDeviceModbusPointDO point = modbusPointMapper.selectById(id); | ||
| 103 | + if (point == null) { | ||
| 104 | + throw exception(DEVICE_MODBUS_POINT_NOT_EXISTS); | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + private void validateDeviceModbusPointUnique(Long deviceId, String identifier, Long excludeId) { | ||
| 109 | + IotDeviceModbusPointDO point = modbusPointMapper.selectByDeviceIdAndIdentifier(deviceId, identifier); | ||
| 110 | + if (point != null && ObjUtil.notEqual(point.getId(), excludeId)) { | ||
| 111 | + throw exception(DEVICE_MODBUS_POINT_EXISTS); | ||
| 112 | + } | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + @Override | ||
| 116 | + public IotDeviceModbusPointDO getDeviceModbusPoint(Long id) { | ||
| 117 | + return modbusPointMapper.selectById(id); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + @Override | ||
| 121 | + public PageResult<IotDeviceModbusPointDO> getDeviceModbusPointPage(IotDeviceModbusPointPageReqVO pageReqVO) { | ||
| 122 | + return modbusPointMapper.selectPage(pageReqVO); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + @Override | ||
| 126 | + public Map<Long, List<IotDeviceModbusPointDO>> getEnabledDeviceModbusPointMapByDeviceIds(Collection<Long> deviceIds) { | ||
| 127 | + if (CollUtil.isEmpty(deviceIds)) { | ||
| 128 | + return Collections.emptyMap(); | ||
| 129 | + } | ||
| 130 | + List<IotDeviceModbusPointDO> pointList = modbusPointMapper.selectListByDeviceIdsAndStatus( | ||
| 131 | + deviceIds, CommonStatusEnum.ENABLE.getStatus()); | ||
| 132 | + return convertMultiMap(pointList, IotDeviceModbusPointDO::getDeviceId); | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | +} |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
| @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; | 4 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; |
| 5 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; | 5 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; |
| 6 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; | 6 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; |
| 7 | -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | 7 | +import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; |
| 8 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 8 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 9 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; | 9 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 10 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; | 10 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
| @@ -17,7 +17,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; | @@ -17,7 +17,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; | ||
| 17 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; | 17 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; |
| 18 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; | 18 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; |
| 19 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | 19 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; |
| 20 | -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | 20 | +import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; |
| 21 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 21 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 22 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; | 22 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 23 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; | 23 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; |
| @@ -29,6 +29,7 @@ import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoChangeReqDTO; | @@ -29,6 +29,7 @@ import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoChangeReqDTO; | ||
| 29 | import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO; | 29 | import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO; |
| 30 | import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetRespDTO; | 30 | import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetRespDTO; |
| 31 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; | 31 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; |
| 32 | +import cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils; | ||
| 32 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | 33 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 33 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; | 34 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; |
| 34 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | 35 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| @@ -819,8 +820,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { | @@ -819,8 +820,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { | ||
| 819 | if (BooleanUtil.isFalse(product.getRegisterEnabled())) { | 820 | if (BooleanUtil.isFalse(product.getRegisterEnabled())) { |
| 820 | throw exception(DEVICE_REGISTER_DISABLED); | 821 | throw exception(DEVICE_REGISTER_DISABLED); |
| 821 | } | 822 | } |
| 822 | - // 1.3 验证 productSecret | ||
| 823 | - if (ObjUtil.notEqual(product.getProductSecret(), reqDTO.getProductSecret())) { | 823 | + // 1.3 【重要!!!】验证签名 |
| 824 | + if (!IotProductAuthUtils.verifySign(reqDTO.getProductKey(), reqDTO.getDeviceName(), | ||
| 825 | + product.getProductSecret(), reqDTO.getSign())) { | ||
| 824 | throw exception(DEVICE_REGISTER_SECRET_INVALID); | 826 | throw exception(DEVICE_REGISTER_SECRET_INVALID); |
| 825 | } | 827 | } |
| 826 | return TenantUtils.execute(product.getTenantId(), () -> { | 828 | return TenantUtils.execute(product.getTenantId(), () -> { |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaTaskRecordServiceImpl.java
| @@ -3,11 +3,15 @@ package cn.iocoder.yudao.module.iot.service.ota; | @@ -3,11 +3,15 @@ package cn.iocoder.yudao.module.iot.service.ota; | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.convert.Convert; | 4 | import cn.hutool.core.convert.Convert; |
| 5 | import cn.hutool.core.lang.Assert; | 5 | import cn.hutool.core.lang.Assert; |
| 6 | -import cn.hutool.core.map.MapUtil; | ||
| 7 | import cn.hutool.core.util.StrUtil; | 6 | import cn.hutool.core.util.StrUtil; |
| 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 8 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 9 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 9 | import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO; | 10 | import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.task.record.IotOtaTaskRecordPageReqVO; |
| 11 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 10 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 12 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 13 | +import cn.iocoder.yudao.module.iot.core.topic.ota.IotDeviceOtaProgressReqDTO; | ||
| 14 | +import cn.iocoder.yudao.module.iot.core.topic.ota.IotDeviceOtaUpgradeReqDTO; | ||
| 11 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | 15 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 12 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; | 16 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; |
| 13 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO; | 17 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO; |
| @@ -133,9 +137,9 @@ public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService { | @@ -133,9 +137,9 @@ public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService { | ||
| 133 | public boolean pushOtaTaskRecord(IotOtaTaskRecordDO record, IotOtaFirmwareDO fireware, IotDeviceDO device) { | 137 | public boolean pushOtaTaskRecord(IotOtaTaskRecordDO record, IotOtaFirmwareDO fireware, IotDeviceDO device) { |
| 134 | try { | 138 | try { |
| 135 | // 1. 推送 OTA 任务记录 | 139 | // 1. 推送 OTA 任务记录 |
| 136 | - IotDeviceMessage message = IotDeviceMessage.buildOtaUpgrade( | ||
| 137 | - fireware.getVersion(), fireware.getFileUrl(), fireware.getFileSize(), | ||
| 138 | - fireware.getFileDigestAlgorithm(), fireware.getFileDigestValue()); | 140 | + IotDeviceOtaUpgradeReqDTO params = BeanUtils.toBean(fireware, IotDeviceOtaUpgradeReqDTO.class); |
| 141 | + IotDeviceMessage message = IotDeviceMessage.requestOf( | ||
| 142 | + IotDeviceMessageMethodEnum.OTA_UPGRADE.getMethod(), params); | ||
| 139 | deviceMessageService.sendDeviceMessage(message, device); | 143 | deviceMessageService.sendDeviceMessage(message, device); |
| 140 | 144 | ||
| 141 | // 2. 更新 OTA 升级记录状态为进行中 | 145 | // 2. 更新 OTA 升级记录状态为进行中 |
| @@ -163,17 +167,16 @@ public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService { | @@ -163,17 +167,16 @@ public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService { | ||
| 163 | 167 | ||
| 164 | @Override | 168 | @Override |
| 165 | @Transactional(rollbackFor = Exception.class) | 169 | @Transactional(rollbackFor = Exception.class) |
| 166 | - @SuppressWarnings("unchecked") | ||
| 167 | public void updateOtaRecordProgress(IotDeviceDO device, IotDeviceMessage message) { | 170 | public void updateOtaRecordProgress(IotDeviceDO device, IotDeviceMessage message) { |
| 168 | // 1.1 参数解析 | 171 | // 1.1 参数解析 |
| 169 | - Map<String, Object> params = (Map<String, Object>) message.getParams(); | ||
| 170 | - String version = MapUtil.getStr(params, "version"); | 172 | + IotDeviceOtaProgressReqDTO params = JsonUtils.convertObject(message.getParams(), IotDeviceOtaProgressReqDTO.class); |
| 173 | + String version = params.getVersion(); | ||
| 171 | Assert.notBlank(version, "version 不能为空"); | 174 | Assert.notBlank(version, "version 不能为空"); |
| 172 | - Integer status = MapUtil.getInt(params, "status"); | 175 | + Integer status = params.getStatus(); |
| 173 | Assert.notNull(status, "status 不能为空"); | 176 | Assert.notNull(status, "status 不能为空"); |
| 174 | Assert.notNull(IotOtaTaskRecordStatusEnum.of(status), "status 状态不正确"); | 177 | Assert.notNull(IotOtaTaskRecordStatusEnum.of(status), "status 状态不正确"); |
| 175 | - String description = MapUtil.getStr(params, "description"); | ||
| 176 | - Integer progress = MapUtil.getInt(params, "progress"); | 178 | + String description = params.getDescription(); |
| 179 | + Integer progress = params.getProgress(); | ||
| 177 | Assert.notNull(progress, "progress 不能为空"); | 180 | Assert.notNull(progress, "progress 不能为空"); |
| 178 | Assert.isTrue(progress >= 0 && progress <= 100, "progress 必须在 0-100 之间"); | 181 | Assert.isTrue(progress >= 0 && progress <= 100, "progress 必须在 0-100 之间"); |
| 179 | // 1.2 查询 OTA 升级记录 | 182 | // 1.2 查询 OTA 升级记录 |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
| @@ -10,6 +10,9 @@ import javax.annotation.Nullable; | @@ -10,6 +10,9 @@ import javax.annotation.Nullable; | ||
| 10 | import java.time.LocalDateTime; | 10 | import java.time.LocalDateTime; |
| 11 | import java.util.Collection; | 11 | import java.util.Collection; |
| 12 | import java.util.List; | 12 | import java.util.List; |
| 13 | +import java.util.Map; | ||
| 14 | + | ||
| 15 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | ||
| 13 | 16 | ||
| 14 | /** | 17 | /** |
| 15 | * IoT 产品 Service 接口 | 18 | * IoT 产品 Service 接口 |
| @@ -122,10 +125,35 @@ public interface IotProductService { | @@ -122,10 +125,35 @@ public interface IotProductService { | ||
| 122 | Long getProductCount(@Nullable LocalDateTime createTime); | 125 | Long getProductCount(@Nullable LocalDateTime createTime); |
| 123 | 126 | ||
| 124 | /** | 127 | /** |
| 128 | + * 批量获得产品列表 | ||
| 129 | + * | ||
| 130 | + * @param ids 产品编号集合 | ||
| 131 | + * @return 产品列表 | ||
| 132 | + */ | ||
| 133 | + List<IotProductDO> getProductList(Collection<Long> ids); | ||
| 134 | + | ||
| 135 | + /** | ||
| 136 | + * 批量获得产品 Map | ||
| 137 | + * | ||
| 138 | + * @param ids 产品编号集合 | ||
| 139 | + * @return 产品 Map(key: 产品编号, value: 产品) | ||
| 140 | + */ | ||
| 141 | + default Map<Long, IotProductDO> getProductMap(Collection<Long> ids) { | ||
| 142 | + return convertMap(getProductList(ids), IotProductDO::getId); | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + /** | ||
| 125 | * 批量校验产品存在 | 146 | * 批量校验产品存在 |
| 126 | * | 147 | * |
| 127 | * @param ids 产品编号集合 | 148 | * @param ids 产品编号集合 |
| 128 | */ | 149 | */ |
| 129 | void validateProductsExist(Collection<Long> ids); | 150 | void validateProductsExist(Collection<Long> ids); |
| 130 | 151 | ||
| 152 | + /** | ||
| 153 | + * 同步产品的 TDengine 表结构 | ||
| 154 | + * | ||
| 155 | + * 目的:当 MySQL 和 TDengine 不同步时,强制将已发布产品的表结构同步到 TDengine 中 | ||
| 156 | + */ | ||
| 157 | + void syncProductPropertyTable(); | ||
| 158 | + | ||
| 131 | } | 159 | } |
| 132 | \ No newline at end of file | 160 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
| @@ -15,8 +15,10 @@ import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; | @@ -15,8 +15,10 @@ import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; | ||
| 15 | import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; | 15 | import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; |
| 16 | import com.baomidou.dynamic.datasource.annotation.DSTransactional; | 16 | import com.baomidou.dynamic.datasource.annotation.DSTransactional; |
| 17 | import jakarta.annotation.Resource; | 17 | import jakarta.annotation.Resource; |
| 18 | +import lombok.extern.slf4j.Slf4j; | ||
| 18 | import org.springframework.cache.annotation.CacheEvict; | 19 | import org.springframework.cache.annotation.CacheEvict; |
| 19 | import org.springframework.cache.annotation.Cacheable; | 20 | import org.springframework.cache.annotation.Cacheable; |
| 21 | +import org.springframework.context.annotation.Lazy; | ||
| 20 | import org.springframework.stereotype.Service; | 22 | import org.springframework.stereotype.Service; |
| 21 | import org.springframework.validation.annotation.Validated; | 23 | import org.springframework.validation.annotation.Validated; |
| 22 | 24 | ||
| @@ -33,6 +35,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; | @@ -33,6 +35,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; | ||
| 33 | * | 35 | * |
| 34 | * @author ahh | 36 | * @author ahh |
| 35 | */ | 37 | */ |
| 38 | +@Slf4j | ||
| 36 | @Service | 39 | @Service |
| 37 | @Validated | 40 | @Validated |
| 38 | public class IotProductServiceImpl implements IotProductService { | 41 | public class IotProductServiceImpl implements IotProductService { |
| @@ -41,9 +44,10 @@ public class IotProductServiceImpl implements IotProductService { | @@ -41,9 +44,10 @@ public class IotProductServiceImpl implements IotProductService { | ||
| 41 | private IotProductMapper productMapper; | 44 | private IotProductMapper productMapper; |
| 42 | 45 | ||
| 43 | @Resource | 46 | @Resource |
| 44 | - private IotDevicePropertyService devicePropertyDataService; | ||
| 45 | - @Resource | ||
| 46 | private IotDeviceService deviceService; | 47 | private IotDeviceService deviceService; |
| 48 | + @Resource | ||
| 49 | + @Lazy // 延迟加载,避免循环依赖 | ||
| 50 | + private IotDevicePropertyService devicePropertyDataService; | ||
| 47 | 51 | ||
| 48 | @Override | 52 | @Override |
| 49 | public Long createProduct(IotProductSaveReqVO createReqVO) { | 53 | public Long createProduct(IotProductSaveReqVO createReqVO) { |
| @@ -172,6 +176,32 @@ public class IotProductServiceImpl implements IotProductService { | @@ -172,6 +176,32 @@ public class IotProductServiceImpl implements IotProductService { | ||
| 172 | } | 176 | } |
| 173 | 177 | ||
| 174 | @Override | 178 | @Override |
| 179 | + public List<IotProductDO> getProductList(Collection<Long> ids) { | ||
| 180 | + return productMapper.selectByIds(ids); | ||
| 181 | + } | ||
| 182 | + | ||
| 183 | + @Override | ||
| 184 | + public void syncProductPropertyTable() { | ||
| 185 | + // 1. 获取所有已发布的产品 | ||
| 186 | + List<IotProductDO> products = productMapper.selectListByStatus( | ||
| 187 | + IotProductStatusEnum.PUBLISHED.getStatus()); | ||
| 188 | + log.info("[syncProductPropertyTable][开始同步,已发布产品数量({})]", products.size()); | ||
| 189 | + | ||
| 190 | + // 2. 遍历同步 TDengine 表结构(创建产品超级表数据模型) | ||
| 191 | + int successCount = 0; | ||
| 192 | + for (IotProductDO product : products) { | ||
| 193 | + try { | ||
| 194 | + devicePropertyDataService.defineDevicePropertyData(product.getId()); | ||
| 195 | + successCount++; | ||
| 196 | + log.info("[syncProductPropertyTable][产品({}/{}) 同步成功]", product.getId(), product.getName()); | ||
| 197 | + } catch (Exception e) { | ||
| 198 | + log.error("[syncProductPropertyTable][产品({}/{}) 同步失败]", product.getId(), product.getName(), e); | ||
| 199 | + } | ||
| 200 | + } | ||
| 201 | + log.info("[syncProductPropertyTable][同步完成,成功({}/{})个]", successCount, products.size()); | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + @Override | ||
| 175 | public void validateProductsExist(Collection<Long> ids) { | 205 | public void validateProductsExist(Collection<Long> ids) { |
| 176 | if (CollUtil.isEmpty(ids)) { | 206 | if (CollUtil.isEmpty(ids)) { |
| 177 | return; | 207 | return; |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataRuleCacheableAction.java
| @@ -14,8 +14,6 @@ import java.time.Duration; | @@ -14,8 +14,6 @@ import java.time.Duration; | ||
| 14 | 14 | ||
| 15 | // TODO @芋艿:数据库 | 15 | // TODO @芋艿:数据库 |
| 16 | // TODO @芋艿:mqtt | 16 | // TODO @芋艿:mqtt |
| 17 | -// TODO @芋艿:tcp | ||
| 18 | -// TODO @芋艿:websocket | ||
| 19 | 17 | ||
| 20 | /** | 18 | /** |
| 21 | * 可缓存的 {@link IotDataRuleAction} 抽象实现 | 19 | * 可缓存的 {@link IotDataRuleAction} 抽象实现 |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceControlSceneRuleAction.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDevicePropertySetSceneRuleAction.java
| @@ -15,7 +15,6 @@ import lombok.extern.slf4j.Slf4j; | @@ -15,7 +15,6 @@ import lombok.extern.slf4j.Slf4j; | ||
| 15 | import org.springframework.stereotype.Component; | 15 | import org.springframework.stereotype.Component; |
| 16 | 16 | ||
| 17 | import java.util.List; | 17 | import java.util.List; |
| 18 | -import java.util.Map; | ||
| 19 | 18 | ||
| 20 | /** | 19 | /** |
| 21 | * IoT 设备属性设置的 {@link IotSceneRuleAction} 实现类 | 20 | * IoT 设备属性设置的 {@link IotSceneRuleAction} 实现类 |
| @@ -24,7 +23,7 @@ import java.util.Map; | @@ -24,7 +23,7 @@ import java.util.Map; | ||
| 24 | */ | 23 | */ |
| 25 | @Component | 24 | @Component |
| 26 | @Slf4j | 25 | @Slf4j |
| 27 | -public class IotDeviceControlSceneRuleAction implements IotSceneRuleAction { | 26 | +public class IotDevicePropertySetSceneRuleAction implements IotSceneRuleAction { |
| 28 | 27 | ||
| 29 | @Resource | 28 | @Resource |
| 30 | private IotDeviceService deviceService; | 29 | private IotDeviceService deviceService; |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java
| @@ -15,6 +15,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | @@ -15,6 +15,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; | ||
| 15 | import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotThingModelMapper; | 15 | import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotThingModelMapper; |
| 16 | import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; | 16 | import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; |
| 17 | import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; | 17 | import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; |
| 18 | +import cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusPointService; | ||
| 18 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; | 19 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; |
| 19 | import jakarta.annotation.Resource; | 20 | import jakarta.annotation.Resource; |
| 20 | import lombok.extern.slf4j.Slf4j; | 21 | import lombok.extern.slf4j.Slf4j; |
| @@ -50,6 +51,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { | @@ -50,6 +51,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { | ||
| 50 | @Resource | 51 | @Resource |
| 51 | @Lazy // 延迟加载,解决循环依赖 | 52 | @Lazy // 延迟加载,解决循环依赖 |
| 52 | private IotProductService productService; | 53 | private IotProductService productService; |
| 54 | + @Resource | ||
| 55 | + @Lazy // 延迟加载,解决循环依赖 | ||
| 56 | + private IotDeviceModbusPointService deviceModbusPointService; | ||
| 53 | 57 | ||
| 54 | @Override | 58 | @Override |
| 55 | @Transactional(rollbackFor = Exception.class) | 59 | @Transactional(rollbackFor = Exception.class) |
| @@ -84,7 +88,11 @@ public class IotThingModelServiceImpl implements IotThingModelService { | @@ -84,7 +88,11 @@ public class IotThingModelServiceImpl implements IotThingModelService { | ||
| 84 | IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(updateReqVO); | 88 | IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(updateReqVO); |
| 85 | thingModelMapper.updateById(thingModel); | 89 | thingModelMapper.updateById(thingModel); |
| 86 | 90 | ||
| 87 | - // 3. 删除缓存 | 91 | + // 3. 同步更新 Modbus 点位的冗余字段(identifier、name) |
| 92 | + deviceModbusPointService.updateDeviceModbusPointByThingModel( | ||
| 93 | + updateReqVO.getId(), updateReqVO.getIdentifier(), updateReqVO.getName()); | ||
| 94 | + | ||
| 95 | + // 4. 删除缓存 | ||
| 88 | deleteThingModelListCache(updateReqVO.getProductId()); | 96 | deleteThingModelListCache(updateReqVO.getProductId()); |
| 89 | } | 97 | } |
| 90 | 98 |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleTimerConditionIntegrationTest.java
| @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.service.rule.scene; | @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.service.rule.scene; | ||
| 3 | import cn.hutool.core.collection.ListUtil; | 3 | import cn.hutool.core.collection.ListUtil; |
| 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 5 | import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | 5 | import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; |
| 6 | -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | 6 | +import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; |
| 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; |
| 9 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | 9 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcherTest.java
| 1 | package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; | 1 | package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | 3 | +import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; |
| 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 5 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | 5 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; |
| 6 | import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; | 6 | import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcherTest.java
| 1 | package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; | 1 | package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | 3 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; |
| 4 | -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | 4 | +import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; |
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | 6 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; |
| 7 | import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; | 7 | import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java
| 1 | package cn.iocoder.yudao.module.iot.core.biz; | 1 | package cn.iocoder.yudao.module.iot.core.biz; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; | ||
| 5 | -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO; | ||
| 6 | -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; | ||
| 7 | -import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; | 4 | +import cn.iocoder.yudao.module.iot.core.biz.dto.*; |
| 8 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; | 5 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; |
| 9 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; | 6 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; |
| 10 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; | 7 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; |
| @@ -50,4 +47,12 @@ public interface IotDeviceCommonApi { | @@ -50,4 +47,12 @@ public interface IotDeviceCommonApi { | ||
| 50 | */ | 47 | */ |
| 51 | CommonResult<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO); | 48 | CommonResult<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO); |
| 52 | 49 | ||
| 50 | + /** | ||
| 51 | + * 获取 Modbus 设备配置列表 | ||
| 52 | + * | ||
| 53 | + * @param listReqDTO 查询参数 | ||
| 54 | + * @return Modbus 设备配置列表 | ||
| 55 | + */ | ||
| 56 | + CommonResult<List<IotModbusDeviceConfigRespDTO>> getModbusDeviceConfigList(IotModbusDeviceConfigListReqDTO listReqDTO); | ||
| 57 | + | ||
| 53 | } | 58 | } |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceRespDTO.java
| @@ -34,8 +34,12 @@ public class IotDeviceRespDTO { | @@ -34,8 +34,12 @@ public class IotDeviceRespDTO { | ||
| 34 | */ | 34 | */ |
| 35 | private Long productId; | 35 | private Long productId; |
| 36 | /** | 36 | /** |
| 37 | - * 编解码器类型 | 37 | + * 协议类型 |
| 38 | */ | 38 | */ |
| 39 | - private String codecType; | 39 | + private String protocolType; |
| 40 | + /** | ||
| 41 | + * 序列化类型 | ||
| 42 | + */ | ||
| 43 | + private String serializeType; | ||
| 40 | 44 | ||
| 41 | } | 45 | } |
| 42 | \ No newline at end of file | 46 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusDeviceConfigListReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.biz.dto; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | +import lombok.experimental.Accessors; | ||
| 5 | + | ||
| 6 | +import java.util.Set; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * IoT Modbus 设备配置列表查询 Request DTO | ||
| 10 | + * | ||
| 11 | + * @author 芋道源码 | ||
| 12 | + */ | ||
| 13 | +@Data | ||
| 14 | +@Accessors(chain = true) | ||
| 15 | +public class IotModbusDeviceConfigListReqDTO { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 状态 | ||
| 19 | + */ | ||
| 20 | + private Integer status; | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 模式 | ||
| 24 | + */ | ||
| 25 | + private Integer mode; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 协议类型 | ||
| 29 | + */ | ||
| 30 | + private String protocolType; | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 设备 ID 集合 | ||
| 34 | + */ | ||
| 35 | + private Set<Long> deviceIds; | ||
| 36 | + | ||
| 37 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusDeviceConfigRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.biz.dto; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import java.util.List; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * IoT Modbus 设备配置 Response DTO | ||
| 9 | + * | ||
| 10 | + * @author 芋道源码 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class IotModbusDeviceConfigRespDTO { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 设备编号 | ||
| 17 | + */ | ||
| 18 | + private Long deviceId; | ||
| 19 | + /** | ||
| 20 | + * 产品标识 | ||
| 21 | + */ | ||
| 22 | + private String productKey; | ||
| 23 | + /** | ||
| 24 | + * 设备名称 | ||
| 25 | + */ | ||
| 26 | + private String deviceName; | ||
| 27 | + | ||
| 28 | + // ========== Modbus 连接配置 ========== | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * Modbus 服务器 IP 地址 | ||
| 32 | + */ | ||
| 33 | + private String ip; | ||
| 34 | + /** | ||
| 35 | + * Modbus 服务器端口 | ||
| 36 | + */ | ||
| 37 | + private Integer port; | ||
| 38 | + /** | ||
| 39 | + * 从站地址 | ||
| 40 | + */ | ||
| 41 | + private Integer slaveId; | ||
| 42 | + /** | ||
| 43 | + * 连接超时时间,单位:毫秒 | ||
| 44 | + */ | ||
| 45 | + private Integer timeout; | ||
| 46 | + /** | ||
| 47 | + * 重试间隔,单位:毫秒 | ||
| 48 | + */ | ||
| 49 | + private Integer retryInterval; | ||
| 50 | + /** | ||
| 51 | + * 模式 | ||
| 52 | + */ | ||
| 53 | + private Integer mode; | ||
| 54 | + /** | ||
| 55 | + * 数据帧格式 | ||
| 56 | + */ | ||
| 57 | + private Integer frameFormat; | ||
| 58 | + | ||
| 59 | + // ========== Modbus 点位配置 ========== | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 点位列表 | ||
| 63 | + */ | ||
| 64 | + private List<IotModbusPointRespDTO> points; | ||
| 65 | + | ||
| 66 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotModbusPointRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.biz.dto; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusByteOrderEnum; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusRawDataTypeEnum; | ||
| 5 | +import lombok.Data; | ||
| 6 | + | ||
| 7 | +import java.math.BigDecimal; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * IoT Modbus 点位配置 Response DTO | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | +@Data | ||
| 15 | +public class IotModbusPointRespDTO { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 点位编号 | ||
| 19 | + */ | ||
| 20 | + private Long id; | ||
| 21 | + /** | ||
| 22 | + * 属性标识符(物模型的 identifier) | ||
| 23 | + */ | ||
| 24 | + private String identifier; | ||
| 25 | + /** | ||
| 26 | + * 属性名称(物模型的 name) | ||
| 27 | + */ | ||
| 28 | + private String name; | ||
| 29 | + | ||
| 30 | + // ========== Modbus 协议配置 ========== | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * Modbus 功能码 | ||
| 34 | + * | ||
| 35 | + * 取值范围:FC01-04(读线圈、读离散输入、读保持寄存器、读输入寄存器) | ||
| 36 | + */ | ||
| 37 | + private Integer functionCode; | ||
| 38 | + /** | ||
| 39 | + * 寄存器起始地址 | ||
| 40 | + */ | ||
| 41 | + private Integer registerAddress; | ||
| 42 | + /** | ||
| 43 | + * 寄存器数量 | ||
| 44 | + */ | ||
| 45 | + private Integer registerCount; | ||
| 46 | + /** | ||
| 47 | + * 字节序 | ||
| 48 | + * | ||
| 49 | + * 枚举 {@link IotModbusByteOrderEnum} | ||
| 50 | + */ | ||
| 51 | + private String byteOrder; | ||
| 52 | + /** | ||
| 53 | + * 原始数据类型 | ||
| 54 | + * | ||
| 55 | + * 枚举 {@link IotModbusRawDataTypeEnum} | ||
| 56 | + */ | ||
| 57 | + private String rawDataType; | ||
| 58 | + /** | ||
| 59 | + * 缩放因子 | ||
| 60 | + */ | ||
| 61 | + private BigDecimal scale; | ||
| 62 | + /** | ||
| 63 | + * 轮询间隔(毫秒) | ||
| 64 | + */ | ||
| 65 | + private Integer pollInterval; | ||
| 66 | + | ||
| 67 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java
| @@ -64,7 +64,7 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> { | @@ -64,7 +64,7 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> { | ||
| 64 | // ========== OTA 固件 ========== | 64 | // ========== OTA 固件 ========== |
| 65 | // 可参考:https://help.aliyun.com/zh/iot/user-guide/perform-ota-updates | 65 | // 可参考:https://help.aliyun.com/zh/iot/user-guide/perform-ota-updates |
| 66 | 66 | ||
| 67 | - OTA_UPGRADE("thing.ota.upgrade", "OTA 固定信息推送", false), | 67 | + OTA_UPGRADE("thing.ota.upgrade", "OTA 固件信息推送", false), |
| 68 | OTA_PROGRESS("thing.ota.progress", "OTA 升级进度上报", true), | 68 | OTA_PROGRESS("thing.ota.progress", "OTA 升级进度上报", true), |
| 69 | 69 | ||
| 70 | ; | 70 | ; |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotProtocolTypeEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.enums; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.ArrayUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.common.core.ArrayValuable; | ||
| 5 | +import lombok.Getter; | ||
| 6 | +import lombok.RequiredArgsConstructor; | ||
| 7 | + | ||
| 8 | +import java.util.Arrays; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * IoT 协议类型枚举 | ||
| 12 | + * | ||
| 13 | + * 用于定义传输层协议类型 | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +@RequiredArgsConstructor | ||
| 18 | +@Getter | ||
| 19 | +public enum IotProtocolTypeEnum implements ArrayValuable<String> { | ||
| 20 | + | ||
| 21 | + TCP("tcp"), | ||
| 22 | + UDP("udp"), | ||
| 23 | + WEBSOCKET("websocket"), | ||
| 24 | + HTTP("http"), | ||
| 25 | + MQTT("mqtt"), | ||
| 26 | + EMQX("emqx"), | ||
| 27 | + COAP("coap"), | ||
| 28 | + MODBUS_TCP_CLIENT("modbus_tcp_client"), | ||
| 29 | + MODBUS_TCP_SERVER("modbus_tcp_server"); | ||
| 30 | + | ||
| 31 | + public static final String[] ARRAYS = Arrays.stream(values()).map(IotProtocolTypeEnum::getType).toArray(String[]::new); | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 类型 | ||
| 35 | + */ | ||
| 36 | + private final String type; | ||
| 37 | + | ||
| 38 | + @Override | ||
| 39 | + public String[] array() { | ||
| 40 | + return ARRAYS; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + public static IotProtocolTypeEnum of(String type) { | ||
| 44 | + return ArrayUtil.firstMatch(e -> e.getType().equals(type), values()); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotSerializeTypeEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.enums; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.ArrayUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.common.core.ArrayValuable; | ||
| 5 | +import lombok.Getter; | ||
| 6 | +import lombok.RequiredArgsConstructor; | ||
| 7 | + | ||
| 8 | +import java.util.Arrays; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * IoT 序列化类型枚举 | ||
| 12 | + * | ||
| 13 | + * 用于定义设备消息的序列化格式 | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +@RequiredArgsConstructor | ||
| 18 | +@Getter | ||
| 19 | +public enum IotSerializeTypeEnum implements ArrayValuable<String> { | ||
| 20 | + | ||
| 21 | + JSON("json"), | ||
| 22 | + BINARY("binary"); | ||
| 23 | + | ||
| 24 | + public static final String[] ARRAYS = Arrays.stream(values()).map(IotSerializeTypeEnum::getType).toArray(String[]::new); | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 类型 | ||
| 28 | + */ | ||
| 29 | + private final String type; | ||
| 30 | + | ||
| 31 | + @Override | ||
| 32 | + public String[] array() { | ||
| 33 | + return ARRAYS; | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + public static IotSerializeTypeEnum of(String type) { | ||
| 37 | + return ArrayUtil.firstMatch(e -> e.getType().equals(type), values()); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceStateEnum.java renamed to yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/device/IotDeviceStateEnum.java
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/modbus/IotModbusByteOrderEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.enums.modbus; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.core.ArrayValuable; | ||
| 4 | +import lombok.Getter; | ||
| 5 | +import lombok.RequiredArgsConstructor; | ||
| 6 | + | ||
| 7 | +import java.util.Arrays; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * IoT Modbus 字节序枚举 | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | +@Getter | ||
| 15 | +@RequiredArgsConstructor | ||
| 16 | +public enum IotModbusByteOrderEnum implements ArrayValuable<String> { | ||
| 17 | + | ||
| 18 | + AB("AB", "大端序(16位)", 2), | ||
| 19 | + BA("BA", "小端序(16位)", 2), | ||
| 20 | + ABCD("ABCD", "大端序(32位)", 4), | ||
| 21 | + CDAB("CDAB", "大端字交换(32位)", 4), | ||
| 22 | + DCBA("DCBA", "小端序(32位)", 4), | ||
| 23 | + BADC("BADC", "小端字交换(32位)", 4); | ||
| 24 | + | ||
| 25 | + public static final String[] ARRAYS = Arrays.stream(values()) | ||
| 26 | + .map(IotModbusByteOrderEnum::getOrder) | ||
| 27 | + .toArray(String[]::new); | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * 字节序 | ||
| 31 | + */ | ||
| 32 | + private final String order; | ||
| 33 | + /** | ||
| 34 | + * 名称 | ||
| 35 | + */ | ||
| 36 | + private final String name; | ||
| 37 | + /** | ||
| 38 | + * 字节数 | ||
| 39 | + */ | ||
| 40 | + private final Integer byteCount; | ||
| 41 | + | ||
| 42 | + @Override | ||
| 43 | + public String[] array() { | ||
| 44 | + return ARRAYS; | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + public static IotModbusByteOrderEnum getByOrder(String order) { | ||
| 48 | + return Arrays.stream(values()) | ||
| 49 | + .filter(e -> e.getOrder().equals(order)) | ||
| 50 | + .findFirst() | ||
| 51 | + .orElse(null); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/modbus/IotModbusFrameFormatEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.enums.modbus; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.core.ArrayValuable; | ||
| 4 | +import lombok.Getter; | ||
| 5 | +import lombok.RequiredArgsConstructor; | ||
| 6 | + | ||
| 7 | +import java.util.Arrays; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * IoT Modbus 数据帧格式枚举 | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | +@Getter | ||
| 15 | +@RequiredArgsConstructor | ||
| 16 | +public enum IotModbusFrameFormatEnum implements ArrayValuable<Integer> { | ||
| 17 | + | ||
| 18 | + MODBUS_TCP(1), | ||
| 19 | + MODBUS_RTU(2); | ||
| 20 | + | ||
| 21 | + public static final Integer[] ARRAYS = Arrays.stream(values()) | ||
| 22 | + .map(IotModbusFrameFormatEnum::getFormat) | ||
| 23 | + .toArray(Integer[]::new); | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 格式 | ||
| 27 | + */ | ||
| 28 | + private final Integer format; | ||
| 29 | + | ||
| 30 | + @Override | ||
| 31 | + public Integer[] array() { | ||
| 32 | + return ARRAYS; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/modbus/IotModbusModeEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.enums.modbus; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.core.ArrayValuable; | ||
| 4 | +import lombok.Getter; | ||
| 5 | +import lombok.RequiredArgsConstructor; | ||
| 6 | + | ||
| 7 | +import java.util.Arrays; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * IoT Modbus 工作模式枚举 | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | +@Getter | ||
| 15 | +@RequiredArgsConstructor | ||
| 16 | +public enum IotModbusModeEnum implements ArrayValuable<Integer> { | ||
| 17 | + | ||
| 18 | + POLLING(1, "云端轮询"), | ||
| 19 | + ACTIVE_REPORT(2, "边缘采集"); | ||
| 20 | + | ||
| 21 | + public static final Integer[] ARRAYS = Arrays.stream(values()) | ||
| 22 | + .map(IotModbusModeEnum::getMode) | ||
| 23 | + .toArray(Integer[]::new); | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 工作模式 | ||
| 27 | + */ | ||
| 28 | + private final Integer mode; | ||
| 29 | + /** | ||
| 30 | + * 模式名称 | ||
| 31 | + */ | ||
| 32 | + private final String name; | ||
| 33 | + | ||
| 34 | + @Override | ||
| 35 | + public Integer[] array() { | ||
| 36 | + return ARRAYS; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/modbus/IotModbusRawDataTypeEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.enums.modbus; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.core.ArrayValuable; | ||
| 4 | +import lombok.Getter; | ||
| 5 | +import lombok.RequiredArgsConstructor; | ||
| 6 | + | ||
| 7 | +import java.util.Arrays; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * IoT Modbus 原始数据类型枚举 | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | +@Getter | ||
| 15 | +@RequiredArgsConstructor | ||
| 16 | +public enum IotModbusRawDataTypeEnum implements ArrayValuable<String> { | ||
| 17 | + | ||
| 18 | + INT16("INT16", "有符号 16 位整数", 1), | ||
| 19 | + UINT16("UINT16", "无符号 16 位整数", 1), | ||
| 20 | + INT32("INT32", "有符号 32 位整数", 2), | ||
| 21 | + UINT32("UINT32", "无符号 32 位整数", 2), | ||
| 22 | + FLOAT("FLOAT", "32 位浮点数", 2), | ||
| 23 | + DOUBLE("DOUBLE", "64 位浮点数", 4), | ||
| 24 | + BOOLEAN("BOOLEAN", "布尔值(用于线圈)", 1), | ||
| 25 | + STRING("STRING", "字符串", null); // null 表示可变长度 | ||
| 26 | + | ||
| 27 | + public static final String[] ARRAYS = Arrays.stream(values()) | ||
| 28 | + .map(IotModbusRawDataTypeEnum::getType) | ||
| 29 | + .toArray(String[]::new); | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 数据类型 | ||
| 33 | + */ | ||
| 34 | + private final String type; | ||
| 35 | + /** | ||
| 36 | + * 名称 | ||
| 37 | + */ | ||
| 38 | + private final String name; | ||
| 39 | + /** | ||
| 40 | + * 寄存器数量(null 表示可变) | ||
| 41 | + */ | ||
| 42 | + private final Integer registerCount; | ||
| 43 | + | ||
| 44 | + @Override | ||
| 45 | + public String[] array() { | ||
| 46 | + return ARRAYS; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + public static IotModbusRawDataTypeEnum getByType(String type) { | ||
| 50 | + return Arrays.stream(values()) | ||
| 51 | + .filter(e -> e.getType().equals(type)) | ||
| 52 | + .findFirst() | ||
| 53 | + .orElse(null); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/IotMessageBus.java
| @@ -24,4 +24,14 @@ public interface IotMessageBus { | @@ -24,4 +24,14 @@ public interface IotMessageBus { | ||
| 24 | */ | 24 | */ |
| 25 | void register(IotMessageSubscriber<?> subscriber); | 25 | void register(IotMessageSubscriber<?> subscriber); |
| 26 | 26 | ||
| 27 | + /** | ||
| 28 | + * 取消注册消息订阅者 | ||
| 29 | + * | ||
| 30 | + * @param subscriber 订阅者 | ||
| 31 | + */ | ||
| 32 | + default void unregister(IotMessageSubscriber<?> subscriber) { | ||
| 33 | + // TODO 芋艿:暂时不实现,需求量不大,但是 | ||
| 34 | + // throw new UnsupportedOperationException("取消注册消息订阅者功能,尚未实现"); | ||
| 35 | + } | ||
| 36 | + | ||
| 27 | } | 37 | } |
| 28 | \ No newline at end of file | 38 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/IotMessageSubscriber.java
| @@ -26,4 +26,16 @@ public interface IotMessageSubscriber<T> { | @@ -26,4 +26,16 @@ public interface IotMessageSubscriber<T> { | ||
| 26 | */ | 26 | */ |
| 27 | void onMessage(T message); | 27 | void onMessage(T message); |
| 28 | 28 | ||
| 29 | + /** | ||
| 30 | + * 启动订阅 | ||
| 31 | + */ | ||
| 32 | + default void start() { | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 停止订阅 | ||
| 37 | + */ | ||
| 38 | + default void stop() { | ||
| 39 | + } | ||
| 40 | + | ||
| 29 | } | 41 | } |
| 30 | \ No newline at end of file | 42 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/message/IotDeviceMessage.java
| 1 | package cn.iocoder.yudao.module.iot.core.mq.message; | 1 | package cn.iocoder.yudao.module.iot.core.mq.message; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.map.MapUtil; | ||
| 4 | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; | 3 | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; |
| 5 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | 4 | import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; |
| 6 | -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | 5 | +import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum; |
| 6 | +import cn.iocoder.yudao.module.iot.core.topic.state.IotDeviceStateUpdateReqDTO; | ||
| 7 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 7 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; |
| 8 | import lombok.AllArgsConstructor; | 8 | import lombok.AllArgsConstructor; |
| 9 | import lombok.Builder; | 9 | import lombok.Builder; |
| @@ -60,7 +60,7 @@ public class IotDeviceMessage { | @@ -60,7 +60,7 @@ public class IotDeviceMessage { | ||
| 60 | */ | 60 | */ |
| 61 | private String serverId; | 61 | private String serverId; |
| 62 | 62 | ||
| 63 | - // ========== codec(编解码)字段 ========== | 63 | + // ========== serialize(序列化)相关字段 ========== |
| 64 | 64 | ||
| 65 | /** | 65 | /** |
| 66 | * 请求编号 | 66 | * 请求编号 |
| @@ -72,7 +72,7 @@ public class IotDeviceMessage { | @@ -72,7 +72,7 @@ public class IotDeviceMessage { | ||
| 72 | * 请求方法 | 72 | * 请求方法 |
| 73 | * | 73 | * |
| 74 | * 枚举 {@link IotDeviceMessageMethodEnum} | 74 | * 枚举 {@link IotDeviceMessageMethodEnum} |
| 75 | - * 例如说:thing.property.report 属性上报 | 75 | + * 例如说:thing.property.post 属性上报 |
| 76 | */ | 76 | */ |
| 77 | private String method; | 77 | private String method; |
| 78 | /** | 78 | /** |
| @@ -94,7 +94,7 @@ public class IotDeviceMessage { | @@ -94,7 +94,7 @@ public class IotDeviceMessage { | ||
| 94 | */ | 94 | */ |
| 95 | private String msg; | 95 | private String msg; |
| 96 | 96 | ||
| 97 | - // ========== 基础方法:只传递"codec(编解码)字段" ========== | 97 | + // ========== 基础方法:只传递"serialize(序列化)相关字段" ========== |
| 98 | 98 | ||
| 99 | public static IotDeviceMessage requestOf(String method) { | 99 | public static IotDeviceMessage requestOf(String method) { |
| 100 | return requestOf(null, method, null); | 100 | return requestOf(null, method, null); |
| @@ -149,20 +149,12 @@ public class IotDeviceMessage { | @@ -149,20 +149,12 @@ public class IotDeviceMessage { | ||
| 149 | 149 | ||
| 150 | public static IotDeviceMessage buildStateUpdateOnline() { | 150 | public static IotDeviceMessage buildStateUpdateOnline() { |
| 151 | return requestOf(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(), | 151 | return requestOf(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(), |
| 152 | - MapUtil.of("state", IotDeviceStateEnum.ONLINE.getState())); | 152 | + new IotDeviceStateUpdateReqDTO(IotDeviceStateEnum.ONLINE.getState())); |
| 153 | } | 153 | } |
| 154 | 154 | ||
| 155 | public static IotDeviceMessage buildStateOffline() { | 155 | public static IotDeviceMessage buildStateOffline() { |
| 156 | return requestOf(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(), | 156 | return requestOf(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(), |
| 157 | - MapUtil.of("state", IotDeviceStateEnum.OFFLINE.getState())); | ||
| 158 | - } | ||
| 159 | - | ||
| 160 | - public static IotDeviceMessage buildOtaUpgrade(String version, String fileUrl, Long fileSize, | ||
| 161 | - String fileDigestAlgorithm, String fileDigestValue) { | ||
| 162 | - return requestOf(IotDeviceMessageMethodEnum.OTA_UPGRADE.getMethod(), MapUtil.builder() | ||
| 163 | - .put("version", version).put("fileUrl", fileUrl).put("fileSize", fileSize) | ||
| 164 | - .put("fileDigestAlgorithm", fileDigestAlgorithm).put("fileDigestValue", fileDigestValue) | ||
| 165 | - .build()); | 157 | + new IotDeviceStateUpdateReqDTO(IotDeviceStateEnum.OFFLINE.getState())); |
| 166 | } | 158 | } |
| 167 | 159 | ||
| 168 | } | 160 | } |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotDeviceRegisterReqDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.auth; | 1 | package cn.iocoder.yudao.module.iot.core.topic.auth; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 3 | import jakarta.validation.constraints.NotEmpty; | 4 | import jakarta.validation.constraints.NotEmpty; |
| 4 | import lombok.Data; | 5 | import lombok.Data; |
| 5 | 6 | ||
| 6 | /** | 7 | /** |
| 7 | * IoT 设备动态注册 Request DTO | 8 | * IoT 设备动态注册 Request DTO |
| 8 | * <p> | 9 | * <p> |
| 9 | - * 用于直连设备/网关的一型一密动态注册:使用 productSecret 验证,返回 deviceSecret | 10 | + * 用于 {@link IotDeviceMessageMethodEnum#DEVICE_REGISTER} 消息的 params 参数 |
| 11 | + * <p> | ||
| 12 | + * 直连设备/网关的一型一密动态注册:使用 productSecret 验证,返回 deviceSecret | ||
| 10 | * | 13 | * |
| 11 | * @author 芋道源码 | 14 | * @author 芋道源码 |
| 12 | * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a> | 15 | * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a> |
| @@ -27,9 +30,11 @@ public class IotDeviceRegisterReqDTO { | @@ -27,9 +30,11 @@ public class IotDeviceRegisterReqDTO { | ||
| 27 | private String deviceName; | 30 | private String deviceName; |
| 28 | 31 | ||
| 29 | /** | 32 | /** |
| 30 | - * 产品密钥 | 33 | + * 注册签名 |
| 34 | + * | ||
| 35 | + * @see cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils#buildSign(String, String, String) | ||
| 31 | */ | 36 | */ |
| 32 | - @NotEmpty(message = "产品密钥不能为空") | ||
| 33 | - private String productSecret; | 37 | + @NotEmpty(message = "签名不能为空") |
| 38 | + private String sign; | ||
| 34 | 39 | ||
| 35 | } | 40 | } |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotDeviceRegisterRespDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.auth; | 1 | package cn.iocoder.yudao.module.iot.core.topic.auth; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 3 | import lombok.AllArgsConstructor; | 4 | import lombok.AllArgsConstructor; |
| 4 | import lombok.Data; | 5 | import lombok.Data; |
| 5 | import lombok.NoArgsConstructor; | 6 | import lombok.NoArgsConstructor; |
| @@ -7,7 +8,7 @@ import lombok.NoArgsConstructor; | @@ -7,7 +8,7 @@ import lombok.NoArgsConstructor; | ||
| 7 | /** | 8 | /** |
| 8 | * IoT 设备动态注册 Response DTO | 9 | * IoT 设备动态注册 Response DTO |
| 9 | * <p> | 10 | * <p> |
| 10 | - * 用于直连设备/网关的一型一密动态注册响应 | 11 | + * 用于 {@link IotDeviceMessageMethodEnum#DEVICE_REGISTER} 响应的设备信息 |
| 11 | * | 12 | * |
| 12 | * @author 芋道源码 | 13 | * @author 芋道源码 |
| 13 | * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a> | 14 | * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a> |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotSubDeviceRegisterReqDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.auth; | 1 | package cn.iocoder.yudao.module.iot.core.topic.auth; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 3 | import jakarta.validation.constraints.NotEmpty; | 4 | import jakarta.validation.constraints.NotEmpty; |
| 4 | import lombok.Data; | 5 | import lombok.Data; |
| 5 | 6 | ||
| 6 | /** | 7 | /** |
| 7 | * IoT 子设备动态注册 Request DTO | 8 | * IoT 子设备动态注册 Request DTO |
| 8 | * <p> | 9 | * <p> |
| 9 | - * 用于 thing.auth.register.sub 消息的 params 数组元素 | ||
| 10 | - * | 10 | + * 用于 {@link IotDeviceMessageMethodEnum#SUB_DEVICE_REGISTER} 消息的 params 数组元素 |
| 11 | + * <p> | ||
| 11 | * 特殊:网关子设备的动态注册,必须已经创建好该网关子设备(不然哪来的 {@link #deviceName} 字段)。更多的好处,是设备不用提前烧录 deviceSecret 密钥。 | 12 | * 特殊:网关子设备的动态注册,必须已经创建好该网关子设备(不然哪来的 {@link #deviceName} 字段)。更多的好处,是设备不用提前烧录 deviceSecret 密钥。 |
| 12 | * | 13 | * |
| 13 | * @author 芋道源码 | 14 | * @author 芋道源码 |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotSubDeviceRegisterRespDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.auth; | 1 | package cn.iocoder.yudao.module.iot.core.topic.auth; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 3 | import lombok.AllArgsConstructor; | 4 | import lombok.AllArgsConstructor; |
| 4 | import lombok.Data; | 5 | import lombok.Data; |
| 5 | import lombok.NoArgsConstructor; | 6 | import lombok.NoArgsConstructor; |
| @@ -7,7 +8,7 @@ import lombok.NoArgsConstructor; | @@ -7,7 +8,7 @@ import lombok.NoArgsConstructor; | ||
| 7 | /** | 8 | /** |
| 8 | * IoT 子设备动态注册 Response DTO | 9 | * IoT 子设备动态注册 Response DTO |
| 9 | * <p> | 10 | * <p> |
| 10 | - * 用于 thing.auth.register.sub 响应的设备信息 | 11 | + * 用于 {@link IotDeviceMessageMethodEnum#SUB_DEVICE_REGISTER} 响应的设备信息 |
| 11 | * | 12 | * |
| 12 | * @author 芋道源码 | 13 | * @author 芋道源码 |
| 13 | * @see <a href="https://help.aliyun.com/zh/iot/user-guide/register-devices">阿里云 - 动态注册子设备</a> | 14 | * @see <a href="https://help.aliyun.com/zh/iot/user-guide/register-devices">阿里云 - 动态注册子设备</a> |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/config/IotDeviceConfigPushReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.topic.config; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 4 | +import lombok.AllArgsConstructor; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.NoArgsConstructor; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * IoT 设备配置推送 Request DTO | ||
| 10 | + * <p> | ||
| 11 | + * 用于 {@link IotDeviceMessageMethodEnum#CONFIG_PUSH} 下行消息的 params 参数 | ||
| 12 | + * | ||
| 13 | + * @author 芋道源码 | ||
| 14 | + * @see <a href="https://help.aliyun.com/zh/iot/user-guide/remote-configuration-1">阿里云 - 远程配置</a> | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +@NoArgsConstructor | ||
| 18 | +@AllArgsConstructor | ||
| 19 | +public class IotDeviceConfigPushReqDTO { | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 配置编号 | ||
| 23 | + */ | ||
| 24 | + private String configId; | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 配置文件大小(字节) | ||
| 28 | + */ | ||
| 29 | + private Long configSize; | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 签名方法 | ||
| 33 | + */ | ||
| 34 | + private String signMethod; | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * 签名 | ||
| 38 | + */ | ||
| 39 | + private String sign; | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 配置文件下载地址 | ||
| 43 | + */ | ||
| 44 | + private String url; | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * 获取类型 | ||
| 48 | + * <p> | ||
| 49 | + * file: 文件 | ||
| 50 | + * content: 内容 | ||
| 51 | + */ | ||
| 52 | + private String getType; | ||
| 53 | + | ||
| 54 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/event/IotDeviceEventPostReqDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.event; | 1 | package cn.iocoder.yudao.module.iot.core.topic.event; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 3 | import lombok.Data; | 4 | import lombok.Data; |
| 4 | 5 | ||
| 5 | /** | 6 | /** |
| 6 | * IoT 设备事件上报 Request DTO | 7 | * IoT 设备事件上报 Request DTO |
| 7 | * <p> | 8 | * <p> |
| 8 | - * 用于 thing.event.post 消息的 params 参数 | 9 | + * 用于 {@link IotDeviceMessageMethodEnum#EVENT_POST} 消息的 params 参数 |
| 9 | * | 10 | * |
| 10 | * @author 芋道源码 | 11 | * @author 芋道源码 |
| 11 | * @see <a href="http://help.aliyun.com/zh/marketplace/device-reporting-events">阿里云 - 设备上报事件</a> | 12 | * @see <a href="http://help.aliyun.com/zh/marketplace/device-reporting-events">阿里云 - 设备上报事件</a> |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/ota/IotDeviceOtaProgressReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.topic.ota; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 4 | +import lombok.AllArgsConstructor; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.NoArgsConstructor; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * IoT 设备 OTA 升级进度上报 Request DTO | ||
| 10 | + * <p> | ||
| 11 | + * 用于 {@link IotDeviceMessageMethodEnum#OTA_PROGRESS} 上行消息的 params 参数 | ||
| 12 | + * | ||
| 13 | + * @author 芋道源码 | ||
| 14 | + * @see <a href="https://help.aliyun.com/zh/iot/user-guide/perform-ota-updates">阿里云 - OTA 升级</a> | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +@NoArgsConstructor | ||
| 18 | +@AllArgsConstructor | ||
| 19 | +public class IotDeviceOtaProgressReqDTO { | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 固件版本号 | ||
| 23 | + */ | ||
| 24 | + private String version; | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 升级状态 | ||
| 28 | + */ | ||
| 29 | + private Integer status; | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 描述信息 | ||
| 33 | + */ | ||
| 34 | + private String description; | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * 升级进度(0-100) | ||
| 38 | + */ | ||
| 39 | + private Integer progress; | ||
| 40 | + | ||
| 41 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/ota/IotDeviceOtaUpgradeReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.topic.ota; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 4 | +import lombok.AllArgsConstructor; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.NoArgsConstructor; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * IoT 设备 OTA 固件升级推送 Request DTO | ||
| 10 | + * <p> | ||
| 11 | + * 用于 {@link IotDeviceMessageMethodEnum#OTA_UPGRADE} 下行消息的 params 参数 | ||
| 12 | + * | ||
| 13 | + * @author 芋道源码 | ||
| 14 | + * @see <a href="https://help.aliyun.com/zh/iot/user-guide/perform-ota-updates">阿里云 - OTA 升级</a> | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +@NoArgsConstructor | ||
| 18 | +@AllArgsConstructor | ||
| 19 | +public class IotDeviceOtaUpgradeReqDTO { | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 固件版本号 | ||
| 23 | + */ | ||
| 24 | + private String version; | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 固件文件下载地址 | ||
| 28 | + */ | ||
| 29 | + private String fileUrl; | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 固件文件大小(字节) | ||
| 33 | + */ | ||
| 34 | + private Long fileSize; | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * 固件文件摘要算法 | ||
| 38 | + */ | ||
| 39 | + private String fileDigestAlgorithm; | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 固件文件摘要值 | ||
| 43 | + */ | ||
| 44 | + private String fileDigestValue; | ||
| 45 | + | ||
| 46 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/property/IotDevicePropertyPackPostReqDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.property; | 1 | package cn.iocoder.yudao.module.iot.core.topic.property; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 3 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; | 4 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 4 | import lombok.Data; | 5 | import lombok.Data; |
| 5 | 6 | ||
| @@ -9,7 +10,7 @@ import java.util.Map; | @@ -9,7 +10,7 @@ import java.util.Map; | ||
| 9 | /** | 10 | /** |
| 10 | * IoT 设备属性批量上报 Request DTO | 11 | * IoT 设备属性批量上报 Request DTO |
| 11 | * <p> | 12 | * <p> |
| 12 | - * 用于 thing.event.property.pack.post 消息的 params 参数 | 13 | + * 用于 {@link IotDeviceMessageMethodEnum#PROPERTY_PACK_POST} 消息的 params 参数 |
| 13 | * | 14 | * |
| 14 | * @author 芋道源码 | 15 | * @author 芋道源码 |
| 15 | * @see <a href="http://help.aliyun.com/zh/marketplace/gateway-reports-data-in-batches">阿里云 - 网关批量上报数据</a> | 16 | * @see <a href="http://help.aliyun.com/zh/marketplace/gateway-reports-data-in-batches">阿里云 - 网关批量上报数据</a> |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/property/IotDevicePropertyPostReqDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.property; | 1 | package cn.iocoder.yudao.module.iot.core.topic.property; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 4 | + | ||
| 3 | import java.util.HashMap; | 5 | import java.util.HashMap; |
| 4 | import java.util.Map; | 6 | import java.util.Map; |
| 5 | 7 | ||
| 6 | /** | 8 | /** |
| 7 | * IoT 设备属性上报 Request DTO | 9 | * IoT 设备属性上报 Request DTO |
| 8 | * <p> | 10 | * <p> |
| 9 | - * 用于 thing.property.post 消息的 params 参数 | 11 | + * 用于 {@link IotDeviceMessageMethodEnum#PROPERTY_POST} 消息的 params 参数 |
| 10 | * <p> | 12 | * <p> |
| 11 | * 本质是一个 Map,key 为属性标识符,value 为属性值 | 13 | * 本质是一个 Map,key 为属性标识符,value 为属性值 |
| 12 | * | 14 | * |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/property/IotDevicePropertySetReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.topic.property; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 4 | + | ||
| 5 | +import java.util.HashMap; | ||
| 6 | +import java.util.Map; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * IoT 设备属性设置 Request DTO | ||
| 10 | + * <p> | ||
| 11 | + * 用于 {@link IotDeviceMessageMethodEnum#PROPERTY_SET} 下行消息的 params 参数 | ||
| 12 | + * <p> | ||
| 13 | + * 本质是一个 Map,key 为属性标识符,value 为属性值 | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +public class IotDevicePropertySetReqDTO extends HashMap<String, Object> { | ||
| 18 | + | ||
| 19 | + public IotDevicePropertySetReqDTO() { | ||
| 20 | + super(); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + public IotDevicePropertySetReqDTO(Map<String, Object> properties) { | ||
| 24 | + super(properties); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 创建属性设置 DTO | ||
| 29 | + * | ||
| 30 | + * @param properties 属性数据 | ||
| 31 | + * @return DTO 对象 | ||
| 32 | + */ | ||
| 33 | + public static IotDevicePropertySetReqDTO of(Map<String, Object> properties) { | ||
| 34 | + return new IotDevicePropertySetReqDTO(properties); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/service/IotDeviceServiceInvokeReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.topic.service; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 4 | +import lombok.AllArgsConstructor; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.NoArgsConstructor; | ||
| 7 | + | ||
| 8 | +import java.util.Map; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * IoT 设备服务调用 Request DTO | ||
| 12 | + * <p> | ||
| 13 | + * 用于 {@link IotDeviceMessageMethodEnum#SERVICE_INVOKE} 下行消息的 params 参数 | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +@Data | ||
| 18 | +@NoArgsConstructor | ||
| 19 | +@AllArgsConstructor | ||
| 20 | +public class IotDeviceServiceInvokeReqDTO { | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 服务标识符 | ||
| 24 | + */ | ||
| 25 | + private String identifier; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 服务输入参数 | ||
| 29 | + */ | ||
| 30 | + private Map<String, Object> inputParams; | ||
| 31 | + | ||
| 32 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/state/IotDeviceStateUpdateReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.topic.state; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 4 | +import lombok.AllArgsConstructor; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.NoArgsConstructor; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * IoT 设备状态更新 Request DTO | ||
| 10 | + * <p> | ||
| 11 | + * 用于 {@link IotDeviceMessageMethodEnum#STATE_UPDATE} 消息的 params 参数 | ||
| 12 | + * | ||
| 13 | + * @author 芋道源码 | ||
| 14 | + */ | ||
| 15 | +@Data | ||
| 16 | +@NoArgsConstructor | ||
| 17 | +@AllArgsConstructor | ||
| 18 | +public class IotDeviceStateUpdateReqDTO { | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 设备状态 | ||
| 22 | + */ | ||
| 23 | + private Integer state; | ||
| 24 | + | ||
| 25 | +} |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoAddReqDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; | 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; | 3 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; |
| 4 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 4 | import jakarta.validation.constraints.NotEmpty; | 5 | import jakarta.validation.constraints.NotEmpty; |
| 5 | import lombok.Data; | 6 | import lombok.Data; |
| 6 | 7 | ||
| @@ -9,7 +10,7 @@ import java.util.List; | @@ -9,7 +10,7 @@ import java.util.List; | ||
| 9 | /** | 10 | /** |
| 10 | * IoT 设备拓扑添加 Request DTO | 11 | * IoT 设备拓扑添加 Request DTO |
| 11 | * <p> | 12 | * <p> |
| 12 | - * 用于 thing.topo.add 消息的 params 参数 | 13 | + * 用于 {@link IotDeviceMessageMethodEnum#TOPO_ADD} 消息的 params 参数 |
| 13 | * | 14 | * |
| 14 | * @author 芋道源码 | 15 | * @author 芋道源码 |
| 15 | * @see <a href="http://help.aliyun.com/zh/marketplace/add-topological-relationship">阿里云 - 添加拓扑关系</a> | 16 | * @see <a href="http://help.aliyun.com/zh/marketplace/add-topological-relationship">阿里云 - 添加拓扑关系</a> |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoChangeReqDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; | 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 3 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; | 4 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 4 | import lombok.AllArgsConstructor; | 5 | import lombok.AllArgsConstructor; |
| 5 | import lombok.Data; | 6 | import lombok.Data; |
| @@ -10,7 +11,7 @@ import java.util.List; | @@ -10,7 +11,7 @@ import java.util.List; | ||
| 10 | /** | 11 | /** |
| 11 | * IoT 设备拓扑关系变更通知 Request DTO | 12 | * IoT 设备拓扑关系变更通知 Request DTO |
| 12 | * <p> | 13 | * <p> |
| 13 | - * 用于 thing.topo.change 下行消息的 params 参数 | 14 | + * 用于 {@link IotDeviceMessageMethodEnum#TOPO_CHANGE} 下行消息的 params 参数 |
| 14 | * | 15 | * |
| 15 | * @author 芋道源码 | 16 | * @author 芋道源码 |
| 16 | * @see <a href="https://help.aliyun.com/zh/marketplace/notify-gateway-topology-changes">阿里云 - 通知网关拓扑关系变化</a> | 17 | * @see <a href="https://help.aliyun.com/zh/marketplace/notify-gateway-topology-changes">阿里云 - 通知网关拓扑关系变化</a> |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoDeleteReqDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; | 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 3 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; | 4 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 4 | import jakarta.validation.Valid; | 5 | import jakarta.validation.Valid; |
| 5 | import jakarta.validation.constraints.NotEmpty; | 6 | import jakarta.validation.constraints.NotEmpty; |
| @@ -10,7 +11,7 @@ import java.util.List; | @@ -10,7 +11,7 @@ import java.util.List; | ||
| 10 | /** | 11 | /** |
| 11 | * IoT 设备拓扑删除 Request DTO | 12 | * IoT 设备拓扑删除 Request DTO |
| 12 | * <p> | 13 | * <p> |
| 13 | - * 用于 thing.topo.delete 消息的 params 参数 | 14 | + * 用于 {@link IotDeviceMessageMethodEnum#TOPO_DELETE} 消息的 params 参数 |
| 14 | * | 15 | * |
| 15 | * @author 芋道源码 | 16 | * @author 芋道源码 |
| 16 | * @see <a href="https://help.aliyun.com/zh/marketplace/delete-a-topological-relationship">阿里云 - 删除拓扑关系</a> | 17 | * @see <a href="https://help.aliyun.com/zh/marketplace/delete-a-topological-relationship">阿里云 - 删除拓扑关系</a> |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoGetReqDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; | 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 3 | import lombok.Data; | 4 | import lombok.Data; |
| 4 | 5 | ||
| 5 | /** | 6 | /** |
| 6 | * IoT 设备拓扑关系获取 Request DTO | 7 | * IoT 设备拓扑关系获取 Request DTO |
| 7 | * <p> | 8 | * <p> |
| 8 | - * 用于 thing.topo.get 请求的 params 参数(目前为空,预留扩展) | 9 | + * 用于 {@link IotDeviceMessageMethodEnum#TOPO_GET} 请求的 params 参数(目前为空,预留扩展) |
| 9 | * | 10 | * |
| 10 | * @author 芋道源码 | 11 | * @author 芋道源码 |
| 11 | * @see <a href="https://help.aliyun.com/zh/marketplace/obtain-topological-relationship">阿里云 - 获取拓扑关系</a> | 12 | * @see <a href="https://help.aliyun.com/zh/marketplace/obtain-topological-relationship">阿里云 - 获取拓扑关系</a> |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/topo/IotDeviceTopoGetRespDTO.java
| 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; | 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 3 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; | 4 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 4 | import lombok.Data; | 5 | import lombok.Data; |
| 5 | 6 | ||
| @@ -8,7 +9,7 @@ import java.util.List; | @@ -8,7 +9,7 @@ import java.util.List; | ||
| 8 | /** | 9 | /** |
| 9 | * IoT 设备拓扑关系获取 Response DTO | 10 | * IoT 设备拓扑关系获取 Response DTO |
| 10 | * <p> | 11 | * <p> |
| 11 | - * 用于 thing.topo.get 响应 | 12 | + * 用于 {@link IotDeviceMessageMethodEnum#TOPO_GET} 响应 |
| 12 | * | 13 | * |
| 13 | * @author 芋道源码 | 14 | * @author 芋道源码 |
| 14 | * @see <a href="https://help.aliyun.com/zh/marketplace/obtain-topological-relationship">阿里云 - 获取拓扑关系</a> | 15 | * @see <a href="https://help.aliyun.com/zh/marketplace/obtain-topological-relationship">阿里云 - 获取拓扑关系</a> |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceAuthUtils.java
| @@ -25,6 +25,14 @@ public class IotDeviceAuthUtils { | @@ -25,6 +25,14 @@ public class IotDeviceAuthUtils { | ||
| 25 | return String.format("%s.%s", productKey, deviceName); | 25 | return String.format("%s.%s", productKey, deviceName); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | + public static String buildClientIdFromUsername(String username) { | ||
| 29 | + IotDeviceIdentity identity = parseUsername(username); | ||
| 30 | + if (identity == null) { | ||
| 31 | + return null; | ||
| 32 | + } | ||
| 33 | + return buildClientId(identity.getProductKey(), identity.getDeviceName()); | ||
| 34 | + } | ||
| 35 | + | ||
| 28 | public static String buildUsername(String productKey, String deviceName) { | 36 | public static String buildUsername(String productKey, String deviceName) { |
| 29 | return String.format("%s&%s", deviceName, productKey); | 37 | return String.format("%s&%s", deviceName, productKey); |
| 30 | } | 38 | } |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotProductAuthUtils.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.core.util; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 4 | +import cn.hutool.crypto.digest.DigestUtil; | ||
| 5 | +import cn.hutool.crypto.digest.HmacAlgorithm; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * IoT 产品【动态注册】认证工具类 | ||
| 9 | + * <p> | ||
| 10 | + * 用于一型一密场景,使用 productSecret 生成签名 | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | +public class IotProductAuthUtils { | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 生成设备动态注册签名 | ||
| 18 | + * | ||
| 19 | + * @param productKey 产品标识 | ||
| 20 | + * @param deviceName 设备名称 | ||
| 21 | + * @param productSecret 产品密钥 | ||
| 22 | + * @return 签名 | ||
| 23 | + */ | ||
| 24 | + public static String buildSign(String productKey, String deviceName, String productSecret) { | ||
| 25 | + String content = buildContent(productKey, deviceName); | ||
| 26 | + return DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.utf8Bytes(productSecret)) | ||
| 27 | + .digestHex(content); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 验证设备动态注册签名 | ||
| 32 | + * | ||
| 33 | + * @param productKey 产品标识 | ||
| 34 | + * @param deviceName 设备名称 | ||
| 35 | + * @param productSecret 产品密钥 | ||
| 36 | + * @param sign 待验证的签名 | ||
| 37 | + * @return 是否验证通过 | ||
| 38 | + */ | ||
| 39 | + public static boolean verifySign(String productKey, String deviceName, String productSecret, String sign) { | ||
| 40 | + String expectedSign = buildSign(productKey, deviceName, productSecret); | ||
| 41 | + return expectedSign.equals(sign); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 构建签名内容 | ||
| 46 | + * | ||
| 47 | + * @param productKey 产品标识 | ||
| 48 | + * @param deviceName 设备名称 | ||
| 49 | + * @return 签名内容 | ||
| 50 | + */ | ||
| 51 | + private static String buildContent(String productKey, String deviceName) { | ||
| 52 | + return "deviceName" + deviceName + "productKey" + productKey; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | +} |
yudao-module-iot/yudao-module-iot-gateway/pom.xml
| @@ -33,7 +33,7 @@ | @@ -33,7 +33,7 @@ | ||
| 33 | <groupId>org.apache.rocketmq</groupId> | 33 | <groupId>org.apache.rocketmq</groupId> |
| 34 | <artifactId>rocketmq-spring-boot-starter</artifactId> | 34 | <artifactId>rocketmq-spring-boot-starter</artifactId> |
| 35 | <!-- TODO @芋艿:消息队列,后续可能去掉,默认不使用 rocketmq --> | 35 | <!-- TODO @芋艿:消息队列,后续可能去掉,默认不使用 rocketmq --> |
| 36 | -<!-- <optional>true</optional> --> | 36 | + <optional>true</optional> |
| 37 | </dependency> | 37 | </dependency> |
| 38 | 38 | ||
| 39 | <!-- 工具类相关 --> | 39 | <!-- 工具类相关 --> |
| @@ -48,6 +48,13 @@ | @@ -48,6 +48,13 @@ | ||
| 48 | <artifactId>vertx-mqtt</artifactId> | 48 | <artifactId>vertx-mqtt</artifactId> |
| 49 | </dependency> | 49 | </dependency> |
| 50 | 50 | ||
| 51 | + <!-- Modbus 相关 --> | ||
| 52 | + <dependency> | ||
| 53 | + <groupId>com.ghgande</groupId> | ||
| 54 | + <artifactId>j2mod</artifactId> | ||
| 55 | + <version>3.2.1</version> | ||
| 56 | + </dependency> | ||
| 57 | + | ||
| 51 | <!-- CoAP 相关 - Eclipse Californium --> | 58 | <!-- CoAP 相关 - Eclipse Californium --> |
| 52 | <dependency> | 59 | <dependency> |
| 53 | <groupId>org.eclipse.californium</groupId> | 60 | <groupId>org.eclipse.californium</groupId> |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/alink/IotAlinkDeviceMessageCodec.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.gateway.codec.alink; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.lang.Assert; | ||
| 4 | -import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 5 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 6 | -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 7 | -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec; | ||
| 8 | -import lombok.AllArgsConstructor; | ||
| 9 | -import lombok.Data; | ||
| 10 | -import lombok.NoArgsConstructor; | ||
| 11 | -import org.springframework.stereotype.Component; | ||
| 12 | - | ||
| 13 | -/** | ||
| 14 | - * 阿里云 Alink {@link IotDeviceMessage} 的编解码器 | ||
| 15 | - * | ||
| 16 | - * @author 芋道源码 | ||
| 17 | - */ | ||
| 18 | -@Component | ||
| 19 | -public class IotAlinkDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 20 | - | ||
| 21 | - public static final String TYPE = "Alink"; | ||
| 22 | - | ||
| 23 | - @Data | ||
| 24 | - @NoArgsConstructor | ||
| 25 | - @AllArgsConstructor | ||
| 26 | - private static class AlinkMessage { | ||
| 27 | - | ||
| 28 | - public static final String VERSION_1 = "1.0"; | ||
| 29 | - | ||
| 30 | - /** | ||
| 31 | - * 消息 ID,且每个消息 ID 在当前设备具有唯一性 | ||
| 32 | - */ | ||
| 33 | - private String id; | ||
| 34 | - | ||
| 35 | - /** | ||
| 36 | - * 版本号 | ||
| 37 | - */ | ||
| 38 | - private String version; | ||
| 39 | - | ||
| 40 | - /** | ||
| 41 | - * 请求方法 | ||
| 42 | - */ | ||
| 43 | - private String method; | ||
| 44 | - | ||
| 45 | - /** | ||
| 46 | - * 请求参数 | ||
| 47 | - */ | ||
| 48 | - private Object params; | ||
| 49 | - | ||
| 50 | - /** | ||
| 51 | - * 响应结果 | ||
| 52 | - */ | ||
| 53 | - private Object data; | ||
| 54 | - /** | ||
| 55 | - * 响应错误码 | ||
| 56 | - */ | ||
| 57 | - private Integer code; | ||
| 58 | - /** | ||
| 59 | - * 响应提示 | ||
| 60 | - * | ||
| 61 | - * 特殊:这里阿里云是 message,为了保持和项目的 {@link CommonResult#getMsg()} 一致。 | ||
| 62 | - */ | ||
| 63 | - private String msg; | ||
| 64 | - | ||
| 65 | - } | ||
| 66 | - | ||
| 67 | - @Override | ||
| 68 | - public String type() { | ||
| 69 | - return TYPE; | ||
| 70 | - } | ||
| 71 | - | ||
| 72 | - @Override | ||
| 73 | - public byte[] encode(IotDeviceMessage message) { | ||
| 74 | - AlinkMessage alinkMessage = new AlinkMessage(message.getRequestId(), AlinkMessage.VERSION_1, | ||
| 75 | - message.getMethod(), message.getParams(), message.getData(), message.getCode(), message.getMsg()); | ||
| 76 | - return JsonUtils.toJsonByte(alinkMessage); | ||
| 77 | - } | ||
| 78 | - | ||
| 79 | - @Override | ||
| 80 | - @SuppressWarnings("DataFlowIssue") | ||
| 81 | - public IotDeviceMessage decode(byte[] bytes) { | ||
| 82 | - AlinkMessage alinkMessage = JsonUtils.parseObject(bytes, AlinkMessage.class); | ||
| 83 | - Assert.notNull(alinkMessage, "消息不能为空"); | ||
| 84 | - Assert.equals(alinkMessage.getVersion(), AlinkMessage.VERSION_1, "消息版本号必须是 1.0"); | ||
| 85 | - return IotDeviceMessage.of(alinkMessage.getId(), alinkMessage.getMethod(), alinkMessage.getParams(), | ||
| 86 | - alinkMessage.getData(), alinkMessage.getCode(), alinkMessage.getMsg()); | ||
| 87 | - } | ||
| 88 | - | ||
| 89 | -} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/package-info.java deleted
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/simple/package-info.java deleted
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/IotTcpJsonDeviceMessageCodec.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.gateway.codec.tcp; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.lang.Assert; | ||
| 4 | -import cn.hutool.core.util.StrUtil; | ||
| 5 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 6 | -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 7 | -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec; | ||
| 8 | -import lombok.AllArgsConstructor; | ||
| 9 | -import lombok.Data; | ||
| 10 | -import lombok.NoArgsConstructor; | ||
| 11 | -import org.springframework.stereotype.Component; | ||
| 12 | - | ||
| 13 | -/** | ||
| 14 | - * TCP/UDP JSON 格式 {@link IotDeviceMessage} 编解码器 | ||
| 15 | - * | ||
| 16 | - * 采用纯 JSON 格式传输,格式如下: | ||
| 17 | - * { | ||
| 18 | - * "id": "消息 ID", | ||
| 19 | - * "method": "消息方法", | ||
| 20 | - * "params": {...}, // 请求参数 | ||
| 21 | - * "data": {...}, // 响应结果 | ||
| 22 | - * "code": 200, // 响应错误码 | ||
| 23 | - * "msg": "success", // 响应提示 | ||
| 24 | - * "timestamp": 时间戳 | ||
| 25 | - * } | ||
| 26 | - * | ||
| 27 | - * @author 芋道源码 | ||
| 28 | - */ | ||
| 29 | -@Component | ||
| 30 | -public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec { | ||
| 31 | - | ||
| 32 | - public static final String TYPE = "TCP_JSON"; | ||
| 33 | - | ||
| 34 | - @Data | ||
| 35 | - @NoArgsConstructor | ||
| 36 | - @AllArgsConstructor | ||
| 37 | - private static class TcpJsonMessage { | ||
| 38 | - | ||
| 39 | - /** | ||
| 40 | - * 消息 ID,且每个消息 ID 在当前设备具有唯一性 | ||
| 41 | - */ | ||
| 42 | - private String id; | ||
| 43 | - | ||
| 44 | - /** | ||
| 45 | - * 请求方法 | ||
| 46 | - */ | ||
| 47 | - private String method; | ||
| 48 | - | ||
| 49 | - /** | ||
| 50 | - * 请求参数 | ||
| 51 | - */ | ||
| 52 | - private Object params; | ||
| 53 | - | ||
| 54 | - /** | ||
| 55 | - * 响应结果 | ||
| 56 | - */ | ||
| 57 | - private Object data; | ||
| 58 | - | ||
| 59 | - /** | ||
| 60 | - * 响应错误码 | ||
| 61 | - */ | ||
| 62 | - private Integer code; | ||
| 63 | - | ||
| 64 | - /** | ||
| 65 | - * 响应提示 | ||
| 66 | - */ | ||
| 67 | - private String msg; | ||
| 68 | - | ||
| 69 | - /** | ||
| 70 | - * 时间戳 | ||
| 71 | - */ | ||
| 72 | - private Long timestamp; | ||
| 73 | - | ||
| 74 | - } | ||
| 75 | - | ||
| 76 | - @Override | ||
| 77 | - public String type() { | ||
| 78 | - return TYPE; | ||
| 79 | - } | ||
| 80 | - | ||
| 81 | - @Override | ||
| 82 | - public byte[] encode(IotDeviceMessage message) { | ||
| 83 | - TcpJsonMessage tcpJsonMessage = new TcpJsonMessage( | ||
| 84 | - message.getRequestId(), | ||
| 85 | - message.getMethod(), | ||
| 86 | - message.getParams(), | ||
| 87 | - message.getData(), | ||
| 88 | - message.getCode(), | ||
| 89 | - message.getMsg(), | ||
| 90 | - System.currentTimeMillis()); | ||
| 91 | - return JsonUtils.toJsonByte(tcpJsonMessage); | ||
| 92 | - } | ||
| 93 | - | ||
| 94 | - @Override | ||
| 95 | - @SuppressWarnings("DataFlowIssue") | ||
| 96 | - public IotDeviceMessage decode(byte[] bytes) { | ||
| 97 | - String jsonStr = StrUtil.utf8Str(bytes).trim(); | ||
| 98 | - TcpJsonMessage tcpJsonMessage = JsonUtils.parseObject(jsonStr, TcpJsonMessage.class); | ||
| 99 | - Assert.notNull(tcpJsonMessage, "消息不能为空"); | ||
| 100 | - Assert.notBlank(tcpJsonMessage.getMethod(), "消息方法不能为空"); | ||
| 101 | - return IotDeviceMessage.of( | ||
| 102 | - tcpJsonMessage.getId(), | ||
| 103 | - tcpJsonMessage.getMethod(), | ||
| 104 | - tcpJsonMessage.getParams(), | ||
| 105 | - tcpJsonMessage.getData(), | ||
| 106 | - tcpJsonMessage.getCode(), | ||
| 107 | - tcpJsonMessage.getMsg()); | ||
| 108 | - } | ||
| 109 | - | ||
| 110 | -} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.config; | 1 | package cn.iocoder.yudao.module.iot.gateway.config; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | ||
| 4 | -import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapDownstreamSubscriber; | ||
| 5 | -import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapUpstreamProtocol; | ||
| 6 | -import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxAuthEventProtocol; | ||
| 7 | -import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxDownstreamSubscriber; | ||
| 8 | -import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol; | ||
| 9 | -import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber; | ||
| 10 | -import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; | ||
| 11 | -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber; | ||
| 12 | -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; | ||
| 13 | -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager; | ||
| 14 | -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstreamHandler; | ||
| 15 | -import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpDownstreamSubscriber; | ||
| 16 | -import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpUpstreamProtocol; | ||
| 17 | -import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager; | ||
| 18 | -import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpDownstreamSubscriber; | ||
| 19 | -import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpUpstreamProtocol; | ||
| 20 | -import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager; | ||
| 21 | -import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketDownstreamSubscriber; | ||
| 22 | -import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketUpstreamProtocol; | ||
| 23 | -import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.manager.IotWebSocketConnectionManager; | ||
| 24 | -import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; | ||
| 25 | -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | ||
| 26 | -import io.vertx.core.Vertx; | ||
| 27 | -import lombok.extern.slf4j.Slf4j; | ||
| 28 | -import org.springframework.beans.factory.annotation.Qualifier; | ||
| 29 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 3 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocolManager; |
| 4 | +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager; | ||
| 30 | import org.springframework.boot.context.properties.EnableConfigurationProperties; | 5 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 31 | import org.springframework.context.annotation.Bean; | 6 | import org.springframework.context.annotation.Bean; |
| 32 | import org.springframework.context.annotation.Configuration; | 7 | import org.springframework.context.annotation.Configuration; |
| 33 | 8 | ||
| 9 | +/** | ||
| 10 | + * IoT 网关配置类 | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 34 | @Configuration | 14 | @Configuration |
| 35 | @EnableConfigurationProperties(IotGatewayProperties.class) | 15 | @EnableConfigurationProperties(IotGatewayProperties.class) |
| 36 | -@Slf4j | ||
| 37 | public class IotGatewayConfiguration { | 16 | public class IotGatewayConfiguration { |
| 38 | 17 | ||
| 39 | - /** | ||
| 40 | - * IoT 网关 HTTP 协议配置类 | ||
| 41 | - */ | ||
| 42 | - @Configuration | ||
| 43 | - @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.http", name = "enabled", havingValue = "true") | ||
| 44 | - @Slf4j | ||
| 45 | - public static class HttpProtocolConfiguration { | ||
| 46 | - | ||
| 47 | - @Bean(name = "httpVertx", destroyMethod = "close") | ||
| 48 | - public Vertx httpVertx() { | ||
| 49 | - return Vertx.vertx(); | ||
| 50 | - } | ||
| 51 | - | ||
| 52 | - @Bean | ||
| 53 | - public IotHttpUpstreamProtocol iotHttpUpstreamProtocol(IotGatewayProperties gatewayProperties, | ||
| 54 | - @Qualifier("httpVertx") Vertx httpVertx) { | ||
| 55 | - return new IotHttpUpstreamProtocol(gatewayProperties.getProtocol().getHttp(), httpVertx); | ||
| 56 | - } | ||
| 57 | - | ||
| 58 | - @Bean | ||
| 59 | - public IotHttpDownstreamSubscriber iotHttpDownstreamSubscriber(IotHttpUpstreamProtocol httpUpstreamProtocol, | ||
| 60 | - IotMessageBus messageBus) { | ||
| 61 | - return new IotHttpDownstreamSubscriber(httpUpstreamProtocol, messageBus); | ||
| 62 | - } | ||
| 63 | - } | ||
| 64 | - | ||
| 65 | - /** | ||
| 66 | - * IoT 网关 EMQX 协议配置类 | ||
| 67 | - */ | ||
| 68 | - @Configuration | ||
| 69 | - @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.emqx", name = "enabled", havingValue = "true") | ||
| 70 | - @Slf4j | ||
| 71 | - public static class EmqxProtocolConfiguration { | ||
| 72 | - | ||
| 73 | - @Bean(name = "emqxVertx", destroyMethod = "close") | ||
| 74 | - public Vertx emqxVertx() { | ||
| 75 | - return Vertx.vertx(); | ||
| 76 | - } | ||
| 77 | - | ||
| 78 | - @Bean | ||
| 79 | - public IotEmqxAuthEventProtocol iotEmqxAuthEventProtocol(IotGatewayProperties gatewayProperties, | ||
| 80 | - @Qualifier("emqxVertx") Vertx emqxVertx) { | ||
| 81 | - return new IotEmqxAuthEventProtocol(gatewayProperties.getProtocol().getEmqx(), emqxVertx); | ||
| 82 | - } | ||
| 83 | - | ||
| 84 | - @Bean | ||
| 85 | - public IotEmqxUpstreamProtocol iotEmqxUpstreamProtocol(IotGatewayProperties gatewayProperties, | ||
| 86 | - @Qualifier("emqxVertx") Vertx emqxVertx) { | ||
| 87 | - return new IotEmqxUpstreamProtocol(gatewayProperties.getProtocol().getEmqx(), emqxVertx); | ||
| 88 | - } | ||
| 89 | - | ||
| 90 | - @Bean | ||
| 91 | - public IotEmqxDownstreamSubscriber iotEmqxDownstreamSubscriber(IotEmqxUpstreamProtocol mqttUpstreamProtocol, | ||
| 92 | - IotMessageBus messageBus) { | ||
| 93 | - return new IotEmqxDownstreamSubscriber(mqttUpstreamProtocol, messageBus); | ||
| 94 | - } | 18 | + @Bean |
| 19 | + public IotMessageSerializerManager iotMessageSerializerManager() { | ||
| 20 | + return new IotMessageSerializerManager(); | ||
| 95 | } | 21 | } |
| 96 | 22 | ||
| 97 | - /** | ||
| 98 | - * IoT 网关 TCP 协议配置类 | ||
| 99 | - */ | ||
| 100 | - @Configuration | ||
| 101 | - @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.tcp", name = "enabled", havingValue = "true") | ||
| 102 | - @Slf4j | ||
| 103 | - public static class TcpProtocolConfiguration { | ||
| 104 | - | ||
| 105 | - @Bean(name = "tcpVertx", destroyMethod = "close") | ||
| 106 | - public Vertx tcpVertx() { | ||
| 107 | - return Vertx.vertx(); | ||
| 108 | - } | ||
| 109 | - | ||
| 110 | - @Bean | ||
| 111 | - public IotTcpUpstreamProtocol iotTcpUpstreamProtocol(IotGatewayProperties gatewayProperties, | ||
| 112 | - IotDeviceService deviceService, | ||
| 113 | - IotDeviceMessageService messageService, | ||
| 114 | - IotTcpConnectionManager connectionManager, | ||
| 115 | - @Qualifier("tcpVertx") Vertx tcpVertx) { | ||
| 116 | - return new IotTcpUpstreamProtocol(gatewayProperties.getProtocol().getTcp(), | ||
| 117 | - deviceService, messageService, connectionManager, tcpVertx); | ||
| 118 | - } | ||
| 119 | - | ||
| 120 | - @Bean | ||
| 121 | - public IotTcpDownstreamSubscriber iotTcpDownstreamSubscriber(IotTcpUpstreamProtocol protocolHandler, | ||
| 122 | - IotDeviceMessageService messageService, | ||
| 123 | - IotTcpConnectionManager connectionManager, | ||
| 124 | - IotMessageBus messageBus) { | ||
| 125 | - return new IotTcpDownstreamSubscriber(protocolHandler, messageService, connectionManager, messageBus); | ||
| 126 | - } | ||
| 127 | - | ||
| 128 | - } | ||
| 129 | - | ||
| 130 | - /** | ||
| 131 | - * IoT 网关 MQTT 协议配置类 | ||
| 132 | - */ | ||
| 133 | - @Configuration | ||
| 134 | - @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.mqtt", name = "enabled", havingValue = "true") | ||
| 135 | - @Slf4j | ||
| 136 | - public static class MqttProtocolConfiguration { | ||
| 137 | - | ||
| 138 | - @Bean(name = "mqttVertx", destroyMethod = "close") | ||
| 139 | - public Vertx mqttVertx() { | ||
| 140 | - return Vertx.vertx(); | ||
| 141 | - } | ||
| 142 | - | ||
| 143 | - @Bean | ||
| 144 | - public IotMqttUpstreamProtocol iotMqttUpstreamProtocol(IotGatewayProperties gatewayProperties, | ||
| 145 | - IotDeviceMessageService messageService, | ||
| 146 | - IotMqttConnectionManager connectionManager, | ||
| 147 | - @Qualifier("mqttVertx") Vertx mqttVertx) { | ||
| 148 | - return new IotMqttUpstreamProtocol(gatewayProperties.getProtocol().getMqtt(), messageService, | ||
| 149 | - connectionManager, mqttVertx); | ||
| 150 | - } | ||
| 151 | - | ||
| 152 | - @Bean | ||
| 153 | - public IotMqttDownstreamHandler iotMqttDownstreamHandler(IotDeviceMessageService messageService, | ||
| 154 | - IotMqttConnectionManager connectionManager) { | ||
| 155 | - return new IotMqttDownstreamHandler(messageService, connectionManager); | ||
| 156 | - } | ||
| 157 | - | ||
| 158 | - @Bean | ||
| 159 | - public IotMqttDownstreamSubscriber iotMqttDownstreamSubscriber(IotMqttUpstreamProtocol mqttUpstreamProtocol, | ||
| 160 | - IotMqttDownstreamHandler downstreamHandler, | ||
| 161 | - IotMessageBus messageBus) { | ||
| 162 | - return new IotMqttDownstreamSubscriber(mqttUpstreamProtocol, downstreamHandler, messageBus); | ||
| 163 | - } | ||
| 164 | - | ||
| 165 | - } | ||
| 166 | - | ||
| 167 | - /** | ||
| 168 | - * IoT 网关 UDP 协议配置类 | ||
| 169 | - */ | ||
| 170 | - @Configuration | ||
| 171 | - @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.udp", name = "enabled", havingValue = "true") | ||
| 172 | - @Slf4j | ||
| 173 | - public static class UdpProtocolConfiguration { | ||
| 174 | - | ||
| 175 | - @Bean(name = "udpVertx", destroyMethod = "close") | ||
| 176 | - public Vertx udpVertx() { | ||
| 177 | - return Vertx.vertx(); | ||
| 178 | - } | ||
| 179 | - | ||
| 180 | - @Bean | ||
| 181 | - public IotUdpUpstreamProtocol iotUdpUpstreamProtocol(IotGatewayProperties gatewayProperties, | ||
| 182 | - IotDeviceService deviceService, | ||
| 183 | - IotDeviceMessageService messageService, | ||
| 184 | - IotUdpSessionManager sessionManager, | ||
| 185 | - @Qualifier("udpVertx") Vertx udpVertx) { | ||
| 186 | - return new IotUdpUpstreamProtocol(gatewayProperties.getProtocol().getUdp(), | ||
| 187 | - deviceService, messageService, sessionManager, udpVertx); | ||
| 188 | - } | ||
| 189 | - | ||
| 190 | - @Bean | ||
| 191 | - public IotUdpDownstreamSubscriber iotUdpDownstreamSubscriber(IotUdpUpstreamProtocol protocolHandler, | ||
| 192 | - IotDeviceMessageService messageService, | ||
| 193 | - IotUdpSessionManager sessionManager, | ||
| 194 | - IotMessageBus messageBus) { | ||
| 195 | - return new IotUdpDownstreamSubscriber(protocolHandler, messageService, sessionManager, messageBus); | ||
| 196 | - } | ||
| 197 | - | ||
| 198 | - } | ||
| 199 | - | ||
| 200 | - /** | ||
| 201 | - * IoT 网关 CoAP 协议配置类 | ||
| 202 | - */ | ||
| 203 | - @Configuration | ||
| 204 | - @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.coap", name = "enabled", havingValue = "true") | ||
| 205 | - @Slf4j | ||
| 206 | - public static class CoapProtocolConfiguration { | ||
| 207 | - | ||
| 208 | - @Bean | ||
| 209 | - public IotCoapUpstreamProtocol iotCoapUpstreamProtocol(IotGatewayProperties gatewayProperties) { | ||
| 210 | - return new IotCoapUpstreamProtocol(gatewayProperties.getProtocol().getCoap()); | ||
| 211 | - } | ||
| 212 | - | ||
| 213 | - @Bean | ||
| 214 | - public IotCoapDownstreamSubscriber iotCoapDownstreamSubscriber(IotCoapUpstreamProtocol coapUpstreamProtocol, | ||
| 215 | - IotMessageBus messageBus) { | ||
| 216 | - return new IotCoapDownstreamSubscriber(coapUpstreamProtocol, messageBus); | ||
| 217 | - } | ||
| 218 | - | ||
| 219 | - } | ||
| 220 | - | ||
| 221 | - /** | ||
| 222 | - * IoT 网关 WebSocket 协议配置类 | ||
| 223 | - */ | ||
| 224 | - @Configuration | ||
| 225 | - @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.websocket", name = "enabled", havingValue = "true") | ||
| 226 | - @Slf4j | ||
| 227 | - public static class WebSocketProtocolConfiguration { | ||
| 228 | - | ||
| 229 | - @Bean(name = "websocketVertx", destroyMethod = "close") | ||
| 230 | - public Vertx websocketVertx() { | ||
| 231 | - return Vertx.vertx(); | ||
| 232 | - } | ||
| 233 | - | ||
| 234 | - @Bean | ||
| 235 | - public IotWebSocketUpstreamProtocol iotWebSocketUpstreamProtocol(IotGatewayProperties gatewayProperties, | ||
| 236 | - IotDeviceService deviceService, | ||
| 237 | - IotDeviceMessageService messageService, | ||
| 238 | - IotWebSocketConnectionManager connectionManager, | ||
| 239 | - @Qualifier("websocketVertx") Vertx websocketVertx) { | ||
| 240 | - return new IotWebSocketUpstreamProtocol(gatewayProperties.getProtocol().getWebsocket(), | ||
| 241 | - deviceService, messageService, connectionManager, websocketVertx); | ||
| 242 | - } | ||
| 243 | - | ||
| 244 | - @Bean | ||
| 245 | - public IotWebSocketDownstreamSubscriber iotWebSocketDownstreamSubscriber(IotWebSocketUpstreamProtocol protocolHandler, | ||
| 246 | - IotDeviceMessageService messageService, | ||
| 247 | - IotWebSocketConnectionManager connectionManager, | ||
| 248 | - IotMessageBus messageBus) { | ||
| 249 | - return new IotWebSocketDownstreamSubscriber(protocolHandler, messageService, connectionManager, messageBus); | ||
| 250 | - } | ||
| 251 | - | 23 | + @Bean |
| 24 | + public IotProtocolManager iotProtocolManager(IotGatewayProperties gatewayProperties) { | ||
| 25 | + return new IotProtocolManager(gatewayProperties); | ||
| 252 | } | 26 | } |
| 253 | 27 | ||
| 254 | } | 28 | } |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java
| 1 | package cn.iocoder.yudao.module.iot.gateway.config; | 1 | package cn.iocoder.yudao.module.iot.gateway.config; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 4 | +import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapConfig; | ||
| 5 | +import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxConfig; | ||
| 6 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpConfig; | ||
| 7 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.IotModbusTcpClientConfig; | ||
| 8 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.IotModbusTcpServerConfig; | ||
| 9 | +import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttConfig; | ||
| 10 | +import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpConfig; | ||
| 11 | +import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpConfig; | ||
| 12 | +import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketConfig; | ||
| 13 | +import jakarta.validation.Valid; | ||
| 3 | import jakarta.validation.constraints.NotEmpty; | 14 | import jakarta.validation.constraints.NotEmpty; |
| 4 | import jakarta.validation.constraints.NotNull; | 15 | import jakarta.validation.constraints.NotNull; |
| 5 | import lombok.Data; | 16 | import lombok.Data; |
| @@ -24,9 +35,9 @@ public class IotGatewayProperties { | @@ -24,9 +35,9 @@ public class IotGatewayProperties { | ||
| 24 | private TokenProperties token; | 35 | private TokenProperties token; |
| 25 | 36 | ||
| 26 | /** | 37 | /** |
| 27 | - * 协议配置 | 38 | + * 协议实例列表 |
| 28 | */ | 39 | */ |
| 29 | - private ProtocolProperties protocol; | 40 | + private List<ProtocolProperties> protocols; |
| 30 | 41 | ||
| 31 | @Data | 42 | @Data |
| 32 | public static class RpcProperties { | 43 | public static class RpcProperties { |
| @@ -65,581 +76,157 @@ public class IotGatewayProperties { | @@ -65,581 +76,157 @@ public class IotGatewayProperties { | ||
| 65 | 76 | ||
| 66 | } | 77 | } |
| 67 | 78 | ||
| 79 | + /** | ||
| 80 | + * 协议实例配置 | ||
| 81 | + */ | ||
| 68 | @Data | 82 | @Data |
| 69 | public static class ProtocolProperties { | 83 | public static class ProtocolProperties { |
| 70 | 84 | ||
| 71 | /** | 85 | /** |
| 72 | - * HTTP 组件配置 | ||
| 73 | - */ | ||
| 74 | - private HttpProperties http; | ||
| 75 | - | ||
| 76 | - /** | ||
| 77 | - * EMQX 组件配置 | ||
| 78 | - */ | ||
| 79 | - private EmqxProperties emqx; | ||
| 80 | - | ||
| 81 | - /** | ||
| 82 | - * TCP 组件配置 | ||
| 83 | - */ | ||
| 84 | - private TcpProperties tcp; | ||
| 85 | - | ||
| 86 | - /** | ||
| 87 | - * MQTT 组件配置 | ||
| 88 | - */ | ||
| 89 | - private MqttProperties mqtt; | ||
| 90 | - | ||
| 91 | - /** | ||
| 92 | - * MQTT WebSocket 组件配置 | ||
| 93 | - */ | ||
| 94 | - private MqttWsProperties mqttWs; | ||
| 95 | - | ||
| 96 | - /** | ||
| 97 | - * UDP 组件配置 | 86 | + * 协议实例 ID,如 "http-alink"、"tcp-binary" |
| 98 | */ | 87 | */ |
| 99 | - private UdpProperties udp; | ||
| 100 | - | 88 | + @NotEmpty(message = "协议实例 ID 不能为空") |
| 89 | + private String id; | ||
| 101 | /** | 90 | /** |
| 102 | - * CoAP 组件配置 | 91 | + * 是否启用 |
| 103 | */ | 92 | */ |
| 104 | - private CoapProperties coap; | ||
| 105 | - | 93 | + @NotNull(message = "是否启用不能为空") |
| 94 | + private Boolean enabled = true; | ||
| 106 | /** | 95 | /** |
| 107 | - * WebSocket 组件配置 | 96 | + * 协议类型 |
| 97 | + * | ||
| 98 | + * @see cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum | ||
| 108 | */ | 99 | */ |
| 109 | - private WebSocketProperties websocket; | ||
| 110 | - | ||
| 111 | - } | ||
| 112 | - | ||
| 113 | - @Data | ||
| 114 | - public static class HttpProperties { | ||
| 115 | - | ||
| 116 | - /** | ||
| 117 | - * 是否开启 | ||
| 118 | - */ | ||
| 119 | - @NotNull(message = "是否开启不能为空") | ||
| 120 | - private Boolean enabled; | 100 | + @NotEmpty(message = "协议类型不能为空") |
| 101 | + private String protocol; | ||
| 121 | /** | 102 | /** |
| 122 | * 服务端口 | 103 | * 服务端口 |
| 123 | - */ | ||
| 124 | - private Integer serverPort; | ||
| 125 | - | ||
| 126 | - /** | ||
| 127 | - * 是否开启 SSL | ||
| 128 | - */ | ||
| 129 | - @NotNull(message = "是否开启 SSL 不能为空") | ||
| 130 | - private Boolean sslEnabled = false; | ||
| 131 | - | ||
| 132 | - /** | ||
| 133 | - * SSL 证书路径 | ||
| 134 | - */ | ||
| 135 | - private String sslKeyPath; | ||
| 136 | - /** | ||
| 137 | - * SSL 证书路径 | ||
| 138 | - */ | ||
| 139 | - private String sslCertPath; | ||
| 140 | - | ||
| 141 | - } | ||
| 142 | - | ||
| 143 | - @Data | ||
| 144 | - public static class EmqxProperties { | ||
| 145 | - | ||
| 146 | - /** | ||
| 147 | - * 是否开启 | ||
| 148 | - */ | ||
| 149 | - @NotNull(message = "是否开启不能为空") | ||
| 150 | - private Boolean enabled; | ||
| 151 | - | ||
| 152 | - /** | ||
| 153 | - * HTTP 服务端口(默认:8090) | ||
| 154 | - */ | ||
| 155 | - private Integer httpPort = 8090; | ||
| 156 | - | ||
| 157 | - /** | ||
| 158 | - * MQTT 服务器地址 | ||
| 159 | - */ | ||
| 160 | - @NotEmpty(message = "MQTT 服务器地址不能为空") | ||
| 161 | - private String mqttHost; | ||
| 162 | - | ||
| 163 | - /** | ||
| 164 | - * MQTT 服务器端口(默认:1883) | ||
| 165 | - */ | ||
| 166 | - @NotNull(message = "MQTT 服务器端口不能为空") | ||
| 167 | - private Integer mqttPort = 1883; | ||
| 168 | - | ||
| 169 | - /** | ||
| 170 | - * MQTT 用户名 | ||
| 171 | - */ | ||
| 172 | - @NotEmpty(message = "MQTT 用户名不能为空") | ||
| 173 | - private String mqttUsername; | ||
| 174 | - | ||
| 175 | - /** | ||
| 176 | - * MQTT 密码 | ||
| 177 | - */ | ||
| 178 | - @NotEmpty(message = "MQTT 密码不能为空") | ||
| 179 | - private String mqttPassword; | ||
| 180 | - | ||
| 181 | - /** | ||
| 182 | - * MQTT 客户端的 SSL 开关 | ||
| 183 | - */ | ||
| 184 | - @NotNull(message = "MQTT 是否开启 SSL 不能为空") | ||
| 185 | - private Boolean mqttSsl = false; | ||
| 186 | - | ||
| 187 | - /** | ||
| 188 | - * MQTT 客户端 ID(如果为空,系统将自动生成) | ||
| 189 | - */ | ||
| 190 | - @NotEmpty(message = "MQTT 客户端 ID 不能为空") | ||
| 191 | - private String mqttClientId; | ||
| 192 | - | ||
| 193 | - /** | ||
| 194 | - * MQTT 订阅的主题 | ||
| 195 | - */ | ||
| 196 | - @NotEmpty(message = "MQTT 主题不能为空") | ||
| 197 | - private List<@NotEmpty(message = "MQTT 主题不能为空") String> mqttTopics; | ||
| 198 | - | ||
| 199 | - /** | ||
| 200 | - * 默认 QoS 级别 | ||
| 201 | * <p> | 104 | * <p> |
| 202 | - * 0 - 最多一次 | ||
| 203 | - * 1 - 至少一次 | ||
| 204 | - * 2 - 刚好一次 | 105 | + * 不同协议含义不同: |
| 106 | + * 1. TCP/UDP/HTTP/WebSocket/MQTT/CoAP:对应网关自身监听的服务端口 | ||
| 107 | + * 2. EMQX:对应网关提供给 EMQX 回调的 HTTP Hook 端口(/mqtt/auth、/mqtt/acl、/mqtt/event) | ||
| 205 | */ | 108 | */ |
| 206 | - private Integer mqttQos = 1; | ||
| 207 | - | 109 | + @NotNull(message = "服务端口不能为空") |
| 110 | + private Integer port; | ||
| 208 | /** | 111 | /** |
| 209 | - * 连接超时时间(秒) | 112 | + * 序列化类型(可选) |
| 113 | + * | ||
| 114 | + * @see cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum | ||
| 115 | + * | ||
| 116 | + * 为什么是可选的呢? | ||
| 117 | + * 1. {@link IotProtocolTypeEnum#HTTP}、{@link IotProtocolTypeEnum#COAP} 协议,目前强制是 JSON 格式 | ||
| 118 | + * 2. {@link IotProtocolTypeEnum#EMQX} 协议,目前支持根据产品(设备)配置的序列化类型来解析 | ||
| 210 | */ | 119 | */ |
| 211 | - private Integer connectTimeoutSeconds = 10; | 120 | + private String serialize; |
| 212 | 121 | ||
| 213 | - /** | ||
| 214 | - * 重连延迟时间(毫秒) | ||
| 215 | - */ | ||
| 216 | - private Long reconnectDelayMs = 5000L; | 122 | + // ========== SSL 配置 ========== |
| 217 | 123 | ||
| 218 | /** | 124 | /** |
| 219 | - * 是否启用 Clean Session (清理会话) | ||
| 220 | - * true: 每次连接都是新会话,Broker 不保留离线消息和订阅关系。 | ||
| 221 | - * 对于网关这类“永远在线”且会主动重新订阅的应用,建议为 true。 | 125 | + * SSL 配置(可选,配置文件中不配置则为 null) |
| 222 | */ | 126 | */ |
| 223 | - private Boolean cleanSession = true; | 127 | + @Valid |
| 128 | + private SslConfig ssl; | ||
| 224 | 129 | ||
| 225 | - /** | ||
| 226 | - * 心跳间隔(秒) | ||
| 227 | - * 用于保持连接活性,及时发现网络中断。 | ||
| 228 | - */ | ||
| 229 | - private Integer keepAliveIntervalSeconds = 60; | 130 | + // ========== 各协议配置 ========== |
| 230 | 131 | ||
| 231 | /** | 132 | /** |
| 232 | - * 最大未确认消息队列大小 | ||
| 233 | - * 限制已发送但未收到 Broker 确认的 QoS 1/2 消息数量,用于流量控制。 | 133 | + * HTTP 协议配置 |
| 234 | */ | 134 | */ |
| 235 | - private Integer maxInflightQueue = 10000; | ||
| 236 | - | 135 | + @Valid |
| 136 | + private IotHttpConfig http; | ||
| 237 | /** | 137 | /** |
| 238 | - * 是否信任所有 SSL 证书 | ||
| 239 | - * 警告:此配置会绕过证书验证,仅建议在开发和测试环境中使用! | ||
| 240 | - * 在生产环境中,应设置为 false,并配置正确的信任库。 | 138 | + * WebSocket 协议配置 |
| 241 | */ | 139 | */ |
| 242 | - private Boolean trustAll = false; | 140 | + @Valid |
| 141 | + private IotWebSocketConfig websocket; | ||
| 243 | 142 | ||
| 244 | /** | 143 | /** |
| 245 | - * 遗嘱消息配置 (用于网关异常下线时通知其他系统) | 144 | + * TCP 协议配置 |
| 246 | */ | 145 | */ |
| 247 | - private final Will will = new Will(); | ||
| 248 | - | 146 | + @Valid |
| 147 | + private IotTcpConfig tcp; | ||
| 249 | /** | 148 | /** |
| 250 | - * 高级 SSL/TLS 配置 (用于生产环境) | 149 | + * UDP 协议配置 |
| 251 | */ | 150 | */ |
| 252 | - private final Ssl sslOptions = new Ssl(); | 151 | + @Valid |
| 152 | + private IotUdpConfig udp; | ||
| 253 | 153 | ||
| 254 | /** | 154 | /** |
| 255 | - * 遗嘱消息 (Last Will and Testament) | 155 | + * CoAP 协议配置 |
| 256 | */ | 156 | */ |
| 257 | - @Data | ||
| 258 | - public static class Will { | ||
| 259 | - | ||
| 260 | - /** | ||
| 261 | - * 是否启用遗嘱消息 | ||
| 262 | - */ | ||
| 263 | - private boolean enabled = false; | ||
| 264 | - /** | ||
| 265 | - * 遗嘱消息主题 | ||
| 266 | - */ | ||
| 267 | - private String topic; | ||
| 268 | - /** | ||
| 269 | - * 遗嘱消息内容 | ||
| 270 | - */ | ||
| 271 | - private String payload; | ||
| 272 | - /** | ||
| 273 | - * 遗嘱消息 QoS 等级 | ||
| 274 | - */ | ||
| 275 | - private Integer qos = 1; | ||
| 276 | - /** | ||
| 277 | - * 遗嘱消息是否作为保留消息发布 | ||
| 278 | - */ | ||
| 279 | - private boolean retain = true; | ||
| 280 | - | ||
| 281 | - } | 157 | + @Valid |
| 158 | + private IotCoapConfig coap; | ||
| 282 | 159 | ||
| 283 | /** | 160 | /** |
| 284 | - * 高级 SSL/TLS 配置 | 161 | + * MQTT 协议配置 |
| 285 | */ | 162 | */ |
| 286 | - @Data | ||
| 287 | - public static class Ssl { | ||
| 288 | - | ||
| 289 | - /** | ||
| 290 | - * 密钥库(KeyStore)路径,例如:classpath:certs/client.jks | ||
| 291 | - * 包含客户端自己的证书和私钥,用于向服务端证明身份(双向认证)。 | ||
| 292 | - */ | ||
| 293 | - private String keyStorePath; | ||
| 294 | - /** | ||
| 295 | - * 密钥库密码 | ||
| 296 | - */ | ||
| 297 | - private String keyStorePassword; | ||
| 298 | - /** | ||
| 299 | - * 信任库(TrustStore)路径,例如:classpath:certs/trust.jks | ||
| 300 | - * 包含服务端信任的 CA 证书,用于验证服务端的身份,防止中间人攻击。 | ||
| 301 | - */ | ||
| 302 | - private String trustStorePath; | ||
| 303 | - /** | ||
| 304 | - * 信任库密码 | ||
| 305 | - */ | ||
| 306 | - private String trustStorePassword; | ||
| 307 | - | ||
| 308 | - } | ||
| 309 | - | ||
| 310 | - } | ||
| 311 | - | ||
| 312 | - @Data | ||
| 313 | - public static class TcpProperties { | ||
| 314 | - | 163 | + @Valid |
| 164 | + private IotMqttConfig mqtt; | ||
| 315 | /** | 165 | /** |
| 316 | - * 是否开启 | 166 | + * EMQX 协议配置 |
| 317 | */ | 167 | */ |
| 318 | - @NotNull(message = "是否开启不能为空") | ||
| 319 | - private Boolean enabled; | 168 | + @Valid |
| 169 | + private IotEmqxConfig emqx; | ||
| 320 | 170 | ||
| 321 | /** | 171 | /** |
| 322 | - * 服务器端口 | 172 | + * Modbus TCP Client 协议配置 |
| 323 | */ | 173 | */ |
| 324 | - private Integer port = 8091; | 174 | + @Valid |
| 175 | + private IotModbusTcpClientConfig modbusTcpClient; | ||
| 325 | 176 | ||
| 326 | /** | 177 | /** |
| 327 | - * 心跳超时时间(毫秒) | 178 | + * Modbus TCP Server 协议配置 |
| 328 | */ | 179 | */ |
| 329 | - private Long keepAliveTimeoutMs = 30000L; | ||
| 330 | - | ||
| 331 | - /** | ||
| 332 | - * 最大连接数 | ||
| 333 | - */ | ||
| 334 | - private Integer maxConnections = 1000; | ||
| 335 | - | ||
| 336 | - /** | ||
| 337 | - * 是否启用SSL | ||
| 338 | - */ | ||
| 339 | - private Boolean sslEnabled = false; | ||
| 340 | - | ||
| 341 | - /** | ||
| 342 | - * SSL证书路径 | ||
| 343 | - */ | ||
| 344 | - private String sslCertPath; | ||
| 345 | - | ||
| 346 | - /** | ||
| 347 | - * SSL私钥路径 | ||
| 348 | - */ | ||
| 349 | - private String sslKeyPath; | 180 | + @Valid |
| 181 | + private IotModbusTcpServerConfig modbusTcpServer; | ||
| 350 | 182 | ||
| 351 | } | 183 | } |
| 352 | 184 | ||
| 185 | + /** | ||
| 186 | + * SSL 配置 | ||
| 187 | + */ | ||
| 353 | @Data | 188 | @Data |
| 354 | - public static class MqttProperties { | ||
| 355 | - | ||
| 356 | - /** | ||
| 357 | - * 是否开启 | ||
| 358 | - */ | ||
| 359 | - @NotNull(message = "是否开启不能为空") | ||
| 360 | - private Boolean enabled; | ||
| 361 | - | ||
| 362 | - /** | ||
| 363 | - * 服务器端口 | ||
| 364 | - */ | ||
| 365 | - private Integer port = 1883; | ||
| 366 | - | ||
| 367 | - /** | ||
| 368 | - * 最大消息大小(字节) | ||
| 369 | - */ | ||
| 370 | - private Integer maxMessageSize = 8192; | ||
| 371 | - | ||
| 372 | - /** | ||
| 373 | - * 连接超时时间(秒) | ||
| 374 | - */ | ||
| 375 | - private Integer connectTimeoutSeconds = 60; | ||
| 376 | - /** | ||
| 377 | - * 保持连接超时时间(秒) | ||
| 378 | - */ | ||
| 379 | - private Integer keepAliveTimeoutSeconds = 300; | 189 | + public static class SslConfig { |
| 380 | 190 | ||
| 381 | /** | 191 | /** |
| 382 | * 是否启用 SSL | 192 | * 是否启用 SSL |
| 383 | */ | 193 | */ |
| 384 | - private Boolean sslEnabled = false; | ||
| 385 | - /** | ||
| 386 | - * SSL 配置 | ||
| 387 | - */ | ||
| 388 | - private SslOptions sslOptions = new SslOptions(); | ||
| 389 | - | ||
| 390 | - /** | ||
| 391 | - * SSL 配置选项 | ||
| 392 | - */ | ||
| 393 | - @Data | ||
| 394 | - public static class SslOptions { | ||
| 395 | - | ||
| 396 | - /** | ||
| 397 | - * 密钥证书选项 | ||
| 398 | - */ | ||
| 399 | - private io.vertx.core.net.KeyCertOptions keyCertOptions; | ||
| 400 | - /** | ||
| 401 | - * 信任选项 | ||
| 402 | - */ | ||
| 403 | - private io.vertx.core.net.TrustOptions trustOptions; | ||
| 404 | - /** | ||
| 405 | - * SSL 证书路径 | ||
| 406 | - */ | ||
| 407 | - private String certPath; | ||
| 408 | - /** | ||
| 409 | - * SSL 私钥路径 | ||
| 410 | - */ | ||
| 411 | - private String keyPath; | ||
| 412 | - /** | ||
| 413 | - * 信任存储路径 | ||
| 414 | - */ | ||
| 415 | - private String trustStorePath; | ||
| 416 | - /** | ||
| 417 | - * 信任存储密码 | ||
| 418 | - */ | ||
| 419 | - private String trustStorePassword; | ||
| 420 | - | ||
| 421 | - } | ||
| 422 | - | ||
| 423 | - } | ||
| 424 | - | ||
| 425 | - @Data | ||
| 426 | - public static class MqttWsProperties { | ||
| 427 | - | ||
| 428 | - /** | ||
| 429 | - * 是否开启 | ||
| 430 | - */ | ||
| 431 | - @NotNull(message = "是否开启不能为空") | ||
| 432 | - private Boolean enabled; | ||
| 433 | - | ||
| 434 | - /** | ||
| 435 | - * WebSocket 服务器端口(默认:8083) | ||
| 436 | - */ | ||
| 437 | - private Integer port = 8083; | ||
| 438 | - | ||
| 439 | - /** | ||
| 440 | - * WebSocket 路径(默认:/mqtt) | ||
| 441 | - */ | ||
| 442 | - @NotEmpty(message = "WebSocket 路径不能为空") | ||
| 443 | - private String path = "/mqtt"; | ||
| 444 | - | ||
| 445 | - /** | ||
| 446 | - * 最大消息大小(字节) | ||
| 447 | - */ | ||
| 448 | - private Integer maxMessageSize = 8192; | ||
| 449 | - | ||
| 450 | - /** | ||
| 451 | - * 连接超时时间(秒) | ||
| 452 | - */ | ||
| 453 | - private Integer connectTimeoutSeconds = 60; | ||
| 454 | - | ||
| 455 | - /** | ||
| 456 | - * 保持连接超时时间(秒) | ||
| 457 | - */ | ||
| 458 | - private Integer keepAliveTimeoutSeconds = 300; | ||
| 459 | - | ||
| 460 | - /** | ||
| 461 | - * 是否启用 SSL(wss://) | ||
| 462 | - */ | ||
| 463 | - private Boolean sslEnabled = false; | ||
| 464 | - | ||
| 465 | - /** | ||
| 466 | - * SSL 配置 | ||
| 467 | - */ | ||
| 468 | - private SslOptions sslOptions = new SslOptions(); | ||
| 469 | - | ||
| 470 | - /** | ||
| 471 | - * WebSocket 子协议(通常为 "mqtt" 或 "mqttv3.1") | ||
| 472 | - */ | ||
| 473 | - @NotEmpty(message = "WebSocket 子协议不能为空") | ||
| 474 | - private String subProtocol = "mqtt"; | ||
| 475 | - | ||
| 476 | - /** | ||
| 477 | - * 最大帧大小(字节) | ||
| 478 | - */ | ||
| 479 | - private Integer maxFrameSize = 65536; | ||
| 480 | - | ||
| 481 | - /** | ||
| 482 | - * SSL 配置选项 | ||
| 483 | - */ | ||
| 484 | - @Data | ||
| 485 | - public static class SslOptions { | ||
| 486 | - | ||
| 487 | - /** | ||
| 488 | - * 密钥证书选项 | ||
| 489 | - */ | ||
| 490 | - private io.vertx.core.net.KeyCertOptions keyCertOptions; | ||
| 491 | - | ||
| 492 | - /** | ||
| 493 | - * 信任选项 | ||
| 494 | - */ | ||
| 495 | - private io.vertx.core.net.TrustOptions trustOptions; | ||
| 496 | - | ||
| 497 | - /** | ||
| 498 | - * SSL 证书路径 | ||
| 499 | - */ | ||
| 500 | - private String certPath; | ||
| 501 | - | ||
| 502 | - /** | ||
| 503 | - * SSL 私钥路径 | ||
| 504 | - */ | ||
| 505 | - private String keyPath; | ||
| 506 | - | ||
| 507 | - /** | ||
| 508 | - * 信任存储路径 | ||
| 509 | - */ | ||
| 510 | - private String trustStorePath; | ||
| 511 | - | ||
| 512 | - /** | ||
| 513 | - * 信任存储密码 | ||
| 514 | - */ | ||
| 515 | - private String trustStorePassword; | ||
| 516 | - | ||
| 517 | - } | ||
| 518 | - | ||
| 519 | - } | ||
| 520 | - | ||
| 521 | - @Data | ||
| 522 | - public static class UdpProperties { | ||
| 523 | - | ||
| 524 | - /** | ||
| 525 | - * 是否开启 | ||
| 526 | - */ | ||
| 527 | - @NotNull(message = "是否开启不能为空") | ||
| 528 | - private Boolean enabled; | 194 | + @NotNull(message = "是否启用 SSL 不能为空") |
| 195 | + private Boolean ssl = false; | ||
| 529 | 196 | ||
| 530 | /** | 197 | /** |
| 531 | - * 服务端口(默认 8093) | ||
| 532 | - */ | ||
| 533 | - private Integer port = 8093; | ||
| 534 | - | ||
| 535 | - /** | ||
| 536 | - * 接收缓冲区大小(默认 64KB) | 198 | + * SSL 证书路径 |
| 537 | */ | 199 | */ |
| 538 | - private Integer receiveBufferSize = 65536; | 200 | + @NotEmpty(message = "SSL 证书路径不能为空") |
| 201 | + private String sslCertPath; | ||
| 539 | 202 | ||
| 540 | /** | 203 | /** |
| 541 | - * 发送缓冲区大小(默认 64KB) | 204 | + * SSL 私钥路径 |
| 542 | */ | 205 | */ |
| 543 | - private Integer sendBufferSize = 65536; | 206 | + @NotEmpty(message = "SSL 私钥路径不能为空") |
| 207 | + private String sslKeyPath; | ||
| 544 | 208 | ||
| 545 | /** | 209 | /** |
| 546 | - * 会话超时时间(毫秒,默认 60 秒) | 210 | + * 密钥库(KeyStore)路径 |
| 547 | * <p> | 211 | * <p> |
| 548 | - * 用于清理不活跃的设备地址映射 | ||
| 549 | - */ | ||
| 550 | - private Long sessionTimeoutMs = 60000L; | ||
| 551 | - | ||
| 552 | - /** | ||
| 553 | - * 会话清理间隔(毫秒,默认 30 秒) | ||
| 554 | - */ | ||
| 555 | - private Long sessionCleanIntervalMs = 30000L; | ||
| 556 | - | ||
| 557 | - } | ||
| 558 | - | ||
| 559 | - @Data | ||
| 560 | - public static class CoapProperties { | ||
| 561 | - | ||
| 562 | - /** | ||
| 563 | - * 是否开启 | ||
| 564 | - */ | ||
| 565 | - @NotNull(message = "是否开启不能为空") | ||
| 566 | - private Boolean enabled; | ||
| 567 | - | ||
| 568 | - /** | ||
| 569 | - * 服务端口(CoAP 默认端口 5683) | 212 | + * 包含客户端自己的证书和私钥,用于向服务端证明身份(双向认证) |
| 570 | */ | 213 | */ |
| 571 | - @NotNull(message = "服务端口不能为空") | ||
| 572 | - private Integer port = 5683; | ||
| 573 | - | 214 | + private String keyStorePath; |
| 574 | /** | 215 | /** |
| 575 | - * 最大消息大小(字节) | 216 | + * 密钥库密码 |
| 576 | */ | 217 | */ |
| 577 | - @NotNull(message = "最大消息大小不能为空") | ||
| 578 | - private Integer maxMessageSize = 1024; | 218 | + private String keyStorePassword; |
| 579 | 219 | ||
| 580 | /** | 220 | /** |
| 581 | - * ACK 超时时间(毫秒) | ||
| 582 | - */ | ||
| 583 | - @NotNull(message = "ACK 超时时间不能为空") | ||
| 584 | - private Integer ackTimeout = 2000; | ||
| 585 | - | ||
| 586 | - /** | ||
| 587 | - * 最大重传次数 | ||
| 588 | - */ | ||
| 589 | - @NotNull(message = "最大重传次数不能为空") | ||
| 590 | - private Integer maxRetransmit = 4; | ||
| 591 | - | ||
| 592 | - } | ||
| 593 | - | ||
| 594 | - @Data | ||
| 595 | - public static class WebSocketProperties { | ||
| 596 | - | ||
| 597 | - /** | ||
| 598 | - * 是否开启 | ||
| 599 | - */ | ||
| 600 | - @NotNull(message = "是否开启不能为空") | ||
| 601 | - private Boolean enabled; | ||
| 602 | - | ||
| 603 | - /** | ||
| 604 | - * 服务器端口(默认:8094) | ||
| 605 | - */ | ||
| 606 | - private Integer port = 8094; | ||
| 607 | - | ||
| 608 | - /** | ||
| 609 | - * WebSocket 路径(默认:/ws) | ||
| 610 | - */ | ||
| 611 | - @NotEmpty(message = "WebSocket 路径不能为空") | ||
| 612 | - private String path = "/ws"; | ||
| 613 | - | ||
| 614 | - /** | ||
| 615 | - * 最大消息大小(字节,默认 64KB) | ||
| 616 | - */ | ||
| 617 | - private Integer maxMessageSize = 65536; | ||
| 618 | - | ||
| 619 | - /** | ||
| 620 | - * 最大帧大小(字节,默认 64KB) | ||
| 621 | - */ | ||
| 622 | - private Integer maxFrameSize = 65536; | ||
| 623 | - | ||
| 624 | - /** | ||
| 625 | - * 空闲超时时间(秒,默认 60) | ||
| 626 | - */ | ||
| 627 | - private Integer idleTimeoutSeconds = 60; | ||
| 628 | - | ||
| 629 | - /** | ||
| 630 | - * 是否启用 SSL(wss://) | ||
| 631 | - */ | ||
| 632 | - private Boolean sslEnabled = false; | ||
| 633 | - | ||
| 634 | - /** | ||
| 635 | - * SSL 证书路径 | 221 | + * 信任库(TrustStore)路径 |
| 222 | + * <p> | ||
| 223 | + * 包含服务端信任的 CA 证书,用于验证服务端的身份 | ||
| 636 | */ | 224 | */ |
| 637 | - private String sslCertPath; | ||
| 638 | - | 225 | + private String trustStorePath; |
| 639 | /** | 226 | /** |
| 640 | - * SSL 私钥路径 | 227 | + * 信任库密码 |
| 641 | */ | 228 | */ |
| 642 | - private String sslKeyPath; | 229 | + private String trustStorePassword; |
| 643 | 230 | ||
| 644 | } | 231 | } |
| 645 | 232 |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxDownstreamSubscriber.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/AbstractIotProtocolDownstreamSubscriber.java
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.emqx; | 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 3 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | 4 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 4 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | 5 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; |
| 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | 6 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | 7 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; |
| 7 | -import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router.IotEmqxDownstreamHandler; | ||
| 8 | -import jakarta.annotation.PostConstruct; | 8 | +import lombok.AllArgsConstructor; |
| 9 | import lombok.extern.slf4j.Slf4j; | 9 | import lombok.extern.slf4j.Slf4j; |
| 10 | 10 | ||
| 11 | /** | 11 | /** |
| 12 | - * IoT 网关 EMQX 订阅者:接收下行给设备的消息 | 12 | + * IoT 协议下行消息订阅者抽象类 |
| 13 | + * | ||
| 14 | + * 负责接收来自消息总线的下行消息,并委托给子类进行业务处理 | ||
| 13 | * | 15 | * |
| 14 | * @author 芋道源码 | 16 | * @author 芋道源码 |
| 15 | */ | 17 | */ |
| 18 | +@AllArgsConstructor | ||
| 16 | @Slf4j | 19 | @Slf4j |
| 17 | -public class IotEmqxDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { | 20 | +public abstract class AbstractIotProtocolDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { |
| 18 | 21 | ||
| 19 | - private final IotEmqxDownstreamHandler downstreamHandler; | 22 | + private final IotProtocol protocol; |
| 20 | 23 | ||
| 21 | private final IotMessageBus messageBus; | 24 | private final IotMessageBus messageBus; |
| 22 | 25 | ||
| 23 | - private final IotEmqxUpstreamProtocol protocol; | ||
| 24 | - | ||
| 25 | - public IotEmqxDownstreamSubscriber(IotEmqxUpstreamProtocol protocol, IotMessageBus messageBus) { | ||
| 26 | - this.protocol = protocol; | ||
| 27 | - this.messageBus = messageBus; | ||
| 28 | - this.downstreamHandler = new IotEmqxDownstreamHandler(protocol); | ||
| 29 | - } | ||
| 30 | - | ||
| 31 | - @PostConstruct | ||
| 32 | - public void init() { | ||
| 33 | - messageBus.register(this); | ||
| 34 | - } | ||
| 35 | - | ||
| 36 | @Override | 26 | @Override |
| 37 | public String getTopic() { | 27 | public String getTopic() { |
| 38 | return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId()); | 28 | return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId()); |
| 39 | } | 29 | } |
| 40 | 30 | ||
| 31 | + /** | ||
| 32 | + * 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | ||
| 33 | + */ | ||
| 41 | @Override | 34 | @Override |
| 42 | public String getGroup() { | 35 | public String getGroup() { |
| 43 | - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | ||
| 44 | return getTopic(); | 36 | return getTopic(); |
| 45 | } | 37 | } |
| 46 | 38 | ||
| 47 | @Override | 39 | @Override |
| 40 | + public void start() { | ||
| 41 | + messageBus.register(this); | ||
| 42 | + log.info("[start][{} 下行消息订阅成功,Topic:{}]", protocol.getType().name(), getTopic()); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + @Override | ||
| 46 | + public void stop() { | ||
| 47 | + messageBus.unregister(this); | ||
| 48 | + log.info("[stop][{} 下行消息订阅已停止,Topic:{}]", protocol.getType().name(), getTopic()); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + @Override | ||
| 48 | public void onMessage(IotDeviceMessage message) { | 52 | public void onMessage(IotDeviceMessage message) { |
| 49 | log.debug("[onMessage][接收到下行消息, messageId: {}, method: {}, deviceId: {}]", | 53 | log.debug("[onMessage][接收到下行消息, messageId: {}, method: {}, deviceId: {}]", |
| 50 | message.getId(), message.getMethod(), message.getDeviceId()); | 54 | message.getId(), message.getMethod(), message.getDeviceId()); |
| 51 | try { | 55 | try { |
| 52 | // 1. 校验 | 56 | // 1. 校验 |
| 53 | String method = message.getMethod(); | 57 | String method = message.getMethod(); |
| 54 | - if (method == null) { | 58 | + if (StrUtil.isBlank(method)) { |
| 55 | log.warn("[onMessage][消息方法为空, messageId: {}, deviceId: {}]", | 59 | log.warn("[onMessage][消息方法为空, messageId: {}, deviceId: {}]", |
| 56 | message.getId(), message.getDeviceId()); | 60 | message.getId(), message.getDeviceId()); |
| 57 | return; | 61 | return; |
| 58 | } | 62 | } |
| 59 | 63 | ||
| 60 | // 2. 处理下行消息 | 64 | // 2. 处理下行消息 |
| 61 | - downstreamHandler.handle(message); | 65 | + handleMessage(message); |
| 62 | } catch (Exception e) { | 66 | } catch (Exception e) { |
| 63 | log.error("[onMessage][处理下行消息失败, messageId: {}, method: {}, deviceId: {}]", | 67 | log.error("[onMessage][处理下行消息失败, messageId: {}, method: {}, deviceId: {}]", |
| 64 | message.getId(), message.getMethod(), message.getDeviceId(), e); | 68 | message.getId(), message.getMethod(), message.getDeviceId(), e); |
| 65 | } | 69 | } |
| 66 | } | 70 | } |
| 67 | 71 | ||
| 68 | -} | ||
| 69 | \ No newline at end of file | 72 | \ No newline at end of file |
| 73 | + /** | ||
| 74 | + * 处理下行消息 | ||
| 75 | + * | ||
| 76 | + * @param message 下行消息 | ||
| 77 | + */ | ||
| 78 | + protected abstract void handleMessage(IotDeviceMessage message); | ||
| 79 | + | ||
| 80 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocol.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * IoT 协议接口 | ||
| 7 | + * | ||
| 8 | + * 定义传输层协议的生命周期管理 | ||
| 9 | + * | ||
| 10 | + * @author 芋道源码 | ||
| 11 | + */ | ||
| 12 | +public interface IotProtocol { | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * 获取协议实例 ID | ||
| 16 | + * | ||
| 17 | + * @return 协议实例 ID,如 "http-alink"、"tcp-binary" | ||
| 18 | + */ | ||
| 19 | + String getId(); | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 获取服务器 ID(用于消息追踪,全局唯一) | ||
| 23 | + * | ||
| 24 | + * @return 服务器 ID | ||
| 25 | + */ | ||
| 26 | + String getServerId(); | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 获取协议类型 | ||
| 30 | + * | ||
| 31 | + * @return 协议类型枚举 | ||
| 32 | + */ | ||
| 33 | + IotProtocolTypeEnum getType(); | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 启动协议服务 | ||
| 37 | + */ | ||
| 38 | + void start(); | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 停止协议服务 | ||
| 42 | + */ | ||
| 43 | + void stop(); | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 检查协议服务是否正在运行 | ||
| 47 | + * | ||
| 48 | + * @return 是否正在运行 | ||
| 49 | + */ | ||
| 50 | + boolean isRunning(); | ||
| 51 | + | ||
| 52 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocolManager.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.hutool.core.util.BooleanUtil; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 6 | +import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; | ||
| 7 | +import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapProtocol; | ||
| 8 | +import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxProtocol; | ||
| 9 | +import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpProtocol; | ||
| 10 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpclient.IotModbusTcpClientProtocol; | ||
| 11 | +import cn.iocoder.yudao.module.iot.gateway.protocol.modbus.tcpserver.IotModbusTcpServerProtocol; | ||
| 12 | +import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttProtocol; | ||
| 13 | +import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpProtocol; | ||
| 14 | +import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpProtocol; | ||
| 15 | +import cn.iocoder.yudao.module.iot.gateway.protocol.websocket.IotWebSocketProtocol; | ||
| 16 | +import lombok.Getter; | ||
| 17 | +import lombok.extern.slf4j.Slf4j; | ||
| 18 | +import org.springframework.context.SmartLifecycle; | ||
| 19 | + | ||
| 20 | +import java.util.ArrayList; | ||
| 21 | +import java.util.List; | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * IoT 协议管理器:负责根据配置创建和管理协议实例 | ||
| 25 | + * | ||
| 26 | + * @author 芋道源码 | ||
| 27 | + */ | ||
| 28 | +@Slf4j | ||
| 29 | +public class IotProtocolManager implements SmartLifecycle { | ||
| 30 | + | ||
| 31 | + private final IotGatewayProperties gatewayProperties; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 协议实例列表 | ||
| 35 | + */ | ||
| 36 | + private final List<IotProtocol> protocols = new ArrayList<>(); | ||
| 37 | + | ||
| 38 | + @Getter | ||
| 39 | + private volatile boolean running = false; | ||
| 40 | + | ||
| 41 | + public IotProtocolManager(IotGatewayProperties gatewayProperties) { | ||
| 42 | + this.gatewayProperties = gatewayProperties; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + @Override | ||
| 46 | + public void start() { | ||
| 47 | + if (running) { | ||
| 48 | + return; | ||
| 49 | + } | ||
| 50 | + List<IotGatewayProperties.ProtocolProperties> protocolConfigs = gatewayProperties.getProtocols(); | ||
| 51 | + if (CollUtil.isEmpty(protocolConfigs)) { | ||
| 52 | + log.info("[start][没有配置协议实例,跳过启动]"); | ||
| 53 | + return; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + for (IotGatewayProperties.ProtocolProperties config : protocolConfigs) { | ||
| 57 | + if (BooleanUtil.isFalse(config.getEnabled())) { | ||
| 58 | + log.info("[start][协议实例 {} 未启用,跳过]", config.getId()); | ||
| 59 | + continue; | ||
| 60 | + } | ||
| 61 | + IotProtocol protocol = createProtocol(config); | ||
| 62 | + if (protocol == null) { | ||
| 63 | + continue; | ||
| 64 | + } | ||
| 65 | + protocol.start(); | ||
| 66 | + protocols.add(protocol); | ||
| 67 | + } | ||
| 68 | + running = true; | ||
| 69 | + log.info("[start][协议管理器启动完成,共启动 {} 个协议实例]", protocols.size()); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + @Override | ||
| 73 | + public void stop() { | ||
| 74 | + if (!running) { | ||
| 75 | + return; | ||
| 76 | + } | ||
| 77 | + for (IotProtocol protocol : protocols) { | ||
| 78 | + try { | ||
| 79 | + protocol.stop(); | ||
| 80 | + } catch (Exception e) { | ||
| 81 | + log.error("[stop][协议实例 {} 停止失败]", protocol.getId(), e); | ||
| 82 | + } | ||
| 83 | + } | ||
| 84 | + protocols.clear(); | ||
| 85 | + running = false; | ||
| 86 | + log.info("[stop][协议管理器已停止]"); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + /** | ||
| 90 | + * 创建协议实例 | ||
| 91 | + * | ||
| 92 | + * @param config 协议实例配置 | ||
| 93 | + * @return 协议实例 | ||
| 94 | + */ | ||
| 95 | + @SuppressWarnings({"EnhancedSwitchMigration"}) | ||
| 96 | + private IotProtocol createProtocol(IotGatewayProperties.ProtocolProperties config) { | ||
| 97 | + IotProtocolTypeEnum protocolType = IotProtocolTypeEnum.of(config.getProtocol()); | ||
| 98 | + if (protocolType == null) { | ||
| 99 | + log.error("[createProtocol][协议实例 {} 的协议类型 {} 不存在]", config.getId(), config.getProtocol()); | ||
| 100 | + return null; | ||
| 101 | + } | ||
| 102 | + switch (protocolType) { | ||
| 103 | + case HTTP: | ||
| 104 | + return createHttpProtocol(config); | ||
| 105 | + case TCP: | ||
| 106 | + return createTcpProtocol(config); | ||
| 107 | + case UDP: | ||
| 108 | + return createUdpProtocol(config); | ||
| 109 | + case COAP: | ||
| 110 | + return createCoapProtocol(config); | ||
| 111 | + case WEBSOCKET: | ||
| 112 | + return createWebSocketProtocol(config); | ||
| 113 | + case MQTT: | ||
| 114 | + return createMqttProtocol(config); | ||
| 115 | + case EMQX: | ||
| 116 | + return createEmqxProtocol(config); | ||
| 117 | + case MODBUS_TCP_CLIENT: | ||
| 118 | + return createModbusTcpClientProtocol(config); | ||
| 119 | + case MODBUS_TCP_SERVER: | ||
| 120 | + return createModbusTcpServerProtocol(config); | ||
| 121 | + default: | ||
| 122 | + throw new IllegalArgumentException(String.format( | ||
| 123 | + "[createProtocol][协议实例 %s 的协议类型 %s 暂不支持]", config.getId(), protocolType)); | ||
| 124 | + } | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + /** | ||
| 128 | + * 创建 HTTP 协议实例 | ||
| 129 | + * | ||
| 130 | + * @param config 协议实例配置 | ||
| 131 | + * @return HTTP 协议实例 | ||
| 132 | + */ | ||
| 133 | + private IotHttpProtocol createHttpProtocol(IotGatewayProperties.ProtocolProperties config) { | ||
| 134 | + return new IotHttpProtocol(config); | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + /** | ||
| 138 | + * 创建 TCP 协议实例 | ||
| 139 | + * | ||
| 140 | + * @param config 协议实例配置 | ||
| 141 | + * @return TCP 协议实例 | ||
| 142 | + */ | ||
| 143 | + private IotTcpProtocol createTcpProtocol(IotGatewayProperties.ProtocolProperties config) { | ||
| 144 | + return new IotTcpProtocol(config); | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + /** | ||
| 148 | + * 创建 UDP 协议实例 | ||
| 149 | + * | ||
| 150 | + * @param config 协议实例配置 | ||
| 151 | + * @return UDP 协议实例 | ||
| 152 | + */ | ||
| 153 | + private IotUdpProtocol createUdpProtocol(IotGatewayProperties.ProtocolProperties config) { | ||
| 154 | + return new IotUdpProtocol(config); | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + /** | ||
| 158 | + * 创建 CoAP 协议实例 | ||
| 159 | + * | ||
| 160 | + * @param config 协议实例配置 | ||
| 161 | + * @return CoAP 协议实例 | ||
| 162 | + */ | ||
| 163 | + private IotCoapProtocol createCoapProtocol(IotGatewayProperties.ProtocolProperties config) { | ||
| 164 | + return new IotCoapProtocol(config); | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + /** | ||
| 168 | + * 创建 WebSocket 协议实例 | ||
| 169 | + * | ||
| 170 | + * @param config 协议实例配置 | ||
| 171 | + * @return WebSocket 协议实例 | ||
| 172 | + */ | ||
| 173 | + private IotWebSocketProtocol createWebSocketProtocol(IotGatewayProperties.ProtocolProperties config) { | ||
| 174 | + return new IotWebSocketProtocol(config); | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + /** | ||
| 178 | + * 创建 MQTT 协议实例 | ||
| 179 | + * | ||
| 180 | + * @param config 协议实例配置 | ||
| 181 | + * @return MQTT 协议实例 | ||
| 182 | + */ | ||
| 183 | + private IotMqttProtocol createMqttProtocol(IotGatewayProperties.ProtocolProperties config) { | ||
| 184 | + return new IotMqttProtocol(config); | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + /** | ||
| 188 | + * 创建 EMQX 协议实例 | ||
| 189 | + * | ||
| 190 | + * @param config 协议实例配置 | ||
| 191 | + * @return EMQX 协议实例 | ||
| 192 | + */ | ||
| 193 | + private IotEmqxProtocol createEmqxProtocol(IotGatewayProperties.ProtocolProperties config) { | ||
| 194 | + return new IotEmqxProtocol(config); | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + /** | ||
| 198 | + * 创建 Modbus TCP Client 协议实例 | ||
| 199 | + * | ||
| 200 | + * @param config 协议实例配置 | ||
| 201 | + * @return Modbus TCP Client 协议实例 | ||
| 202 | + */ | ||
| 203 | + private IotModbusTcpClientProtocol createModbusTcpClientProtocol(IotGatewayProperties.ProtocolProperties config) { | ||
| 204 | + return new IotModbusTcpClientProtocol(config); | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + /** | ||
| 208 | + * 创建 Modbus TCP Server 协议实例 | ||
| 209 | + * | ||
| 210 | + * @param config 协议实例配置 | ||
| 211 | + * @return Modbus TCP Server 协议实例 | ||
| 212 | + */ | ||
| 213 | + private IotModbusTcpServerProtocol createModbusTcpServerProtocol(IotGatewayProperties.ProtocolProperties config) { | ||
| 214 | + return new IotModbusTcpServerProtocol(config); | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapConfig.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.coap; | ||
| 2 | + | ||
| 3 | +import jakarta.validation.constraints.Min; | ||
| 4 | +import jakarta.validation.constraints.NotNull; | ||
| 5 | +import lombok.Data; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * IoT CoAP 协议配置 | ||
| 9 | + * | ||
| 10 | + * @author 芋道源码 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class IotCoapConfig { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 最大消息大小(字节) | ||
| 17 | + */ | ||
| 18 | + @NotNull(message = "最大消息大小不能为空") | ||
| 19 | + @Min(value = 64, message = "最大消息大小必须大于 64 字节") | ||
| 20 | + private Integer maxMessageSize = 1024; | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * ACK 超时时间(毫秒) | ||
| 24 | + */ | ||
| 25 | + @NotNull(message = "ACK 超时时间不能为空") | ||
| 26 | + @Min(value = 100, message = "ACK 超时时间必须大于 100 毫秒") | ||
| 27 | + private Integer ackTimeoutMs = 2000; | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * 最大重传次数 | ||
| 31 | + */ | ||
| 32 | + @NotNull(message = "最大重传次数不能为空") | ||
| 33 | + @Min(value = 0, message = "最大重传次数必须大于等于 0") | ||
| 34 | + private Integer maxRetransmit = 4; | ||
| 35 | + | ||
| 36 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapDownstreamSubscriber.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.coap; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | ||
| 4 | -import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; | ||
| 5 | -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 6 | -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 7 | -import jakarta.annotation.PostConstruct; | ||
| 8 | -import lombok.RequiredArgsConstructor; | ||
| 9 | -import lombok.extern.slf4j.Slf4j; | ||
| 10 | - | ||
| 11 | -/** | ||
| 12 | - * IoT 网关 CoAP 订阅者:接收下行给设备的消息 | ||
| 13 | - * | ||
| 14 | - * @author 芋道源码 | ||
| 15 | - */ | ||
| 16 | -@RequiredArgsConstructor | ||
| 17 | -@Slf4j | ||
| 18 | -public class IotCoapDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> { | ||
| 19 | - | ||
| 20 | - private final IotCoapUpstreamProtocol protocol; | ||
| 21 | - | ||
| 22 | - private final IotMessageBus messageBus; | ||
| 23 | - | ||
| 24 | - @PostConstruct | ||
| 25 | - public void init() { | ||
| 26 | - messageBus.register(this); | ||
| 27 | - } | ||
| 28 | - | ||
| 29 | - @Override | ||
| 30 | - public String getTopic() { | ||
| 31 | - return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId()); | ||
| 32 | - } | ||
| 33 | - | ||
| 34 | - @Override | ||
| 35 | - public String getGroup() { | ||
| 36 | - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | ||
| 37 | - return getTopic(); | ||
| 38 | - } | ||
| 39 | - | ||
| 40 | - @Override | ||
| 41 | - public void onMessage(IotDeviceMessage message) { | ||
| 42 | - // 如需支持,可通过 CoAP Observe 模式实现(设备订阅资源,服务器推送变更) | ||
| 43 | - log.warn("[onMessage][IoT 网关 CoAP 协议暂不支持下行消息,忽略消息:{}]", message); | ||
| 44 | - } | ||
| 45 | - | ||
| 46 | -} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapProtocol.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.coap; | ||
| 2 | + | ||
| 3 | +import cn.hutool.extra.spring.SpringUtil; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | ||
| 6 | +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 7 | +import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties.ProtocolProperties; | ||
| 8 | +import cn.iocoder.yudao.module.iot.gateway.protocol.IotProtocol; | ||
| 9 | +import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.downstream.IotCoapDownstreamSubscriber; | ||
| 10 | +import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream.IotCoapAuthHandler; | ||
| 11 | +import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream.IotCoapAuthResource; | ||
| 12 | +import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream.IotCoapRegisterHandler; | ||
| 13 | +import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream.IotCoapRegisterResource; | ||
| 14 | +import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream.IotCoapRegisterSubHandler; | ||
| 15 | +import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream.IotCoapRegisterSubResource; | ||
| 16 | +import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream.IotCoapUpstreamHandler; | ||
| 17 | +import cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream.IotCoapUpstreamTopicResource; | ||
| 18 | +import lombok.Getter; | ||
| 19 | +import lombok.extern.slf4j.Slf4j; | ||
| 20 | +import org.eclipse.californium.core.CoapResource; | ||
| 21 | +import org.eclipse.californium.core.CoapServer; | ||
| 22 | +import org.eclipse.californium.core.config.CoapConfig; | ||
| 23 | +import org.eclipse.californium.elements.config.Configuration; | ||
| 24 | +import cn.hutool.core.lang.Assert; | ||
| 25 | + | ||
| 26 | +import java.util.concurrent.TimeUnit; | ||
| 27 | + | ||
| 28 | +/** | ||
| 29 | + * IoT CoAP 协议实现 | ||
| 30 | + * <p> | ||
| 31 | + * 基于 Eclipse Californium 实现,支持: | ||
| 32 | + * 1. 认证:POST /auth | ||
| 33 | + * 2. 设备动态注册:POST /auth/register/device | ||
| 34 | + * 3. 子设备动态注册:POST /auth/register/sub-device/{productKey}/{deviceName} | ||
| 35 | + * 4. 属性上报:POST /topic/sys/{productKey}/{deviceName}/thing/property/post | ||
| 36 | + * 5. 事件上报:POST /topic/sys/{productKey}/{deviceName}/thing/event/post | ||
| 37 | + * | ||
| 38 | + * @author 芋道源码 | ||
| 39 | + */ | ||
| 40 | +@Slf4j | ||
| 41 | +public class IotCoapProtocol implements IotProtocol { | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 协议配置 | ||
| 45 | + */ | ||
| 46 | + private final ProtocolProperties properties; | ||
| 47 | + /** | ||
| 48 | + * 服务器 ID(用于消息追踪,全局唯一) | ||
| 49 | + */ | ||
| 50 | + @Getter | ||
| 51 | + private final String serverId; | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 运行状态 | ||
| 55 | + */ | ||
| 56 | + @Getter | ||
| 57 | + private volatile boolean running = false; | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * CoAP 服务器 | ||
| 61 | + */ | ||
| 62 | + private CoapServer coapServer; | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * 下行消息订阅者 | ||
| 66 | + */ | ||
| 67 | + private IotCoapDownstreamSubscriber downstreamSubscriber; | ||
| 68 | + | ||
| 69 | + public IotCoapProtocol(ProtocolProperties properties) { | ||
| 70 | + IotCoapConfig coapConfig = properties.getCoap(); | ||
| 71 | + Assert.notNull(coapConfig, "CoAP 协议配置(coap)不能为空"); | ||
| 72 | + this.properties = properties; | ||
| 73 | + this.serverId = IotDeviceMessageUtils.generateServerId(properties.getPort()); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + @Override | ||
| 77 | + public String getId() { | ||
| 78 | + return properties.getId(); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + @Override | ||
| 82 | + public IotProtocolTypeEnum getType() { | ||
| 83 | + return IotProtocolTypeEnum.COAP; | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + @Override | ||
| 87 | + public void start() { | ||
| 88 | + if (running) { | ||
| 89 | + log.warn("[start][IoT CoAP 协议 {} 已经在运行中]", getId()); | ||
| 90 | + return; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + try { | ||
| 94 | + // 1.1 创建 CoAP 配置 | ||
| 95 | + IotCoapConfig coapConfig = properties.getCoap(); | ||
| 96 | + Configuration config = Configuration.createStandardWithoutFile(); | ||
| 97 | + config.set(CoapConfig.COAP_PORT, properties.getPort()); | ||
| 98 | + config.set(CoapConfig.MAX_MESSAGE_SIZE, coapConfig.getMaxMessageSize()); | ||
| 99 | + config.set(CoapConfig.ACK_TIMEOUT, coapConfig.getAckTimeoutMs(), TimeUnit.MILLISECONDS); | ||
| 100 | + config.set(CoapConfig.MAX_RETRANSMIT, coapConfig.getMaxRetransmit()); | ||
| 101 | + // 1.2 创建 CoAP 服务器 | ||
| 102 | + coapServer = new CoapServer(config); | ||
| 103 | + | ||
| 104 | + // 2.1 添加 /auth 认证资源 | ||
| 105 | + IotCoapAuthHandler authHandler = new IotCoapAuthHandler(serverId); | ||
| 106 | + IotCoapAuthResource authResource = new IotCoapAuthResource(authHandler); | ||
| 107 | + coapServer.add(authResource); | ||
| 108 | + // 2.2 添加 /auth/register/device 设备动态注册资源(一型一密) | ||
| 109 | + IotCoapRegisterHandler registerHandler = new IotCoapRegisterHandler(); | ||
| 110 | + IotCoapRegisterResource registerResource = new IotCoapRegisterResource(registerHandler); | ||
| 111 | + // 2.3 添加 /auth/register/sub-device/{productKey}/{deviceName} 子设备动态注册资源 | ||
| 112 | + IotCoapRegisterSubHandler registerSubHandler = new IotCoapRegisterSubHandler(); | ||
| 113 | + IotCoapRegisterSubResource registerSubResource = new IotCoapRegisterSubResource(registerSubHandler); | ||
| 114 | + authResource.add(new CoapResource("register") {{ | ||
| 115 | + add(registerResource); | ||
| 116 | + add(registerSubResource); | ||
| 117 | + }}); | ||
| 118 | + // 2.4 添加 /topic 根资源(用于上行消息) | ||
| 119 | + IotCoapUpstreamHandler upstreamHandler = new IotCoapUpstreamHandler(serverId); | ||
| 120 | + IotCoapUpstreamTopicResource topicResource = new IotCoapUpstreamTopicResource(serverId, upstreamHandler); | ||
| 121 | + coapServer.add(topicResource); | ||
| 122 | + | ||
| 123 | + // 3. 启动服务器 | ||
| 124 | + coapServer.start(); | ||
| 125 | + running = true; | ||
| 126 | + log.info("[start][IoT CoAP 协议 {} 启动成功,端口:{},serverId:{}]", | ||
| 127 | + getId(), properties.getPort(), serverId); | ||
| 128 | + | ||
| 129 | + // 4. 启动下行消息订阅者 | ||
| 130 | + IotMessageBus messageBus = SpringUtil.getBean(IotMessageBus.class); | ||
| 131 | + this.downstreamSubscriber = new IotCoapDownstreamSubscriber(this, messageBus); | ||
| 132 | + this.downstreamSubscriber.start(); | ||
| 133 | + } catch (Exception e) { | ||
| 134 | + log.error("[start][IoT CoAP 协议 {} 启动失败]", getId(), e); | ||
| 135 | + stop0(); | ||
| 136 | + throw e; | ||
| 137 | + } | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + @Override | ||
| 141 | + public void stop() { | ||
| 142 | + if (!running) { | ||
| 143 | + return; | ||
| 144 | + } | ||
| 145 | + stop0(); | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + private void stop0() { | ||
| 149 | + // 1. 停止下行消息订阅者 | ||
| 150 | + if (downstreamSubscriber != null) { | ||
| 151 | + try { | ||
| 152 | + downstreamSubscriber.stop(); | ||
| 153 | + log.info("[stop][IoT CoAP 协议 {} 下行消息订阅者已停止]", getId()); | ||
| 154 | + } catch (Exception e) { | ||
| 155 | + log.error("[stop][IoT CoAP 协议 {} 下行消息订阅者停止失败]", getId(), e); | ||
| 156 | + } | ||
| 157 | + downstreamSubscriber = null; | ||
| 158 | + } | ||
| 159 | + | ||
| 160 | + // 2. 关闭 CoAP 服务器 | ||
| 161 | + if (coapServer != null) { | ||
| 162 | + try { | ||
| 163 | + coapServer.stop(); | ||
| 164 | + coapServer.destroy(); | ||
| 165 | + coapServer = null; | ||
| 166 | + log.info("[stop][IoT CoAP 协议 {} 服务器已停止]", getId()); | ||
| 167 | + } catch (Exception e) { | ||
| 168 | + log.error("[stop][IoT CoAP 协议 {} 服务器停止失败]", getId(), e); | ||
| 169 | + } | ||
| 170 | + } | ||
| 171 | + running = false; | ||
| 172 | + log.info("[stop][IoT CoAP 协议 {} 已停止]", getId()); | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotCoapUpstreamProtocol.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.gateway.protocol.coap; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; | ||
| 4 | -import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; | ||
| 5 | -import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapAuthHandler; | ||
| 6 | -import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapAuthResource; | ||
| 7 | -import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapRegisterHandler; | ||
| 8 | -import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapRegisterResource; | ||
| 9 | -import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapUpstreamTopicResource; | ||
| 10 | -import cn.iocoder.yudao.module.iot.gateway.protocol.coap.router.IotCoapUpstreamHandler; | ||
| 11 | -import jakarta.annotation.PostConstruct; | ||
| 12 | -import jakarta.annotation.PreDestroy; | ||
| 13 | -import lombok.Getter; | ||
| 14 | -import lombok.extern.slf4j.Slf4j; | ||
| 15 | -import org.eclipse.californium.core.CoapResource; | ||
| 16 | -import org.eclipse.californium.core.CoapServer; | ||
| 17 | -import org.eclipse.californium.core.config.CoapConfig; | ||
| 18 | -import org.eclipse.californium.elements.config.Configuration; | ||
| 19 | - | ||
| 20 | -import java.util.concurrent.TimeUnit; | ||
| 21 | - | ||
| 22 | -/** | ||
| 23 | - * IoT 网关 CoAP 协议:接收设备上行消息 | ||
| 24 | - * | ||
| 25 | - * 基于 Eclipse Californium 实现,支持: | ||
| 26 | - * 1. 认证:POST /auth | ||
| 27 | - * 2. 属性上报:POST /topic/sys/{productKey}/{deviceName}/thing/property/post | ||
| 28 | - * 3. 事件上报:POST /topic/sys/{productKey}/{deviceName}/thing/event/post | ||
| 29 | - * | ||
| 30 | - * @author 芋道源码 | ||
| 31 | - */ | ||
| 32 | -@Slf4j | ||
| 33 | -public class IotCoapUpstreamProtocol { | ||
| 34 | - | ||
| 35 | - private final IotGatewayProperties.CoapProperties coapProperties; | ||
| 36 | - | ||
| 37 | - private CoapServer coapServer; | ||
| 38 | - | ||
| 39 | - @Getter | ||
| 40 | - private final String serverId; | ||
| 41 | - | ||
| 42 | - public IotCoapUpstreamProtocol(IotGatewayProperties.CoapProperties coapProperties) { | ||
| 43 | - this.coapProperties = coapProperties; | ||
| 44 | - this.serverId = IotDeviceMessageUtils.generateServerId(coapProperties.getPort()); | ||
| 45 | - } | ||
| 46 | - | ||
| 47 | - @PostConstruct | ||
| 48 | - public void start() { | ||
| 49 | - try { | ||
| 50 | - // 1.1 创建网络配置(Californium 3.x API) | ||
| 51 | - Configuration config = Configuration.createStandardWithoutFile(); | ||
| 52 | - config.set(CoapConfig.COAP_PORT, coapProperties.getPort()); | ||
| 53 | - config.set(CoapConfig.MAX_MESSAGE_SIZE, coapProperties.getMaxMessageSize()); | ||
| 54 | - config.set(CoapConfig.ACK_TIMEOUT, coapProperties.getAckTimeout(), TimeUnit.MILLISECONDS); | ||
| 55 | - config.set(CoapConfig.MAX_RETRANSMIT, coapProperties.getMaxRetransmit()); | ||
| 56 | - // 1.2 创建 CoAP 服务器 | ||
| 57 | - coapServer = new CoapServer(config); | ||
| 58 | - | ||
| 59 | - // 2.1 添加 /auth 认证资源 | ||
| 60 | - IotCoapAuthHandler authHandler = new IotCoapAuthHandler(); | ||
| 61 | - IotCoapAuthResource authResource = new IotCoapAuthResource(this, authHandler); | ||
| 62 | - coapServer.add(authResource); | ||
| 63 | - // 2.2 添加 /auth/register/device 设备动态注册资源(一型一密) | ||
| 64 | - IotCoapRegisterHandler registerHandler = new IotCoapRegisterHandler(); | ||
| 65 | - IotCoapRegisterResource registerResource = new IotCoapRegisterResource(registerHandler); | ||
| 66 | - authResource.add(new CoapResource("register") {{ | ||
| 67 | - add(registerResource); | ||
| 68 | - }}); | ||
| 69 | - // 2.3 添加 /topic 根资源(用于上行消息) | ||
| 70 | - IotCoapUpstreamHandler upstreamHandler = new IotCoapUpstreamHandler(); | ||
| 71 | - IotCoapUpstreamTopicResource topicResource = new IotCoapUpstreamTopicResource(this, upstreamHandler); | ||
| 72 | - coapServer.add(topicResource); | ||
| 73 | - | ||
| 74 | - // 3. 启动服务器 | ||
| 75 | - coapServer.start(); | ||
| 76 | - log.info("[start][IoT 网关 CoAP 协议启动成功,端口:{},资源:/auth, /auth/register/device, /topic]", coapProperties.getPort()); | ||
| 77 | - } catch (Exception e) { | ||
| 78 | - log.error("[start][IoT 网关 CoAP 协议启动失败]", e); | ||
| 79 | - throw e; | ||
| 80 | - } | ||
| 81 | - } | ||
| 82 | - | ||
| 83 | - @PreDestroy | ||
| 84 | - public void stop() { | ||
| 85 | - if (coapServer != null) { | ||
| 86 | - try { | ||
| 87 | - coapServer.stop(); | ||
| 88 | - log.info("[stop][IoT 网关 CoAP 协议已停止]"); | ||
| 89 | - } catch (Exception e) { | ||
| 90 | - log.error("[stop][IoT 网关 CoAP 协议停止失败]", e); | ||
| 91 | - } | ||
| 92 | - } | ||
| 93 | - } | ||
| 94 | - | ||
| 95 | -} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/downstream/IotCoapDownstreamSubscriber.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.downstream; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 5 | +import cn.iocoder.yudao.module.iot.gateway.protocol.AbstractIotProtocolDownstreamSubscriber; | ||
| 6 | +import cn.iocoder.yudao.module.iot.gateway.protocol.coap.IotCoapProtocol; | ||
| 7 | +import lombok.extern.slf4j.Slf4j; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * IoT 网关 CoAP 订阅者:接收下行给设备的消息 | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | +@Slf4j | ||
| 15 | +public class IotCoapDownstreamSubscriber extends AbstractIotProtocolDownstreamSubscriber { | ||
| 16 | + | ||
| 17 | + public IotCoapDownstreamSubscriber(IotCoapProtocol protocol, IotMessageBus messageBus) { | ||
| 18 | + super(protocol, messageBus); | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + @Override | ||
| 22 | + protected void handleMessage(IotDeviceMessage message) { | ||
| 23 | + // 如需支持,可通过 CoAP Observe 模式实现(设备订阅资源,服务器推送变更) | ||
| 24 | + log.warn("[handleMessage][IoT 网关 CoAP 协议暂不支持下行消息,忽略消息:{}]", message); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstream/IotCoapAbstractHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.hutool.core.util.ArrayUtil; | ||
| 5 | +import cn.hutool.core.util.ObjUtil; | ||
| 6 | +import cn.hutool.core.util.StrUtil; | ||
| 7 | +import cn.hutool.extra.spring.SpringUtil; | ||
| 8 | +import cn.iocoder.yudao.framework.common.exception.ServiceException; | ||
| 9 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 10 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 11 | +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; | ||
| 12 | +import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; | ||
| 13 | +import lombok.extern.slf4j.Slf4j; | ||
| 14 | +import org.eclipse.californium.core.coap.CoAP; | ||
| 15 | +import org.eclipse.californium.core.coap.MediaTypeRegistry; | ||
| 16 | +import org.eclipse.californium.core.coap.Option; | ||
| 17 | +import org.eclipse.californium.core.server.resources.CoapExchange; | ||
| 18 | + | ||
| 19 | +import java.util.List; | ||
| 20 | + | ||
| 21 | +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*; | ||
| 22 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + * IoT 网关 CoAP 协议的处理器抽象基类:提供通用的前置处理(认证)、请求解析、响应处理、全局的异常捕获等 | ||
| 26 | + * | ||
| 27 | + * @author 芋道源码 | ||
| 28 | + */ | ||
| 29 | +@Slf4j | ||
| 30 | +public abstract class IotCoapAbstractHandler { | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 自定义 CoAP Option 编号,用于携带 Token | ||
| 34 | + * <p> | ||
| 35 | + * CoAP Option 范围 2048-65535 属于实验/自定义范围 | ||
| 36 | + */ | ||
| 37 | + public static final int OPTION_TOKEN = 2088; | ||
| 38 | + | ||
| 39 | + private final IotDeviceTokenService deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 处理 CoAP 请求(模板方法) | ||
| 43 | + * | ||
| 44 | + * @param exchange CoAP 交换对象 | ||
| 45 | + */ | ||
| 46 | + public final void handle(CoapExchange exchange) { | ||
| 47 | + try { | ||
| 48 | + // 1. 前置处理 | ||
| 49 | + beforeHandle(exchange); | ||
| 50 | + | ||
| 51 | + // 2. 执行业务逻辑 | ||
| 52 | + CommonResult<Object> result = handle0(exchange); | ||
| 53 | + writeResponse(exchange, result); | ||
| 54 | + } catch (ServiceException e) { | ||
| 55 | + // 业务异常,返回对应的错误码和消息 | ||
| 56 | + writeResponse(exchange, CommonResult.error(e.getCode(), e.getMessage())); | ||
| 57 | + } catch (IllegalArgumentException e) { | ||
| 58 | + // 参数校验异常(hutool Assert 抛出),返回 BAD_REQUEST | ||
| 59 | + writeResponse(exchange, CommonResult.error(BAD_REQUEST.getCode(), e.getMessage())); | ||
| 60 | + } catch (Exception e) { | ||
| 61 | + // 其他未知异常,返回 INTERNAL_SERVER_ERROR | ||
| 62 | + log.error("[handle][CoAP 请求处理异常]", e); | ||
| 63 | + writeResponse(exchange, CommonResult.error(INTERNAL_SERVER_ERROR)); | ||
| 64 | + } | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + /** | ||
| 68 | + * 处理 CoAP 请求(子类实现) | ||
| 69 | + * | ||
| 70 | + * @param exchange CoAP 交换对象 | ||
| 71 | + * @return 处理结果 | ||
| 72 | + */ | ||
| 73 | + protected abstract CommonResult<Object> handle0(CoapExchange exchange); | ||
| 74 | + | ||
| 75 | + /** | ||
| 76 | + * 前置处理:认证等 | ||
| 77 | + * | ||
| 78 | + * @param exchange CoAP 交换对象 | ||
| 79 | + */ | ||
| 80 | + private void beforeHandle(CoapExchange exchange) { | ||
| 81 | + // 1.1 如果不需要认证,则不走前置处理 | ||
| 82 | + if (!requiresAuthentication()) { | ||
| 83 | + return; | ||
| 84 | + } | ||
| 85 | + // 1.2 从自定义 Option 获取 token | ||
| 86 | + String token = getTokenFromOption(exchange); | ||
| 87 | + if (StrUtil.isEmpty(token)) { | ||
| 88 | + throw exception(UNAUTHORIZED); | ||
| 89 | + } | ||
| 90 | + // 1.3 校验 token | ||
| 91 | + IotDeviceIdentity deviceInfo = deviceTokenService.verifyToken(token); | ||
| 92 | + if (deviceInfo == null) { | ||
| 93 | + throw exception(UNAUTHORIZED); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + // 2.1 解析 productKey 和 deviceName | ||
| 97 | + List<String> uriPath = exchange.getRequestOptions().getUriPath(); | ||
| 98 | + String productKey = getProductKey(uriPath); | ||
| 99 | + String deviceName = getDeviceName(uriPath); | ||
| 100 | + if (StrUtil.isEmpty(productKey) || StrUtil.isEmpty(deviceName)) { | ||
| 101 | + throw exception(BAD_REQUEST); | ||
| 102 | + } | ||
| 103 | + // 2.2 校验设备信息是否匹配 | ||
| 104 | + if (ObjUtil.notEqual(productKey, deviceInfo.getProductKey()) | ||
| 105 | + || ObjUtil.notEqual(deviceName, deviceInfo.getDeviceName())) { | ||
| 106 | + throw exception(FORBIDDEN); | ||
| 107 | + } | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + // ========== Token 相关方法 ========== | ||
| 111 | + | ||
| 112 | + /** | ||
| 113 | + * 是否需要认证(子类可覆盖) | ||
| 114 | + * <p> | ||
| 115 | + * 默认不需要认证 | ||
| 116 | + * | ||
| 117 | + * @return 是否需要认证 | ||
| 118 | + */ | ||
| 119 | + protected boolean requiresAuthentication() { | ||
| 120 | + return false; | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + /** | ||
| 124 | + * 从 URI 路径中获取 productKey(子类实现) | ||
| 125 | + * <p> | ||
| 126 | + * 默认抛出异常,需要认证的子类必须实现此方法 | ||
| 127 | + * | ||
| 128 | + * @param uriPath URI 路径 | ||
| 129 | + * @return productKey | ||
| 130 | + */ | ||
| 131 | + protected String getProductKey(List<String> uriPath) { | ||
| 132 | + throw new UnsupportedOperationException("子类需要实现 getProductKey 方法"); | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + /** | ||
| 136 | + * 从 URI 路径中获取 deviceName(子类实现) | ||
| 137 | + * <p> | ||
| 138 | + * 默认抛出异常,需要认证的子类必须实现此方法 | ||
| 139 | + * | ||
| 140 | + * @param uriPath URI 路径 | ||
| 141 | + * @return deviceName | ||
| 142 | + */ | ||
| 143 | + protected String getDeviceName(List<String> uriPath) { | ||
| 144 | + throw new UnsupportedOperationException("子类需要实现 getDeviceName 方法"); | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + /** | ||
| 148 | + * 从自定义 CoAP Option 中获取 Token | ||
| 149 | + * | ||
| 150 | + * @param exchange CoAP 交换对象 | ||
| 151 | + * @return Token 值,如果不存在则返回 null | ||
| 152 | + */ | ||
| 153 | + protected String getTokenFromOption(CoapExchange exchange) { | ||
| 154 | + Option option = CollUtil.findOne(exchange.getRequestOptions().getOthers(), | ||
| 155 | + o -> o.getNumber() == OPTION_TOKEN); | ||
| 156 | + return option != null ? new String(option.getValue()) : null; | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + // ========== 序列化相关方法 ========== | ||
| 160 | + | ||
| 161 | + /** | ||
| 162 | + * 解析请求体为指定类型 | ||
| 163 | + * | ||
| 164 | + * @param exchange CoAP 交换对象 | ||
| 165 | + * @param clazz 目标类型 | ||
| 166 | + * @param <T> 目标类型泛型 | ||
| 167 | + * @return 解析后的对象,解析失败返回 null | ||
| 168 | + */ | ||
| 169 | + protected <T> T deserializeRequest(CoapExchange exchange, Class<T> clazz) { | ||
| 170 | + byte[] payload = exchange.getRequestPayload(); | ||
| 171 | + if (ArrayUtil.isEmpty(payload)) { | ||
| 172 | + return null; | ||
| 173 | + } | ||
| 174 | + return JsonUtils.parseObject(payload, clazz); | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + private static String serializeResponse(Object data) { | ||
| 178 | + return JsonUtils.toJsonString(data); | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + protected void writeResponse(CoapExchange exchange, CommonResult<?> data) { | ||
| 182 | + String json = serializeResponse(data); | ||
| 183 | + exchange.respond(CoAP.ResponseCode.CONTENT, json, MediaTypeRegistry.APPLICATION_JSON); | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | +} |
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/handler/upstream/IotCoapAuthHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.gateway.protocol.coap.handler.upstream; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.lang.Assert; | ||
| 4 | +import cn.hutool.core.map.MapUtil; | ||
| 5 | +import cn.hutool.core.util.BooleanUtil; | ||
| 6 | +import cn.hutool.extra.spring.SpringUtil; | ||
| 7 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 8 | +import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; | ||
| 9 | +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; | ||
| 10 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 11 | +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; | ||
| 12 | +import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; | ||
| 13 | +import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; | ||
| 14 | +import lombok.extern.slf4j.Slf4j; | ||
| 15 | +import org.eclipse.californium.core.server.resources.CoapExchange; | ||
| 16 | + | ||
| 17 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 18 | +import static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_AUTH_FAIL; | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * IoT 网关 CoAP 协议的【认证】处理器 | ||
| 22 | + * | ||
| 23 | + * @author 芋道源码 | ||
| 24 | + */ | ||
| 25 | +@Slf4j | ||
| 26 | +public class IotCoapAuthHandler extends IotCoapAbstractHandler { | ||
| 27 | + | ||
| 28 | + private final String serverId; | ||
| 29 | + | ||
| 30 | + private final IotDeviceTokenService deviceTokenService; | ||
| 31 | + private final IotDeviceCommonApi deviceApi; | ||
| 32 | + private final IotDeviceMessageService deviceMessageService; | ||
| 33 | + | ||
| 34 | + public IotCoapAuthHandler(String serverId) { | ||
| 35 | + this.serverId = serverId; | ||
| 36 | + this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); | ||
| 37 | + this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class); | ||
| 38 | + this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + @Override | ||
| 42 | + @SuppressWarnings("DuplicatedCode") | ||
| 43 | + protected CommonResult<Object> handle0(CoapExchange exchange) { | ||
| 44 | + // 1. 解析参数 | ||
| 45 | + IotDeviceAuthReqDTO request = deserializeRequest(exchange, IotDeviceAuthReqDTO.class); | ||
| 46 | + Assert.notNull(request, "请求体不能为空"); | ||
| 47 | + Assert.notBlank(request.getClientId(), "clientId 不能为空"); | ||
| 48 | + Assert.notBlank(request.getUsername(), "username 不能为空"); | ||
| 49 | + Assert.notBlank(request.getPassword(), "password 不能为空"); | ||
| 50 | + | ||
| 51 | + // 2.1 执行认证 | ||
| 52 | + CommonResult<Boolean> result = deviceApi.authDevice(request); | ||
| 53 | + result.checkError(); | ||
| 54 | + if (BooleanUtil.isFalse(result.getData())) { | ||
| 55 | + throw exception(DEVICE_AUTH_FAIL); | ||
| 56 | + } | ||
| 57 | + // 2.2 生成 Token | ||
| 58 | + IotDeviceIdentity deviceInfo = deviceTokenService.parseUsername(request.getUsername()); | ||
| 59 | + Assert.notNull(deviceInfo, "设备信息不能为空"); | ||
| 60 | + String token = deviceTokenService.createToken(deviceInfo.getProductKey(), deviceInfo.getDeviceName()); | ||
| 61 | + Assert.notBlank(token, "生成 token 不能为空"); | ||
| 62 | + | ||
| 63 | + // 3. 执行上线 | ||
| 64 | + IotDeviceMessage message = IotDeviceMessage.buildStateUpdateOnline(); | ||
| 65 | + deviceMessageService.sendDeviceMessage(message, | ||
| 66 | + deviceInfo.getProductKey(), deviceInfo.getDeviceName(), serverId); | ||
| 67 | + | ||
| 68 | + // 4. 构建响应数据 | ||
| 69 | + return CommonResult.success(MapUtil.of("token", token)); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | +} |