package com.inspect.job.task;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.inspect.access.base.model.InfraredModel;
import com.inspect.base.core.constant.AlgConstants;
import com.inspect.base.core.constant.RedisConst;
import com.inspect.base.core.domain.FtpResult;
import com.inspect.base.core.domain.Response;
import com.inspect.base.core.enums.ExecStatus;
import com.inspect.base.core.enums.PresetAction;
import com.inspect.base.core.enums.StaEnum;
import com.inspect.base.core.enums.TaskStatus;
import com.inspect.base.core.sftp.SftpClient;
import com.inspect.base.core.sftp.SftpUploadEntity;
import com.inspect.base.core.utils.CalendarUtil;
import com.inspect.base.core.utils.DateUtils;
import com.inspect.base.core.utils.HttpClientUtils;
import com.inspect.base.core.constant.Color;
import com.inspect.base.core.utils.StringUtils;
import com.inspect.base.redis.service.RedisService;
import com.inspect.job.client.TaskExecClient;
import com.inspect.job.client.XgiotInvokeClient;
import com.inspect.job.domain.ShaoXinBigModel;
import com.inspect.job.domain.livesipb.RecordData;
import com.inspect.job.domain.livesipb.Stream;
import com.inspect.job.domain.task.*;
import com.inspect.base.core.domain.maintain.MaintainRegion;
import com.inspect.job.mapper.PatrolDeviceStateMapper;
import com.inspect.job.util.HttpClientUtil;
import com.inspect.system.base.domain.SysDictData;

import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import feign.RetryableException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

@Component("jobMainTask")
@RestController
@RequestMapping({"/jobtask"})
public class JobMainTask {
    private final Logger log = LoggerFactory.getLogger(JobMainTask.class);

    public static final String TASK_TYPE_SILENCE = "999";
    public static final String TASK_TYPE_LINKAGE = "5";
    public static final String REDIS_TASK_SILENCE_DEVICE = "REDIS_TASK_SILENCE_DEVICE_";
    public static final String TASK_ENABLE = "0";
    public static final String TASK_STATUS_SUCCESS = "1";
    public static final String TASK_STATUS_RUN = "2";
    public static final String TASK_STATUS_STOP = "3";

    public static final String TASK_STOP_EXPIRE = "TASK_STOP_EXPIRE";
    public static final int TASK_STOP_EXPIRE_DEFAULT = 3600;

    private String BIG_FILE_POINT = "BIG_FILE_POINT";
    private String API_CONTROL_PRESET = "/api/v1/control/preset";

    private static int execRecordCounter = 0;

    private AtomicInteger asyncTaskPatrolPointCnt = new AtomicInteger(0);

    @Value("${liveSIPB.url}")
    private String liveIVS_URL;
    @Value("${sftp.ip}")
    private String ip;
    @Value("${sftp.port}")
    private String port;
    @Value("${sftp.username}")
    private String userName;
    @Value("${sftp.password}")
    private String password;
    //    @Value("${sftp.basePath}")
//    private String basePath;
    @Value("${sftp.stationCode}")
    private String stationCode;

    @Value("${spring.profiles.active}")
    private String activeProfile;

    @Value("${jobtask.corePoolSize:20}")
    private int corePoolSize;

    @Value("${jobtask.maximumPoolSize:50}")
    private int maximumPoolSize;

    @Value("${jobtask.keepAliveTime:100}")
    private int keepAliveTime;

    @Value("${task.scheduler.batch-size:100}")
    private int taskBatchSize;

    @Value("${task.test-mode:false}")
    private boolean testMode;

    @Resource
    private IvsOperateRetryableDelegate ivsOperateRetryableDelegate;

    private ExecutorService threadPool;

    final ShaoXinBigModel shaoXinBigModel;

    final TaskExecClient taskExecClient;

    final RedisService redisService;

    final XgiotInvokeClient remoteInvokeService;

    final SftpClient sftpClient;

    final PatrolDeviceStateMapper patrolDeviceStateMapper;

    private final Lock controlLock = new ReentrantLock();
    private final Condition pauseCondition = controlLock.newCondition();

    // 控制标志
    private volatile boolean stop = false;
    private volatile boolean pause = false;
    // 用于状态查询线程
    private final ScheduledExecutorService statusUpdater = Executors.newSingleThreadScheduledExecutor();

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Autowired
    public JobMainTask(
            ShaoXinBigModel shaoXinBigModel,
            TaskExecClient taskExecClient,
            RedisService redisService,
            XgiotInvokeClient remoteInvokeService,
            SftpClient sftpClient,
            PatrolDeviceStateMapper patrolDeviceStateMapper) {
        this.shaoXinBigModel = shaoXinBigModel;
        this.taskExecClient = taskExecClient;
        this.redisService = redisService;
        this.remoteInvokeService = remoteInvokeService;
        this.sftpClient = sftpClient;
        this.patrolDeviceStateMapper = patrolDeviceStateMapper;
    }

    @PostConstruct
    private void init() {
        threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000));
    }

    // 等待恢复
    private void waitIfPaused() {
        controlLock.lock();
        try {
            while (pause && !stop) {
                pauseCondition.await(); // 所有线程阻塞在此
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 不中断会引发潜在问题
        } finally {
            controlLock.unlock();
        }
    }

    // 外部触发恢复
    public void resumeIfPaused() {
        controlLock.lock();
        try {
            if (pause) {
                pause = false;
                pauseCondition.signalAll(); // 唤醒所有暂停线程
            }
        } finally {
            controlLock.unlock();
        }
    }

    // 用于外部强制停止所有线程
    public void stopAll() {
        stop = true;
        resumeIfPaused(); // 确保唤醒阻塞线程让它们能感知 stop = true
    }

    // 模拟查询状态
    private int queryPatrolTaskStatus(final String taskPatrolId) {
        int result = 0;
        String taskPatrolledId = taskPatrolId.split(StringUtils.UNDERLINE)[1] + "_" + taskPatrolId.split(StringUtils.UNDERLINE)[2];
        PatrolTaskStatus patrolTaskStatus = taskExecClient.selectPatrolTaskStatusByTaskPatrolledId(taskPatrolledId);
        if (patrolTaskStatus == null) {
            return result;
        }

        try {
            result = Integer.parseInt(patrolTaskStatus.getPatrolStatus());
        } catch (Exception e) {
            result = -1;
        }

        return result;
    }

    private void startThread(
            final PatrolTaskInfo patrolTaskInfo,
            final PatrolPresetPos patrolPresetPos,
            final PatrolPresetAction patrolPresetAction,
            final int hour,
            final int minute,
            final PatrolTask task,
            final String[] cycleExecuteTime,
            final boolean isCycle) {
        if (Integer.parseInt(cycleExecuteTime[0]) == hour && Integer.parseInt(cycleExecuteTime[1]) == minute) {
            //logger.info("[GABRIEL] Running task: {}", task.getTaskName() + " hour : " + hour + " minute : " + minute);
            threadPool.submit(() -> {
                String time = isCycle ? (cycleExecuteTime[0] + cycleExecuteTime[1] + cycleExecuteTime[2]) : (hour + minute + cycleExecuteTime[2]);
                String date = new SimpleDateFormat("yyyyMMdd").format(new Date());
                PatrolTaskExecRecord patrolTaskExecRecord =
                        PatrolTaskExecRecord.builder()
                                .planStartTime(isCycle ? task.getCycleStartTime() : task.getIntervalStartTime())
                                .taskPatrolId(task.getDevNo() + "_" + task.getTaskCode() + "_" + date + time)
                                .oldTaskPatrolId(task.getTaskCode() + "_" + date + time)
                                .taskName(task.getTaskName())
                                .taskCode(task.getTaskCode())
                                .taskState(TASK_STATUS_RUN)
                                .startTime(new Date())
                                .taskProgress("0")
                                .description("")
                                .devNo(task.getDevNo())
                                .taskType(task.getType())
                                .taskPriority(task.getPriority())
                                .taskId(task.getTaskId())
                                .stationName(task.getStationName())
                                .stationCode(task.getStationCode())
                                .build();
                log.info("[GABRIEL] Running task: {}", task.getTaskName() + " hour : " + hour + " minute: " + minute + " OldTaskPatrolledId: " + patrolTaskExecRecord.getOldTaskPatrolId() + " TaskPatrolledId:" + patrolTaskExecRecord.getTaskPatrolId());
                taskExecClient.addPatrolTaskExecRecord(patrolTaskExecRecord);
                patrolTaskInfo.setTaskMajorId(task.getTaskId().toString());
                patrolTaskInfo.setDevNo(task.getDevNo());
                cycleExecute(patrolTaskExecRecord,
                        patrolTaskInfo,
                        patrolPresetPos,
                        patrolPresetAction,
                        task);
            });
        }
    }

    private void startThread(
            final PatrolTaskInfo patrolTaskInfo,
            final PatrolPresetPos patrolPresetPos,
            final PatrolPresetAction patrolPresetAction,
            final PatrolTask task) {
        threadPool.submit(new Runnable() {
            public void run() {
                int count = 0;
                List patrolTaskInfoList = null;

                try {
                    patrolTaskInfo.setTaskMajorId(String.valueOf(task.getTaskId()));
                    patrolTaskInfo.setDevNo(task.getDevNo());
                    patrolTaskInfoList = taskExecClient.selectTaskInfoList(patrolTaskInfo);
                } catch (Exception e) {
                    log.error("error", e);
                }

                MaintainRegion maintainRegion = new MaintainRegion();
                maintainRegion.setEnable("1");
                List maintAreaList = null;

                try {
                    maintAreaList = taskExecClient.selectMaintainRegionList(maintainRegion);
                } catch (Exception e) {
                    log.error("error", e);
                }

                List<String> deviceIds = new ArrayList<>();

                for (Object object : Objects.requireNonNull(maintAreaList)) {
                    MaintainRegion area = (MaintainRegion) object;
                    if (area.getStartTime().getTime() < System.currentTimeMillis() && area.getEndTime().getTime() > System.currentTimeMillis()) {
                        deviceIds.addAll(Arrays.asList(area.getPointList().split(StringUtils.COMMA)));
                    }
                }

                for (Object object : Objects.requireNonNull(patrolTaskInfoList)) {
                    PatrolTaskInfo taskInfo = (PatrolTaskInfo) object;
                    if (!deviceIds.contains(taskInfo.getDeviceId())) {
                        ++count;
                    }
                }

                log.info("检修区deviceId ：{}", deviceIds);
                log.info("count ：{}", count);
                if (count > 0) {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
                    PatrolTaskExecRecord patrolTaskExecRecord = new PatrolTaskExecRecord();
                    Date time = task.getFixedStartTime();
                    if (TASK_TYPE_SILENCE.equals(task.getType())) {
                        time = new Date();
                    }

                    patrolTaskExecRecord.setOldTaskPatrolId(task.getTaskCode() + "_" + sdf.format(time));
                    Date fixedTime = task.getFixedStartTime();
                    if (TASK_TYPE_SILENCE.equals(task.getType())) {
                        fixedTime = new Date();
                    }

                    patrolTaskExecRecord.setTaskPatrolId(task.getDevNo() + "_" + task.getTaskCode() + "_" + sdf.format(fixedTime));
                    patrolTaskExecRecord.setTaskName(task.getTaskName());
                    patrolTaskExecRecord.setTaskCode(task.getTaskCode());
                    patrolTaskExecRecord.setTaskState(TASK_STATUS_RUN);
                    patrolTaskExecRecord.setPlanStartTime(task.getCycleStartTime());
                    patrolTaskExecRecord.setStartTime(new Date());
                    patrolTaskExecRecord.setTaskProgress("0");
                    patrolTaskExecRecord.setEndTime(task.getCycleEndTime());
                    patrolTaskExecRecord.setDescription("");
                    patrolTaskExecRecord.setDevNo(task.getDevNo());
                    patrolTaskExecRecord.setTaskType(task.getType());
                    patrolTaskExecRecord.setTaskPriority(task.getPriority());
                    patrolTaskExecRecord.setTaskId(task.getTaskId());
                    patrolTaskExecRecord.setStationName(task.getStationName());
                    patrolTaskExecRecord.setStationCode(task.getStationCode());

                    try {
                        taskExecClient.addPatrolTaskExecRecord(patrolTaskExecRecord);
                    } catch (Exception e) {
                        log.error("error", e);
                    }

                    patrolTaskInfo.setTaskMajorId(task.getTaskId().toString());
                    patrolTaskInfo.setDevNo(task.getDevNo());
                    prePointExecControl(patrolTaskExecRecord, patrolPresetPos, patrolPresetAction, patrolTaskInfoList);
                }

            }
        });
    }

    private void cycleExecute(PatrolTaskExecRecord patrolTaskExecRecord, PatrolTaskInfo patrolTaskInfo, PatrolPresetPos patrolPresetPos, PatrolPresetAction patrolPresetAction, PatrolTask task) {
        List patrolTaskInfoList = null;

        try {
            patrolTaskInfoList = this.taskExecClient.selectTaskInfoList(patrolTaskInfo);
        } catch (Exception e) {
            log.error("error", e);
        }

        this.prePointExecControl(patrolTaskExecRecord, patrolPresetPos, patrolPresetAction, patrolTaskInfoList);
    }

    private void prePointExecControl(PatrolTaskExecRecord patrolTaskExecRecord, PatrolPresetPos patrolPresetPos, PatrolPresetAction patrolPresetAction, List<PatrolTaskInfo> patrolTaskInfoList) {
        PatrolTaskExecRecord taskExecRecord = null;

        try {
            taskExecRecord = this.taskExecClient.selectPatrolTaskExecRecordByTaskPatrolId(patrolTaskExecRecord.getTaskPatrolId());
        } catch (Exception e) {
            log.error("error", e);
        }

        if (taskExecRecord != null && TASK_STATUS_RUN.equals(taskExecRecord.getTaskState())) {
            MaintainRegion maintainRegion = new MaintainRegion();
            maintainRegion.setEnable("1");
            List maintAreaList = null;

            try {
                maintAreaList = this.taskExecClient.selectMaintainRegionList(maintainRegion);
            } catch (Exception e) {
                log.error("error", e);
            }

            List<String> deviceIds = new ArrayList<>();
            this.execRemoveMaintainArea(patrolTaskInfoList, maintAreaList, deviceIds);
            boolean continueExce = true;
            int patrolTaskInfoListSize = patrolTaskInfoList.size();

            for (PatrolTaskInfo taskInfo : patrolTaskInfoList) {
                if (!deviceIds.contains(taskInfo.getDeviceId())) {
                    try {
                        taskExecRecord = this.taskExecClient.selectPatrolTaskExecRecordByTaskPatrolId(patrolTaskExecRecord.getTaskPatrolId());
                    } catch (Exception e) {
                        log.error("error", e);
                    }

                    if (taskExecRecord != null && (TASK_STATUS_STOP.equals(taskExecRecord.getTaskState()) || "4".equals(taskExecRecord.getTaskState()))) {
                        return;
                    }

                    taskExecRecord = prePointExec(taskExecRecord, taskInfo, patrolTaskInfoListSize);
                    if (TASK_STATUS_RUN.equals(taskExecRecord.getTaskState()) && taskExecRecord.getDeviceId() != null && taskExecRecord.getExecPointId() != null && taskInfo.getDeviceId().equals(taskExecRecord.getDeviceId())) {
                        patrolTaskExecRecord.setDeviceId(null);
                        patrolTaskExecRecord.setExecPointId(null);

                        try {
                            this.taskExecClient.updatePatrolTaskExecRecord(patrolTaskExecRecord);
                        } catch (Exception e) {
                            log.error("error", e);
                        }

                        continueExce = true;
                    }
                }
            }
        }

    }

    private String getTaskPatrolledId(final String devNo, final String taskPatrolId) {
        String[] ids = taskPatrolId.split(StringUtils.UNDERLINE);
        if (ids.length == 3) {
            return devNo + StringUtils.UNDERLINE + ids[1] + StringUtils.UNDERLINE + ids[2];
        } else {
            return StringUtils.EMPTY;
        }
    }

    private boolean isPointFinished(final PatrolTaskInfo taskInfo, final PatrolTaskExecRecord execRecord) {
        final String deviceId = taskInfo.getDeviceId();
        String taskPatrolId = getTaskPatrolledId(taskInfo.getDevNo(), execRecord.getTaskPatrolId());
        List<PatrolTaskPointExecRecord> pointExecRecords = taskExecClient.selectPatrolTaskPointExecRecordList(
                PatrolTaskPointExecRecord.builder().taskPatrolledId(taskPatrolId).deviceId(deviceId).isOk(1).build());
        return !pointExecRecords.isEmpty();
    }

    private void myDelay(long millis) {
        try {
            Thread.sleep(millis);
        } catch (Exception e) {
            log.error("error", e);
        }
    }

    private PatrolPresetPosSlim queryPatrolPresetPos(final String uuid, final String patrolPointId) {
        PatrolPresetPosSlim presetPosSlim = null;
        try {
            presetPosSlim = taskExecClient.selectPresetCodeSlimByPatrolPointId(
                    PatrolPresetPosSlim.builder()
                            .patrolPointId(Long.parseLong(patrolPointId))
                            .isEnable("1")
                            .build());
        } catch (Exception e) {
            e.printStackTrace();
            log.info("Error queryPatrolPresetPos: {}", patrolPointId);
        }

        if (presetPosSlim == null) {
            presetPosSlim = PatrolPresetPosSlim.builder()
                    .presetPosId(patrolPointId)
                    .channelType("vl")
                    .patrolPointId(Long.valueOf(patrolPointId))
                    .presetPosName("EMPTY_POS_V_INFO_DEVICE_ID_" + patrolPointId)
                    .channelCode("ffffffffffffffffff#ffffffffffffffffffffffffffffffff")
                    .videoNvrCode("ffffffffffffffffffffffffffffffff")
                    .build();
        }

        log.info(Color.CYAN + "queryPatrolPresetPos uuid: {}, patrolPresetPosSlim: {}" + Color.END, uuid, presetPosSlim);
        return presetPosSlim;
    }

    private String getInfraredAddress(final int index, final PatrolPresetPosSlim presetPosSlim) {
        String address = presetPosSlim.getAddress();
        String[] parts = address.split(StringUtils.COLON);
        if (index >= 0 && index < parts.length) {
            parts[index] = presetPosSlim.getPresetPosCode();
            return String.join(StringUtils.COLON, parts);
        }

        log.info("getInfraredAddress invalid infrared address: {}, posId: {}", presetPosSlim.getAddress(), presetPosSlim.getPresetPosId());
        return address;
    }

    private PatrolTaskExecRecord prePointExec(PatrolTaskExecRecord taskExecRecord, PatrolTaskInfo patrolTaskInfo, int infoListSize) {
        String uuid = UUID.randomUUID().toString().trim().replaceAll(StringUtils.DASH, StringUtils.EMPTY);
        final PatrolPresetPosSlim presetPos = queryPatrolPresetPos(uuid, patrolTaskInfo.getDeviceId());
        if (StringUtils.isNotEmpty(presetPos.getAddress())) {
            log.info("prePointExec remake infrared address: raw: {}", presetPos.getAddress());
            String infraredAddress = getInfraredAddress(3, presetPos);
            presetPos.setAddress(infraredAddress);
            log.info("prePointExec remake infrared address: cooked: {}", presetPos.getAddress());
        }
        boolean noError = ivsOperateRetryableDelegate.setCameraToPreset(uuid, presetPos);
        if (!testMode && noError) {
            log.info("setCameraToPreset no error, delay 20 seconds");
            myDelay(20000);
        } else {
            log.info("setCameraToPreset has error, delay 2 seconds");
            myDelay(2000);
        }

        StringBuffer fileTypes = new StringBuffer();
        StringBuffer filePaths = new StringBuffer();
        PatrolPresetAction patrolPresetAction = PatrolPresetAction.builder().isEnable("1").presetPosId(presetPos.getPresetPosId()).build();
        List<PatrolPresetAction> patrolPresetActionList = taskExecClient.selectPatrolPresetActionList(patrolPresetAction);
        PatrolPresetAction presetAction = patrolPresetActionList.isEmpty() ?
                PatrolPresetAction.builder().presetPosId(presetPos.getPresetPosId()).actionType("1").photoCount(1L).photoGap(1).build()
                : patrolPresetActionList.get(0);

        String basePath = (StringUtils.isEmpty(taskExecRecord.getStationCode()) ? stationCode : taskExecRecord.getStationCode())
                + "/" + DateUtils.getYearEven()
                + "/" + DateUtils.getMonthEven()
                + "/" + DateUtils.getDayEven()
                + "/" + taskExecRecord.getTaskCode()
                + "/";
        log.info("PRE_POINT_EXEC patrolPointId: {}, actionId: {}, actionType: {}, deviceId: {}",
                taskExecRecord.getOldTaskPatrolId(),
                presetAction.getPresetActionId(),
                presetAction.getActionType(),
                presetPos.getPatrolPointId());
        String taskPatrolId = taskExecRecord.getTaskPatrolId();
        if (PresetAction.PHOTO.getCode().equals(presetAction.getActionType())) {
            final String chanType = presetPos.getChannelType();
            //boolean bOk = false;
            try {
                log.info("PHOTO_PRESET_TYPE taskPatrolId: {}, chanType: {}, patrolPointId: {}, channelCode: {}, videoNvrCode: {}",
                        taskExecRecord.getOldTaskPatrolId(),
                        chanType,
                        presetPos.getPatrolPointId(),
                        presetPos.getChannelCode(),
                        presetPos.getVideoNvrCode());
                if ("vl".equals(chanType)) {
                    fileTypes.append(2).append(",");
                    String paramUrl = liveIVS_URL + "/api/v1/device/channelsnap?serial="
                            + presetPos.getVideoNvrCode()
                            + "&realtime=true&code=" + presetPos.getChannelCode()
                            + "&name=" + URLEncoder.encode(presetPos.getPresetPosName(), ("UTF-8"))
                            + "&id=" + presetPos.getPatrolPointId();
                    String paramFileName = taskPatrolId.split(StringUtils.UNDERLINE)[1] + StringUtils.UNDERLINE
                            + taskPatrolId.split(StringUtils.UNDERLINE)[2] + StringUtils.UNDERLINE
                            + presetPos.getPatrolPointId() + StringUtils.UNDERLINE
                            + patrolTaskInfo.getDevNo() + ".jpg";
                    String paramFileDir = basePath + "CCD";
                    log.info("paramUrl: {}, paramFileName: {}, paramFileDir: {}", paramUrl, paramFileName, paramFileDir);
                    FtpResult ftpResult = getFileAndSave(paramUrl, paramFileName, paramFileDir);
                    if (ftpResult != null && StringUtils.isNotEmpty(ftpResult.getFilepath())) {
                        log.info("filePath: {}", ftpResult.getFilepath());
                        //bOk = ftpResult.isOk();
                        filePaths.append("/" + ftpResult.getFilepath());
                    }

                    recordPersist(taskExecRecord, infoListSize, patrolTaskInfo, presetPos, fileTypes, filePaths);
                } else if ("ir".equals(chanType)) {
                    Map<String, String> algSubtypeIdMap = taskExecClient.getAlgTypeListByPatrolPointId(String.valueOf(presetPos.getPatrolPointId()));
                    // 任务指定算法类型：巡视详情的图片类型加载
                    boolean isSpecifiedAlg = StringUtils.isNotEmpty(taskExecRecord.getSpecifiedAlg());
                    fileTypes.append(isSpecifiedAlg ? "2" : "1").append(",");
                    String paramUrl = liveIVS_URL
                            + "/api/v1/device/channelsnap?serial=" + presetPos.getVideoNvrCode()
                            + "&realtime=true&code=" + presetPos.getChannelCode();
                    final String algSubtypeCode = algSubtypeIdMap == null ? StringUtils.EMPTY :
                            algSubtypeIdMap.getOrDefault(AlgConstants.ALG_SUBTYPE_CODE, StringUtils.EMPTY);
                    log.info("INFRARED_PROC, taskPatrolId: {}, chanType: {}, patrolPointId: {}, algorithmTypeMap: {}， algSubtypeCode: {}",
                            taskExecRecord.getOldTaskPatrolId(),
                            chanType,
                            presetPos.getPatrolPointId(),
                            algSubtypeIdMap,
                            algSubtypeCode);
                    if (algSubtypeCode.contains(AlgConstants.INFRA_1800)
                            || algSubtypeCode.contains(AlgConstants.INFRA_YU3)
                            || algSubtypeCode.contains(AlgConstants.INFRA_CAMERA)
                            || algSubtypeCode.contains(AlgConstants.INFRA_CAMERA_REVERSE)) {
                        String paramFileName = taskPatrolId.split(StringUtils.UNDERLINE)[1] + StringUtils.UNDERLINE
                                + taskPatrolId.split(StringUtils.UNDERLINE)[2] + StringUtils.UNDERLINE
                                + presetPos.getPatrolPointId() + StringUtils.UNDERLINE
                                + patrolTaskInfo.getDevNo() + ".jpg";
                        if (algSubtypeCode.contains(AlgConstants.INFRA_1800)) {
                            Map<String, String> threshold = patrolDeviceStateMapper.selectPatrolPresetParam(presetPos.getPatrolPointId());
                            final String temperUrl = liveIVS_URL + "/api/v1/device/temper";
                            JSONObject temperParam = new JSONObject();
                            temperParam.put("ruleId", threshold.get("preset_param_code"));
                            log.info("[infra_1800] =======ruleId: {}", threshold.get("preset_param_code"));
                            temperParam.put("presetId", Integer.parseInt(presetPos.getPresetPosCode()));
                            temperParam.put("cameraCode", presetPos.getChannelCode());
                            final String temperUrlRes = HttpClientUtils.sendPostAgain(temperUrl, temperParam.toString());
                            log.info("INFRARED_PROC INFRA_1800 prePointExec temperUrlRes: {}", temperUrlRes);
                            paramFileName = taskPatrolId.split(StringUtils.UNDERLINE)[1] + StringUtils.UNDERLINE
                                    + taskPatrolId.split(StringUtils.UNDERLINE)[2] + StringUtils.UNDERLINE
                                    + presetPos.getPatrolPointId() + StringUtils.UNDERLINE
                                    + patrolTaskInfo.getDevNo() + "_" + temperUrlRes + ".jpg";
                        } else if (algSubtypeCode.contains(AlgConstants.INFRA_CAMERA)) {
                            paramUrl = paramUrl
                                    + "&host=" + presetPos.getHost()
                                    + "&port=" + presetPos.getPort()
                                    + "&username=" + presetPos.getUsername()
                                    + "&password=" + presetPos.getPassword()
                                    + "&address=" + presetPos.getAddress();
                            log.info("INFRARED_PROC INFRA_CAMERA prePointExec paramUrl: {}", paramUrl);
                        } else if (algSubtypeCode.contains(AlgConstants.INFRA_CAMERA_REVERSE)) {
                            paramUrl = paramUrl
                                    + "&host=" + presetPos.getHost()
                                    + "&port=" + presetPos.getPort()
                                    + "&username=" + presetPos.getUsername()
                                    + "&password=" + presetPos.getPassword()
                                    + "&address=" + presetPos.getAddress();
                            log.info("INFRARED_PROC INFRA_CAMERA_REVERSE prePointExec paramUrl: {}", paramUrl);
                        }

                        String paramFileDir = basePath + "CCD";
                        log.info("INFRARED_PROC: paramUrl: {}, paramFileName: {}, paramFileDir: {}", paramUrl, paramFileName, paramFileDir);
                        FtpResult ftpResult = getFileAndSave(paramUrl, paramFileName, paramFileDir);
                        if (ftpResult != null && StringUtils.isNotEmpty(ftpResult.getFilepath())) {
                            log.info("INFRARED_PROC: filePath: {}", ftpResult.getFilepath());
                            filePaths.append("/" + ftpResult.getFilepath());
                        }
                        recordPersist(taskExecRecord, infoListSize, patrolTaskInfo, presetPos, fileTypes, filePaths);
                    } else { // 古老的红外处理流程，保留勿删除
                        log.info("INFRARED_PROC_OLD Failed to Cover id: {}, patrolPointId: {}: cameraCode: {}",
                                presetPos.getPresetPosId(),
                                presetPos.getPatrolPointId(),
                                presetPos.getChannelCode());
                        InfraredModel infraredModel = new InfraredModel();
                        infraredModel.setChannelSerial(presetPos.getChannelCode());
                        infraredModel.setPresetId(Integer.parseInt(presetPos.getPresetPosCode()));
                        EqpBook eqpbook = new EqpBook();
                        eqpbook.setPatrolDeviceCode(presetPos.getPatrolDeviceCode());
                        List<EqpBook> eqpBookListList = taskExecClient.selectBasedataEqpBookList(eqpbook);
                        if (!eqpBookListList.isEmpty()) {
                            Response ir_gen = remoteInvokeService.invoke("IR_GEN", presetPos.getPatrolDeviceCode().split("#")[0], new HashMap<>());
                            log.info("ir_gen: {}", JSONObject.toJSONString(ir_gen));
                            Object data = ir_gen.getData();
                            String path = JSONObject.parseObject(JSONObject.toJSONString(data)).getString("filePath");
                            path = path.split("33333")[1];
                            filePaths.append(path).append(",");
                            log.info("INFRA:{}", taskExecRecord.getTaskName() + " path : " + path);
                        }
                    }
                } else {
                    log.error("PATROL_POINT_NO_CHAN_TYPE id: {}, patrolPointId: {}: cameraCode: {}",
                            presetPos.getPresetPosId(),
                            presetPos.getPatrolPointId(),
                            presetPos.getChannelCode());
                }
            } catch (Exception e) {
                log.error("error", e);
            }

            // recordPersist(taskExecRecord, infoListSize, patrolTaskInfo, presetPos, fileTypes, filePaths);
        } else if (PresetAction.VIDEO.getCode().equals(presetAction.getActionType())) {
            log.info("VIDEO_PRESET_TYPE taskPatrolId: {}, chanType: {}, patrolPointId: {}, channelCode: {}, videoNvrCode: {}",
                    taskExecRecord.getOldTaskPatrolId(),
                    presetPos.getChannelType(),
                    presetPos.getPatrolPointId(),
                    presetPos.getChannelCode(),
                    presetPos.getVideoNvrCode());
            fileTypes.append(4).append(",");
            String jsonStr = HttpClientUtil.getHttpResultJson(liveIVS_URL + "/api/v1/stream/start?serial=" + presetPos.getVideoNvrCode() + "&code=" + presetPos.getChannelCode());
            log.info("先调用直播开始, 获取流对象: /api/v1/stream/start?serial= :{} ", presetPos.getVideoNvrCode() + "&code=" + presetPos.getChannelCode());
            log.info("/api/v1/stream/start 返回 json 字符串{} ", jsonStr);
            JSONObject UserInfoReturn = JSON.parseObject(jsonStr);
            Stream stream = JSON.toJavaObject(UserInfoReturn, Stream.class);
            try {
                String rel = HttpClientUtils.get(liveIVS_URL + "/api/v1/record/start", "streamid=" + stream.getStreamID() + "&code=" + presetPos.getChannelCode());
                log.info("视频录像开始: /api/v1/record/start streamid={} ", stream.getStreamID() + "，code=" + presetPos.getChannelCode() + "， 调用 /api/v1/record/start 返回值=" + rel);
                if (rel.isEmpty()) {
                    rel = HttpClientUtils.get(liveIVS_URL + "/api/v1/record/start", "streamid=" + stream.getStreamID() + "&code=" + presetPos.getChannelCode());
                    log.info("一次调用失败，第二次调用 视频录像开始: /api/v1/record/start streamid={} ", stream.getStreamID() + "，code=" + presetPos.getChannelCode() + "， 调用 /api/v1/record/start 返回值=" + rel);
                }
            } catch (Exception e) {
                log.error("error", e);
            }

            final int videoTime = presetAction.getVideoTime();
            Timer mTimer = new Timer();
            mTimer.schedule(new TimerTask() {
                int mCount = 0;

                @Override
                public void run() {
                    mCount++;
                    PatrolTaskExecRecord tempRecord = taskExecClient.selectPatrolTaskExecRecordByTaskPatrolId(taskPatrolId);
                    if (mCount >= videoTime + 2 || !isRunningState(tempRecord.getTaskPatrolId())) {
                        log.info("视频录像结束：查询当前巡检任务执行状态，当状态为暂停或者终止时，退出当前执行方法：TaskPatrolledId :{}", taskPatrolId);
                        videotapeRecordStop(filePaths, stream, presetPos.getChannelCode(), basePath, presetPos.getPatrolPointId(), presetPos.getVideoNvrCode());
                        mTimer.cancel();
                    }
                }
            }, 0, 1000L);
        } else {
            log.info("UNKNOWN_PRESET_TYPE taskPatrolId: {}, chanType: {}, patrolPointId: {}, channelCode: {}, videoNvrCode: {}",
                    taskExecRecord.getOldTaskPatrolId(),
                    presetPos.getChannelType(),
                    presetPos.getPatrolPointId(),
                    presetPos.getChannelCode(),
                    presetPos.getVideoNvrCode());
        }

        return taskExecRecord;
    }

    private String getRectangle(final String presetPosId) {
        List<PatrolPresetParam> patrolPresetParams
                = taskExecClient.selectPatrolPresetParamList(PatrolPresetParam.builder().presetPosId(Long.parseLong(presetPosId)).build());
        StringBuilder rectangles = new StringBuilder();
        if (patrolPresetParams != null) {
            for (PatrolPresetParam presetParam : patrolPresetParams) {
                rectangles.append(presetParam.getCoordinate()).append(";");
            }
        }

        return rectangles.toString();
    }

    synchronized private PatrolTaskPointExecRecord addPointRecord(PatrolTaskExecRecord taskExecRecord,
                                             PatrolTaskInfo taskInfo,
                                             PatrolPresetPosSlim presetPos,
                                             StringBuffer fileTypes,
                                             StringBuffer filePaths) {
        String rectangle = getRectangle(presetPos.getPresetPosId());
        String taskPatrolId = getTaskPatrolledId(taskInfo.getDevNo(), taskExecRecord.getTaskPatrolId());
        final String deviceId = taskInfo.getDeviceId();
        log.info("ADD_POINT_RECORD crud add taskPatrolId: {}, patrolPointId: {}",
                taskExecRecord.getOldTaskPatrolId(), deviceId);

        PatrolTaskPointExecRecord pointExecRecord = PatrolTaskPointExecRecord
                .builder()
                .patrolDeviceName(taskInfo.getEbookName())
                .patrolDeviceCode(taskInfo.getDevNo())
                .taskName(taskExecRecord.getTaskName())
                .taskCode(taskExecRecord.getTaskCode())
                .deviceName(taskInfo.getDeviceName())
                .deviceId(deviceId)
                .recognitionType(presetPos.getAlgSubtypeCode())
                .fileType(fileTypes.toString())
                .filePath(filePaths.toString())
                .rectangle(rectangle)
                .taskPatrolledId(taskPatrolId)
                .oldTaskPatrolledId(taskExecRecord.getOldTaskPatrolId())
                .patrolPointId(presetPos.getPatrolPointId())
                .time(new Date())
                .isOk(1)
                .build();
        taskExecClient.addPatrolTaskPointExecRecord(pointExecRecord);
        log.info(Color.RED + "ADD_POINT_2_RECORD crud add taskPatrolId: {}, deviceId: {}" + Color.END,
                taskExecRecord.getOldTaskPatrolId(), deviceId);
        //if(!testMode)
//        {
//            String patrolId = taskExecRecord.getTaskPatrolId();
//            String[] ids = patrolId.split(StringUtils.UNDERLINE);
//            String taskPatrolIdRemote = ids[1] + "_" + ids[2];
//            callRemoteSendMsgCtrlMode(taskPatrolIdRemote, taskExecRecord.getTotalAlgorithmNumber(), pointExecRecord);/*上报巡视结果*/
//        }
        return pointExecRecord;
    }

    private void callRemoteSendMsgRunMode(final String taskCode, final String taskName, final String taskPatrolId, final String taskProgress, final String state) {
        TaskState taskState = new TaskState();
        taskState.setTaskCode(taskCode);
        taskState.setTaskName(taskName);
        taskState.setTaskPatrolledId(taskPatrolId.split(StringUtils.UNDERLINE)[1] + StringUtils.UNDERLINE + taskPatrolId.split(StringUtils.UNDERLINE)[2]);
        taskState.setTaskProgress(taskProgress);
        taskState.setTaskState(state);
        List<TaskState> taskStates = new ArrayList<>();
        taskStates.add(taskState);
        TaskStateData taskStateData = new TaskStateData();
        taskStateData.setType(StaEnum.RunState.getCode());
        taskStateData.setReceiveCode("");
        taskStateData.setSendCode("摄像头");
        taskStateData.setTaskStateList(taskStates);
        Object patrolRunResult = JSONArray.toJSON(taskStateData);
        try {
            log.info("update patrol run state: {}", patrolRunResult.toString());
            startThreadGetInfo(StaEnum.RunState.getCode(), patrolRunResult.toString());
        } catch (Exception e) {
            log.error("error", e);
        }
    }

    private void callRemoteSendMsgCtrlMode(
            final String taskPatrolId,
            final int totalNumber,
            PatrolTaskPointExecRecord pointExecRecord) {
        PatrolResult patrolResult = PatrolResult.builder()
                .taskPatrolledId(taskPatrolId)
                .deviceId(pointExecRecord.getDeviceId())
                .deviceName(pointExecRecord.getDeviceName())
                .filePath(pointExecRecord.getFilePath())
                .fileType(pointExecRecord.getFileType())
                .patrolDeviceCode(pointExecRecord.getPatrolDeviceCode())
                .patrolDeviceName(pointExecRecord.getPatrolDeviceName())
                .rectangle(pointExecRecord.getRectangle())
                .taskCode(pointExecRecord.getTaskCode())
                .taskName(pointExecRecord.getTaskName())
                .time(DateUtils.format(DateUtils.yyyyMMddHHmmss2, new Date()))
                .unit(StringUtils.EMPTY)
                .valid("1")
                .value(StringUtils.EMPTY)
                .valueType(StringUtils.EMPTY)
                .valueUnit(StringUtils.EMPTY)
                .build();
        String recognitionType = algMapping(pointExecRecord.getPatrolPointId());
        log.info("----callRemoteSendMsgCtrlMode recognitionType: {}", recognitionType);
        patrolResult.setRecognitionType(recognitionType);
        List<PatrolResult> patrolResults = new ArrayList<>();
        patrolResults.add(patrolResult);
        PatrolResultData patrolResultData = PatrolResultData
                .builder()
                .type(StaEnum.CtlModel.getCode())
                .receiveCode("")
                .sendCode("")
                .items(patrolResults)
                .totalNumber(totalNumber)
                .build();
        Object patrolResultDataJsonObj = JSONArray.toJSON(patrolResultData);
        try {
            log.info("update patrol run result: {} ", patrolResultDataJsonObj.toString());
            startThreadGetInfo(StaEnum.CtlModel.getCode(), patrolResultDataJsonObj.toString());
        } catch (Exception e) {
            log.error("error", e);
        }
    }

    synchronized private int addCursorNumber(String taskPatrolId,
                                             int infoListSize,
                                             PatrolTaskPointExecRecord pointExecRecord,
                                             int totalAlgorithNumber) {
        PatrolTaskExecRecord taskExecRecord = taskExecClient.selectPatrolTaskExecRecordByTaskPatrolId(taskPatrolId);

        int total = taskExecRecord.getTotalNumber();
        int success = taskExecRecord.getFinishNumber();
        int cursor;
        if (taskExecRecord.getCursorNumber() == null) {
            cursor = 1;
        } else {
            cursor = taskExecRecord.getCursorNumber();
            if (cursor < total) {
                cursor = taskExecRecord.getCursorNumber() + 1;
            }
        }

        taskExecRecord.setCursorNumber(cursor);
        //if (bOk) {
        success++;
        log.info("EXECUTE_POINT_SUCCESS taskPatrolId: {}, cursor: {}, success: {}, total: {}, totalAlgorithNumber: {}",
                taskExecRecord.getOldTaskPatrolId(),
                cursor,
                success,
                total,
                totalAlgorithNumber);
        taskExecRecord.setFinishNumber(success);
        String taskProgress = decimalFormatNum(success, total);// task_progress=success/total -> patrol_task_exec_record
        taskExecRecord.setTaskProgress(taskProgress);
//            List<PatrolTaskInfo> totalInfos = getInfosByRecord(taskExecRecord);
        List<PatrolTaskPointExecRecord> finishedInfos = taskExecClient.selectPatrolTaskPointExecRecordList(PatrolTaskPointExecRecord.builder().oldTaskPatrolledId(taskExecRecord.getOldTaskPatrolId()).build());
        String totalProgress = decimalFormatNum(finishedInfos.size(), total);// task_progress=finish/total -> patrol_task_status
        log.info("EXECUTE_POINT_ADD_CURSOR taskPatrolId: {}, totalProgress: {}, finish: {}, batchSize: {}, total: {}",
                taskExecRecord.getOldTaskPatrolId(),
                totalProgress,
                finishedInfos.size(),
                infoListSize,
                total);
        callRemoteSendMsgRunMode(
                taskExecRecord.getTaskCode(),
                taskExecRecord.getTaskName(),
                taskExecRecord.getTaskPatrolId(),
                totalProgress,
                TaskStatus.RUNNING.getCode()/*运行中百分比上报*/
        );

        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            log.error("error", e);
        }

        {
            String patrolId = taskExecRecord.getTaskPatrolId();
            String[] ids = patrolId.split(StringUtils.UNDERLINE);
            String taskPatrolIdRemote = ids[1] + "_" + ids[2];
            callRemoteSendMsgCtrlMode(taskPatrolIdRemote, totalAlgorithNumber, pointExecRecord);/*上报巡视结果*/
        }
//        }
//        else {
//            log.info("execute point fail, cursor: {}, success: {}, total: {}", cursor, success, total);
//        }
        taskExecClient.updatePatrolTaskExecRecord(taskExecRecord);
        return cursor;
    }

    synchronized private void resetRecordCounter(String taskCode) {
        execRecordCounter = 0;
        redisService.setCacheObjectOfTask(RedisConst.TASK_RECORD_CNT, taskCode, 0);
    }

    private void setRedisRecordCount(String taskCode, final int size) {
        redisService.setCacheObjectOfTask(RedisConst.TASK_RECORD_CNT, taskCode, size);
    }

    synchronized private void resetRedisCurrentCode(String curTaskCode) {
        final String taskCode = redisService.getCacheObjectOfTask(RedisConst.TASK_CURRENT_CODE, curTaskCode);
        if (StringUtils.isNotEmpty(taskCode)) {
            redisService.deleteObject(taskCode);
        }
        redisService.deleteObjectOfTask(RedisConst.TASK_CURRENT_CODE, curTaskCode);
    }

    synchronized private void windUpRecord(String taskPatrolId, int infoListSize) {
        PatrolTaskExecRecord execRecord = taskExecClient.selectPatrolTaskExecRecordByTaskPatrolId(taskPatrolId);
        final int total = execRecord.getTotalNumber();
        final int cursor = execRecord.getCursorNumber();
        final int finish = execRecord.getFinishNumber();
        log.info("cursor:{}, finish:{}, total:{}, taskPatrolId:{}", cursor, finish, total, taskPatrolId);
        if (cursor == total) {
            execRecordCounter++;
            execRecord.setEndTime(new Date());
            if (finish == total) { // record下的点全部执行成功
                //仅仅靠一个record成功执行不能代表所有的record都执行成功,只能设置该record执行成功标记
                String taskProgress = decimalFormatNum(finish, total);
                execRecord.setTaskProgress(taskProgress);
                execRecord.setTaskState(TaskStatus.DONE.getCode()); // 子任务在当前轮回中遍历完成标记
//                List<PatrolTaskInfo> totalInfos = getInfosByRecord(execRecord);
                List<PatrolTaskPointExecRecord> finishedInfos = taskExecClient.selectPatrolTaskPointExecRecordList(PatrolTaskPointExecRecord.builder().oldTaskPatrolledId(execRecord.getOldTaskPatrolId()).build());
                String totalProgress = decimalFormatNum(finishedInfos.size(), infoListSize);
                log.info("windUpRecord totalProgress: {}, finish: {}, total: {}", totalProgress, finishedInfos.size(), infoListSize);
//                callRemoteSendMsgRunMode(
//                        execRecord.getTaskCode(),
//                        execRecord.getTaskName(),
//                        execRecord.getTaskPatrolId(),
//                        totalProgress,
//                        TaskStatus.DONE.getCode()/*点位全部执行成功,上报进度100%*/
//                );
                final Integer redisRecCnt = redisService.getCacheObjectOfTask(RedisConst.TASK_RECORD_CNT, execRecord.getTaskCode());
                log.info("execRecordCounter: {}, redisRecCnt: {}", execRecordCounter, redisRecCnt);
                if (execRecordCounter == redisRecCnt) { // 最后一个record
                    resetRecordCounter(execRecord.getTaskCode());
                    resetRedisCurrentCode(execRecord.getTaskCode());
                    redisService.deleteObjectOfTask(RedisConst.IMMEDIATELY_EXEC_TASK_TIME, execRecord.getTaskCode());
                }
            } else { // 该record下的点没有全部执行成功
                //设置本record的执行状态为终止
                execRecord.setTaskState(TaskStatus.HALTED.getCode());
//                List<PatrolTaskInfo> totalInfos = getInfosByRecord(execRecord);
                List<PatrolTaskPointExecRecord> finishedInfos = taskExecClient.selectPatrolTaskPointExecRecordList(PatrolTaskPointExecRecord.builder().oldTaskPatrolledId(execRecord.getOldTaskPatrolId()).build());
                String totalProgress = decimalFormatNum(finishedInfos.size(), infoListSize);
                log.info("finish not equal total totalProgress: {}, finish: {}, total: {}", totalProgress, finishedInfos.size(), infoListSize);
                callRemoteSendMsgRunMode(
                        execRecord.getTaskCode(),
                        execRecord.getTaskName(),
                        execRecord.getTaskPatrolId(),
                        totalProgress,
                        TaskStatus.DONE.getCode()/*点位全部执行成功,上报进度100%*/
                );
                final int redisRecCnt = redisService.getCacheObjectOfTask(RedisConst.TASK_RECORD_CNT, execRecord.getTaskCode());
                log.info("finish not equal total execRecordCounter: {}, redisRecCnt: {}", execRecordCounter, redisRecCnt);
                if (execRecordCounter == redisRecCnt) {
                    resetRecordCounter(execRecord.getTaskCode());
                    resetRedisCurrentCode(execRecord.getTaskCode());
                    redisService.deleteObjectOfTask(RedisConst.IMMEDIATELY_EXEC_TASK_TIME, execRecord.getTaskCode());
                }
            }
        }
        taskExecClient.updatePatrolTaskExecRecord(execRecord);
    }

    synchronized private PatrolTaskExecRecord recordPersist(
            PatrolTaskExecRecord taskExecRecord,
            int infoListSize,
            PatrolTaskInfo taskInfo,
            PatrolPresetPosSlim presetPos,
            StringBuffer fileTypes,
            StringBuffer filePaths) {

        //log.info(Color.CYAN + "recordPersistTrace bOk: {}" + Color.END, bOk);
        PatrolTaskPointExecRecord patrolTaskPointExecRecord = null;
        try {
            patrolTaskPointExecRecord = addPointRecord(taskExecRecord, taskInfo, presetPos, fileTypes, filePaths);
        } catch (Exception e) {
            log.error("error", e);
        }
        //log.info(Color.CYAN + "**************************addPointRecord end" + Color.END);

        log.info(Color.CYAN + "addCursorNumberStart" + Color.END);
        int cursorNumber = 0;
        try {
            cursorNumber = addCursorNumber(taskExecRecord.getTaskPatrolId(), infoListSize, patrolTaskPointExecRecord, taskExecRecord.getTotalAlgorithmNumber());
        } catch (Exception e) {
            log.error("error", e);
        }

        PatrolTaskExecRecord taskExecRecordEx = taskExecClient.selectPatrolTaskExecRecordByTaskPatrolId(taskExecRecord.getTaskPatrolId());
        int totalNumber = taskExecRecordEx.getTotalNumber();
        log.info(Color.CYAN + "ADD_CURSOR_NUMBER_END taskPatrolId: {}, cursorNumber: {}, batchNumber: {}, total: {}" + Color.END,
                taskExecRecordEx.getOldTaskPatrolId(), cursorNumber, infoListSize, totalNumber);

        //if (cursorNumber == infoListSize) {// devNo下对应的点执行完毕
        if (cursorNumber == totalNumber) {// devNo下对应的点执行完毕
            log.info(Color.CYAN + "WINDUP_RECORD_START taskPatrolId: {}" + Color.END, taskExecRecord.getOldTaskPatrolId());
            windUpRecord(taskExecRecord.getTaskPatrolId(), infoListSize);
            log.info(Color.CYAN + "WINDUP_RECORD_END taskPatrolId: {}" + Color.END, taskExecRecord.getOldTaskPatrolId());
        }

        return taskExecRecord;
    }

    private void startThreadGetInfo(String type, Object obj) {
        taskExecClient.getInfo(type, obj.toString());
    }

    private void videotapeRecordStop(StringBuffer filePaths, Stream stream, String code, String basePath, Long patrolpointId, String devNo) {
        try {
            log.info("视频录像停止 /api/v1/record/stop?streamid={} ", stream.getStreamID() + ",code=" + code);
            String jsonStr = HttpClientUtil.getHttpResultJson(this.liveIVS_URL + "/api/v1/record/stop?streamid=" + stream.getStreamID() + "&code=" + code);
            log.info("视频录像停止 返回json字符串 /api/v1/record/stop?jsonStr={} ", jsonStr);
            JSONObject jsonObject = JSON.parseObject(jsonStr);
            RecordData recordData = JSON.toJavaObject(jsonObject, RecordData.class);
            String path = recordData.getRecordList().get(0).getDownloadURL();
            FtpResult ftpResult = getFileAndSave(path, patrolpointId + "_" + devNo + "_" + path.split("/")[path.split("/").length - 1].split("_")[2] + ".mp4", basePath + "Video");
            filePaths.append(path).append(",");
            log.info("视频保存路径 path={} ", path);
        } catch (Exception e) {
            log.error("error", e);
        }

    }

    private String algMapping(Long patrolPointId) {
        String dictValue = StringUtils.EMPTY;
        DBPatrolPoint dbPatrolPoint = taskExecClient.selectBasedataPatrolPointByPatrolPointId(patrolPointId);
        List<SysDictData> sysDictDataList = taskExecClient.selectDictDataList(SysDictData.builder().build());
        if (dbPatrolPoint == null
                || sysDictDataList == null || sysDictDataList.isEmpty()
                || StringUtils.isEmpty(dbPatrolPoint.getAlgSubtypeIds())) {
            log.info("algMapping MATCH recognition_type, dictValue empty return.");
            return dictValue;
        }

        boolean exist = false;
        String[] ids = dbPatrolPoint.getAlgSubtypeIds().split(StringUtils.COMMA);
        for (String id : ids) {
            for (SysDictData sysDictData : sysDictDataList) {
                if (Long.parseLong(id) == sysDictData.getDictSort()) {
                    if (!exist && sysDictData.getRemark() == null) {
                        dictValue = sysDictData.getDictValue();
                    } else if (!exist) {
                        dictValue = sysDictData.getRemark();
                        exist = true;
                    }
                }
            }
        }

        log.info("algMapping MATCH recognition_type: {}", dictValue);
        return dictValue;
    }

    public static String getWeekOfDate(Date date) {
        String[] weekDays = new String[]{"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int w = cal.get(7) - 1;
        if (w < 0) {
            w = 0;
        }

        return weekDays[w];
    }

    @PostMapping({"/immediatelyExecTask"})
    public void immediatelyExecTask(@RequestBody PatrolTask patrolTaskParam) {
        List<PatrolTask> patrolTasks = taskExecClient.getFilterPatrolTaskList(patrolTaskParam.getTaskCode());
        PatrolTask patrolTask = patrolTasks.get(0);
        log.info("Filter_task: param: {}, size: {}, result:{}", patrolTaskParam.getTaskCode(), patrolTasks.size(), patrolTask);
        String taskCode = redisService.getCacheObjectOfTask(RedisConst.TASK_CURRENT_CODE, patrolTaskParam.getTaskCode());
//        if (StringUtils.isNotEmpty(taskCode)) {
//            log.info(Color.CYAN + "TASK SLOT IS TAKEN BY: {}" + Color.END, taskCode);
//            return;
//        }

        PatrolTaskInfo patrolTaskInfo = new PatrolTaskInfo();
        // patrolTaskList.add(patrolTask);
        List<PatrolTask> patrolTaskList = new ArrayList<>(patrolTasks);
        List<PatrolTask> cameraPatrolTasks = new ArrayList<>();
        assembleVideoTask(patrolTaskList, cameraPatrolTasks);
        String redisImmediatelyExecTaskTime = redisService.getCacheObjectOfTask(RedisConst.IMMEDIATELY_EXEC_TASK_TIME, patrolTask.getTaskCode());
//        if (redisImmediatelyExecTaskTime != null) {
//            log.info(Color.CYAN + "IMMEDIATELY_EXEC_TASK_TIME TASK TAKEN :{}" + Color.END, redisImmediatelyExecTaskTime);
//            return;
//        }

        if (shaoXinBigModel.isSwitchStatus()) {
            log.info("[BIG] Temporary gathering, close bigSwitch in nacos");
            redisService.redisTemplate.opsForValue().set(BIG_FILE_POINT, -1);
        }

//        String immediatelyExecTaskTime = DateUtils.parseDateToStr(DateUtils.yyyyMMddHHmmss, new Date());
        String immediatelyExecTaskTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
        redisService.setCacheObjectOfTask(RedisConst.IMMEDIATELY_EXEC_TASK_TIME, patrolTask.getTaskCode(), immediatelyExecTaskTime);
        log.info("SET TASK_CURRENT_CODE: key: {}, value: {}", RedisConst.TASK_CURRENT_CODE + patrolTask.getTaskCode(), RedisConst.TASK_CODE + patrolTask.getTaskCode() + StringUtils.AT + immediatelyExecTaskTime);
        resetRecordCounter(patrolTask.getTaskCode());
        log.info("TASK REC CNT: {}", cameraPatrolTasks.size());//devType是2的设备数
        setRedisRecordCount(patrolTask.getTaskCode(), cameraPatrolTasks.size());
        List<PatrolTaskInfo> taskInfos = new ArrayList<>();
        for (PatrolTask task : cameraPatrolTasks) {
            List<PatrolTaskInfo> infosByRecord = getInfosByRecord(PatrolTaskExecRecord.builder().devNo(task.getDevNo()).taskId(task.getTaskId()).build());
            taskInfos.addAll(infosByRecord);
        }
        log.info("当前任务: {}, taskCode: {}, taskInfos.size: {}", patrolTask.getTaskName(), patrolTask.getTaskCode(), taskInfos.size());
        String[] devNos = patrolTask.getDevNo().split(StringUtils.COMMA);
        PatrolTaskExecRecord patrolTaskExecRecord = new PatrolTaskExecRecord();
        Date time = patrolTask.getFixedStartTime();
        //String timeStr = time == null ? immediatelyExecTaskTime : DateUtils.parseDateToStr(DateUtils.yyyyMMddHHmmss, time);
        String timeStr = immediatelyExecTaskTime;
        patrolTaskExecRecord.setTaskPatrolId(devNos[0] + "_" + patrolTask.getTaskCode() + "_" + immediatelyExecTaskTime);// 这边暂定devNos[0]
        //patrolTaskExecRecord.setOldTaskPatrolId(patrolTask.getTaskCode() + "_" + timeStr + "_" + immediatelyExecTaskTime);
        patrolTaskExecRecord.setOldTaskPatrolId(patrolTask.getTaskCode() + "_" + timeStr);
        patrolTaskExecRecord.setTaskName(patrolTask.getTaskName());
        patrolTaskExecRecord.setTaskCode(patrolTask.getTaskCode());
        patrolTaskExecRecord.setTaskState(TaskStatus.RUNNING.getCode());
        patrolTaskExecRecord.setPlanStartTime(patrolTask.getCycleStartTime());
        patrolTaskExecRecord.setStartTime(new Date());
        patrolTaskExecRecord.setTaskProgress("0");
        patrolTaskExecRecord.setEndTime(patrolTask.getCycleEndTime());
        patrolTaskExecRecord.setDescription("");
        patrolTaskExecRecord.setDevNo(devNos[0]);
        patrolTaskExecRecord.setTaskType(patrolTask.getType());
        patrolTaskExecRecord.setTaskPriority(patrolTask.getPriority());
        patrolTaskExecRecord.setTaskId(patrolTask.getTaskId());
        patrolTaskExecRecord.setStationName(patrolTask.getStationName());
        patrolTaskExecRecord.setStationCode(patrolTask.getStationCode());
        patrolTaskExecRecord.setDeviceId("0");
        patrolTaskExecRecord.setExecPointId(0L);
        patrolTaskExecRecord.setTotalNumber(taskInfos.isEmpty() ? 1 : taskInfos.size());
        patrolTaskExecRecord.setSpecifiedAlg(patrolTask.getSpecifiedAlg());
        log.info("taskName: {}, taskCode: {}, taskPatrolledId: {}",
                patrolTaskExecRecord.getTaskName(), patrolTaskExecRecord.getTaskCode(), patrolTaskExecRecord.getTaskPatrolId());
        taskExecClient.addPatrolTaskExecRecord(patrolTaskExecRecord);
//            patrolTaskInfo.setTaskMajorId(task.getTaskId().toString());
//            patrolTaskInfo.setDevNo(task.getDevNo());
//            taskExecClient.selectTaskInfoList(patrolTaskInfo);
        taskExecClient.updatePatrolTaskStatus(patrolTask);
        threadPool.submit(() -> prePointExecImmediate(patrolTaskExecRecord, taskInfos));
    }

    @PostMapping({"/resumeExecTask"})
    public void resumeExecTask(final String taskId) {
        log.info("resumeExecTask taskId: {}", taskId);
        StatusMonitor monitor = TaskStatusManager.get(taskId);
        if (monitor != null) {
            synchronized (monitor) {
                monitor.notifyAll();
            }
        }
    }

    private List<PatrolTaskInfo> getInfosByRecord(final PatrolTaskExecRecord record) {
        List<PatrolTaskInfo> patrolTaskInfoList = new ArrayList<>();
        List<PatrolTaskInfo> patrolTaskInfoListEx = new ArrayList<>();
        try {
            patrolTaskInfoList = taskExecClient.selectTaskInfoList(PatrolTaskInfo.builder()
                    .devNo(record.getDevNo())
                    .taskMajorId(record.getTaskId().toString())
                    .build());

            for (PatrolTaskInfo patrolTaskInfo : patrolTaskInfoList) {
                PatrolPresetPos patrolPresetPos = PatrolPresetPos.builder()
                        .patrolPointId(Long.parseLong(patrolTaskInfo.getDeviceId()))
                        .isEnable("1")
                        .build();
                List<PatrolPresetPos> presetPosList = taskExecClient.selectPatrolPresetPosList(patrolPresetPos);
                PatrolPresetPos presetPos = !presetPosList.isEmpty() ? presetPosList.get(0) :
                        PatrolPresetPos.builder().presetPosId("1")
                                .presetPosCode("1")
                                .channelCode("ffffffffffffffffff#ffffffffffffffffffffffffffffffff")
                                .channelId("999999")
                                .patrolPointId(1234567890L)
                                .channelType("vl")
                                .isEnable("1")
                                .eqpBookId("1")
                                .build();
                String deviceCode = "ffffffffffffffffff";
                try {
                    String[] channelCodeList = presetPos.getChannelCode().split(StringUtils.HASHTAG);
                    deviceCode = channelCodeList[0];
                } catch (Exception e) {
                    log.error("presetPos illegal channelCode: {} with deviceId: {}", presetPos.getChannelCode(), patrolTaskInfo.getDeviceId());
                }

                patrolTaskInfo.setDeviceCode(deviceCode);
                patrolTaskInfoListEx.add(patrolTaskInfo);
            }
            return patrolTaskInfoMaintainAreaRemove(patrolTaskInfoListEx);//把检修区域的点位删除
        } catch (Exception e) {
            log.error("error", e);
            return patrolTaskInfoList;
        }
    }

    private void assembleVideoTask(List<PatrolTask> patrolTaskList, List<PatrolTask> cameraPatrolTasks) {
        if (patrolTaskList != null) {
            String jobAreas = taskExecClient.getConfigByKey("JOB_AREAS");
            log.info("jobAreas: {}", jobAreas);
            List<String> jobAreaList = new ArrayList<>();
            if (StringUtils.isNotBlank(jobAreas)) {
                String[] jobAreaArr = jobAreas.split(StringUtils.COMMA);
                for (int i = 0; i < jobAreaArr.length; i++) {
                    jobAreaList.add(jobAreaArr[i]);
                }
            }
            log.info("jobAreaList: {}", JSONObject.toJSONString(jobAreaList));

            for (PatrolTask task : patrolTaskList) {
                if (!StringUtils.isEmpty(task.getDevType())) {
                    String[] devTypes = task.getDevType().split(StringUtils.COMMA);
                    String[] devNos = task.getDevNo().split(StringUtils.COMMA);
                    for (int i = 0; i < devTypes.length; ++i) {
                        if ("2".equals(devTypes[i])) {
                            if (jobAreaList.isEmpty() || jobAreaList.contains(devNos[i])) {
                                PatrolTask cameraPatrolTask = new PatrolTask();
                                cameraPatrolTask.setTaskId(task.getTaskId());
                                cameraPatrolTask.setStationName(task.getStationName());
                                cameraPatrolTask.setStationCode(task.getStationCode());
                                cameraPatrolTask.setType(task.getType());
                                cameraPatrolTask.setTaskCode(task.getTaskCode());
                                cameraPatrolTask.setTaskName(task.getTaskName());
                                cameraPatrolTask.setPriority(task.getPriority());
                                cameraPatrolTask.setDeviceLevel(task.getDeviceLevel());
                                cameraPatrolTask.setDeviceList(task.getDeviceList());
                                cameraPatrolTask.setFixedStartTime(task.getFixedStartTime());
                                cameraPatrolTask.setCycleMonth(task.getCycleMonth());
                                cameraPatrolTask.setCycleWeek(task.getCycleWeek());
                                cameraPatrolTask.setCycleExecuteTime(task.getCycleExecuteTime());
                                cameraPatrolTask.setCycleStartTime(task.getCycleStartTime());
                                cameraPatrolTask.setCycleEndTime(task.getCycleEndTime());
                                cameraPatrolTask.setIntervalNumber(task.getIntervalNumber());
                                cameraPatrolTask.setIntervalType(task.getIntervalType());
                                cameraPatrolTask.setIntervalExecuteTime(task.getIntervalExecuteTime());
                                cameraPatrolTask.setIntervalStartTime(task.getIntervalStartTime());
                                cameraPatrolTask.setIntervalEndTime(task.getIntervalEndTime());
                                cameraPatrolTask.setInvalidStartTime(task.getInvalidStartTime());
                                cameraPatrolTask.setInvalidEndTime(task.getInvalidEndTime());
                                cameraPatrolTask.setIsEnable(task.getIsEnable());
                                cameraPatrolTask.setLinkageState(task.getLinkageState());
                                cameraPatrolTask.setDevType("2");
                                cameraPatrolTask.setCreator(task.getCreator());
                                cameraPatrolTask.setFileStatus(task.getFileStatus());
                                cameraPatrolTask.setTaskCycle(task.getTaskCycle());
                                cameraPatrolTask.setDevNo(devNos[i]);
                                cameraPatrolTask.setStorage(task.getStorage());
                                cameraPatrolTask.setTaskProgress(task.getTaskProgress());
                                cameraPatrolTask.setTaskState(task.getTaskState());
                                cameraPatrolTask.setExecutionMode(task.getExecutionMode());
                                cameraPatrolTask.setBeginTime(task.getBeginTime());
                                cameraPatrolTask.setEndTime(task.getEndTime());
                                cameraPatrolTask.setAreaId(task.getAreaId());
                                cameraPatrolTask.setAreaName(task.getAreaName());
                                cameraPatrolTasks.add(cameraPatrolTask);
                            }
                        }
                    }
                }
            }
        }
    }

//    private void prePointExecImmediate(final PatrolTaskExecRecord patrolTaskExecRecord, List<PatrolTaskInfo> patrolTaskInfoList) {
////        final List<PatrolTaskInfo> patrolTaskInfoList = getInfosByRecord(patrolTaskExecRecord);
//        //patrolTaskInfoList size: 12 devNo: camera, taskId: 10032
//        log.info("EXEC patrolTaskInfoList size: {} devNo: {}, taskId: {}", patrolTaskInfoList.size(), patrolTaskExecRecord.getDevNo(), patrolTaskExecRecord.getTaskId());
//        for (final PatrolTaskInfo taskInfo : patrolTaskInfoList) {
//            log.info("EXEC Immediate taskPatrolId: {}, patrolPointId: {}, lineId: {}", patrolTaskExecRecord.getTaskPatrolId(), taskInfo.getDeviceId(), taskInfo.getLineId());
//            prePointExec(patrolTaskExecRecord, taskInfo, patrolTaskInfoList.size());
//
//            String taskPatrolledId = patrolTaskExecRecord.getTaskPatrolId().split(StringUtils.UNDERLINE)[1] + "_" + patrolTaskExecRecord.getTaskPatrolId().split(StringUtils.UNDERLINE)[2];
//            PatrolTaskStatus patrolTaskStatus = new PatrolTaskStatus();
//            patrolTaskStatus.setTaskPatrolledId(taskPatrolledId);
//            List<PatrolTaskStatus> list = taskExecClient.selectPatrolTaskStatusList(patrolTaskStatus);
//            if(list.size() > 0) {
//                log.info("-----------------------2 TaskState: {}", list.get(0).getTaskState());
//                if("4".equals(list.get(0).getTaskState())) {//终止
//                    log.info("-----------------------2 task terminate: {}", taskPatrolledId);
//                    return;
//                }
//            } else {
//                log.info("-----------------------2 selectPatrolTaskStatusList is empty");
//            }
//        }
//    }

    // 任务分拆异步执行V1.0
//    private void prePointExecImmediate(final PatrolTaskExecRecord patrolTaskExecRecord, List<PatrolTaskInfo> patrolTaskInfoList) {
//        int batchSize = taskBatchSize;
//        int total = patrolTaskInfoList.size();
//        log.info(Color.CYAN + "batchSize: {}, total: {}" + Color.END, batchSize, total);
//
//        int index = 1;
//        for (int i = 0; i < total; i += batchSize) {
//            int end = Math.min(i + batchSize, total);
//            List<PatrolTaskInfo> batch = patrolTaskInfoList.subList(i, end);
//            // 提交每一批次任务到线程池
//            final int threadCnt = index;
//            threadPool.submit(() -> handlePrePointBatch(threadCnt, patrolTaskExecRecord, batch));
//            index++;
//        }
//    }

    private int calcTaskAlgorithmTotalNumber(List<PatrolTaskInfo> patrolTaskInfoList, String specifiedAlg) {
        int totalAlgorithmNumber = 0;
        for (PatrolTaskInfo patrolTaskInfo : patrolTaskInfoList) {
            // 任务指定算法类型：累加任务点位算法个数
            if (StringUtils.isNotEmpty(specifiedAlg)) {
                String[] algSubtypeIds = specifiedAlg.split(StringUtils.COMMA);
                totalAlgorithmNumber += algSubtypeIds.length;
            } else {
                String result = taskExecClient.getAlgSubtypeIdsByPatrolPointId(patrolTaskInfo.getDeviceId());
                if (StringUtils.isNotEmpty(result)) {
                    String[] algSubtypeIds = result.split(StringUtils.COMMA);
                    totalAlgorithmNumber += algSubtypeIds.length;
                }
            }
        }

        return totalAlgorithmNumber;
    }

    private void prePointExecImmediate(final PatrolTaskExecRecord patrolTaskExecRecord, List<PatrolTaskInfo> patrolTaskInfoList) {
        Map<String, List<PatrolTaskInfo>> deviceMap = new HashMap<>();
        for (PatrolTaskInfo patrolTaskInfo : patrolTaskInfoList) {
            deviceMap.computeIfAbsent(patrolTaskInfo.getDeviceCode(), k -> new ArrayList<>()).add(patrolTaskInfo);
        }

        int totalAlgorithmNumber = calcTaskAlgorithmTotalNumber(patrolTaskInfoList, patrolTaskExecRecord.getSpecifiedAlg());
        patrolTaskExecRecord.setTotalAlgorithmNumber(totalAlgorithmNumber);

        asyncTaskPatrolPointCnt.set(0);
        int batchSize = taskBatchSize;
        int total = patrolTaskInfoList.size();
        log.info(Color.CYAN + "threadId: {}, batchSize: {}, total: {}, totalAlgorithmNumber: {}" + Color.END, Thread.currentThread().getId(), batchSize, total, totalAlgorithmNumber);
        List<PatrolTaskInfo> currentGroup = new ArrayList<>();
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        int index = 1;
        for (List<PatrolTaskInfo> sameCodeList : deviceMap.values()) {
            if (sameCodeList.size() > batchSize) {
                String[] deviceCodeArray = sameCodeList.stream().map(PatrolTaskInfo::getDeviceCode).toArray(String[]::new);
                log.info("BATCH_BIG_GROUP deviceCodeArray: {}", String.join(StringUtils.COMMA, deviceCodeArray));
                // 大组直接提交处理
                final int threadCnt = index;
                futures.add(CompletableFuture.runAsync(() -> handlePrePointBatch(threadCnt, patrolTaskExecRecord, sameCodeList), threadPool));
                index++;
            } else {
                if (currentGroup.size() + sameCodeList.size() > batchSize) {
                    // 当前组满了，提交处理
                    final int threadCnt = index;
                    List<PatrolTaskInfo> finalGroup = new ArrayList<>(currentGroup);
                    String[] deviceCodeArray = finalGroup.stream().map(PatrolTaskInfo::getDeviceCode).toArray(String[]::new);
                    log.info("BATCH_FINAL_GROUP deviceCodeArray: {}", String.join(StringUtils.COMMA, deviceCodeArray));
                    futures.add(CompletableFuture.runAsync(() -> handlePrePointBatch(threadCnt, patrolTaskExecRecord, finalGroup), threadPool));
                    currentGroup.clear();
                    index++;
                }
                currentGroup.addAll(sameCodeList);
            }
        }

        if (!currentGroup.isEmpty()) {
            final int threadCnt = index;
            List<PatrolTaskInfo> finalGroup = new ArrayList<>(currentGroup);
            String[] deviceCodeArray = finalGroup.stream().map(PatrolTaskInfo::getDeviceCode).toArray(String[]::new);
            log.info("BATCH_LAST_FINAL_GROUP deviceCodeArray: {}", String.join(StringUtils.COMMA, deviceCodeArray));
            futures.add(CompletableFuture.runAsync(() -> handlePrePointBatch(threadCnt, patrolTaskExecRecord, finalGroup), threadPool));
        }

        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        completedTaskConsummate(patrolTaskExecRecord);
    }

    private void handlePrePointBatch(final int threadCnt, final PatrolTaskExecRecord patrolTaskExecRecord, final List<PatrolTaskInfo> taskInfoBatch) {
        asyncTaskPatrolPointCnt.getAndAdd(taskInfoBatch.size());
        log.info("HANDLE_PRESET_POINT_BATCH patrolPointId: {}, threadCnt: {}, asyncTaskPatrolPointCnt: {}, batch size: {}, devNo: {}, taskId: {}",
                patrolTaskExecRecord.getOldTaskPatrolId(),
                threadCnt,
                asyncTaskPatrolPointCnt.get(),
                taskInfoBatch.size(),
                patrolTaskExecRecord.getDevNo(),
                patrolTaskExecRecord.getTaskId());

        final String taskPatrolId = patrolTaskExecRecord.getOldTaskPatrolId();
        StatusMonitor monitor = TaskStatusManager.get(patrolTaskExecRecord.getOldTaskPatrolId());
        if (monitor == null) {
            monitor = TaskStatusManager.register(taskPatrolId, () -> {
                PatrolTaskStatus patrolTaskStatus = taskExecClient.selectPatrolTaskStatusByTaskPatrolledId(taskPatrolId);
                int taskState;
                try {
                    taskState = Integer.parseInt(patrolTaskStatus.getTaskState());
                } catch (Exception e) {
                    taskState = -1;
                }
                return taskState;
            });
        }
        for (final PatrolTaskInfo taskInfo : taskInfoBatch) {
            log.info("handlePrePointBatch taskPatrolId: {}, patrolPointId: {}, lineId: {}", patrolTaskExecRecord.getTaskPatrolId(), taskInfo.getDeviceId(), taskInfo.getLineId());
            int status = monitor.getStatus();
            if (status == Integer.parseInt(TaskStatus.HALTED.getCode())) {
                log.info("task terminate taskPatrolId: {}", taskPatrolId);
                break;
            } else if (status == Integer.parseInt(TaskStatus.PAUSED.getCode())) {
                synchronized (monitor) {
                    try {
                        log.info("task pause taskPatrolId: {}", taskPatrolId);
                        monitor.wait(); // 等待外部唤醒
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            } else if (status == -1 || status == 0) {
                log.info("task status error state taskPatrolId: {}", taskPatrolId);
            }
            prePointExec(patrolTaskExecRecord, taskInfo, taskInfoBatch.size());
        }
    }

    private void completedTaskConsummate(final PatrolTaskExecRecord patrolTaskExecRecord) {
        TaskStatusManager.remove(patrolTaskExecRecord.getOldTaskPatrolId());
        boolean isConsummate = redisService.deleteObjectOfTask(RedisConst.TASK_CURRENT_CODE, patrolTaskExecRecord.getTaskCode());
        log.info("CONSUMMATE_COMPLETED_TASK taskPatrolId: {}, isConsummate: {}",
                patrolTaskExecRecord.getOldTaskPatrolId(),
                isConsummate ? "false" : "true");
    }

    public List<List<PatrolTaskInfo>> optimizedGroup(List<PatrolTaskInfo> list) {
        Map<String, List<PatrolTaskInfo>> deviceMap = new HashMap<>();

        // 遍历原始列表，按 deviceCode 聚合
        for (PatrolTaskInfo item : list) {
            deviceMap.computeIfAbsent(item.getDeviceCode(), k -> new ArrayList<>()).add(item);
        }

        List<List<PatrolTaskInfo>> result = new ArrayList<>();
        List<PatrolTaskInfo> currentGroup = new ArrayList<>();
        int maxGroupSize = 100;

        for (List<PatrolTaskInfo> group : deviceMap.values()) {
            if (group.size() > maxGroupSize) {
                // 超过最大组，单独成组
                result.add(group);
            } else {
                if (currentGroup.size() + group.size() > maxGroupSize) {
                    result.add(currentGroup);
                    currentGroup = new ArrayList<>();
                }
                currentGroup.addAll(group);
            }
        }

        if (!currentGroup.isEmpty()) {
            result.add(currentGroup);
        }

        return result;
    }

    private void execRemoveMaintainArea(List<PatrolTaskInfo> patrolTaskInfoList, List<MaintainRegion> maintAreaList, List<String> deviceIds) {
        for (MaintainRegion area : maintAreaList) {
            if (area.getStartTime().getTime() < System.currentTimeMillis() && area.getEndTime().getTime() > System.currentTimeMillis()) {
                deviceIds.addAll(Arrays.asList(area.getPointList().split(StringUtils.COMMA)));
                String[] pointIds = area.getPointList().split(StringUtils.COMMA);
                patrolTaskInfoMaintainAreaRemove(patrolTaskInfoList);
            }
        }

    }

    private List<PatrolTaskInfo> patrolTaskInfoMaintainAreaRemove(final List<PatrolTaskInfo> patrolTaskInfoList) {
        List<PatrolTaskInfo> resList = new ArrayList<>();
        List<MaintainRegion> maintainAreaList = taskExecClient.selectMaintainRegionList(MaintainRegion.builder().enable("1").build());
        List<String> maintainIds = new ArrayList<>();
        for (MaintainRegion maintainArea : maintainAreaList) {
            if (maintainArea.getStartTime().getTime() < System.currentTimeMillis() && maintainArea.getEndTime().getTime() > System.currentTimeMillis()) {
                String[] pointIdsTemp = maintainArea.getPointList().split(StringUtils.COMMA);
                String[] deviceIdsTemp = maintainArea.getDeviceList().split(StringUtils.COMMA);
                maintainIds.addAll(Arrays.asList(pointIdsTemp));
                maintainIds.addAll(Arrays.asList(deviceIdsTemp));
            }
        }

        for (PatrolTaskInfo patrolTaskInfo : patrolTaskInfoList) {
            final String deviceId = patrolTaskInfo.getDeviceId();
            //log.info("deviceId: {}", deviceId);
            if (!maintainIds.contains(deviceId)) {
                resList.add(patrolTaskInfo);
            }
        }

        return resList;
    }

    public FtpResult getFileAndSave(String url, String fileName, String fileTypeDir) {
        try {
            log.info("saveFile STREAM url: {}", url);
            String[] split = url.split(StringUtils.QUESTION);
            final String streamUrl = split[0];
            final String streamParam = split[split.length - 1];
            log.info("STREAM URL: {}, PARAM: {}", streamUrl, streamParam);
            String streamParam2 = streamParam.replace(StringUtils.HASHTAG, ("%23"));
            URLConnection connection = HttpClientUtils.getConnection(streamUrl, streamParam2);
            boolean bOk = connection.getInputStream().available() > 0;
            //if (connection.getInputStream().available() > 0) {
            /**
             * 测试：将图片保存到本地
             */
//                try (InputStream in = connection.getInputStream()) {
//                    Files.copy(in, Paths.get("/test.jpg"));
//                }

            /**
             * 将图片上传到ftp
             */
            SftpUploadEntity sftpUploadEntity = new SftpUploadEntity(connection.getInputStream());
            sftpUploadEntity.setFilePath(fileTypeDir);
            sftpUploadEntity.setFileName(fileName.split(StringUtils.HASHTAG)[0]);
            final String filepath = sftpClient.upload(sftpUploadEntity);
            return FtpResult.builder().isOk(bOk).filepath(filepath).build();
            //}
//            else {
//                log.info("STREAM URL: {}, PARAM: {}, null return!!!!!!!!!!!!!!!!!!!!1", streamUrl, streamParam);
//                return StringUtils.EMPTY;
//            }
        } catch (Exception e) {
            log.error("error", e);
            return FtpResult.builder().isOk(false).filepath(StringUtils.EMPTY).build();
        }
    }

    private String decimalFormatNum(int current, int all) {
        if (all == 0) {
            return "0.0"; // 防止除以0
        }

        DecimalFormat nf = new DecimalFormat("0.0");
        nf.setMaximumIntegerDigits(3);
        float decimalFormatNum = (float) (current) / (float) all;
        if (decimalFormatNum > 1) {
            decimalFormatNum = 1;
        }
        return nf.format(decimalFormatNum * 100.0F);
    }

    private boolean isRunningState(final String patrolId) {
        PatrolTaskExecRecord record = taskExecClient.selectPatrolTaskExecRecordByTaskPatrolId(patrolId);
        return record != null && TaskStatus.RUNNING.getCode().equals(record.getTaskState());
    }

    @SuppressWarnings({"unused"})
    public void generalTask() {
        log.info("***************************** JobTaskTimer generalTask *************************************");

        if (shaoXinBigModel.isSwitchStatus()) {
            log.info("[GABRIEL][BIG-MODEL] gathering temporary handling, need to close nacos bigSwitch");
            redisService.redisTemplate.opsForValue().set(this.BIG_FILE_POINT, -1);
        }

        boolean isCycle = false;
        boolean isInterval = false;
        String[] cycleExecuteTime = new String[3];

        String currentWeek = getWeekOfDate(new Date());
        Calendar now = Calendar.getInstance();
        int day = DateUtils.getDayInt();
        int hour = DateUtils.getHourInt();
        int minute = DateUtils.getMinuteInt();
        Date d = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        try {
            List<PatrolTask> patrolTaskList = taskExecClient.selectTaskList(PatrolTask.builder().isEnable(TASK_ENABLE).build());
            log.info(Color.YELLOW + "[GABRIEL] {inspect-main} selectTaskList task count: {}" + Color.END, patrolTaskList.size());
            for (PatrolTask task : patrolTaskList) {
                if (TASK_TYPE_SILENCE.equals(task.getType()) || TASK_TYPE_LINKAGE.equals(task.getType())) {
                    continue;
                }

                List<PatrolTaskExecRecord> patrolTaskExecRecordList =
                        taskExecClient.selectCameraPatrolTaskExecRecordList(task.getDevNo(), Collections.singletonList(TaskStatus.RUNNING.getCode()));
                if (!patrolTaskExecRecordList.isEmpty()) {
                    String time = "";
                    String[] intervalExecuteTime;
                    if (isCycle) {
                        int i;
                        String[] cycleExecuteTimes;
                        String week;
                        if (StringUtils.isNotEmpty(task.getCycleMonth())) {
                            intervalExecuteTime = task.getCycleMonth().split(StringUtils.COMMA);
                            cycleExecuteTimes = task.getCycleMonth().split(StringUtils.COMMA);

                            for (i = 0; i < intervalExecuteTime.length; ++i) {
                                week = intervalExecuteTime[i];
                                time = cycleExecuteTimes[i].replaceAll(StringUtils.COLON, StringUtils.EMPTY);
                                if (day == Integer.parseInt(week) && task.getCycleExecuteTime() != null) {
                                    redisService.setCacheObject(RedisConst.TASK_PATROL_ID_EX + task.getDevNo() + "_" + task.getTaskCode() + "_" + sdf.format(d) + time, task);
                                }
                            }
                        } else if (StringUtils.isNotEmpty(task.getCycleWeek())) {
                            intervalExecuteTime = task.getCycleWeek().split(StringUtils.COMMA);
                            cycleExecuteTimes = task.getCycleExecuteTime().split(StringUtils.COMMA);
                            for (i = 0; i < intervalExecuteTime.length; ++i) {
                                week = intervalExecuteTime[i];
                                time = cycleExecuteTimes[i].replaceAll(StringUtils.COLON, StringUtils.EMPTY);
                                if (currentWeek.equals(week)) {
                                    redisService.setCacheObject(RedisConst.TASK_PATROL_ID_EX + task.getDevNo() + "_" + task.getTaskCode() + "_" + sdf.format(d) + time, task);
                                }
                            }
                        }
                    } else if (isInterval) {
                        intervalExecuteTime = task.getIntervalExecuteTime().split(StringUtils.COLON);
                        int j;
                        if ("1".equals(task.getIntervalType())) {
                            for (j = 0; j < task.getIntervalNumber(); ++j) {
                                if (j != 0) {
                                    intervalExecuteTime[0] = String.valueOf(Integer.parseInt(intervalExecuteTime[0]) + 1);
                                }

                                time = hour + minute + cycleExecuteTime[2];
                                redisService.setCacheObject(RedisConst.TASK_PATROL_ID_EX + task.getDevNo() + "_" + task.getTaskCode() + "_" + sdf.format(d) + time, task);
                            }
                        } else if ("2".equals(task.getIntervalType())) {
                            for (j = 0; j < task.getIntervalNumber(); ++j) {
                                if (j != 0) {
                                    intervalExecuteTime[0] = String.valueOf(Integer.parseInt(intervalExecuteTime[0]) + 1);
                                }

                                redisService.setCacheObject(RedisConst.TASK_PATROL_ID_EX + task.getDevNo() + "_" + task.getTaskCode() + "_" + sdf.format(d) + time, task);
                            }
                        }
                    }
                } else {
                    long current = 0L;
                    long fixedStart = 1L;
                    patrolTaskExecRecordList = taskExecClient.selectCameraPatrolTaskExecRecordList(task.getDevNo(), Arrays.asList(TaskStatus.DONE.getCode(), TaskStatus.PAUSED.getCode()));
                    if (!patrolTaskExecRecordList.isEmpty()) {
                        PatrolTaskExecRecord patrolTaskExecRecord = patrolTaskExecRecordList.get(patrolTaskExecRecordList.size() - 1);
                        current = TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis());
                        fixedStart = TimeUnit.MILLISECONDS.toMinutes(patrolTaskExecRecord.getStartTime().getTime());
                    }

                    if (current != fixedStart) {
                        PatrolPresetPos patrolPresetPos = new PatrolPresetPos();
                        PatrolPresetAction patrolPresetAction = new PatrolPresetAction();
                        PatrolTaskInfo patrolTaskInfo = new PatrolTaskInfo();
                        patrolTaskInfo.setTaskMajorId(String.valueOf(task.getTaskId()));
                        patrolTaskInfo.setDevNo(task.getDevNo());
                        if (task.getFixedStartTime() != null
                                && ExecStatus.REGULAR.getCode().equals(task.getExecutionStatus())) {
                            isCycle = false;
                            isInterval = false;
                            long currentMinutes = TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis());
                            long fixedStartMinutes = TimeUnit.MILLISECONDS.toMinutes(task.getFixedStartTime().getTime());
                            if (currentMinutes == fixedStartMinutes) {
                                try {
                                    Thread.sleep(200L);
                                } catch (InterruptedException interruptedException) {
                                    interruptedException.printStackTrace();
                                }

                                startThread(patrolTaskInfo, patrolPresetPos, patrolPresetAction, task);
                            }
                        } else {
                            String[] intervalExecuteTime;
                            if (StringUtils.isNotEmpty(task.getCycleExecuteTime())
                                    && task.getCycleStartTime() != null
                                    && Integer.parseInt(sdf.format(d)) >= Integer.parseInt(sdf.format(task.getCycleStartTime()))
                                    && Integer.parseInt(sdf.format(d)) <= Integer.parseInt(sdf.format(task.getCycleEndTime()))
                                    && ExecStatus.PERIODIC.getCode().equals(task.getExecutionStatus())) {
                                isCycle = true;
                                isInterval = false;
                                int i;
                                String month;
                                String[] cycleExecuteTimes;
                                if (StringUtils.isNotEmpty(task.getCycleWeek())) {
                                    intervalExecuteTime = task.getCycleWeek().split(StringUtils.COMMA);
                                    cycleExecuteTimes = task.getCycleExecuteTime().split(StringUtils.COMMA);
                                    for (i = 0; i < intervalExecuteTime.length; ++i) {
                                        month = intervalExecuteTime[i];
                                        if (currentWeek.equals(month)) {
                                            cycleExecuteTime = cycleExecuteTimes[i].split(StringUtils.COLON);
                                            startThread(patrolTaskInfo,
                                                    patrolPresetPos,
                                                    patrolPresetAction,
                                                    hour,
                                                    minute,
                                                    task,
                                                    cycleExecuteTime,
                                                    true);
                                        }
                                    }
                                } else if (StringUtils.isNotEmpty(task.getCycleMonth())) {
                                    intervalExecuteTime = task.getCycleMonth().split(StringUtils.COMMA);
                                    cycleExecuteTimes = task.getCycleExecuteTime().split(StringUtils.COMMA);
                                    for (i = 0; i < intervalExecuteTime.length; ++i) {
                                        month = intervalExecuteTime[i];
                                        if (day == Integer.parseInt(month) && task.getCycleExecuteTime() != null) {
                                            cycleExecuteTime = cycleExecuteTimes[i].split(StringUtils.COLON);
                                            startThread(patrolTaskInfo,
                                                    patrolPresetPos,
                                                    patrolPresetAction,
                                                    hour,
                                                    minute,
                                                    task,
                                                    cycleExecuteTime,
                                                    true);
                                        }
                                    }
                                }
                            } else if (StringUtils.isNotEmpty(task.getIntervalExecuteTime())
                                    && task.getIntervalStartTime() != null
                                    && Integer.parseInt(sdf.format(d)) >= Integer.parseInt(sdf.format(task.getIntervalStartTime()))
                                    && Integer.parseInt(sdf.format(d)) <= Integer.parseInt(sdf.format(task.getIntervalEndTime()))
                                    && ExecStatus.INTERVAL.getCode().equals(task.getExecutionStatus())) {
                                isCycle = false;
                                isInterval = true;
                                intervalExecuteTime = task.getIntervalExecuteTime().split(StringUtils.COLON);
                                if ("1".equals(task.getIntervalType())) {
                                    for (int j = 0; j < task.getIntervalNumber(); ++j) {
                                        if (j != 0) {
                                            intervalExecuteTime[0] = String.valueOf(Integer.parseInt(intervalExecuteTime[0]) + 1);
                                        }
                                        startThread(patrolTaskInfo, patrolPresetPos, patrolPresetAction, hour, minute, task, intervalExecuteTime, false);
                                    }
                                } else if ("2".equals(task.getIntervalType())) {
                                    for (int j = 0; j < task.getIntervalNumber(); ++j) {
                                        if (j != 0) {
                                            intervalExecuteTime[0] = String.valueOf(Integer.parseInt(intervalExecuteTime[0]) + 1);
                                        }
                                        startThread(patrolTaskInfo, patrolPresetPos, patrolPresetAction, hour, minute, task, intervalExecuteTime, false);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            if (e instanceof RetryableException)
                log.warn(Color.YELLOW + "[GABRIEL] {inspect-main} selectTaskList is not online!" + Color.END);
            else
                e.printStackTrace();
        }
    }

    @SuppressWarnings({"unused"})
    public void continueTask() {
        log.info("***************************** JobTaskTimer continueTask *************************************");

        try {
            /**
             * select from patrol_task_exec_record where task_state=2(running)
             */
            List<PatrolTaskExecRecord> execRecords
                    = taskExecClient.selectAbeyancePatrolTaskExecRecordList(PatrolTaskExecRecord.builder().build());
            for (PatrolTaskExecRecord execRecord : execRecords) {
                threadPool.submit(() -> {
                    final String deviceId = execRecord.getDeviceId();
                    if (!StringUtils.isEmpty(deviceId)) {
                        log.info(Color.YELLOW + "[JOB] RESUME devNo: {}, taskId: {}, deviceId: {}" + Color.END, execRecord.getDevNo(), execRecord.getTaskId(), deviceId);
                        prePointContinueExec(execRecord);
                    }
                });
            }
        } catch (Exception e) {
            if (e instanceof RetryableException) {
                log.warn(Color.YELLOW + "[GABRIEL] inspect-main is not online!" + Color.END);
            } else {
                log.warn(Color.YELLOW + "[GABRIEL] other exception!" + Color.END);
            }
        }
    }

    private void prePointContinueExec(PatrolTaskExecRecord patrolTaskExecRecord) {
        PatrolTaskInfo patrolTaskInfo = PatrolTaskInfo.builder()
                .devNo(patrolTaskExecRecord.getDevNo())
                .taskMajorId(patrolTaskExecRecord.getTaskCode())
                .build();
        List<PatrolTaskInfo> patrolTaskInfoList = taskExecClient.selectTaskInfoList(patrolTaskInfo);
        /**
         * 从当前正在维修表maintain_region表中查询当前正在维修的点位，
         * 并把这些点位排除出巡检任务
         */
        List<PatrolTaskInfo> patrolTaskInfoListTemp = patrolTaskInfoMaintainAreaRemove(patrolTaskInfoList);
        boolean bContinue = false;
        int size = patrolTaskInfoListTemp.size();
        /**
         * 该任务下面的patrol_task_info中的设备点一个都没有执行(patrol_task_point_exec_record查不到任何该任务点的执行记录)
         * 这种情况下当前点一定要执行prePointExec，否则就会遗漏该点
         */
        for (PatrolTaskInfo taskInfo : patrolTaskInfoListTemp) {
            final String deviceIdOfInfo = taskInfo.getDeviceId();
            if (!bContinue) {
                final String deviceIdOfRecord = patrolTaskExecRecord.getDeviceId();
                //log.info("[JOB] devId: {}, devId2: {}", deviceIdOfRecord, deviceIdOfInfo);
                if (deviceIdOfInfo.equals(deviceIdOfRecord)) {
                    Long execPointId = patrolTaskExecRecord.getExecPointId();
                    log.info("[JOB] TASK RESUMED, LOCK IT! {}", "POINT=" + execPointId + ", NAME=" + patrolTaskExecRecord.getTaskName() + ", ID=" + patrolTaskExecRecord.getTaskPatrolId());
                    patrolTaskExecRecord.setDeviceId("-1");
                    patrolTaskExecRecord.setExecPointId(-1L);
                    taskExecClient.updatePatrolTaskExecRecord(patrolTaskExecRecord);
                    bContinue = true;
                    /**
                     * 该任务下面的patrol_task_info中的设备点一个都没有执行(patrol_task_point_exec_record查不到任何该任务点的执行记录)
                     * 这种情况下当前点一定要执行prePointExec，否则就会遗漏该点
                     */
                    if (execPointId == 0L) {
                        prePointExec(patrolTaskExecRecord, taskInfo, size);
                    }
                }
            } else {
                prePointExec(patrolTaskExecRecord, taskInfo, size);
            }
        }
    }

    @SuppressWarnings({"unused"})
    public void silenceTask() {
        log.info("***************************** JobTaskTimer silenceTask *************************************");

        PatrolTask patrolTask = new PatrolTask();
        patrolTask.setType(TASK_TYPE_SILENCE);
        patrolTask.setIsEnable(TASK_ENABLE);
        Set<String> noRunDevices = new HashSet();
        List<PatrolTask> patrolTaskList = taskExecClient.selectTaskList(patrolTask);
        List<String> taskStates = new ArrayList<>();
        taskStates.add(TASK_STATUS_RUN);
        taskStates.add(TASK_STATUS_STOP);
        Iterator<PatrolTask> iterator = patrolTaskList.iterator();
        while (iterator.hasNext()) {
            PatrolTask task = iterator.next();
            List<PatrolTaskExecRecord> patrolTaskExecRecordList = taskExecClient.selectCameraPatrolTaskExecRecordList(task.getDevNo(), taskStates);
            String key = REDIS_TASK_SILENCE_DEVICE + task.getDevNo();
            if (patrolTaskExecRecordList.isEmpty()) {
                if (!redisService.hasKey(key)) {
                    log.info("[GABRIEL] key1: {}", key);
                    redisService.setCacheObject(key, new Date());
                } else {
                    Date deviceStopTime = this.redisService.getCacheObject(key);
                    if (CalendarUtil.expire(deviceStopTime, task.getIntervalNumber())) {
                        redisService.deleteObject(key);
                        this.log.info("silenceTask 静默任务开始执行 任务编号 ：{}", task.getTaskCode());
                        PatrolTaskInfo patrolTaskInfo = new PatrolTaskInfo();
                        patrolTaskInfo.setTaskMajorId(task.getTaskId().toString());
                        patrolTaskInfo.setDevNo(task.getDevNo());
                        this.startThread(patrolTaskInfo, new PatrolPresetPos(), new PatrolPresetAction(), task);
                    }
                }
            } else if (!TASK_TYPE_SILENCE.equals((patrolTaskExecRecordList.get(0)).getTaskType())) {
                noRunDevices.add(task.getDevNo());
            }
        }

        if (!noRunDevices.isEmpty()) {
            noRunDevices.forEach((devNo) -> {
                redisService.deleteObject(REDIS_TASK_SILENCE_DEVICE + devNo);
            });
        }
    }

    @SuppressWarnings({"unused"})
    public void linkageTask() {
        log.info("***************************** JobTaskTimer linkageTask *************************************");

        PatrolTask patrolTask = new PatrolTask();
        patrolTask.setType(TASK_TYPE_LINKAGE);
        patrolTask.setLinkageState("1");
        PatrolTaskInfo patrolTaskInfo = new PatrolTaskInfo();
        PatrolPresetPos patrolPresetPos = new PatrolPresetPos();
        PatrolPresetAction patrolPresetAction = new PatrolPresetAction();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        List<PatrolTask> patrolTaskList = taskExecClient.selectTaskList(patrolTask);
        for (PatrolTask task : patrolTaskList) {
            Date d = new Date();
            PatrolTaskExecRecord patrolTaskExecRecord = new PatrolTaskExecRecord();
            patrolTaskExecRecord.setTaskPatrolId(task.getDevNo() + "_" + task.getTaskCode() + "_" + sdf.format(d));
            patrolTaskExecRecord.setOldTaskPatrolId(task.getTaskCode() + "_" + sdf.format(d));
            patrolTaskExecRecord.setTaskName(task.getTaskName());
            patrolTaskExecRecord.setTaskCode(task.getTaskCode());
            patrolTaskExecRecord.setTaskState(TASK_STATUS_RUN);
            patrolTaskExecRecord.setPlanStartTime(task.getCycleStartTime());
            patrolTaskExecRecord.setStartTime(new Date());
            patrolTaskExecRecord.setTaskProgress("");
            patrolTaskExecRecord.setEndTime(task.getCycleEndTime());
            patrolTaskExecRecord.setDescription("");
            patrolTaskExecRecord.setDevNo(task.getDevNo());
            patrolTaskExecRecord.setTaskType(task.getType());
            patrolTaskExecRecord.setTaskPriority(task.getPriority());
            patrolTaskExecRecord.setTaskId(task.getTaskId());
            patrolTaskExecRecord.setStationName(task.getStationName());
            patrolTaskExecRecord.setStationCode(task.getStationCode());
            log.info("linkageTask 联动任务执行 任务编号 ：{}", task.getTaskCode());
            taskExecClient.addPatrolTaskExecRecord(patrolTaskExecRecord);
            log.info("添加 联动任务执行 主表信息 完成 ：{}", "任务执行id TaskPatrolledId ：" + patrolTaskExecRecord.getTaskPatrolId());
            patrolTaskInfo.setTaskMajorId(task.getTaskId().toString());
            patrolTaskInfo.setDevNo(task.getDevNo());
            List<PatrolTaskInfo> patrolTaskInfoList = taskExecClient.selectTaskInfoList(patrolTaskInfo);
            linkagePrePointExceControl(patrolTaskExecRecord, patrolPresetPos, patrolPresetAction, patrolTaskInfoList);
            task.setLinkageState("2");
            taskExecClient.updatePatrolTask(task);
        }
    }

    private void linkagePrePointExceControl(PatrolTaskExecRecord patrolTaskExecRecord, PatrolPresetPos patrolPresetPos, PatrolPresetAction patrolPresetAction, List<PatrolTaskInfo> patrolTaskInfoList) {
        PatrolTaskExecRecord taskExecRecord = this.taskExecClient.selectPatrolTaskExecRecordByTaskPatrolId(patrolTaskExecRecord.getTaskPatrolId());
        if (taskExecRecord != null && TASK_STATUS_RUN.equals(taskExecRecord.getTaskState())) {
            boolean continueExce = true;
            int patrolTaskInfoListSize = patrolTaskInfoList.size();

            for (int ii = 0; ii < patrolTaskInfoListSize; ++ii) {
                PatrolTaskInfo taskInfo = patrolTaskInfoList.get(ii);
                taskExecRecord = this.taskExecClient.selectPatrolTaskExecRecordByTaskPatrolId(patrolTaskExecRecord.getTaskPatrolId());
                if (taskExecRecord != null && (TASK_STATUS_STOP.equals(taskExecRecord.getTaskState()) || "4".equals(taskExecRecord.getTaskState()))) {
                    continueExce = false;
                }

                log.info("联动任务执行预置位 信息 ：{}", "DeviceId ：" + taskInfo.getDeviceId() + ",DevNo:" + taskInfo.getDevNo());
                prePointExec(taskExecRecord, taskInfo, patrolTaskInfoListSize);
            }
        }
    }

    private String getTaskPatrolId(final PatrolTask patrolTask) {
        String time = DateUtils.format(DateUtils.yyyyMMddHHmmss, patrolTask.getFixedStartTime());
        String[] devNos = patrolTask.getDevNo().split(StringUtils.COMMA);
        return devNos[0] + "_" + patrolTask.getTaskCode() + "_" + time;
    }

    private String getOldTaskPatrolId(final PatrolTask patrolTask) {
        String time = DateUtils.format(DateUtils.yyyyMMddHHmmss, patrolTask.getFixedStartTime());
        //return patrolTask.getTaskCode() + "_" + time + "_" + time;
        return patrolTask.getTaskCode() + "_" + time;
    }

    @SuppressWarnings({"unused"})
    public void execEveryDayTask(List<PatrolTask> patrolTaskList) {
        log.debug("***************************** JobTaskTimer execEveryDayTask with parameters *************************************");
        for (PatrolTask patrolTask : patrolTaskList) {
            List<PatrolTaskInfo> taskInfos = new ArrayList<>();
            String[] devTypes = patrolTask.getDevType().split(StringUtils.COMMA);
            String[] devNos = patrolTask.getDevNo().split(StringUtils.COMMA);
            for (int i = 0; i < devNos.length; ++i) {
                List<PatrolTaskInfo> infosByRecord = getInfosByRecord(PatrolTaskExecRecord.builder().devNo(devNos[i]).taskId(patrolTask.getTaskId()).build());
                taskInfos.addAll(infosByRecord);
            }
            log.info("execEveryDayTask taskInfos size: {}", taskInfos.size());
            log.debug("execEveryDayTask patrolTask: {}", patrolTask);
            final String taskPatrolId = getTaskPatrolId(patrolTask);
            final String oldTaskPatrolId = getOldTaskPatrolId(patrolTask);
            log.info(Color.YELLOW + "[JOB] NEW EXEC RECORD: {}, OLD: {}" + Color.END, taskPatrolId, oldTaskPatrolId);
            setRedisRecordCount(patrolTaskList.get(0).getTaskCode(), patrolTaskList.size());
            PatrolTaskExecRecord patrolTaskExecRecord = PatrolTaskExecRecord.builder()
                    .taskPatrolId(taskPatrolId)
                    .oldTaskPatrolId(oldTaskPatrolId)
                    .taskName(patrolTask.getTaskName())
                    .taskCode(patrolTask.getTaskCode())
                    .taskState(TaskStatus.RUNNING.getCode())
                    .planStartTime(patrolTask.getCycleStartTime())
                    .startTime(new Date())
                    .taskProgress("0")
                    .endTime(patrolTask.getCycleEndTime())
                    .description("")
                    .devNo(patrolTask.getDevNo().split(StringUtils.COMMA)[0])
                    .taskType(patrolTask.getType())
                    .taskPriority(patrolTask.getPriority())
                    .taskId(patrolTask.getTaskId())
                    .stationName(patrolTask.getStationName())
                    .stationCode(patrolTask.getStationCode())
                    .deviceId("0")
                    .execPointId(0L)
                    .totalNumber(taskInfos.isEmpty() ? 1 : taskInfos.size())
                    .specifiedAlg(patrolTask.getSpecifiedAlg())
                    .build();
            try {
                taskExecClient.addPatrolTaskExecRecord(patrolTaskExecRecord);
                taskExecClient.updatePatrolTaskStatus(patrolTask);
            } catch (Exception e) {
                log.error("error", e);
            }
            threadPool.submit(() -> {
                log.info(Color.YELLOW + "[JOB] THREAD START devNo: {}, taskId: {}" + Color.END, patrolTaskExecRecord.getDevNo(), patrolTaskExecRecord.getTaskId());
                prePointExecImmediate(patrolTaskExecRecord, taskInfos);
                log.info(Color.YELLOW + "[JOB] THREAD END devNo: {}, taskId: {}" + Color.END, patrolTaskExecRecord.getDevNo(), patrolTaskExecRecord.getTaskId());
            });
        }
    }

    @SuppressWarnings({"unused"})
    public void execRepeatDayTask(List<PatrolTask> patrolTaskList) {
        log.info("***************************** JobTaskTimer execRepeatDayTask with parameters *************************************");

//        for (PatrolTask patrolTask : patrolTaskList) {
//            final String taskPatrolId = getTaskPatrolId(patrolTask);
//            final String oldTaskPatrolId = getOldTaskPatrolId(patrolTask);
//            log.info(Color.YELLOW + "[JOB] REPEAT EXEC RECORD: {}, OLD: {}" + Color.END, taskPatrolId, oldTaskPatrolId);
//            List<PatrolTaskExecRecord> taskExecRecords =
//                    taskExecClient.selectPatrolTaskExecRecordList(PatrolTaskExecRecord.builder()
//                            .taskPatrolId(taskPatrolId)
//                            .oldTaskPatrolId(oldTaskPatrolId)
//                            .build());
//            if (taskExecRecords.isEmpty()) continue;
//
//            PatrolTaskExecRecord execRecord = taskExecRecords.get(0);//理论上来说只能有一条
//            int finish = execRecord.getFinishNumber();
//            int total = execRecord.getTotalNumber();
//            int times = execRecord.getRebootTimes();
//            if (finish < total) {
//                log.info(Color.YELLOW + "[JOB] REPEAT EXEC RECORD: id: {}, finish: {}, total: {}, times: {}, progress: {}" + Color.END, execRecord.getTaskPatrolId(), finish, total, times, execRecord.getTaskProgress());
//                incrementRedisRecordCounter();
//                threadPool.submit(() -> {
//                    log.info(Color.YELLOW + "[JOB] REPEAT START devNo: {}, taskId: {}" + Color.END, execRecord.getDevNo(), execRecord.getTaskId());
//                    prePointExecImmediate(execRecord);
//                    log.info(Color.YELLOW + "[JOB] REPEAT END devNo: {}, taskId: {}" + Color.END, execRecord.getDevNo(), execRecord.getTaskId());
//                });
//            }
//        }
    }

    private boolean isTaskSlotEmpty(String taskCode) {
        String taskCurrentCode = redisService.getCacheObjectOfTask(RedisConst.TASK_CURRENT_CODE, taskCode);
        Integer redisRecCnt = redisService.getCacheObjectOfTask(RedisConst.TASK_RECORD_CNT, taskCode);
        log.debug(Color.YELLOW + "[JOB] TASK_CURRENT_CODE: {}, TASK_RECORD_CNT: {}" + Color.END, taskCurrentCode, redisRecCnt);
        return StringUtils.isEmpty(taskCurrentCode) ||
                !StringUtils.isEmpty(taskCurrentCode) && (redisRecCnt == null || redisRecCnt == 0);
    }

    private synchronized void extractRedisTask(String redisKey, String taskCode) {
        List<PatrolTask> patrolTaskList = JSON.parseArray(redisService.getCacheObject(redisKey), PatrolTask.class);
        // 筛选出可见光任务
        List<PatrolTask> patrolTaskListEx = new ArrayList<>();
        List<PatrolTask> patrolTaskControlList = new ArrayList<>();
        for (PatrolTask patrolTask : patrolTaskList) {
            log.debug("EXTRACT_REDIS_TASK devType: {}", patrolTask.getDevType());
            if(StringUtils.isNotEmpty(patrolTask.getDevType())
                    && patrolTask.getDevType().contains("2")) {
                patrolTaskListEx.add(patrolTask);
            }

            if(StringUtils.isNotEmpty(patrolTask.getDevType())
                    && (patrolTask.getDevType().contains("0") || patrolTask.getDevType().contains("1"))) {
                patrolTaskControlList.add(patrolTask);
            }
        }

        if (!patrolTaskControlList.isEmpty()) {
            log.info(Color.YELLOW + "PATROL_TASK_LIST_DEVICE: {}, task list size: {}" + Color.END, redisKey, patrolTaskControlList.size());
            redisService.setCacheObjectOfTask(RedisConst.TASK_CURRENT_CODE, taskCode, redisKey, 1L, TimeUnit.HOURS);
            // 执行机器狗，无人机设备任务
            taskExecClient.execDeviceTask(patrolTaskControlList);
        }

        if (!patrolTaskListEx.isEmpty()) {
            log.info(Color.YELLOW + "PATROL_TASK_LIST_VL: {}, task list size: {}" + Color.END, redisKey, patrolTaskListEx.size());
            redisService.setCacheObjectOfTask(RedisConst.TASK_CURRENT_CODE, taskCode, redisKey, 1L, TimeUnit.HOURS);
            execEveryDayTask(patrolTaskListEx);
        }
//        if (patrolTaskList != null && !patrolTaskList.isEmpty()) {
//            log.info(Color.YELLOW + "[JOB] ADD NEW TASK: {}, TASK LIST SIZE: {}" + Color.END, redisKey, patrolTaskList.size());
//            redisService.setCacheObjectOfTask(RedisConst.TASK_CURRENT_CODE, taskCode, redisKey, 5L, TimeUnit.MINUTES);
//            execEveryDayTask(patrolTaskList);
//        }
    }

    Map<String, String> taskInfos = new HashMap<>();

    @Scheduled(cron = "0 10 0 * * ?")
    public void fixedTimer() {
        Date date = new Date();
        log.info("JobMainTask fixedTimer clear taskInfos: {}", taskInfos.size());
        taskInfos.clear();
        Collection<String> redisKeys = redisService.keys(RedisConst.TASK_CODE_EX);
        for (String redisKey : redisKeys) {
            String[] keywords = StringUtils.split(redisKey, StringUtils.AT);
            if (keywords.length == 3) {
                String fixedStartTime = keywords[2];
                if (DateUtils.parse(DateUtils.yyyyMMddHHmmss2, fixedStartTime).before(date)) {
                    log.info("{} JobMainTask fixedTimer clear redisKey: {}", DateUtils.format(DateUtils.yyyyMMddHHmmss2, date), redisKey);
                    redisService.deleteObject(redisKey);
                }

            }
        }
    }

    @SuppressWarnings({"unused"})
    public void execEveryDayTask() {
        log.debug("***************************** JobTaskTimer execEveryDayTask *************************************");

        Collection<String> redisKeys = redisService.keys(RedisConst.TASK_CODE_EX);
        for (String redisKey : redisKeys) {
            try {
                //redisKey = RedisConst.TASK_CODE + task.getTaskCode() + StringUtils.AT + DateUtils.format(DateUtils.yyyyMMddHHmmss2, task.getFixedStartTime())
                String[] keywords = StringUtils.split(redisKey, StringUtils.AT);
                if (keywords.length == 3) {
                    String taskCode = keywords[1];
                    List<PatrolTask> patrolTasks = taskExecClient.selectPatrolTaskList(PatrolTask.builder().taskCode(taskCode).build());
                    if (patrolTasks.isEmpty()) {
                        continue;
                    }
                    String isEnable = patrolTasks.get(0).getIsEnable();
                        log.debug("---execEveryDayTask isEnable: {}, taskCode: {}", isEnable, taskCode);
                    if (isTaskSlotEmpty(taskCode) && "0".equals(isEnable)) {
                        log.debug(Color.YELLOW + "[JOB] TASK_CODE: {}" + Color.END, taskCode);
//                        long currentTime = System.currentTimeMillis();
//                        long currentDays = TimeUnit.MILLISECONDS.toDays(currentTime);
//                        long currentMinutes = TimeUnit.MILLISECONDS.toMinutes(currentTime);
//
//                        String fixedStartTime = keywords[2];
//                        long planTime = DateUtils.parse(DateUtils.yyyyMMddHHmmss2, fixedStartTime).getTime();
//                        long fixedDays = TimeUnit.MILLISECONDS.toDays(planTime);
//                        long fixedStartMinutes = TimeUnit.MILLISECONDS.toMinutes(planTime);

                        String fixedStartTime = keywords[2];
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                        // 解析字符串为 Date
                        Date fixedDate = sdf.parse(fixedStartTime);
                        Date currentDate = new Date(); // 当前时间

                        // 比较时间（精确到分钟）
                        SimpleDateFormat minuteFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
                        String fixedMinute = minuteFormat.format(fixedDate);
                        String currentMinute = minuteFormat.format(currentDate);

                        // 判断是否同一天
                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                        String fixedDay = dateFormat.format(fixedDate);
                        String currentDay = dateFormat.format(currentDate);

                        long diffMinutes = currentDate.getTime() - fixedDate.getTime();
                        if (taskInfos.get(redisKey) == null && fixedDay.equals(currentDay) && (diffMinutes >= 0 && diffMinutes <= 60000 && fixedDate.before(currentDate) || fixedDate.getTime() == currentDate.getTime())) {
                            log.debug(Color.YELLOW + "[JOB] TASK_CODE: {}, FIXED START TIME: {}" + Color.END, taskCode, fixedStartTime);
                            taskInfos.put(redisKey, "1");
                            extractRedisTask(redisKey, taskCode);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("error", e);
            }
        }
    }

    @SuppressWarnings({"unused"})
    public void makeEveryDayTask() {
        log.debug("***************************** JobTaskTimer makeEveryDayTask *************************************");

        try {
            taskExecClient.makeCurrentDayTask();
        } catch (Exception e) {
            log.error("error", e);
        }
    }
}
