Преглед на файлове

【功能新增】IoT:增加 IotDeviceOfflineCheckJob,处理设备超时下线

YunaiV преди 6 месеца
родител
ревизия
39aaeaa298
променени са 10 файла, в които са добавени 135 реда и са изтрити 19 реда
  1. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java
  2. 5 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java
  3. 2 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java
  4. 7 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceReportTimeRedisDAO.java
  5. 53 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java
  6. 11 3
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
  7. 7 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
  8. 22 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java
  9. 19 4
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java
  10. 8 3
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java

@@ -122,7 +122,7 @@ public class IotDeviceController {
     @Parameter(name = "deviceType", description = "设备类型", example = "1")
     public CommonResult<List<IotDeviceRespVO>> getSimpleDeviceList(
             @RequestParam(value = "deviceType", required = false) Integer deviceType) {
-        List<IotDeviceDO> list = deviceService.getDeviceList(deviceType);
+        List<IotDeviceDO> list = deviceService.getDeviceListByDeviceType(deviceType);
         return success(convertList(list, device -> // 只返回 id、name 字段
         new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName())));
     }

+ 5 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java

@@ -52,10 +52,14 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
                 .apply("LOWER(device_key) = {0}", deviceKey.toLowerCase()));
     }
 
-    default List<IotDeviceDO> selectList(Integer deviceType) {
+    default List<IotDeviceDO> selectListByDeviceType(Integer deviceType) {
         return selectList(IotDeviceDO::getDeviceType, deviceType);
     }
 
+    default List<IotDeviceDO> selectListByState(Integer state) {
+        return selectList(IotDeviceDO::getState, state);
+    }
+
     default Long selectCountByGroupId(Long groupId) {
         return selectCount(new LambdaQueryWrapperX<IotDeviceDO>()
                 .apply("FIND_IN_SET(" + groupId + ",group_ids) > 0")

+ 2 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java

@@ -24,7 +24,7 @@ public interface RedisKeyConstants {
      * KEY 格式:{deviceKey}
      * SCORE:上报时间
      */
-    String DEVICE_REPORT_TIME = "device_report_time";
+    String DEVICE_REPORT_TIMES = "device_report_times";
 
     /**
      * 设备信息的数据缓存,使用 Spring Cache 操作
@@ -32,7 +32,7 @@ public interface RedisKeyConstants {
      * KEY 格式:device_${productKey}_${deviceKey}
      * VALUE 数据类型:String(JSON)
      */
-    String DEVICE  = "device";
+    String DEVICE = "device";
 
     /**
      * 物模型的数据缓存,使用 Spring Cache 操作

+ 7 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceReportTimeRedisDAO.java

@@ -7,6 +7,7 @@ import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Repository;
 
 import java.time.LocalDateTime;
+import java.util.Set;
 
 /**
  * 设备的最后上报时间的 Redis DAO
@@ -20,8 +21,13 @@ public class DeviceReportTimeRedisDAO {
     private StringRedisTemplate stringRedisTemplate;
 
     public void update(String deviceKey, LocalDateTime reportTime) {
-        stringRedisTemplate.opsForZSet().add(RedisKeyConstants.DEVICE_REPORT_TIME, deviceKey,
+        stringRedisTemplate.opsForZSet().add(RedisKeyConstants.DEVICE_REPORT_TIMES, deviceKey,
                 LocalDateTimeUtil.toEpochMilli(reportTime));
     }
 
+    public Set<String> range(LocalDateTime maxReportTime) {
+        return stringRedisTemplate.opsForZSet().rangeByScore(RedisKeyConstants.DEVICE_REPORT_TIMES, 0,
+                LocalDateTimeUtil.toEpochMilli(maxReportTime));
+    }
+
 }

+ 53 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java

@@ -1,10 +1,25 @@
 package cn.iocoder.yudao.module.iot.job.device;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
 import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
+import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStateUpdateReqDTO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
+import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
+import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
+import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
+import cn.iocoder.yudao.module.iot.service.device.upstream.IotDeviceUpstreamService;
+import jakarta.annotation.Resource;
 import org.springframework.stereotype.Component;
 
-// TODO @芋艿:待实现
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
 /**
  * IoT 设备离线检查 Job
  *
@@ -15,10 +30,46 @@ import org.springframework.stereotype.Component;
 @Component
 public class IotDeviceOfflineCheckJob implements JobHandler {
 
+    /**
+     * 设备离线超时时间
+     *
+     * TODO 芋艿:暂定 10 分钟,后续看看要不要基于设备或者全局有配置文件
+     */
+    public static final Duration OFFLINE_TIMEOUT = Duration.ofMinutes(10);
+
+    @Resource
+    private IotDeviceService deviceService;
+    @Resource
+    private IotDevicePropertyService devicePropertyService;
+    @Resource
+    private IotDeviceUpstreamService deviceUpstreamService;
+
     @Override
     @TenantJob
     public String execute(String param) {
-        return "";
+        // 1.1 获得在线设备列表
+        List<IotDeviceDO> devices = deviceService.getDeviceListByState(IotDeviceStateEnum.ONLINE.getState());
+        if (CollUtil.isEmpty(devices)) {
+            return JsonUtils.toJsonString(Collections.emptyList());
+        }
+        // 1.2 获取超时的 deviceKey 集合
+        Set<String> timeoutDeviceKeys = devicePropertyService.getDeviceKeysByReportTime(
+                LocalDateTime.now().minus(OFFLINE_TIMEOUT));
+
+        // 2. 下线设备
+        List<String> offlineDeviceKeys = CollUtil.newArrayList();
+        for (IotDeviceDO device : devices) {
+            if (!timeoutDeviceKeys.contains(device.getDeviceKey())) {
+                continue;
+            }
+            offlineDeviceKeys.add(device.getDeviceKey());
+            // 为什么不直接更新状态呢?因为通过 IotDeviceMessage 可以经过一系列的处理,例如说记录日志等等
+            deviceUpstreamService.updateDeviceState(((IotDeviceStateUpdateReqDTO)
+                    new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()).setReportTime(LocalDateTime.now())
+                            .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName()))
+                    .setState((IotDeviceStateEnum.OFFLINE.getState())));
+        }
+        return JsonUtils.toJsonString(offlineDeviceKeys);
     }
 
 }

+ 11 - 3
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java

@@ -93,15 +93,23 @@ public interface IotDeviceService {
     PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO);
 
     /**
-     * 获得设备列表
+     * 基于设备类型,获得设备列表
      *
      * @param deviceType 设备类型
      * @return 设备列表
      */
-    List<IotDeviceDO> getDeviceList(@Nullable Integer deviceType);
+    List<IotDeviceDO> getDeviceListByDeviceType(@Nullable Integer deviceType);
 
     /**
-     * 获得设备数量
+     * 获得状态,获得设备列表
+     *
+     * @param state 状态
+     * @return 设备列表
+     */
+    List<IotDeviceDO> getDeviceListByState(Integer state);
+
+    /**
+     * 基于产品编号,获得设备数量
      *
      * @param productId 产品编号
      * @return 设备数量

+ 7 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java

@@ -226,8 +226,13 @@ public class IotDeviceServiceImpl implements IotDeviceService {
     }
 
     @Override
-    public List<IotDeviceDO> getDeviceList(@Nullable Integer deviceType) {
-        return deviceMapper.selectList(deviceType);
+    public List<IotDeviceDO> getDeviceListByDeviceType(@Nullable Integer deviceType) {
+        return deviceMapper.selectListByDeviceType(deviceType);
+    }
+
+    @Override
+    public List<IotDeviceDO> getDeviceListByState(Integer state) {
+        return deviceMapper.selectListByState(state);
     }
 
     @Override

+ 22 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java

@@ -7,7 +7,9 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
 import jakarta.validation.Valid;
 
+import java.time.LocalDateTime;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * IoT 设备【属性】数据 Service 接口
@@ -16,6 +18,8 @@ import java.util.Map;
  */
 public interface IotDevicePropertyService {
 
+    // ========== 设备属性相关操作 ==========
+
     /**
      * 定义设备属性数据的结构
      *
@@ -46,4 +50,22 @@ public interface IotDevicePropertyService {
      */
     PageResult<IotDevicePropertyRespVO> getHistoryDevicePropertyPage(@Valid IotDevicePropertyHistoryPageReqVO pageReqVO);
 
+    // ========== 设备时间相关操作 ==========
+
+    /**
+     * 获得最后上报时间小于指定时间的设备标识
+     *
+     * @param maxReportTime 最大上报时间
+     * @return 设备标识列表
+     */
+    Set<String> getDeviceKeysByReportTime(LocalDateTime maxReportTime);
+
+    /**
+     * 更新设备上报时间
+     *
+     * @param deviceKey  设备标识
+     * @param reportTime 上报时间
+     */
+    void updateDeviceReportTime(String deviceKey, LocalDateTime reportTime);
+
 }

+ 19 - 4
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java

@@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
 import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO;
 import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO;
+import cn.iocoder.yudao.module.iot.dal.redis.device.DeviceReportTimeRedisDAO;
 import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyMapper;
 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum;
 import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum;
@@ -28,10 +29,8 @@ import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.time.LocalDateTime;
+import java.util.*;
 
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
 
@@ -68,10 +67,14 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
 
     @Resource
     private DevicePropertyRedisDAO deviceDataRedisDAO;
+    @Resource
+    private DeviceReportTimeRedisDAO deviceReportTimeRedisDAO;
 
     @Resource
     private IotDevicePropertyMapper devicePropertyMapper;
 
+    // ========== 设备属性相关操作 ==========
+
     @Override
     public void defineDevicePropertyData(Long productId) {
         // 1.1 查询产品和物模型
@@ -179,4 +182,16 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService {
         }
     }
 
+    // ========== 设备时间相关操作 ==========
+
+    @Override
+    public Set<String> getDeviceKeysByReportTime(LocalDateTime maxReportTime) {
+        return deviceReportTimeRedisDAO.range(maxReportTime);
+    }
+
+    @Override
+    public void updateDeviceReportTime(String deviceKey, LocalDateTime reportTime) {
+        deviceReportTimeRedisDAO.update(deviceKey, reportTime);
+    }
+
 }

+ 8 - 3
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java

@@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
 import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage;
 import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer;
 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
+import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -38,6 +39,8 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
 
     @Resource
     private IotDeviceService deviceService;
+    @Resource
+    private IotDevicePropertyService devicePropertyService;
 
     @Resource
     private IotDeviceProducer deviceProducer;
@@ -107,9 +110,11 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
         // TODO 芋艿:待实现
     }
 
-    private void updateDeviceLastTime(IotDeviceDO deviceDO, IotDeviceUpstreamAbstractReqDTO reqDTO) {
-        // TODO 芋艿:插件状态
-        // TODO 芋艿:操作时间
+    private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) {
+        // 1. TODO 芋艿:插件状态
+
+        // 2. 更新设备的最后时间
+        devicePropertyService.updateDeviceReportTime(device.getDeviceKey(), LocalDateTime.now());
     }
 
     private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) {