【功能新增】IoT:设备拓扑图的添加

This commit is contained in:
YunaiV
2025-02-08 21:44:49 +08:00
parent 4254c06c37
commit bc9b3715b1
17 changed files with 161 additions and 18 deletions

View File

@@ -55,6 +55,12 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi {
return success(true);
}
@Override
public CommonResult<Boolean> addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO) {
deviceUpstreamService.addDeviceTopology(addReqDTO);
return success(true);
}
// ========== 插件相关 ==========
@Override

View File

@@ -61,6 +61,8 @@ public class IotDeviceController {
return success(true);
}
// TODO @芋艿参考阿里云1绑定网关2解绑网关
@PutMapping("/update-group")
@Operation(summary = "更新设备分组")
@PreAuthorize("@ss.hasPermission('iot:device:update')")

View File

@@ -9,8 +9,9 @@ import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
// TODO @芋艿:参考阿里云的物模型,优化 IoT 上下行消息的设计,尽量保持一致(渐进式,不要一口气)!
/**
* 设备消息
* IoT 设备消息
*/
@Data
@NoArgsConstructor

View File

@@ -30,12 +30,12 @@ public interface IotDeviceService {
*
* @param productKey 产品标识
* @param deviceName 设备名称
* @param gatewayId 网关设备 ID
* @param gatewayId 网关设备 ID
* @return 设备
*/
IotDeviceDO createDevice(@NotEmpty(message = "产品标识不能为空") String productKey,
@NotEmpty(message = "设备名称不能为空") String deviceName,
Long gatewayId);
@NotEmpty(message = "设备名称不能为空") String deviceName,
Long gatewayId);
/**
* 更新设备
@@ -58,7 +58,7 @@ public interface IotDeviceService {
/**
* 更新设备状态
*
* @param id 编号
* @param id 编号
* @param state 状态
*/
void updateDeviceState(Long id, Integer state);

View File

@@ -81,8 +81,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
public IotDeviceDO createDevice(String productKey, String deviceName, Long gatewayId) {
String deviceKey = generateDeviceKey();
// 1.1 校验产品是否存在
IotProductDO product = TenantUtils.executeIgnore(() ->
productService.getProductByProductKey(productKey));
IotProductDO product = TenantUtils.executeIgnore(() -> productService.getProductByProductKey(productKey));
if (product == null) {
throw exception(PRODUCT_NOT_EXISTS);
}
@@ -100,7 +99,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
}
private void validateCreateDeviceParam(String productKey, String deviceName, String deviceKey,
Long gatewayId, IotProductDO product) {
Long gatewayId, IotProductDO product) {
TenantUtils.executeIgnore(() -> {
// 校验设备名称在同一产品下是否唯一
if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) {
@@ -120,7 +119,8 @@ public class IotDeviceServiceImpl implements IotDeviceService {
}
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 是不是可以简化!
device.setDeviceSecret(generateDeviceSecret())
@@ -269,7 +269,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
// 2. 更新状态和时间
IotDeviceDO updateObj = new IotDeviceDO().setId(id).setState(state);
if (device.getOnlineTime() == null
&& Objects.equals(state, IotDeviceStateEnum.ONLINE.getState())) {
&& Objects.equals(state, IotDeviceStateEnum.ONLINE.getState())) {
updateObj.setActiveTime(LocalDateTime.now());
}
if (Objects.equals(state, IotDeviceStateEnum.ONLINE.getState())) {
@@ -363,7 +363,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
// 2.1.1 校验字段是否符合要求
try {
ValidationUtils.validate(importDevice);
} catch (ConstraintViolationException ex){
} catch (ConstraintViolationException ex) {
respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage());
return;
}
@@ -427,7 +427,8 @@ public class IotDeviceServiceImpl implements IotDeviceService {
}
@CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName")
public void deleteDeviceCache0(IotDeviceDO device) {}
public void deleteDeviceCache0(IotDeviceDO device) {
}
private IotDeviceServiceImpl getSelf() {
return SpringUtil.getBean(getClass());

View File

@@ -88,6 +88,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.OTA.getType())) {
return otaUpgrade(downstreamReqVO, device, parentDevice);
}
// TODO @芋艿:取消设备的网关的时,要不要下发 REGISTER_UNREGISTER_SUB
throw new IllegalArgumentException("不支持的下行消息类型:" + downstreamReqVO);
}

View File

@@ -55,4 +55,11 @@ public interface IotDeviceUpstreamService {
*/
void registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO);
/**
* 添加设备拓扑
*
* @param addReqDTO 添加设备拓扑 DTO
*/
void addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO);
}

View File

@@ -220,6 +220,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
if (CollUtil.isNotEmpty(registerReqDTO.getParams())) {
registerReqDTO.getParams().forEach(subDevice -> registerDevice0(
subDevice.getProductKey(), subDevice.getDeviceName(), device.getId(), registerReqDTO));
// TODO @芋艿:后续要处理,每个设备是否成功
}
// 3. 发送设备消息
@@ -230,6 +231,52 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
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) {
// 1. 【异步】记录设备与插件实例的映射
pluginInstanceService.updateDevicePluginInstanceProcessIdAsync(device.getDeviceKey(), reqDTO.getProcessId());