【功能新增】IoT:设备拓扑图的添加
This commit is contained in:
parent
4254c06c37
commit
bc9b3715b1
|
@ -62,6 +62,15 @@ public interface IotDeviceUpstreamApi {
|
||||||
@PostMapping(PREFIX + "/register-sub")
|
@PostMapping(PREFIX + "/register-sub")
|
||||||
CommonResult<Boolean> registerSubDevice(@Valid @RequestBody IotDeviceRegisterSubReqDTO registerReqDTO);
|
CommonResult<Boolean> registerSubDevice(@Valid @RequestBody IotDeviceRegisterSubReqDTO registerReqDTO);
|
||||||
|
|
||||||
|
// TODO @芋艿:这个需要 plugins 接入下
|
||||||
|
/**
|
||||||
|
* 注册设备拓扑
|
||||||
|
*
|
||||||
|
* @param addReqDTO 注册设备拓扑 DTO
|
||||||
|
*/
|
||||||
|
@PostMapping(PREFIX + "/add-topology")
|
||||||
|
CommonResult<Boolean> addDeviceTopology(@Valid @RequestBody IotDeviceTopologyAddReqDTO addReqDTO);
|
||||||
|
|
||||||
// ========== 插件相关 ==========
|
// ========== 插件相关 ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,6 +7,8 @@ import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IoT 设备【事件】上报 Request DTO
|
* IoT 设备【事件】上报 Request DTO
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO {
|
public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO {
|
||||||
|
|
|
@ -7,6 +7,8 @@ import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IoT 设备【属性】上报 Request DTO
|
* IoT 设备【属性】上报 Request DTO
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class IotDevicePropertyReportReqDTO extends IotDeviceUpstreamAbstractReqDTO {
|
public class IotDevicePropertyReportReqDTO extends IotDeviceUpstreamAbstractReqDTO {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import java.util.List;
|
||||||
@Data
|
@Data
|
||||||
public class IotDeviceRegisterSubReqDTO extends IotDeviceUpstreamAbstractReqDTO {
|
public class IotDeviceRegisterSubReqDTO extends IotDeviceUpstreamAbstractReqDTO {
|
||||||
|
|
||||||
|
// TODO @芋艿:看看要不要优化命名
|
||||||
/**
|
/**
|
||||||
* 子设备数组
|
* 子设备数组
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -7,6 +7,8 @@ import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IoT 设备【状态】更新 Request DTO
|
* IoT 设备【状态】更新 Request DTO
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class IotDeviceStateUpdateReqDTO extends IotDeviceUpstreamAbstractReqDTO {
|
public class IotDeviceStateUpdateReqDTO extends IotDeviceUpstreamAbstractReqDTO {
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IoT 设备【拓扑】添加 Request DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class IotDeviceTopologyAddReqDTO extends IotDeviceUpstreamAbstractReqDTO {
|
||||||
|
|
||||||
|
// TODO @芋艿:看看要不要优化命名
|
||||||
|
/**
|
||||||
|
* 子设备数组
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "子设备不能为空")
|
||||||
|
private List<IotDeviceRegisterSubReqDTO.Device> params;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class Device {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产品标识
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "产品标识不能为空")
|
||||||
|
private String productKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备名称
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "设备名称不能为空")
|
||||||
|
private String deviceName;
|
||||||
|
|
||||||
|
// TODO @芋艿:阿里云还有 sign 签名
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -30,7 +30,10 @@ public enum IotDeviceMessageIdentifierEnum {
|
||||||
|
|
||||||
REGISTER_REGISTER("register"), // 上行
|
REGISTER_REGISTER("register"), // 上行
|
||||||
REGISTER_REGISTER_SUB("register_sub"), // 上行
|
REGISTER_REGISTER_SUB("register_sub"), // 上行
|
||||||
REGISTER_UNREGISTER_SUB("unregister_sub"),; // 下行
|
REGISTER_UNREGISTER_SUB("unregister_sub"), // 下行
|
||||||
|
|
||||||
|
TOPOLOGY_ADD("topology_add"), // 下行;
|
||||||
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标志符
|
* 标志符
|
||||||
|
|
|
@ -19,7 +19,8 @@ public enum IotDeviceMessageTypeEnum implements ArrayValuable<String> {
|
||||||
SERVICE("service"), // 设备服务:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务
|
SERVICE("service"), // 设备服务:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务
|
||||||
CONFIG("config"), // 设备配置:可参考 https://help.aliyun.com/zh/iot/user-guide/remote-configuration-1 远程配置
|
CONFIG("config"), // 设备配置:可参考 https://help.aliyun.com/zh/iot/user-guide/remote-configuration-1 远程配置
|
||||||
OTA("ota"), // 设备 OTA:可参考 https://help.aliyun.com/zh/iot/user-guide/ota-update OTA 升级
|
OTA("ota"), // 设备 OTA:可参考 https://help.aliyun.com/zh/iot/user-guide/ota-update OTA 升级
|
||||||
REGISTER("register"),; // 设备注册:可参考 https://help.aliyun.com/zh/iot/user-guide/register-devices 设备身份注册
|
REGISTER("register"), // 设备注册:可参考 https://help.aliyun.com/zh/iot/user-guide/register-devices 设备身份注册
|
||||||
|
TOPOLOGY("topology"),; // 设备拓扑:可参考 https://help.aliyun.com/zh/iot/user-guide/manage-topological-relationships 设备拓扑
|
||||||
|
|
||||||
public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageTypeEnum::getType).toArray(String[]::new);
|
public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageTypeEnum::getType).toArray(String[]::new);
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,12 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi {
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonResult<Boolean> addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO) {
|
||||||
|
deviceUpstreamService.addDeviceTopology(addReqDTO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 插件相关 ==========
|
// ========== 插件相关 ==========
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -61,6 +61,8 @@ public class IotDeviceController {
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @芋艿:参考阿里云:1)绑定网关;2)解绑网关
|
||||||
|
|
||||||
@PutMapping("/update-group")
|
@PutMapping("/update-group")
|
||||||
@Operation(summary = "更新设备分组")
|
@Operation(summary = "更新设备分组")
|
||||||
@PreAuthorize("@ss.hasPermission('iot:device:update')")
|
@PreAuthorize("@ss.hasPermission('iot:device:update')")
|
||||||
|
|
|
@ -9,8 +9,9 @@ import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
// TODO @芋艿:参考阿里云的物模型,优化 IoT 上下行消息的设计,尽量保持一致(渐进式,不要一口气)!
|
||||||
/**
|
/**
|
||||||
* 设备消息
|
* IoT 设备消息
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
|
|
@ -81,8 +81,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||||
public IotDeviceDO createDevice(String productKey, String deviceName, Long gatewayId) {
|
public IotDeviceDO createDevice(String productKey, String deviceName, Long gatewayId) {
|
||||||
String deviceKey = generateDeviceKey();
|
String deviceKey = generateDeviceKey();
|
||||||
// 1.1 校验产品是否存在
|
// 1.1 校验产品是否存在
|
||||||
IotProductDO product = TenantUtils.executeIgnore(() ->
|
IotProductDO product = TenantUtils.executeIgnore(() -> productService.getProductByProductKey(productKey));
|
||||||
productService.getProductByProductKey(productKey));
|
|
||||||
if (product == null) {
|
if (product == null) {
|
||||||
throw exception(PRODUCT_NOT_EXISTS);
|
throw exception(PRODUCT_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
@ -120,7 +119,8 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initDevice(IotDeviceDO device, IotProductDO product) {
|
private void initDevice(IotDeviceDO device, IotProductDO product) {
|
||||||
device.setProductId(product.getId()).setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType());
|
device.setProductId(product.getId()).setProductKey(product.getProductKey())
|
||||||
|
.setDeviceType(product.getDeviceType());
|
||||||
// 生成并设置必要的字段
|
// 生成并设置必要的字段
|
||||||
// TODO @芋艿:各种 mqtt 是不是可以简化!
|
// TODO @芋艿:各种 mqtt 是不是可以简化!
|
||||||
device.setDeviceSecret(generateDeviceSecret())
|
device.setDeviceSecret(generateDeviceSecret())
|
||||||
|
@ -363,7 +363,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||||
// 2.1.1 校验字段是否符合要求
|
// 2.1.1 校验字段是否符合要求
|
||||||
try {
|
try {
|
||||||
ValidationUtils.validate(importDevice);
|
ValidationUtils.validate(importDevice);
|
||||||
} catch (ConstraintViolationException ex){
|
} catch (ConstraintViolationException ex) {
|
||||||
respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage());
|
respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -427,7 +427,8 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName")
|
@CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName")
|
||||||
public void deleteDeviceCache0(IotDeviceDO device) {}
|
public void deleteDeviceCache0(IotDeviceDO device) {
|
||||||
|
}
|
||||||
|
|
||||||
private IotDeviceServiceImpl getSelf() {
|
private IotDeviceServiceImpl getSelf() {
|
||||||
return SpringUtil.getBean(getClass());
|
return SpringUtil.getBean(getClass());
|
||||||
|
|
|
@ -88,6 +88,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
|
||||||
if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.OTA.getType())) {
|
if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.OTA.getType())) {
|
||||||
return otaUpgrade(downstreamReqVO, device, parentDevice);
|
return otaUpgrade(downstreamReqVO, device, parentDevice);
|
||||||
}
|
}
|
||||||
|
// TODO @芋艿:取消设备的网关的时,要不要下发 REGISTER_UNREGISTER_SUB ?
|
||||||
throw new IllegalArgumentException("不支持的下行消息类型:" + downstreamReqVO);
|
throw new IllegalArgumentException("不支持的下行消息类型:" + downstreamReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,4 +55,11 @@ public interface IotDeviceUpstreamService {
|
||||||
*/
|
*/
|
||||||
void registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO);
|
void registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加设备拓扑
|
||||||
|
*
|
||||||
|
* @param addReqDTO 添加设备拓扑 DTO
|
||||||
|
*/
|
||||||
|
void addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,6 +220,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
|
||||||
if (CollUtil.isNotEmpty(registerReqDTO.getParams())) {
|
if (CollUtil.isNotEmpty(registerReqDTO.getParams())) {
|
||||||
registerReqDTO.getParams().forEach(subDevice -> registerDevice0(
|
registerReqDTO.getParams().forEach(subDevice -> registerDevice0(
|
||||||
subDevice.getProductKey(), subDevice.getDeviceName(), device.getId(), registerReqDTO));
|
subDevice.getProductKey(), subDevice.getDeviceName(), device.getId(), registerReqDTO));
|
||||||
|
// TODO @芋艿:后续要处理,每个设备是否成功
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 发送设备消息
|
// 3. 发送设备消息
|
||||||
|
@ -230,6 +231,52 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
|
||||||
sendDeviceMessage(message, device);
|
sendDeviceMessage(message, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO) {
|
||||||
|
// 1.1 获得设备
|
||||||
|
log.info("[addDeviceTopology][添加设备拓扑: {}]", addReqDTO);
|
||||||
|
IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
|
||||||
|
addReqDTO.getProductKey(), addReqDTO.getDeviceName());
|
||||||
|
if (device == null) {
|
||||||
|
log.error("[addDeviceTopology][设备({}/{}) 不存在]",
|
||||||
|
addReqDTO.getProductKey(), addReqDTO.getDeviceName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) {
|
||||||
|
log.error("[addDeviceTopology][设备({}/{}) 不是网关设备({}),无法进行拓扑添加]",
|
||||||
|
addReqDTO.getProductKey(), addReqDTO.getDeviceName(), device);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 1.2 记录设备的最后时间
|
||||||
|
updateDeviceLastTime(device, addReqDTO);
|
||||||
|
|
||||||
|
// 2. 处理拓扑
|
||||||
|
if (CollUtil.isNotEmpty(addReqDTO.getParams())) {
|
||||||
|
TenantUtils.execute(device.getTenantId(), () -> {
|
||||||
|
addReqDTO.getParams().forEach(subDevice -> {
|
||||||
|
IotDeviceDO subDeviceDO = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(
|
||||||
|
subDevice.getProductKey(), subDevice.getDeviceName());
|
||||||
|
// TODO @芋艿:后续要处理,每个设备是否成功
|
||||||
|
if (subDeviceDO == null) {
|
||||||
|
log.error("[addDeviceTopology][子设备({}/{}) 不存在]",
|
||||||
|
subDevice.getProductKey(), subDevice.getDeviceName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
deviceService.updateDeviceGateway(subDeviceDO.getId(), device.getId());
|
||||||
|
log.info("[addDeviceTopology][子设备({}/{}) 添加到网关设备({}) 成功]",
|
||||||
|
subDevice.getProductKey(), subDevice.getDeviceName(), device);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 发送设备消息
|
||||||
|
IotDeviceMessage message = BeanUtils.toBean(addReqDTO, IotDeviceMessage.class)
|
||||||
|
.setType(IotDeviceMessageTypeEnum.TOPOLOGY.getType())
|
||||||
|
.setIdentifier(IotDeviceMessageIdentifierEnum.TOPOLOGY_ADD.getIdentifier())
|
||||||
|
.setData(addReqDTO.getParams());
|
||||||
|
sendDeviceMessage(message, device);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) {
|
private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) {
|
||||||
// 1. 【异步】记录设备与插件实例的映射
|
// 1. 【异步】记录设备与插件实例的映射
|
||||||
pluginInstanceService.updateDevicePluginInstanceProcessIdAsync(device.getDeviceKey(), reqDTO.getProcessId());
|
pluginInstanceService.updateDevicePluginInstanceProcessIdAsync(device.getDeviceKey(), reqDTO.getProcessId());
|
||||||
|
|
|
@ -2,10 +2,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.upstream;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
|
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
|
||||||
import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO;
|
import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*;
|
||||||
import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO;
|
|
||||||
import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO;
|
|
||||||
import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO;
|
|
||||||
import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties;
|
import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -42,6 +39,24 @@ public class IotDeviceUpstreamClient implements IotDeviceUpstreamApi {
|
||||||
return doPost(url, reportReqDTO);
|
return doPost(url, reportReqDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @芋艿:待实现
|
||||||
|
@Override
|
||||||
|
public CommonResult<Boolean> registerDevice(IotDeviceRegisterReqDTO registerReqDTO) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO @芋艿:待实现
|
||||||
|
@Override
|
||||||
|
public CommonResult<Boolean> registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO @芋艿:待实现
|
||||||
|
@Override
|
||||||
|
public CommonResult<Boolean> addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommonResult<Boolean> reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) {
|
public CommonResult<Boolean> reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) {
|
||||||
String url = properties.getUpstreamUrl() + URL_PREFIX + "/report-property";
|
String url = properties.getUpstreamUrl() + URL_PREFIX + "/report-property";
|
||||||
|
|
Loading…
Reference in New Issue