package com.ard.work.inspect.service.impl;
|
|
import java.time.LocalTime;
|
import java.util.*;
|
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.Executors;
|
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.stream.Collectors;
|
|
import com.ard.common.core.constant.SecurityConstants;
|
import com.ard.common.core.domain.R;
|
import com.ard.common.core.utils.DateUtils;
|
import com.ard.common.core.utils.StringUtils;
|
import com.ard.common.core.utils.uuid.IdUtils;
|
import com.ard.common.datascope.annotation.DataScope;
|
import com.ard.system.api.RemoteConfigService;
|
import com.ard.work.api.domian.ArdCamera;
|
import com.ard.work.device.camera.service.IArdCameraService;
|
import com.ard.work.inspect.domain.ArdVideoInspectTaskStep;
|
import com.ard.work.inspect.dto.TimeSlot;
|
import com.ard.work.inspect.mapper.ArdVideoInspectTaskStepMapper;
|
import com.ard.work.inspect.task.TaskStatus;
|
import com.ard.work.inspect.task.TaskWrapper;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.stereotype.Service;
|
import com.ard.work.inspect.mapper.ArdVideoInspectTaskMapper;
|
import com.ard.work.inspect.domain.ArdVideoInspectTask;
|
import com.ard.work.inspect.service.IArdVideoInspectTaskService;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import jakarta.annotation.Resource;
|
|
import static java.util.stream.Collectors.toList;
|
|
/**
|
* 视频巡检任务Service业务层处理
|
*
|
* @author ruoyi
|
* @date 2024-09-12
|
*/
|
@Service
|
@Slf4j
|
public class ArdVideoInspectTaskServiceImpl implements IArdVideoInspectTaskService {
|
@Resource
|
private ArdVideoInspectTaskMapper ardVideoInspectTaskMapper;
|
@Resource
|
private ArdVideoInspectTaskStepMapper ardVideoInspectTaskStepMapper;
|
@Resource
|
private IArdCameraService ardCameraService;
|
@Resource
|
private RemoteConfigService remoteConfigService;
|
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(50);
|
private final Map<String, TaskWrapper> taskWrapperMap = new ConcurrentHashMap<>();
|
private final Map<String, List<String>> pauseTaskMap = new ConcurrentHashMap<>();//暂停任务列表
|
|
|
/**
|
* 查询视频巡检任务
|
*
|
* @param id 视频巡检任务主键
|
* @return 视频巡检任务
|
*/
|
@Override
|
public ArdVideoInspectTask selectArdVideoInspectTaskById(String id) {
|
ArdVideoInspectTask task = ardVideoInspectTaskMapper.selectArdVideoInspectTaskById(id);
|
if (task != null) {
|
ArdVideoInspectTaskStep step = new ArdVideoInspectTaskStep();
|
step.setTaskId(task.getId());
|
List<ArdVideoInspectTaskStep> steps = ardVideoInspectTaskStepMapper.selectArdVideoInspectTaskStepList(step);
|
task.setArdVideoInspectTaskSteps(steps);
|
TaskStatus taskStatus = getTaskStatus(task.getId());
|
task.setTaskStatus(taskStatus);
|
}
|
return task;
|
}
|
|
/**
|
* 查询视频巡检任务列表
|
*
|
* @param ardVideoInspectTask 视频巡检任务
|
* @return 视频巡检任务
|
*/
|
@Override
|
@DataScope(deptAlias = "d", userAlias = "u")
|
public List<ArdVideoInspectTask> selectArdVideoInspectTaskList(ArdVideoInspectTask ardVideoInspectTask) {
|
|
List<ArdVideoInspectTask> tasks = ardVideoInspectTaskMapper.selectArdVideoInspectTaskList(ardVideoInspectTask);
|
tasks.forEach(task -> {
|
ArdVideoInspectTaskStep step = new ArdVideoInspectTaskStep();
|
step.setTaskId(task.getId());
|
List<ArdVideoInspectTaskStep> steps = ardVideoInspectTaskStepMapper.selectArdVideoInspectTaskStepList(step);
|
task.setArdVideoInspectTaskSteps(steps);
|
TaskStatus taskStatus = getTaskStatus(task.getId());
|
task.setTaskStatus(taskStatus);
|
});
|
return tasks;
|
}
|
|
/**
|
* 新增视频巡检任务
|
*
|
* @param ardVideoInspectTask 视频巡检任务
|
* @return 结果
|
*/
|
@Override
|
@Transactional
|
public int insertArdVideoInspectTask(ArdVideoInspectTask ardVideoInspectTask) {
|
ardVideoInspectTask.setId(IdUtils.simpleUUID());
|
ardVideoInspectTask.setCreateTime(DateUtils.getNowDate());
|
ardVideoInspectTask.getArdVideoInspectTaskSteps().forEach(step -> {
|
step.setTaskId(ardVideoInspectTask.getId());
|
step.setId(IdUtils.simpleUUID());
|
ardVideoInspectTaskStepMapper.insertArdVideoInspectTaskStep(step);
|
});
|
return ardVideoInspectTaskMapper.insertArdVideoInspectTask(ardVideoInspectTask);
|
}
|
|
/**
|
* 修改视频巡检任务
|
*
|
* @param ardVideoInspectTask 视频巡检任务
|
* @return 结果
|
*/
|
@Override
|
@Transactional
|
public int updateArdVideoInspectTask(ArdVideoInspectTask ardVideoInspectTask) {
|
ardVideoInspectTask.setUpdateTime(DateUtils.getNowDate());
|
//修改先删除任务关联的全部步骤再重新添加
|
ardVideoInspectTaskStepMapper.deleteArdVideoInspectTaskStepByTaskId(ardVideoInspectTask.getId());
|
if (ardVideoInspectTask.getArdVideoInspectTaskSteps() != null) {
|
ardVideoInspectTask.getArdVideoInspectTaskSteps().forEach(step -> {
|
step.setTaskId(ardVideoInspectTask.getId());
|
step.setId(IdUtils.simpleUUID());
|
ardVideoInspectTaskStepMapper.insertArdVideoInspectTaskStep(step);
|
});
|
}
|
return ardVideoInspectTaskMapper.updateArdVideoInspectTask(ardVideoInspectTask);
|
}
|
|
/**
|
* 批量删除视频巡检任务
|
*
|
* @param ids 需要删除的视频巡检任务主键
|
* @return 结果
|
*/
|
@Override
|
public int deleteArdVideoInspectTaskByIds(String[] ids) {
|
Arrays.asList(ids).forEach(id -> {
|
ardVideoInspectTaskStepMapper.deleteArdVideoInspectTaskStepByTaskId(id);
|
});
|
return ardVideoInspectTaskMapper.deleteArdVideoInspectTaskByIds(ids);
|
}
|
|
/**
|
* 删除视频巡检任务信息
|
*
|
* @param id 视频巡检任务主键
|
* @return 结果
|
*/
|
@Override
|
public int deleteArdVideoInspectTaskById(String id) {
|
ardVideoInspectTaskStepMapper.deleteArdVideoInspectTaskStepByTaskId(id);
|
return ardVideoInspectTaskMapper.deleteArdVideoInspectTaskById(id);
|
}
|
|
|
/**
|
* 获取单个相机的空闲时段
|
*
|
* @param cameraId 相机ID
|
* @return 返回空闲时段列表
|
*/
|
public List<TimeSlot> getCameraIdleTimeList(String cameraId) {
|
List<TimeSlot> idleTimeSlots = new LinkedList<>();
|
|
// 获取该相机的占用时段
|
List<TimeSlot> usedPeriods = ardVideoInspectTaskMapper.getTaskUsedCameraPeriods(cameraId);
|
|
// 如果没有占用时间段,返回全天空闲
|
if (usedPeriods.isEmpty()) {
|
idleTimeSlots.add(new TimeSlot(LocalTime.MIN, LocalTime.MAX)); // 全天空闲
|
return idleTimeSlots;
|
}
|
|
// 遍历已用的时间段并处理空闲时间段
|
LocalTime startOfDay = LocalTime.MIN; // 00:00:00
|
LocalTime endOfDay = LocalTime.MAX; // 23:59:59
|
|
LocalTime previousEndTime = startOfDay;
|
|
for (TimeSlot usedPeriod : usedPeriods) {
|
LocalTime currentStartTime = usedPeriod.getStartTime();
|
LocalTime currentEndTime = usedPeriod.getEndTime();
|
|
// 如果当前使用的开始时间在上一个结束时间之后,则存在空闲时间
|
if (previousEndTime.isBefore(currentStartTime)) {
|
idleTimeSlots.add(new TimeSlot(previousEndTime, currentStartTime));
|
}
|
|
// 更新前一个结束时间
|
previousEndTime = currentEndTime;
|
}
|
|
// 检查是否存在最后一个空闲时段(即从最后一个已用时间段结束到当天结束的时间段)
|
if (previousEndTime.isBefore(endOfDay)) {
|
idleTimeSlots.add(new TimeSlot(previousEndTime, endOfDay));
|
}
|
|
return idleTimeSlots;
|
}
|
|
/**
|
* 获取所有相机的空闲时段
|
*/
|
public List<Map<String, Object>> getOptions() {
|
ArdCamera ardCamera = new ArdCamera("1");
|
List<ArdCamera> ardCameras = ardCameraService.selectArdCameraList(ardCamera);
|
return ardCameras.stream()
|
.map(camera -> {
|
Map<String, Object> option = new HashMap<>();
|
option.put("value", camera.getId());
|
option.put("label", camera.getName());
|
option.put("channelList",
|
Optional.ofNullable(camera.getChannelList()).orElse(Collections.emptyList())); // 防止
|
// channelList 为空
|
option.put("idleTimeList", getCameraIdleTimeList(camera.getId()));
|
return option;
|
})
|
.collect(toList());
|
}
|
|
/**
|
* 获取任务关联相机的其他任务
|
*/
|
@Override
|
public List<ArdVideoInspectTask> getOtherTasks(String taskId) {
|
return ardVideoInspectTaskMapper.getOtherTasks(taskId);
|
}
|
|
private final Object lock = new Object();
|
|
/**
|
* 启动任务
|
*/
|
@Override
|
public Boolean startTask(String taskId) {
|
if (taskWrapperMap.containsKey(taskId)) {
|
log.warn("Task with ID {} is already running", taskId);
|
return false;
|
}
|
ArdVideoInspectTask task = selectArdVideoInspectTaskById(taskId);
|
if (task == null) {
|
log.error("Task with ID {} not found", taskId);
|
return false;
|
}
|
TaskWrapper taskWrapper = new TaskWrapper(task);
|
taskWrapperMap.put(task.getId(), taskWrapper); // 存储 TaskWrapper 实例
|
scheduler.submit(taskWrapper);
|
log.info("Task with ID {} has been started", taskId);
|
return true;
|
}
|
|
/**
|
* 暂停任务
|
*/
|
@Override
|
public boolean pauseTask(String taskId) {
|
return Optional.ofNullable(taskWrapperMap.get(taskId))
|
.map(task -> {
|
task.pause();
|
return true;
|
})
|
.orElse(false);
|
}
|
|
/**
|
* 恢复任务
|
*/
|
@Override
|
public boolean resumeTask(String taskId) {
|
return Optional.ofNullable(taskWrapperMap.get(taskId))
|
.map(task -> {
|
if (task.isPaused()) { // 假设 TaskWrapper 有 isPaused 方法
|
task.resume();
|
pauseOtherTasks(taskId);
|
return true;
|
} else {
|
log.warn("Task with ID {} is already running.", taskId);
|
return false;
|
}
|
})
|
.orElse(false);
|
}
|
|
/**
|
* 取消任务
|
*/
|
@Override
|
public boolean cancelTask(String taskId) {
|
log.info(taskId);
|
if (StringUtils.isBlank(taskId)){
|
return false;
|
}
|
if (Objects.isNull(taskWrapperMap.get(taskId))) {
|
return false;
|
}
|
return Optional.ofNullable(taskWrapperMap.get(taskId))
|
.map(task -> {
|
task.cancel();
|
taskWrapperMap.remove(taskId);
|
return true;
|
})
|
.orElse(false);
|
}
|
|
@Override
|
public void manualStartTask(String taskId) {
|
startTask(taskId);
|
pauseOtherTasks(taskId);
|
}
|
|
@Override
|
public void manualStopTask(String taskId) {
|
cancelTask(taskId);
|
//恢复其他任务
|
List<String> tasksToResume = pauseTaskMap.get(taskId);
|
if (tasksToResume != null) {
|
tasksToResume.forEach(this::resumeTask);
|
}
|
}
|
|
//判断当前时间是否在任务的开始时间和结束时间之间
|
public static boolean isWithinTimeRange(ArdVideoInspectTask task) {
|
// 使用 isAfter 和 isBefore 方法来判断
|
return !LocalTime.now().isBefore(task.getStartTime()) && !LocalTime.now().isAfter(task.getEndTime());
|
}
|
|
public TaskStatus getTaskStatus(String taskId) {
|
TaskStatus status = taskWrapperMap.get(taskId) != null ? taskWrapperMap.get(taskId).status :
|
TaskStatus.NOT_RUNNING;
|
return status;
|
}
|
|
//每5秒执行一次自动任务扫描,满足时间的自动启动任务
|
//容器启动后,先等待 10秒,然后每隔 5秒 执行一次
|
// @Scheduled(cron = "0/5 * * * * ?")
|
@Scheduled(fixedRate = 5000, initialDelay = 10000)
|
public void autoStartTask() {
|
ArdVideoInspectTask ardTask = new ArdVideoInspectTask();
|
ardTask.setInspectMode("1");
|
List<ArdVideoInspectTask> tasks = selectArdVideoInspectTaskList(ardTask);
|
synchronized (lock) { // 加锁以防止多线程同时执行
|
tasks.forEach(task -> {
|
|
//查询相机的其他任务是否有正在手动控制并且正在运行
|
List<ArdVideoInspectTask> otherTasks = getOtherTasks(task.getId());
|
boolean isManualControl = otherTasks.stream().anyMatch(
|
otherTask -> {
|
TaskWrapper otherTaskWrapper = taskWrapperMap.get(otherTask.getId());
|
return otherTaskWrapper != null &&
|
otherTask.getInspectMode().equals("0") &&
|
otherTaskWrapper.status.equals(TaskStatus.RUNNING);
|
}
|
);
|
if (isManualControl) {
|
return;
|
}
|
//遍历所有自动任务,对满足启动时间并且未启动的任务进行启动
|
boolean isTaskActive = taskWrapperMap.containsKey(task.getId());
|
boolean withinTimeRange = isWithinTimeRange(task);
|
if (withinTimeRange && !isTaskActive) {
|
startTask(task.getId());
|
} else if (!withinTimeRange && isTaskActive) {
|
cancelTask(task.getId());
|
}
|
});
|
}
|
}
|
|
//暂停当前任务相机的其他任务
|
public void pauseOtherTasks(String taskId) {
|
//获取当前任务相机的其他任务
|
List<ArdVideoInspectTask> otherTasks = getOtherTasks(taskId);
|
// 将需要暂停的任务 ID 转换为列表
|
// 如果任务正在运行,直接暂停
|
List<ArdVideoInspectTask> filteredTasks = otherTasks.stream()
|
.filter(task -> taskWrapperMap.containsKey(task.getId())) // 过滤掉不在 taskWrapperMap 中的任务
|
.collect(Collectors.toList()); // 收集结果到 List 中
|
filteredTasks.forEach(task -> pauseTask(task.getId()));
|
List<String> pauseTaskList =
|
filteredTasks.stream().map(ArdVideoInspectTask::getId).collect(Collectors.toList()); // 收集到列表
|
pauseTaskMap.put(taskId, pauseTaskList);
|
}
|
|
|
}
|