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 | 101 | <artifactId>spring-boot-starter-amqp</artifactId> |
| 102 | 102 | <optional>true</optional> |
| 103 | 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 | 104 | </dependencies> |
| 117 | 105 | |
| 118 | 106 | </project> | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.api.device; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.hutool.core.util.ObjUtil; | |
| 5 | +import cn.hutool.core.util.StrUtil; | |
| 3 | 6 | import cn.iocoder.yudao.framework.common.enums.RpcConstants; |
| 4 | 7 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | 8 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 9 | +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; | |
| 6 | 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 | 12 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; |
| 12 | 13 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; |
| 13 | 14 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; |
| 14 | 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 | 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 | 21 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; |
| 17 | 22 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; |
| 18 | 23 | import jakarta.annotation.Resource; |
| 19 | 24 | import jakarta.annotation.security.PermitAll; |
| 25 | +import org.springframework.context.annotation.Lazy; | |
| 20 | 26 | import org.springframework.context.annotation.Primary; |
| 21 | 27 | import org.springframework.validation.annotation.Validated; |
| 22 | 28 | import org.springframework.web.bind.annotation.PostMapping; |
| 23 | 29 | import org.springframework.web.bind.annotation.RequestBody; |
| 24 | 30 | import org.springframework.web.bind.annotation.RestController; |
| 25 | 31 | |
| 32 | +import java.util.ArrayList; | |
| 26 | 33 | import java.util.List; |
| 34 | +import java.util.Map; | |
| 35 | +import java.util.Set; | |
| 27 | 36 | |
| 28 | 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 | 41 | * IoT 设备 API 实现类 |
| ... | ... | @@ -41,6 +51,12 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { |
| 41 | 51 | private IotDeviceService deviceService; |
| 42 | 52 | @Resource |
| 43 | 53 | private IotProductService productService; |
| 54 | + @Resource | |
| 55 | + @Lazy // 延迟加载,解决循环依赖 | |
| 56 | + private IotDeviceModbusConfigService modbusConfigService; | |
| 57 | + @Resource | |
| 58 | + @Lazy // 延迟加载,解决循环依赖 | |
| 59 | + private IotDeviceModbusPointService modbusPointService; | |
| 44 | 60 | |
| 45 | 61 | @Override |
| 46 | 62 | @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/auth") |
| ... | ... | @@ -58,12 +74,58 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { |
| 58 | 74 | return success(BeanUtils.toBean(device, IotDeviceRespDTO.class, deviceDTO -> { |
| 59 | 75 | IotProductDO product = productService.getProductFromCache(deviceDTO.getProductId()); |
| 60 | 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 | 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 | 129 | @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/register") |
| 68 | 130 | @PermitAll |
| 69 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 4 | 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 | 6 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; |
| 7 | 7 | import io.swagger.v3.oas.annotations.media.Schema; |
| 8 | 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 | 38 | @Schema(description = "请求编号", example = "req_123") |
| 39 | 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 | 42 | private String method; |
| 43 | 43 | |
| 44 | 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 | 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 | 152 | @GetMapping("/simple-list") |
| 145 | 153 | @Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项") |
| 146 | 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 | 67 | @DictFormat(DictTypeConstants.NET_TYPE) |
| 68 | 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 | 80 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 76 | 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 | 1 | package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; |
| 2 | 2 | |
| 3 | 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 | 6 | import cn.iocoder.yudao.module.iot.enums.product.IotNetTypeEnum; |
| 5 | 7 | import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; |
| 6 | 8 | import io.swagger.v3.oas.annotations.media.Schema; |
| ... | ... | @@ -44,9 +46,15 @@ public class IotProductSaveReqVO { |
| 44 | 46 | @InEnum(value = IotNetTypeEnum.class, message = "联网方式必须是 {value}") |
| 45 | 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 | 59 | @Schema(description = "是否开启动态注册", example = "false") |
| 52 | 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 | 5 | import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageReqVO; |
| 6 | 6 | import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryByDateRespVO; |
| 7 | 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 | 9 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; |
| 10 | 10 | import cn.iocoder.yudao.module.iot.service.device.message.IotDeviceMessageService; |
| 11 | 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 | 4 | import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; |
| 5 | 5 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; |
| 6 | 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 | 8 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 9 | 9 | import com.baomidou.mybatisplus.annotation.TableField; |
| 10 | 10 | import com.baomidou.mybatisplus.annotation.TableId; |
| ... | ... | @@ -109,10 +109,6 @@ public class IotDeviceDO extends TenantBaseDO { |
| 109 | 109 | private LocalDateTime activeTime; |
| 110 | 110 | |
| 111 | 111 | /** |
| 112 | - * 设备的 IP 地址 | |
| 113 | - */ | |
| 114 | - private String ip; | |
| 115 | - /** | |
| 116 | 112 | * 固件编号 |
| 117 | 113 | * |
| 118 | 114 | * 关联 {@link IotOtaFirmwareDO#getId()} | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceMessageDO.java
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 | 78 | */ |
| 79 | 79 | private Integer netType; |
| 80 | 80 | /** |
| 81 | - * 数据格式(编解码器类型) | |
| 81 | + * 协议类型 | |
| 82 | 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 | 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 | 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 | 45 | default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) { |
| 42 | 46 | return selectCount(new LambdaQueryWrapperX<IotProductDO>() |
| 43 | 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 | public class DictTypeConstants { |
| 9 | 9 | |
| 10 | 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 | 14 | public static final String PRODUCT_STATUS = "iot_product_status"; |
| 15 | 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 | 54 | ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在"); |
| 55 | 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 | 65 | // ========== OTA 固件相关 1-050-008-000 ========== |
| 58 | 66 | |
| 59 | 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 | 19 | TCP(2, "TCP"), |
| 20 | 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 | 25 | REDIS(21, "Redis"), |
| 26 | 26 | |
| 27 | 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 | 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 5 | 5 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; |
| 6 | 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 | 8 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 9 | 9 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 10 | 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 | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | 5 | import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; |
| 6 | 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 | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 9 | 9 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; |
| 10 | 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 | 3 | import cn.hutool.core.util.ObjectUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| 5 | 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 | 7 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 8 | 8 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; |
| 9 | 9 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| ... | ... | @@ -67,7 +67,6 @@ public class IotDeviceMessageSubscriber implements IotMessageSubscriber<IotDevic |
| 67 | 67 | IotDeviceDO device = deviceService.validateDeviceExistsFromCache(message.getDeviceId()); |
| 68 | 68 | devicePropertyService.updateDeviceReportTimeAsync(device.getId(), LocalDateTime.now()); |
| 69 | 69 | // 1.2 更新设备的连接 server |
| 70 | - // TODO 芋艿:HTTP 网关的上行消息,不应该更新 serverId,会覆盖掉 MQTT 等长连接的 serverId,导致下行消息无法发送。 | |
| 71 | 70 | devicePropertyService.updateDeviceServerIdAsync(device.getId(), message.getServerId()); |
| 72 | 71 | |
| 73 | 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 | 17 | */ |
| 18 | 18 | @Component |
| 19 | 19 | @Slf4j |
| 20 | -public class IotDataRuleMessageHandler implements IotMessageSubscriber<IotDeviceMessage> { | |
| 20 | +public class IotDataRuleMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> { | |
| 21 | 21 | |
| 22 | 22 | @Resource |
| 23 | 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 | 9 | import lombok.extern.slf4j.Slf4j; |
| 10 | 10 | import org.springframework.stereotype.Component; |
| 11 | 11 | |
| 12 | -// TODO @puhui999:后面重构哈 | |
| 13 | 12 | /** |
| 14 | 13 | * 针对 {@link IotDeviceMessage} 的消费者,处理规则场景 |
| 15 | 14 | * |
| ... | ... | @@ -17,7 +16,7 @@ import org.springframework.stereotype.Component; |
| 17 | 16 | */ |
| 18 | 17 | @Component |
| 19 | 18 | @Slf4j |
| 20 | -public class IotSceneRuleMessageHandler implements IotMessageSubscriber<IotDeviceMessage> { | |
| 19 | +public class IotSceneRuleMessageSubscriber implements IotMessageSubscriber<IotDeviceMessage> { | |
| 21 | 20 | |
| 22 | 21 | @Resource |
| 23 | 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 | 4 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; |
| 5 | 5 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; |
| 6 | 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 | 8 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 9 | 9 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 10 | 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 | 17 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; |
| 18 | 18 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; |
| 19 | 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 | 21 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 22 | 22 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 23 | 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 | 29 | import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO; |
| 30 | 30 | import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetRespDTO; |
| 31 | 31 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; |
| 32 | +import cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils; | |
| 32 | 33 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 33 | 34 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; |
| 34 | 35 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| ... | ... | @@ -819,8 +820,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { |
| 819 | 820 | if (BooleanUtil.isFalse(product.getRegisterEnabled())) { |
| 820 | 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 | 826 | throw exception(DEVICE_REGISTER_SECRET_INVALID); |
| 825 | 827 | } |
| 826 | 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 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.convert.Convert; |
| 5 | 5 | import cn.hutool.core.lang.Assert; |
| 6 | -import cn.hutool.core.map.MapUtil; | |
| 7 | 6 | import cn.hutool.core.util.StrUtil; |
| 8 | 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 | 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 | 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 | 15 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 12 | 16 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; |
| 13 | 17 | import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaTaskRecordDO; |
| ... | ... | @@ -133,9 +137,9 @@ public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService { |
| 133 | 137 | public boolean pushOtaTaskRecord(IotOtaTaskRecordDO record, IotOtaFirmwareDO fireware, IotDeviceDO device) { |
| 134 | 138 | try { |
| 135 | 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 | 143 | deviceMessageService.sendDeviceMessage(message, device); |
| 140 | 144 | |
| 141 | 145 | // 2. 更新 OTA 升级记录状态为进行中 |
| ... | ... | @@ -163,17 +167,16 @@ public class IotOtaTaskRecordServiceImpl implements IotOtaTaskRecordService { |
| 163 | 167 | |
| 164 | 168 | @Override |
| 165 | 169 | @Transactional(rollbackFor = Exception.class) |
| 166 | - @SuppressWarnings("unchecked") | |
| 167 | 170 | public void updateOtaRecordProgress(IotDeviceDO device, IotDeviceMessage message) { |
| 168 | 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 | 174 | Assert.notBlank(version, "version 不能为空"); |
| 172 | - Integer status = MapUtil.getInt(params, "status"); | |
| 175 | + Integer status = params.getStatus(); | |
| 173 | 176 | Assert.notNull(status, "status 不能为空"); |
| 174 | 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 | 180 | Assert.notNull(progress, "progress 不能为空"); |
| 178 | 181 | Assert.isTrue(progress >= 0 && progress <= 100, "progress 必须在 0-100 之间"); |
| 179 | 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 | 10 | import java.time.LocalDateTime; |
| 11 | 11 | import java.util.Collection; |
| 12 | 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 | 18 | * IoT 产品 Service 接口 |
| ... | ... | @@ -122,10 +125,35 @@ public interface IotProductService { |
| 122 | 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 | 148 | * @param ids 产品编号集合 |
| 128 | 149 | */ |
| 129 | 150 | void validateProductsExist(Collection<Long> ids); |
| 130 | 151 | |
| 152 | + /** | |
| 153 | + * 同步产品的 TDengine 表结构 | |
| 154 | + * | |
| 155 | + * 目的:当 MySQL 和 TDengine 不同步时,强制将已发布产品的表结构同步到 TDengine 中 | |
| 156 | + */ | |
| 157 | + void syncProductPropertyTable(); | |
| 158 | + | |
| 131 | 159 | } |
| 132 | 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 | 15 | import cn.iocoder.yudao.module.iot.service.device.property.IotDevicePropertyService; |
| 16 | 16 | import com.baomidou.dynamic.datasource.annotation.DSTransactional; |
| 17 | 17 | import jakarta.annotation.Resource; |
| 18 | +import lombok.extern.slf4j.Slf4j; | |
| 18 | 19 | import org.springframework.cache.annotation.CacheEvict; |
| 19 | 20 | import org.springframework.cache.annotation.Cacheable; |
| 21 | +import org.springframework.context.annotation.Lazy; | |
| 20 | 22 | import org.springframework.stereotype.Service; |
| 21 | 23 | import org.springframework.validation.annotation.Validated; |
| 22 | 24 | |
| ... | ... | @@ -33,6 +35,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; |
| 33 | 35 | * |
| 34 | 36 | * @author ahh |
| 35 | 37 | */ |
| 38 | +@Slf4j | |
| 36 | 39 | @Service |
| 37 | 40 | @Validated |
| 38 | 41 | public class IotProductServiceImpl implements IotProductService { |
| ... | ... | @@ -41,9 +44,10 @@ public class IotProductServiceImpl implements IotProductService { |
| 41 | 44 | private IotProductMapper productMapper; |
| 42 | 45 | |
| 43 | 46 | @Resource |
| 44 | - private IotDevicePropertyService devicePropertyDataService; | |
| 45 | - @Resource | |
| 46 | 47 | private IotDeviceService deviceService; |
| 48 | + @Resource | |
| 49 | + @Lazy // 延迟加载,避免循环依赖 | |
| 50 | + private IotDevicePropertyService devicePropertyDataService; | |
| 47 | 51 | |
| 48 | 52 | @Override |
| 49 | 53 | public Long createProduct(IotProductSaveReqVO createReqVO) { |
| ... | ... | @@ -172,6 +176,32 @@ public class IotProductServiceImpl implements IotProductService { |
| 172 | 176 | } |
| 173 | 177 | |
| 174 | 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 | 205 | public void validateProductsExist(Collection<Long> ids) { |
| 176 | 206 | if (CollUtil.isEmpty(ids)) { |
| 177 | 207 | return; | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotDataRuleCacheableAction.java
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 | 15 | import org.springframework.stereotype.Component; |
| 16 | 16 | |
| 17 | 17 | import java.util.List; |
| 18 | -import java.util.Map; | |
| 19 | 18 | |
| 20 | 19 | /** |
| 21 | 20 | * IoT 设备属性设置的 {@link IotSceneRuleAction} 实现类 |
| ... | ... | @@ -24,7 +23,7 @@ import java.util.Map; |
| 24 | 23 | */ |
| 25 | 24 | @Component |
| 26 | 25 | @Slf4j |
| 27 | -public class IotDeviceControlSceneRuleAction implements IotSceneRuleAction { | |
| 26 | +public class IotDevicePropertySetSceneRuleAction implements IotSceneRuleAction { | |
| 28 | 27 | |
| 29 | 28 | @Resource |
| 30 | 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 | 15 | import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotThingModelMapper; |
| 16 | 16 | import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; |
| 17 | 17 | import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; |
| 18 | +import cn.iocoder.yudao.module.iot.service.device.IotDeviceModbusPointService; | |
| 18 | 19 | import cn.iocoder.yudao.module.iot.service.product.IotProductService; |
| 19 | 20 | import jakarta.annotation.Resource; |
| 20 | 21 | import lombok.extern.slf4j.Slf4j; |
| ... | ... | @@ -50,6 +51,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { |
| 50 | 51 | @Resource |
| 51 | 52 | @Lazy // 延迟加载,解决循环依赖 |
| 52 | 53 | private IotProductService productService; |
| 54 | + @Resource | |
| 55 | + @Lazy // 延迟加载,解决循环依赖 | |
| 56 | + private IotDeviceModbusPointService deviceModbusPointService; | |
| 53 | 57 | |
| 54 | 58 | @Override |
| 55 | 59 | @Transactional(rollbackFor = Exception.class) |
| ... | ... | @@ -84,7 +88,11 @@ public class IotThingModelServiceImpl implements IotThingModelService { |
| 84 | 88 | IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(updateReqVO); |
| 85 | 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 | 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 | 3 | import cn.hutool.core.collection.ListUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 5 | 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 | 7 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 8 | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; |
| 9 | 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 | 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 | 4 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 5 | 5 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; |
| 6 | 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 | 1 | package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; |
| 2 | 2 | |
| 3 | 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 | 5 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | 6 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; |
| 7 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.biz; |
| 2 | 2 | |
| 3 | 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 | 5 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; |
| 9 | 6 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; |
| 10 | 7 | import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; |
| ... | ... | @@ -50,4 +47,12 @@ public interface IotDeviceCommonApi { |
| 50 | 47 | */ |
| 51 | 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 | 34 | */ |
| 35 | 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 | 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 | 64 | // ========== OTA 固件 ========== |
| 65 | 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 | 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 | 24 | */ |
| 25 | 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 | 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 | 26 | */ |
| 27 | 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 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.mq.message; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.map.MapUtil; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; |
| 5 | 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 | 7 | import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; |
| 8 | 8 | import lombok.AllArgsConstructor; |
| 9 | 9 | import lombok.Builder; |
| ... | ... | @@ -60,7 +60,7 @@ public class IotDeviceMessage { |
| 60 | 60 | */ |
| 61 | 61 | private String serverId; |
| 62 | 62 | |
| 63 | - // ========== codec(编解码)字段 ========== | |
| 63 | + // ========== serialize(序列化)相关字段 ========== | |
| 64 | 64 | |
| 65 | 65 | /** |
| 66 | 66 | * 请求编号 |
| ... | ... | @@ -72,7 +72,7 @@ public class IotDeviceMessage { |
| 72 | 72 | * 请求方法 |
| 73 | 73 | * |
| 74 | 74 | * 枚举 {@link IotDeviceMessageMethodEnum} |
| 75 | - * 例如说:thing.property.report 属性上报 | |
| 75 | + * 例如说:thing.property.post 属性上报 | |
| 76 | 76 | */ |
| 77 | 77 | private String method; |
| 78 | 78 | /** |
| ... | ... | @@ -94,7 +94,7 @@ public class IotDeviceMessage { |
| 94 | 94 | */ |
| 95 | 95 | private String msg; |
| 96 | 96 | |
| 97 | - // ========== 基础方法:只传递"codec(编解码)字段" ========== | |
| 97 | + // ========== 基础方法:只传递"serialize(序列化)相关字段" ========== | |
| 98 | 98 | |
| 99 | 99 | public static IotDeviceMessage requestOf(String method) { |
| 100 | 100 | return requestOf(null, method, null); |
| ... | ... | @@ -149,20 +149,12 @@ public class IotDeviceMessage { |
| 149 | 149 | |
| 150 | 150 | public static IotDeviceMessage buildStateUpdateOnline() { |
| 151 | 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 | 155 | public static IotDeviceMessage buildStateOffline() { |
| 156 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.topic.auth; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | |
| 3 | 4 | import jakarta.validation.constraints.NotEmpty; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | |
| 6 | 7 | /** |
| 7 | 8 | * IoT 设备动态注册 Request DTO |
| 8 | 9 | * <p> |
| 9 | - * 用于直连设备/网关的一型一密动态注册:使用 productSecret 验证,返回 deviceSecret | |
| 10 | + * 用于 {@link IotDeviceMessageMethodEnum#DEVICE_REGISTER} 消息的 params 参数 | |
| 11 | + * <p> | |
| 12 | + * 直连设备/网关的一型一密动态注册:使用 productSecret 验证,返回 deviceSecret | |
| 10 | 13 | * |
| 11 | 14 | * @author 芋道源码 |
| 12 | 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 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.topic.auth; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | |
| 3 | 4 | import lombok.AllArgsConstructor; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | import lombok.NoArgsConstructor; |
| ... | ... | @@ -7,7 +8,7 @@ import lombok.NoArgsConstructor; |
| 7 | 8 | /** |
| 8 | 9 | * IoT 设备动态注册 Response DTO |
| 9 | 10 | * <p> |
| 10 | - * 用于直连设备/网关的一型一密动态注册响应 | |
| 11 | + * 用于 {@link IotDeviceMessageMethodEnum#DEVICE_REGISTER} 响应的设备信息 | |
| 11 | 12 | * |
| 12 | 13 | * @author 芋道源码 |
| 13 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.topic.auth; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | |
| 3 | 4 | import jakarta.validation.constraints.NotEmpty; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | |
| 6 | 7 | /** |
| 7 | 8 | * IoT 子设备动态注册 Request DTO |
| 8 | 9 | * <p> |
| 9 | - * 用于 thing.auth.register.sub 消息的 params 数组元素 | |
| 10 | - * | |
| 10 | + * 用于 {@link IotDeviceMessageMethodEnum#SUB_DEVICE_REGISTER} 消息的 params 数组元素 | |
| 11 | + * <p> | |
| 11 | 12 | * 特殊:网关子设备的动态注册,必须已经创建好该网关子设备(不然哪来的 {@link #deviceName} 字段)。更多的好处,是设备不用提前烧录 deviceSecret 密钥。 |
| 12 | 13 | * |
| 13 | 14 | * @author 芋道源码 | ... | ... |
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotSubDeviceRegisterRespDTO.java
| 1 | 1 | package cn.iocoder.yudao.module.iot.core.topic.auth; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | |
| 3 | 4 | import lombok.AllArgsConstructor; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | import lombok.NoArgsConstructor; |
| ... | ... | @@ -7,7 +8,7 @@ import lombok.NoArgsConstructor; |
| 7 | 8 | /** |
| 8 | 9 | * IoT 子设备动态注册 Response DTO |
| 9 | 10 | * <p> |
| 10 | - * 用于 thing.auth.register.sub 响应的设备信息 | |
| 11 | + * 用于 {@link IotDeviceMessageMethodEnum#SUB_DEVICE_REGISTER} 响应的设备信息 | |
| 11 | 12 | * |
| 12 | 13 | * @author 芋道源码 |
| 13 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.topic.event; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | |
| 3 | 4 | import lombok.Data; |
| 4 | 5 | |
| 5 | 6 | /** |
| 6 | 7 | * IoT 设备事件上报 Request DTO |
| 7 | 8 | * <p> |
| 8 | - * 用于 thing.event.post 消息的 params 参数 | |
| 9 | + * 用于 {@link IotDeviceMessageMethodEnum#EVENT_POST} 消息的 params 参数 | |
| 9 | 10 | * |
| 10 | 11 | * @author 芋道源码 |
| 11 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.topic.property; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | |
| 3 | 4 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | |
| ... | ... | @@ -9,7 +10,7 @@ import java.util.Map; |
| 9 | 10 | /** |
| 10 | 11 | * IoT 设备属性批量上报 Request DTO |
| 11 | 12 | * <p> |
| 12 | - * 用于 thing.event.property.pack.post 消息的 params 参数 | |
| 13 | + * 用于 {@link IotDeviceMessageMethodEnum#PROPERTY_PACK_POST} 消息的 params 参数 | |
| 13 | 14 | * |
| 14 | 15 | * @author 芋道源码 |
| 15 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.topic.property; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | |
| 4 | + | |
| 3 | 5 | import java.util.HashMap; |
| 4 | 6 | import java.util.Map; |
| 5 | 7 | |
| 6 | 8 | /** |
| 7 | 9 | * IoT 设备属性上报 Request DTO |
| 8 | 10 | * <p> |
| 9 | - * 用于 thing.property.post 消息的 params 参数 | |
| 11 | + * 用于 {@link IotDeviceMessageMethodEnum#PROPERTY_POST} 消息的 params 参数 | |
| 10 | 12 | * <p> |
| 11 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; |
| 4 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | |
| 4 | 5 | import jakarta.validation.constraints.NotEmpty; |
| 5 | 6 | import lombok.Data; |
| 6 | 7 | |
| ... | ... | @@ -9,7 +10,7 @@ import java.util.List; |
| 9 | 10 | /** |
| 10 | 11 | * IoT 设备拓扑添加 Request DTO |
| 11 | 12 | * <p> |
| 12 | - * 用于 thing.topo.add 消息的 params 参数 | |
| 13 | + * 用于 {@link IotDeviceMessageMethodEnum#TOPO_ADD} 消息的 params 参数 | |
| 13 | 14 | * |
| 14 | 15 | * @author 芋道源码 |
| 15 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | |
| 3 | 4 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 4 | 5 | import lombok.AllArgsConstructor; |
| 5 | 6 | import lombok.Data; |
| ... | ... | @@ -10,7 +11,7 @@ import java.util.List; |
| 10 | 11 | /** |
| 11 | 12 | * IoT 设备拓扑关系变更通知 Request DTO |
| 12 | 13 | * <p> |
| 13 | - * 用于 thing.topo.change 下行消息的 params 参数 | |
| 14 | + * 用于 {@link IotDeviceMessageMethodEnum#TOPO_CHANGE} 下行消息的 params 参数 | |
| 14 | 15 | * |
| 15 | 16 | * @author 芋道源码 |
| 16 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | |
| 3 | 4 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 4 | 5 | import jakarta.validation.Valid; |
| 5 | 6 | import jakarta.validation.constraints.NotEmpty; |
| ... | ... | @@ -10,7 +11,7 @@ import java.util.List; |
| 10 | 11 | /** |
| 11 | 12 | * IoT 设备拓扑删除 Request DTO |
| 12 | 13 | * <p> |
| 13 | - * 用于 thing.topo.delete 消息的 params 参数 | |
| 14 | + * 用于 {@link IotDeviceMessageMethodEnum#TOPO_DELETE} 消息的 params 参数 | |
| 14 | 15 | * |
| 15 | 16 | * @author 芋道源码 |
| 16 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | |
| 3 | 4 | import lombok.Data; |
| 4 | 5 | |
| 5 | 6 | /** |
| 6 | 7 | * IoT 设备拓扑关系获取 Request DTO |
| 7 | 8 | * <p> |
| 8 | - * 用于 thing.topo.get 请求的 params 参数(目前为空,预留扩展) | |
| 9 | + * 用于 {@link IotDeviceMessageMethodEnum#TOPO_GET} 请求的 params 参数(目前为空,预留扩展) | |
| 9 | 10 | * |
| 10 | 11 | * @author 芋道源码 |
| 11 | 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 | 1 | package cn.iocoder.yudao.module.iot.core.topic.topo; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | |
| 3 | 4 | import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | |
| ... | ... | @@ -8,7 +9,7 @@ import java.util.List; |
| 8 | 9 | /** |
| 9 | 10 | * IoT 设备拓扑关系获取 Response DTO |
| 10 | 11 | * <p> |
| 11 | - * 用于 thing.topo.get 响应 | |
| 12 | + * 用于 {@link IotDeviceMessageMethodEnum#TOPO_GET} 响应 | |
| 12 | 13 | * |
| 13 | 14 | * @author 芋道源码 |
| 14 | 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 | 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 | 36 | public static String buildUsername(String productKey, String deviceName) { |
| 29 | 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 | 33 | <groupId>org.apache.rocketmq</groupId> |
| 34 | 34 | <artifactId>rocketmq-spring-boot-starter</artifactId> |
| 35 | 35 | <!-- TODO @芋艿:消息队列,后续可能去掉,默认不使用 rocketmq --> |
| 36 | -<!-- <optional>true</optional> --> | |
| 36 | + <optional>true</optional> | |
| 37 | 37 | </dependency> |
| 38 | 38 | |
| 39 | 39 | <!-- 工具类相关 --> |
| ... | ... | @@ -48,6 +48,13 @@ |
| 48 | 48 | <artifactId>vertx-mqtt</artifactId> |
| 49 | 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 | 58 | <!-- CoAP 相关 - Eclipse Californium --> |
| 52 | 59 | <dependency> |
| 53 | 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 | 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 | 5 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 31 | 6 | import org.springframework.context.annotation.Bean; |
| 32 | 7 | import org.springframework.context.annotation.Configuration; |
| 33 | 8 | |
| 9 | +/** | |
| 10 | + * IoT 网关配置类 | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 34 | 14 | @Configuration |
| 35 | 15 | @EnableConfigurationProperties(IotGatewayProperties.class) |
| 36 | -@Slf4j | |
| 37 | 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 | 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 | 14 | import jakarta.validation.constraints.NotEmpty; |
| 4 | 15 | import jakarta.validation.constraints.NotNull; |
| 5 | 16 | import lombok.Data; |
| ... | ... | @@ -24,9 +35,9 @@ public class IotGatewayProperties { |
| 24 | 35 | private TokenProperties token; |
| 25 | 36 | |
| 26 | 37 | /** |
| 27 | - * 协议配置 | |
| 38 | + * 协议实例列表 | |
| 28 | 39 | */ |
| 29 | - private ProtocolProperties protocol; | |
| 40 | + private List<ProtocolProperties> protocols; | |
| 30 | 41 | |
| 31 | 42 | @Data |
| 32 | 43 | public static class RpcProperties { |
| ... | ... | @@ -65,581 +76,157 @@ public class IotGatewayProperties { |
| 65 | 76 | |
| 66 | 77 | } |
| 67 | 78 | |
| 79 | + /** | |
| 80 | + * 协议实例配置 | |
| 81 | + */ | |
| 68 | 82 | @Data |
| 69 | 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 | 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 | 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 | 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 | 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 | 4 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus; |
| 4 | 5 | import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber; |
| 5 | 6 | import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; |
| 6 | 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 | 9 | import lombok.extern.slf4j.Slf4j; |
| 10 | 10 | |
| 11 | 11 | /** |
| 12 | - * IoT 网关 EMQX 订阅者:接收下行给设备的消息 | |
| 12 | + * IoT 协议下行消息订阅者抽象类 | |
| 13 | + * | |
| 14 | + * 负责接收来自消息总线的下行消息,并委托给子类进行业务处理 | |
| 13 | 15 | * |
| 14 | 16 | * @author 芋道源码 |
| 15 | 17 | */ |
| 18 | +@AllArgsConstructor | |
| 16 | 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 | 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 | 26 | @Override |
| 37 | 27 | public String getTopic() { |
| 38 | 28 | return IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId()); |
| 39 | 29 | } |
| 40 | 30 | |
| 31 | + /** | |
| 32 | + * 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | |
| 33 | + */ | |
| 41 | 34 | @Override |
| 42 | 35 | public String getGroup() { |
| 43 | - // 保证点对点消费,需要保证独立的 Group,所以使用 Topic 作为 Group | |
| 44 | 36 | return getTopic(); |
| 45 | 37 | } |
| 46 | 38 | |
| 47 | 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 | 52 | public void onMessage(IotDeviceMessage message) { |
| 49 | 53 | log.debug("[onMessage][接收到下行消息, messageId: {}, method: {}, deviceId: {}]", |
| 50 | 54 | message.getId(), message.getMethod(), message.getDeviceId()); |
| 51 | 55 | try { |
| 52 | 56 | // 1. 校验 |
| 53 | 57 | String method = message.getMethod(); |
| 54 | - if (method == null) { | |
| 58 | + if (StrUtil.isBlank(method)) { | |
| 55 | 59 | log.warn("[onMessage][消息方法为空, messageId: {}, deviceId: {}]", |
| 56 | 60 | message.getId(), message.getDeviceId()); |
| 57 | 61 | return; |
| 58 | 62 | } |
| 59 | 63 | |
| 60 | 64 | // 2. 处理下行消息 |
| 61 | - downstreamHandler.handle(message); | |
| 65 | + handleMessage(message); | |
| 62 | 66 | } catch (Exception e) { |
| 63 | 67 | log.error("[onMessage][处理下行消息失败, messageId: {}, method: {}, deviceId: {}]", |
| 64 | 68 | message.getId(), message.getMethod(), message.getDeviceId(), e); |
| 65 | 69 | } |
| 66 | 70 | } |
| 67 | 71 | |
| 68 | -} | |
| 69 | 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 | +} | ... | ... |