package cn.iocoder.yudao.module.system.service.robot;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.module.mqtt.api.common.CommonApi;
import cn.iocoder.yudao.module.mqtt.api.task.dto.RobotAcceptTaskDTO;
import cn.iocoder.yudao.module.mqtt.enums.path.TaskTypeEnum;
import cn.iocoder.yudao.module.mqtt.enums.task.ExecutionTypeEnum;
import cn.iocoder.yudao.module.system.api.path.vo.RobotClosePathPlantingDTO;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotStatusDataPoseDTO;
import cn.iocoder.yudao.module.system.constant.path.PathPlanningTopicConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotStatusCodeConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotTaskChcheConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotTopicConstant;
import cn.iocoder.yudao.module.system.controller.admin.robot.detail.RobotTaskDetailLogResoVO;
import cn.iocoder.yudao.module.system.controller.admin.robot.detail.RobotTaskDetailLogVO;
import cn.iocoder.yudao.module.system.controller.admin.robot.task.TaskPPDistribution;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.cycle.TaskCycleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.housearea.HouseAreaDO;
import cn.iocoder.yudao.module.system.dal.dataobject.houselane.WareHouseLaneDO;
import cn.iocoder.yudao.module.system.dal.dataobject.houselocation.WareHouseLocationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.information.DeviceInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.positionmap.PositionMapDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotChargeLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotTaskDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotTaskDetailDO;
import cn.iocoder.yudao.module.system.dal.mysql.cycle.TaskCycleMapper;
import cn.iocoder.yudao.module.system.dal.mysql.housearea.HouseAreaMapper;
import cn.iocoder.yudao.module.system.dal.mysql.houselane.WareHouseLaneMapper;
import cn.iocoder.yudao.module.system.dal.mysql.houselocation.WareHouseLocationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.information.DeviceInformationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotChargeLogMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotInformationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotTaskDetailMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotTaskMapper;
import cn.iocoder.yudao.module.system.enums.common.ZeroOneEnum;
import cn.iocoder.yudao.module.system.enums.device.DeviceUseStatusEnum;
import cn.iocoder.yudao.module.system.enums.redis.RobotCacheLockEnum;
import cn.iocoder.yudao.module.system.enums.robot.*;
import cn.iocoder.yudao.module.system.enums.robot.charge.ChargeTaskStatusEnum;
import cn.iocoder.yudao.module.system.enums.robot.task.RobotTaksOrderTypeEnum;
import cn.iocoder.yudao.module.system.service.robot.job.RobotCommonTaskService;
import cn.iocoder.yudao.module.system.util.redis.RedisUtil;
import cn.iocoder.yudao.module.system.util.redis.RedissonUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;

/**
 * 机器人任务主表 Service 实现类
 *
 * @author 陈宾顺
 */
@Slf4j
@Service
@Validated
public class RobotTaskServiceImpl extends ServiceImpl<RobotTaskMapper, RobotTaskDO> implements RobotTaskService {

    private final String RESULT = "SUCCESS";

    @Resource
    private RobotTaskMapper taskMapper;

    @Resource
    private WareHouseLocationMapper locationMapper;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private RedissonUtils redissonUtils;

    @Resource
    private RobotTaskDetailMapper taskDetailMapper;

    @Resource
    private RobotTaskDetailService robotTaskDetailService;

    @Resource
    private RobotInformationMapper informationMapper;

    @Resource
    private RobotChargeLogMapper chargeLogMapper;

    @Resource
    private RobotInformationMapper robotInformationMapper;

    @Resource
    private RobotCommonTaskService robotCommonTaskService;

    @Autowired
    private RobotInformationService robotInformationService;

    @Value("${zn.task-no:ZN}")
    private String taskNo;

    @Value("${zn.do_cycle:true}")
    private Boolean doCycle;

    @Value("${zn.task.check_sku_info:true}")
    private Boolean checkSkuInfo;

    @Resource
    private WareHouseLaneMapper houseLaneMapper;

    @Resource
    private HouseAreaMapper houseAreaMapper;

    @Resource
    private TaskCycleMapper taskCycleMapper;

    @Resource
    private DeviceInformationMapper deviceInformationMapper;

    @Resource
    private CommonApi commonApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createTask(RobotTaskSaveReqVO createReqVO) throws InterruptedException {
        //校验
        doTaskCheck(createReqVO);

        //设置任务号
        if (ObjectUtil.isEmpty(createReqVO.getTaskNo())) {
            String incrementByKey = redisUtil.getIncrementByKey(RobotCacheLockEnum.TASK_NO.getKey());
            createReqVO.setTaskNo(taskNo + DateUtils.getYearMonthDay() + incrementByKey);
        }
        //获取库位id
        RLock lock = redissonUtils.getLock(RobotCacheLockEnum.ROBOT_TASK_ADD_LOCK.getKey());

        String addResult = "";
        if (lock.tryLock(60l, TimeUnit.MINUTES)) {
            try {
                addResult = addTask(createReqVO);
            } catch (Exception e) {
                log.error("下发任务出现异常 :{}", e);
                addResult = e.getMessage();
            } finally {
                lock.unlock();
            }
        } else {
            throw exception(REDISSON_NOT_OBTAIN_LOCK);
        }

        if (!RESULT.equals(addResult)) {
            throw exception0(TASK_CHECK_EXCEPTION.getCode(), addResult);
        }

        return 1l;
    }

    /**
     * 校验
     *
     * @param createReqVO
     */
    private void doTaskCheck(RobotTaskSaveReqVO createReqVO) {
        //校验明细
        if (ObjectUtil.isEmpty(createReqVO.getTaskDetailList())) {
            throw exception(TASK_DATA_NOT_FULL);
        }
        //校验循环任务的配置未开启
        if (DoCycleEnum.YES.getType().equals(createReqVO.getDoCycle()) && !doCycle) {
            throw exception(TASK_CYCLE_NOT_OPEN);
        }
        //搬空任务只能选择线库或者区域
        if (MoveAllEnum.YES.getType().equals(createReqVO.getDoMoveAll())) {
            for (RobotTaskDetailAddVO robotTaskDetailAddVO : createReqVO.getTaskDetailList()) {
                if (ReleaseTakeEnum.TO_LOCATION.getType().equals(robotTaskDetailAddVO.getTakeType())
                        || ReleaseTakeEnum.TO_LOCATION.getType().equals(robotTaskDetailAddVO.getReleaseType())) {
                    throw exception(TASK_ONLY_CHOOSE_LOCATION);
                }
            }
        }
        //校验机器人是否禁用
        Set<String> robotNos = createReqVO.getTaskDetailList()
                .stream()
                .map(RobotTaskDetailAddVO::getRobotNo)
                .collect(Collectors.toSet());
        if (ObjectUtil.isNotEmpty(robotNos)) {
            List<RobotInformationDO> robots = informationMapper.selectByRobotNos(robotNos);
            String robotNo = robots.stream()
                    .filter(v -> RobotTaskModelEnum.REJECTION.getType().equals(v.getRobotTaskModel()))
                    .map(RobotInformationDO::getRobotNo)
                    .collect(Collectors.joining(" "));

            if (ObjectUtil.isNotEmpty(robotNo)) {
                throw exception0(TASK_CHECK_EXCEPTION.getCode(), "以下机器人已被禁用 ", robotNo);
            }
        }
        //校验任务号是否重复
        if (ObjectUtil.isNotEmpty(createReqVO.getTaskNo())) {
            List<RobotTaskDO> taskDOS = taskMapper.selectList(new LambdaQueryWrapperX<RobotTaskDO>()
                    .eq(RobotTaskDO::getTaskNo, createReqVO.getTaskNo()));
            if (ObjectUtil.isNotEmpty(taskDOS)) {
                throw exception(TASK_CHECK_EXIST_NO);
            }
        }

    }

    /**
     * 添加任务
     *
     * @param createReqVO
     */
    @Transactional(rollbackFor = Exception.class)
    public String addTask(RobotTaskSaveReqVO createReqVO) {
        RobotTaskDO task = BeanUtils.toBean(createReqVO, RobotTaskDO.class);
        taskMapper.insert(task);

        List<Long> laneIds = new ArrayList<>();
        List<Long> areaIds = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(task.getSkuInfo()) && checkSkuInfo) {
            List<WareHouseLaneDO> wareHouseLaneDOS = houseLaneMapper.selectList(new LambdaQueryWrapper<WareHouseLaneDO>()
                    .eq(WareHouseLaneDO::getLaneMsg, task.getSkuInfo()));
            if (ObjectUtil.isNotEmpty(wareHouseLaneDOS)) {
                laneIds = wareHouseLaneDOS.stream().map(WareHouseLaneDO::getId).collect(Collectors.toList());
            }
            List<HouseAreaDO> houseAreaDOS = houseAreaMapper.selectList(new LambdaQueryWrapper<HouseAreaDO>()
                    .eq(HouseAreaDO::getSkuInfo, task.getSkuInfo()));
            if (ObjectUtil.isNotEmpty(houseAreaDOS)) {
                areaIds = houseAreaDOS.stream().map(HouseAreaDO::getId).collect(Collectors.toList());
            }
        }

        //查找库位
        List<Long> locationIds = new ArrayList<>();
        if (MoveAllEnum.NO.getType().equals(createReqVO.getDoMoveAll())) {
            locationIds = setSingleLocationIdNo(createReqVO.getTaskDetailList(), task, laneIds, areaIds);
        } else {
            locationIds = setAllLocationIdNo(createReqVO, task.getId(), laneIds, areaIds, task.getSkuInfo());
        }

        // 插入
        createReqVO.getTaskDetailList()
                .forEach(taskDetail -> {
                    taskDetail.setRobotTaskId(task.getId());
                });
        robotTaskDetailService.insertRobotDetailList(createReqVO.getTaskDetailList());

        List<List<Long>> fromPartition = Lists.partition(locationIds, 100);
        fromPartition.forEach(list -> {
            locationMapper.updateLocationLockList(list, task.getId(), LocationLockEnum.NO.getType());
        });

        if (doCycle && ObjectUtil.isNotEmpty(createReqVO.getDoCycle())
                && ZeroOneEnum.ONE.getType().equals(createReqVO.getDoCycle().intValue())) {
            addCycle(task.getId(), createReqVO.getTaskDetailList());
        }

        return RESULT;
    }

    @Transactional(rollbackFor = Exception.class)
    public void addCycle(Long id, List<RobotTaskDetailAddVO> taskDetailList) {
        List<TaskCycleDO> cycleDOList = new ArrayList<>();

        List<Long> laneIds = new ArrayList<>();
        List<Long> areaIds = new ArrayList<>();
        List<Long> locationIds = new ArrayList<>();

        for (RobotTaskDetailAddVO detailAddVO : taskDetailList) {

            if (ReleaseTakeEnum.TO_LANE.getType().equals(detailAddVO.getTakeType())) {
                laneIds.add(detailAddVO.getTakeId());
            } else if (ReleaseTakeEnum.TO_AREA.getType().equals(detailAddVO.getTakeType())) {
                areaIds.add(detailAddVO.getTakeId());
            } else if (ReleaseTakeEnum.TO_LOCATION.getType().equals(detailAddVO.getTakeType())) {
                locationIds.add(detailAddVO.getTakeId());
            }

            if (ReleaseTakeEnum.TO_LANE.getType().equals(detailAddVO.getReleaseType())) {
                laneIds.add(detailAddVO.getTakeId());
            } else if (ReleaseTakeEnum.TO_AREA.getType().equals(detailAddVO.getReleaseType())) {
                areaIds.add(detailAddVO.getTakeId());
            } else if (ReleaseTakeEnum.TO_LOCATION.getType().equals(detailAddVO.getReleaseType())) {
                locationIds.add(detailAddVO.getTakeId());
            }
        }

        LambdaQueryWrapper<WareHouseLocationDO> wrapper = new LambdaQueryWrapper<>();
        if (ObjectUtil.isNotEmpty(laneIds)) {
            wrapper.or(w -> w.in(WareHouseLocationDO::getLaneId, laneIds));
        }
        if (ObjectUtil.isNotEmpty(areaIds)) {
            wrapper.or(w -> w.in(WareHouseLocationDO::getAreaId, areaIds));
        }
        if (ObjectUtil.isNotEmpty(locationIds)) {
            wrapper.or(w -> w.in(WareHouseLocationDO::getId, locationIds));
        }

        List<WareHouseLocationDO> wareHouseLocationDOS = locationMapper.selectList(wrapper);

        wareHouseLocationDOS
                .stream()
                .map(WareHouseLocationDO::getMapItemId)
                .forEach(mapItemId -> {
                    TaskCycleDO build = TaskCycleDO.builder().robotTaskId(id).mapItemId(mapItemId).build();
                    cycleDOList.add(build);
                });
        taskCycleMapper.insertBatch(cycleDOList);

    }

    /**
     * 关闭任务
     *
     * @param updateReqVO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void closeTask(RobotTaskSaveReqVO updateReqVO) {
        // 校验存在
        validateTaskExists(updateReqVO.getId());
        if (ObjectUtil.isEmpty(updateReqVO.getId())) {
            throw exception(TASK_CHECK_ID_EXCEPTION);
        }
        // 更新
        //修改优先级需要订单状态为未开始/处理中
        RobotTaskDO robotTaskDO = taskMapper.selectById(updateReqVO.getId());
        List<RobotTaskDetailDO> taskDetailDOS = taskDetailMapper.queryByTaskId(updateReqVO.getId());

        List<Long> locationIds = new ArrayList<>();
        List<Long> detailIds = new ArrayList<>();
        Set<String> robotNos = new HashSet<>();

        for (RobotTaskDetailDO taskDetailDO : taskDetailDOS) {
            if (!RobotTaskDetailStatusEnum.NEW.getType().equals(taskDetailDO.getTaskStatus())
                    && !RobotTaskDetailStatusEnum.DOING.getType().equals(taskDetailDO.getTaskStatus())) {
                continue;
            }
            //后期需做状态映射
            taskDetailDO.setTaskStatus(updateReqVO.getTaskStatus());

            if (RobotTaskTypeEnum.CHARGE.getType().equals(taskDetailDO.getTaskType())
                    && RobotTaskTypeEnum.PARK.getType().equals(taskDetailDO.getTaskType())) {
                continue;
            }

            if (ObjectUtil.isNotEmpty(taskDetailDO.getRobotNo())) {
                robotNos.add(taskDetailDO.getRobotNo());
                detailIds.add(taskDetailDO.getId());
            }

            if (ObjectUtil.isNotEmpty(taskDetailDO.getFromLocationId())) {
                locationIds.add(taskDetailDO.getFromLocationId());
            }
            if (ObjectUtil.isNotEmpty(taskDetailDO.getToLocationId())) {
                locationIds.add(taskDetailDO.getToLocationId());
            }

            RobotAcceptTaskDTO robotTask = new RobotAcceptTaskDTO();
            robotTask.setOrder_id(taskDetailDO.getId().toString());
            String mac = robotInformationService.getMacByRobotNo(taskDetailDO.getRobotNo());
            robotTask.setTopic(RobotTopicConstant.ROBOT_TASK_TOPIC + mac);
            robotTask.setOrder_type(RobotTaksOrderTypeEnum.TASK.getType());
            robotTask.setExecution_type(ExecutionTypeEnum.CANCEL.getType());
            commonApi.commonMethod(robotTask, robotTask.getTopic());

            RobotClosePathPlantingDTO closePathPlanting = RobotClosePathPlantingDTO.builder()
                    .robotNo(taskDetailDO.getRobotNo())
                    .id(taskDetailDO.getId().toString())
                    .build();
            commonApi.commonMethod(closePathPlanting, PathPlanningTopicConstant.KILL_TASK);
        }

        RobotTaskDO updateObj = BeanUtils.toBean(updateReqVO, RobotTaskDO.class);
        taskMapper.updateById(updateObj);

        taskDetailMapper.updateBatch(taskDetailDOS);

        if (ObjectUtil.isNotEmpty(locationIds)) {
            locationMapper.releaseLocationLockList(locationIds, robotTaskDO.getId()
                    , LocationLockEnum.YES.getType());
        }

        if (ObjectUtil.isEmpty(robotNos)) {
            return;
        }

        List<RobotInformationDO> robotInformationDOS = informationMapper.selectList(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotStatus, RobotStatusEnum.DOING.getType())
                .in(RobotInformationDO::getTaskDetailId, detailIds)
                .in(RobotInformationDO::getRobotNo, robotNos));
        if (ObjectUtil.isEmpty(robotInformationDOS)) {
            return;
        }

        for (RobotInformationDO robotInformationDO : robotInformationDOS) {
            String cargoDetectedKey = RobotTaskChcheConstant.ROBOT_CARGO_DETECTED + robotInformationDO.getMacAddress();
            Object cargoDetected = redisUtil.get(cargoDetectedKey);
            if (ObjectUtil.isEmpty(cargoDetected) || RobotStatusCodeConstant.CARGO_DETECTED_TRUE.equals(cargoDetected)) {
                robotInformationDO.setRobotTaskModel(RobotTaskModelEnum.REJECTION.getType());
            }
            robotInformationDO.setRobotStatus(RobotStatusEnum.STAND_BY.getType());
        }

        informationMapper.updateBatch(robotInformationDOS);

    }

    /**
     * 设置放货和取货的id
     *
     * @param createReqVO
     * @param taskId
     */
    @Transactional(rollbackFor = Exception.class)
    public List<Long> setAllLocationIdNo(RobotTaskSaveReqVO createReqVO, Long taskId,
                                         List<Long> laneIds, List<Long> areaIds, String skuInfo) {
        List<RobotTaskDetailAddVO> taskDetailList = createReqVO.getTaskDetailList();

        List<RobotTaskDetailAddVO> newTaskDetailList = new ArrayList<>();
        List<Long> mapItemIds = new ArrayList<>();
        if (doCycle) {
            List<TaskCycleDO> taskCycleDOS = taskCycleMapper.selectList(new LambdaQueryWrapper<TaskCycleDO>());
            if (ObjectUtil.isNotEmpty(taskCycleDOS)) {
                mapItemIds = taskCycleDOS.stream().map(TaskCycleDO::getMapItemId).collect(Collectors.toList());
            }
        }
        List<WareHouseLocationDO> wareHouseLocationDOS = locationMapper.selectList(new LambdaQueryWrapper<WareHouseLocationDO>()
                .eq(WareHouseLocationDO::getLocationEnable, LocationEnableEnum.NO.getType()));
        if (ObjectUtil.isNotEmpty(wareHouseLocationDOS)) {
            List<Long> mapItemList = wareHouseLocationDOS.stream().map(WareHouseLocationDO::getMapItemId)
                    .collect(Collectors.toList());
            mapItemIds.addAll(mapItemList);
        }
        //被此次任务锁定的空库位id
        List<Long> toLocationIds = new ArrayList<>();
        //被此次任务锁定的有货库位id
        List<Long> fromLocationIds = new ArrayList<>();
        for (RobotTaskDetailAddVO robotTaskVo : taskDetailList) {
            Set<Long> mapIds = new HashSet<>();
            if (ObjectUtil.isNotEmpty(robotTaskVo.getRobotNo())) {
                RobotInformationPageReqVO pageReqVO = new RobotInformationPageReqVO();
                pageReqVO.setRobotNo(robotTaskVo.getRobotNo());
                mapIds = informationMapper.selectPage(pageReqVO).getList()
                        .stream()
                        .findFirst()
                        .map(RobotInformationDO::getFloorAreaJson)
                        .orElse(new HashSet<>());
            }
            //查找有货且非锁定的库位
            WareHouseLocationDO query = null;
            if (ReleaseTakeEnum.TO_LANE.getType().equals(robotTaskVo.getTakeType())) {
                query = WareHouseLocationDO.builder()
                        .laneId(robotTaskVo.getTakeId())
                        .locationEnable(LocationEnableEnum.YES.getType())
                        .locationLock(LocationLockEnum.YES.getType())
                        .locationUseStatus(LocationUseStatusEnum.YES.getType())
                        .build();
            } else if (ReleaseTakeEnum.TO_AREA.getType().equals(robotTaskVo.getTakeType())) {
                query = WareHouseLocationDO.builder()
                        .areaId(robotTaskVo.getTakeId())
                        .locationEnable(LocationEnableEnum.YES.getType())
                        .locationLock(LocationLockEnum.YES.getType())
                        .locationUseStatus(LocationUseStatusEnum.YES.getType())
                        .build();
            }

            List<WareHouseLocationDO> stockList = locationMapper.selectLocations(query, toLocationIds, mapIds,
                    new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
            if (ObjectUtil.isEmpty(stockList)) {
                log.error("取货线库/区域为空或者已锁定或者机器人取放货区域受限制 :{}", robotTaskVo.toString());
                throw new RuntimeException("取货线库/区域为空或者已锁定或者机器人取放货区域受限制");
            }
            //判断取货库位是否存在未完成的任务
            Set<Long> stockLocationIds = stockList.stream().map(WareHouseLocationDO::getId).collect(Collectors.toSet());
            List<RobotTaskDetailDO> doingTasks = taskDetailMapper.queryDoingTaskByIds(stockLocationIds);
            if (ObjectUtil.isNotEmpty(doingTasks)) {
                throw new RuntimeException("取货线库/区域,存在未完成的任务");
            }
            //查找空库位
            WareHouseLocationDO releaseQuery = null;
            if (ReleaseTakeEnum.TO_LANE.getType().equals(robotTaskVo.getReleaseType())) {
                if (ObjectUtil.isEmpty(laneIds) && ObjectUtil.isNotEmpty(skuInfo)) {
                    log.error("查不到此物料对应的线库区域 :{}", robotTaskVo.getReleaseId());
                    throw new RuntimeException("查不到此物料对应的线库区域 " + skuInfo);
                }
                releaseQuery = WareHouseLocationDO.builder()
                        .laneId(robotTaskVo.getReleaseId())
                        .locationEnable(LocationEnableEnum.YES.getType())
                        .locationLock(LocationLockEnum.YES.getType())
                        .locationUseStatus(LocationUseStatusEnum.NO.getType())
                        .build();
            } else if (ReleaseTakeEnum.TO_AREA.getType().equals(robotTaskVo.getReleaseType())) {
                if (ObjectUtil.isEmpty(areaIds) && ObjectUtil.isNotEmpty(skuInfo)) {
                    log.error("查不到此物料对应的区域库位 :{}", robotTaskVo.getReleaseId());
                    throw new RuntimeException("查不到此物料对应的区域库位 " + skuInfo);
                }
                releaseQuery = WareHouseLocationDO.builder()
                        .areaId(robotTaskVo.getReleaseId())
                        .locationEnable(LocationEnableEnum.YES.getType())
                        .locationLock(LocationLockEnum.YES.getType())
                        .locationUseStatus(LocationUseStatusEnum.NO.getType())
                        .build();
            }

            List<WareHouseLocationDO> releaseStockList = locationMapper.selectLocations(releaseQuery, toLocationIds, mapIds,
                    laneIds, areaIds, mapItemIds);
            if (ObjectUtil.isEmpty(releaseStockList) || releaseStockList.size() < stockList.size()) {
                log.error("放货线库/区域库位数量不足或者机器人取放货区域受限制 :{}", robotTaskVo.toString());
                throw new RuntimeException("放货线库/区域库位数量不足或者机器人取放货区域受限制");
            }
            Collections.reverse(releaseStockList);
            //赋值数据
            List<RobotTaskDetailAddVO> addTaskDetailList = getTaskDatail(stockList, releaseStockList, toLocationIds,
                    robotTaskVo, taskId, fromLocationIds);
            newTaskDetailList.addAll(addTaskDetailList);

        }

        createReqVO.setTaskDetailList(newTaskDetailList);

        if (ObjectUtil.isNotEmpty(fromLocationIds)) {
            toLocationIds.addAll(fromLocationIds);
        }
        return toLocationIds;
    }

    /**
     * 赋值数据
     *
     * @param stockList
     * @param releaseStockList
     * @param toLocationIds
     * @param robotTaskVo
     * @return
     */
    private List<RobotTaskDetailAddVO> getTaskDatail(List<WareHouseLocationDO> stockList,
                                                     List<WareHouseLocationDO> releaseStockList,
                                                     List<Long> toLocationIds, RobotTaskDetailAddVO robotTaskVo,
                                                     Long taskId, List<Long> fromLocationIds) {
        List<RobotTaskDetailAddVO> addTaskDetailList = new ArrayList<>();
        for (int i = 0; i < stockList.size(); i++) {
            RobotTaskDetailAddVO robotTaskDetailAddVo = new RobotTaskDetailAddVO();
            robotTaskDetailAddVo.setTaskType(robotTaskVo.getTaskType());
            robotTaskDetailAddVo.setReleaseType(robotTaskVo.getReleaseType());
            robotTaskDetailAddVo.setTakeType(robotTaskVo.getTakeType());
            robotTaskDetailAddVo.setTakeId(robotTaskVo.getTakeId());
            robotTaskDetailAddVo.setReleaseId(robotTaskVo.getReleaseId());
            robotTaskDetailAddVo.setRobotNo(robotTaskVo.getRobotNo());
            robotTaskDetailAddVo.setFromLocationNo(stockList.get(i).getLocationNo());
            robotTaskDetailAddVo.setFromLocationId(stockList.get(i).getId());
            robotTaskDetailAddVo.setToLocationNo(releaseStockList.get(i).getLocationNo());
            robotTaskDetailAddVo.setToLocationId(releaseStockList.get(i).getId());
            robotTaskDetailAddVo.setFromLaneId(stockList.get(i).getLaneId());
            robotTaskDetailAddVo.setToLaneId(releaseStockList.get(i).getLaneId());
            robotTaskDetailAddVo.setFromLocationNumber(stockList.get(i).getLocationNumber());
            robotTaskDetailAddVo.setFromMapItemId(stockList.get(i).getMapItemId());
            robotTaskDetailAddVo.setToMapItemId(releaseStockList.get(i).getMapItemId());
            robotTaskDetailAddVo.setRobotTaskId(taskId);
            robotTaskDetailAddVo.setPriority(robotTaskVo.getPriority());
            robotTaskDetailAddVo.setFromLocationStorey(stockList.get(i).getLocationStorey());
            robotTaskDetailAddVo.setToLocationStorey(releaseStockList.get(i).getLocationStorey());
            addTaskDetailList.add(robotTaskDetailAddVo);

            toLocationIds.add(releaseStockList.get(i).getId());
            fromLocationIds.add(stockList.get(i).getId());
        }
        return addTaskDetailList;
    }

    /**
     * 设置放货和取货的id
     *
     * @param taskDetailList
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public List<Long> setSingleLocationIdNo(List<RobotTaskDetailAddVO> taskDetailList, RobotTaskDO task,
                                            List<Long> laneIds, List<Long> areaIds) {
        //取货中的map_item_id， 此集合不能执行放货的任务
        List<Long> takeMapItemIds = taskDetailMapper.selectTakeMapItemIds();
        if (ObjectUtil.isEmpty(takeMapItemIds)) {
            takeMapItemIds = new ArrayList<>();
        }
        //放货中的map_item_id， 此集合不能执行取货的任务
        List<Long> releaseMapItemIds = taskDetailMapper.selectReleaseMapItemIds();
        if (ObjectUtil.isEmpty(releaseMapItemIds)) {
            releaseMapItemIds = new ArrayList<>();
        }

        if (doCycle) {
            List<TaskCycleDO> taskCycleDOS = taskCycleMapper.selectList(new LambdaQueryWrapper<TaskCycleDO>());
            if (ObjectUtil.isNotEmpty(taskCycleDOS)) {
                List<Long> mapItemIds =
                        taskCycleDOS.stream().map(TaskCycleDO::getMapItemId).collect(Collectors.toList());
                takeMapItemIds.addAll(mapItemIds);
                releaseMapItemIds.addAll(mapItemIds);
            }
        }
        List<WareHouseLocationDO> wareHouseLocationDOS = locationMapper.selectList(new LambdaQueryWrapper<WareHouseLocationDO>()
                .eq(WareHouseLocationDO::getLocationEnable, LocationEnableEnum.NO.getType()));
        if (ObjectUtil.isNotEmpty(wareHouseLocationDOS)) {
            List<Long> mapItemList = wareHouseLocationDOS.stream().map(WareHouseLocationDO::getMapItemId)
                    .collect(Collectors.toList());
            releaseMapItemIds.addAll(mapItemList);
        }
        List<Long> locationIds = new ArrayList<>();

        for (RobotTaskDetailAddVO robotTaskVo : taskDetailList) {
            robotTaskVo.setRobotTaskId(task.getId());
            robotTaskVo.setPriority(task.getPriority());
            RobotTaskTypeEnum robotTaskType = RobotTaskTypeEnum.getRobotTaskType(robotTaskVo.getTaskType());
            switch (robotTaskType) {
                case TAKE_RELEASE:
                    doTakeRelease(robotTaskVo, locationIds, takeMapItemIds, releaseMapItemIds, laneIds, areaIds, task.getSkuInfo());
                    break;
                case PARK:
                    doPark(robotTaskVo, locationIds, takeMapItemIds, releaseMapItemIds);
                    break;
                case CHARGE:
                    doCharge(robotTaskVo, takeMapItemIds, releaseMapItemIds);
                    break;
                case MOVE:
                    doMove(robotTaskVo, locationIds, takeMapItemIds, releaseMapItemIds);
                    break;
                case TAKE:
                    doTake(robotTaskVo, locationIds, takeMapItemIds, releaseMapItemIds);
                    break;
                case RELEASE:
                    doRelease(robotTaskVo, locationIds, takeMapItemIds, releaseMapItemIds, laneIds, areaIds, task.getSkuInfo());
                    break;
                case SCAN:
                    //暂无此功能
                    doScan(robotTaskVo, locationIds, takeMapItemIds, releaseMapItemIds);
                    break;
                case DETECTING_TRAYS:
                    //暂无此功能
                    doDetectingTrays(robotTaskVo, locationIds, takeMapItemIds, releaseMapItemIds);
                    break;
                default:
                    log.error("任务类型不存在 ");
                    throw new RuntimeException("任务类型不存在");
            }
        }
        return locationIds;
    }

    /**
     * 检测托盘类型
     *
     * @param robotTaskVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void doDetectingTrays(RobotTaskDetailAddVO robotTaskVo, List<Long> locationIds, List<Long> takeMapItemIds,
                                 List<Long> releaseMapItemIds) {
        /*setToLocation(robotTaskVo,getMapIdsByRobotNo(robotTaskVo.getRobotNo()),new ArrayList<>());
        locationIds.add(robotTaskVo.getToLocationId());*/
    }

    /**
     * 扫描码
     *
     * @param robotTaskVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void doScan(RobotTaskDetailAddVO robotTaskVo, List<Long> locationIds, List<Long> takeMapItemIds,
                       List<Long> releaseMapItemIds) {
        /*setToLocation(robotTaskVo,getMapIdsByRobotNo(robotTaskVo.getRobotNo()),new ArrayList<>());
        locationIds.add(robotTaskVo.getToLocationId());*/
    }

    /**
     * 仅放货
     *
     * @param robotTaskVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void doRelease(RobotTaskDetailAddVO robotTaskVo, List<Long> locationIds, List<Long> takeMapItemIds,
                          List<Long> releaseMapItemIds, List<Long> laneIds, List<Long> areaIds, String skuInfo) {
        setToLocation(robotTaskVo, getMapIdsByRobotNo(robotTaskVo.getRobotNo()), locationIds, takeMapItemIds, releaseMapItemIds, laneIds, areaIds, skuInfo);
        locationIds.add(robotTaskVo.getToLocationId());
    }

    /**
     * 仅取货
     *
     * @param robotTaskVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void doTake(RobotTaskDetailAddVO robotTaskVo, List<Long> locationIds, List<Long> takeMapItemIds,
                       List<Long> releaseMapItemIds) {
        Set<Long> mapIds = new HashSet<>();
        if (ObjectUtil.isNotEmpty(robotTaskVo.getRobotNo())) {
            mapIds = getMapIdsByRobotNo(robotTaskVo.getRobotNo());
        }
        setFromLocation(robotTaskVo, mapIds, locationIds, takeMapItemIds, releaseMapItemIds);
        locationIds.add(robotTaskVo.getFromLocationId());
    }

    /**
     * 移动
     *
     * @param robotTaskVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void doMove(RobotTaskDetailAddVO robotTaskVo, List<Long> locationIds, List<Long> takeMapItemIds,
                       List<Long> releaseMapItemIds) {
        setToLocation(robotTaskVo, getMapIdsByRobotNo(robotTaskVo.getRobotNo()), locationIds, takeMapItemIds, releaseMapItemIds,
                null, null, null);
        locationIds.add(robotTaskVo.getToLocationId());
    }

    /**
     * 充电
     *
     * @param robotTaskVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void doCharge(RobotTaskDetailAddVO robotTaskVo, List<Long> takeMapItemIds,
                         List<Long> releaseMapItemIds) {

    }

    /**
     * 停车
     *
     * @param robotTaskVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void doPark(RobotTaskDetailAddVO robotTaskVo, List<Long> locationIds, List<Long> takeMapItemIds,
                       List<Long> releaseMapItemIds) {
        // todo 停车后续停到等待点
        /*setToLocation(robotTaskVo,getMapIdsByRobotNo(robotTaskVo.getRobotNo()),locationIds);
        locationIds.add(robotTaskVo.getToLocationId());*/
    }

    /**
     * 查询此机器人对应的楼层/区域(json)
     *
     * @param robotNo
     * @return
     */
    public Set<Long> getMapIdsByRobotNo(String robotNo) {
        if (ObjectUtil.isNotEmpty(robotNo)) {
            RobotInformationPageReqVO pageReqVO = new RobotInformationPageReqVO();
            pageReqVO.setRobotNo(robotNo);
            return informationMapper.selectPage(pageReqVO).getList()
                    .stream()
                    .findFirst()
                    .map(RobotInformationDO::getFloorAreaJson)
                    .orElse(new HashSet<>());
        }
        return new HashSet<>();
    }

    /**
     * 设置停车/目标库位/扫描库位
     */
    @Transactional(rollbackFor = Exception.class)
    public void setToLocation(RobotTaskDetailAddVO robotTaskVo, Set<Long> mapIds, List<Long> locationIds,
                              List<Long> takeMapItemIds, List<Long> releaseMapItemIds,
                              List<Long> laneIds, List<Long> areaIds, String skuInfo) {
        if (ReleaseTakeEnum.TO_LOCATION.getType().equals(robotTaskVo.getReleaseType())) {
            WareHouseLocationDO query = WareHouseLocationDO.builder().id(robotTaskVo.getReleaseId()).build();
            WareHouseLocationDO locationDO = locationMapper.queryAllByLimit(query);
            robotTaskVo.setToLocationNo(locationDO.getLocationNo());
            robotTaskVo.setToLocationId(robotTaskVo.getReleaseId());
            robotTaskVo.setToLocationStorey(locationDO.getLocationStorey());
            robotTaskVo.setToLaneId(locationDO.getLaneId());
            robotTaskVo.setToLocationNumber(locationDO.getLocationNumber());
            robotTaskVo.setToMapItemId(locationDO.getMapItemId());
            if (ObjectUtil.isNotEmpty(mapIds) && !mapIds.contains(locationDO.getMapId())) {
                log.error("机器人不能在此放货库位放货 :{}, 机器人编号 :{}", locationDO.getLocationNo(), robotTaskVo.getRobotNo());
                throw new RuntimeException("机器人不能在此放货库位放货 " + robotTaskVo.getReleaseId());
            }
            if (ObjectUtil.isNotEmpty(takeMapItemIds) && takeMapItemIds.contains(locationDO.getMapItemId())) {
                log.error("此点位存在取货任务 :{}", locationDO.getLocationNo());
                throw new RuntimeException("此库位存在取货任务 " + locationDO.getLocationNo());
            }
            releaseMapItemIds.add(locationDO.getMapItemId());
        } else {
            if (ReleaseTakeEnum.TO_LANE.getType().equals(robotTaskVo.getReleaseType()) && ObjectUtil.isEmpty(laneIds)
                    && ObjectUtil.isNotEmpty(skuInfo)) {
                log.error("查不到此物料对应的线库区域 :{}", robotTaskVo.getReleaseId());
                throw new RuntimeException("查不到此物料对应的线库区域 " + skuInfo);
            }
            if (ReleaseTakeEnum.TO_AREA.getType().equals(robotTaskVo.getReleaseType()) && ObjectUtil.isEmpty(areaIds)
                    && ObjectUtil.isNotEmpty(skuInfo)) {
                log.error("查不到此物料对应的区域库位 :{}", robotTaskVo.getReleaseId());
                throw new RuntimeException("查不到此物料对应的区域库位 " + skuInfo);
            }
            WareHouseLocationDO wareHouseLocationDO =
                    locationMapper.selectByTypeAndId(LocationUseStatusEnum.NO.getType(), robotTaskVo.getReleaseType()
                            , robotTaskVo.getReleaseId(), locationIds, mapIds, takeMapItemIds, laneIds, areaIds);
            if (ObjectUtil.isEmpty(wareHouseLocationDO)) {
                log.error("放货库位为空或者已锁定或者机器人取放货区域受限制 :{}", robotTaskVo.getReleaseId());
                throw new RuntimeException("放货库位为空或者已锁定或者机器人取放货区域受限制 " + robotTaskVo.getReleaseId());
            }
            robotTaskVo.setToLocationNo(wareHouseLocationDO.getLocationNo());
            robotTaskVo.setToLocationId(wareHouseLocationDO.getId());
            robotTaskVo.setToLocationStorey(wareHouseLocationDO.getLocationStorey());
            robotTaskVo.setToLaneId(wareHouseLocationDO.getLaneId());
            robotTaskVo.setToLocationNumber(wareHouseLocationDO.getLocationNumber());
            robotTaskVo.setToMapItemId(wareHouseLocationDO.getMapItemId());
            releaseMapItemIds.add(wareHouseLocationDO.getMapItemId());
        }
    }

    /**
     * 设置取货库位
     *
     * @param robotTaskVo
     * @param mapIds
     * @param locationIds
     */
    @Transactional(rollbackFor = Exception.class)
    public void setFromLocation(RobotTaskDetailAddVO robotTaskVo, Set<Long> mapIds, List<Long> locationIds,
                                List<Long> takeMapItemIds, List<Long> releaseMapItemIds) {
        if (ReleaseTakeEnum.TO_LOCATION.getType().equals(robotTaskVo.getTakeType())) {
            WareHouseLocationDO query = WareHouseLocationDO.builder().id(robotTaskVo.getTakeId()).build();
            WareHouseLocationDO locationDO = locationMapper.queryAllByLimit(query);
            robotTaskVo.setFromLocationNo(locationDO.getLocationNo());
            robotTaskVo.setFromLocationId(robotTaskVo.getTakeId());
            robotTaskVo.setFromLocationStorey(locationDO.getLocationStorey());
            robotTaskVo.setFromLaneId(locationDO.getLaneId());
            robotTaskVo.setFromLocationNumber(locationDO.getLocationNumber());
            robotTaskVo.setFromMapItemId(locationDO.getMapItemId());
            if (ObjectUtil.isNotEmpty(mapIds) && !mapIds.contains(locationDO.getMapId())) {
                log.error("机器人不能在此取货库位取货 :{}, 机器人编号 :{}", locationDO.getLocationNo(), robotTaskVo.getRobotNo());
                throw new RuntimeException("机器人不能在此取货库位取货 " + robotTaskVo.getTakeId());
            }
            if (ObjectUtil.isNotEmpty(releaseMapItemIds) && releaseMapItemIds.contains(locationDO.getMapItemId())) {
                log.error("此取货库位存在放货任务 :{}", locationDO.getLocationNo());
                throw new RuntimeException("此取货库位存在放货任务 " + locationDO.getLocationNo());
            }
            takeMapItemIds.add(locationDO.getMapItemId());
        } else {
            WareHouseLocationDO wareHouseLocationDO =
                    locationMapper.selectByTypeAndId(LocationUseStatusEnum.YES.getType(), robotTaskVo.getTakeType(),
                            robotTaskVo.getTakeId(), locationIds, mapIds, releaseMapItemIds, new ArrayList<>(), new ArrayList<>());
            if (ObjectUtil.isEmpty(wareHouseLocationDO)) {
                log.error("取货库位为空或者已锁定或者机器人取放货区域受限制 :{}", robotTaskVo.getTakeId());
                throw new RuntimeException("取货库位为空或者已锁定或者机器人取放货区域受限制 " + robotTaskVo.getTakeId());
            }
            robotTaskVo.setFromLocationNo(wareHouseLocationDO.getLocationNo());
            robotTaskVo.setFromLocationId(wareHouseLocationDO.getId());
            robotTaskVo.setFromLocationStorey(wareHouseLocationDO.getLocationStorey());
            robotTaskVo.setFromLaneId(wareHouseLocationDO.getLaneId());
            robotTaskVo.setFromLocationNumber(wareHouseLocationDO.getLocationNumber());
            robotTaskVo.setFromMapItemId(wareHouseLocationDO.getMapItemId());
            takeMapItemIds.add(wareHouseLocationDO.getMapItemId());
        }
    }

    /**
     * 取放
     *
     * @param robotTaskVo
     */
    @Transactional(rollbackFor = Exception.class)
    public void doTakeRelease(RobotTaskDetailAddVO robotTaskVo, List<Long> locationIds,
                              List<Long> takeMapItemIds, List<Long> releaseMapItemIds,
                              List<Long> laneIds, List<Long> areaIds, String skuInfo) {
        Set<Long> mapIds = new HashSet<>();
        if (ObjectUtil.isNotEmpty(robotTaskVo.getRobotNo())) {
            mapIds = getMapIdsByRobotNo(robotTaskVo.getRobotNo());
        }

        //校验放货库位
        setToLocation(robotTaskVo, mapIds, locationIds, takeMapItemIds, releaseMapItemIds, laneIds, areaIds, skuInfo);
        locationIds.add(robotTaskVo.getToLocationId());

        //校验取货库位
        setFromLocation(robotTaskVo, mapIds, locationIds, takeMapItemIds, releaseMapItemIds);
        locationIds.add(robotTaskVo.getFromLocationId());

        //验证取货库位是否存在未完成的任务
        Set<Long> set = new HashSet<>();
        set.add(robotTaskVo.getFromLocationId());
        List<RobotTaskDetailDO> doingTasks = taskDetailMapper.queryDoingTaskByIds(set);
        if (ObjectUtil.isNotEmpty(doingTasks)) {
            log.error("取货库位,存在未完成的任务 :{}", robotTaskVo.getTakeId());
            throw new RuntimeException("取货库位,存在未完成的任务 ");
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTask(RobotTaskSaveReqVO updateReqVO) {

        // 校验存在
        validateTaskExists(updateReqVO.getId());
        if (ObjectUtil.isEmpty(updateReqVO.getId())) {
            throw exception(TASK_CHECK_ID_EXCEPTION);
        }
        // 更新
        //修改优先级需要订单状态为未开始/处理中
        RobotTaskDO robotTaskDO = taskMapper.selectById(updateReqVO.getId());
        List<RobotTaskDetailDO> taskDetailDOS = taskDetailMapper.queryByTaskId(updateReqVO.getId());
        if (ObjectUtil.isNotEmpty(updateReqVO.getPriority())
                && !updateReqVO.getPriority().equals(robotTaskDO.getPriority())) {
            /*if (!RobotTaskStatusEnum.NEW.getType().equals(robotTaskDO.getTaskStatus())
                    && !RobotTaskStatusEnum.DOING.getType().equals(robotTaskDO.getTaskStatus())) {
                throw exception(TASK_CHECK_TASK_PRIORITY);
            }*/
            for (RobotTaskDetailDO taskDetailDO : taskDetailDOS) {
                taskDetailDO.setPriority(updateReqVO.getPriority());
            }
        }

        RobotTaskDO updateObj = BeanUtils.toBean(updateReqVO, RobotTaskDO.class);
        taskMapper.updateById(updateObj);

        taskDetailMapper.updateBatch(taskDetailDOS);
    }

    @Override
    public void deleteTask(Long id) {
        // 校验存在
        validateTaskExists(id);
        // 删除
        taskMapper.deleteById(id);
    }

    private void validateTaskExists(Long id) {
        if (taskMapper.selectById(id) == null) {
            throw exception(TASK_NOT_EXISTS);
        }
    }

    @Override
    public RobotTaskDO getTask(Long id) {
        return taskMapper.selectById(id);
    }

    @Override
    public PageResult<RobotTaskRespVO> getTaskPage(RobotTaskPageReqVO pageReqVO) {

        PageResult<RobotTaskDO> pageResult = null;
        PageResult<RobotTaskRespVO> dataPage = new PageResult<>();
        List<RobotTaskDO> list = new ArrayList<>();
        if (ObjectUtil.isEmpty(pageReqVO.getRobotNo())) {
            pageResult = taskMapper.selectPage(pageReqVO);
            dataPage.setTotal(pageResult.getTotal());
            list = pageResult.getList();
        } else {
            IPage mpPage = MyBatisUtils.buildPage(pageReqVO);
            IPage<RobotTaskDO> page = taskMapper.selectPageList(mpPage, pageReqVO);
            dataPage.setTotal(page.getTotal());
            list = page.getRecords();
        }

        List<RobotTaskRespVO> targetList = BeanUtil.copyToList(list, RobotTaskRespVO.class);
        for (RobotTaskRespVO robotTaskRespVO : targetList) {
            robotTaskRespVO.setDetails(taskDetailMapper.queryByTaskId(robotTaskRespVO.getId()));
        }
        dataPage.setList(targetList);
        return dataPage;
    }

    @Override
    public String getTaskNo() {
        String incrementByKey = redisUtil.getIncrementByKey(RobotCacheLockEnum.TASK_NO.getKey());
        return taskNo + DateUtils.getYearMonthDay() + incrementByKey;
    }

    /**
     * PP分配的任务
     *
     * @param message
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void ppDistributionTask(String message) {
        log.info("PP分配任务 :{}", message);

        TaskPPDistribution distribution = JSON.parseObject(message, TaskPPDistribution.class);
        String mac = robotInformationService.getMacByRobotNo(distribution.getRobotNo());
        String robotDoingActionKey = RobotTaskChcheConstant.ROBOT_QUERY_DOING_ACTION + mac;
        redisUtil.del(robotDoingActionKey);

        List<Long> detailIds = new ArrayList<>();

        Map<Long, String> deviceNoMap = new HashMap<>();
        RobotChargeLogDO robotChargeLogs = null;

        /**
         * 充电
         */
        if (TaskTypeEnum.CHARGE.getType().equals(distribution.getType())) {
            robotChargeLogs = chargeLogMapper.selectById(distribution.getId());

            robotChargeLogs.setTaskStatus(ChargeTaskStatusEnum.DOING.getType());
            chargeLogMapper.updateBatch(robotChargeLogs);

            robotInformationMapper.updateRobotListStatus(distribution.getRobotNo(), RobotStatusEnum.CHARGE.getType(),distribution.getId());
            if (ObjectUtil.isNotEmpty(robotChargeLogs.getTaskDetailId())) {
                deviceNoMap.put(robotChargeLogs.getTaskDetailId(), robotChargeLogs.getDeviceNo());
                detailIds.add(robotChargeLogs.getTaskDetailId());
            }
        }

        /**
         * 任务
         */
        if (TaskTypeEnum.TASK.getType().equals(distribution.getType())) {
            chargeDone(distribution.getRobotNo());
            robotInformationMapper.updateRobotListStatus(distribution.getRobotNo(), RobotStatusEnum.DOING.getType(),distribution.getId());
            detailIds.add(distribution.getId());
        }

        if (ObjectUtil.isNotEmpty(detailIds)) {
            List<RobotTaskDetailDO> taskDetailDOS = taskDetailMapper.selectBatchIds(detailIds);
            taskDetailDOS.stream().forEach(taskDetailDO -> {
                taskDetailDO.setTaskStatus(RobotTaskDetailStatusEnum.DOING.getType());
                taskDetailDO.setStartTime(LocalDateTime.now());
                if (ObjectUtil.isEmpty(taskDetailDO.getRobotNo())) {
                    taskDetailDO.setRobotNo(distribution.getRobotNo());
                }
                if (ObjectUtil.isEmpty(taskDetailDO.getToLocationNo())
                        && ObjectUtil.isNotEmpty(deviceNoMap.get(taskDetailDO.getId()))) {
                    taskDetailDO.setToLocationNo(deviceNoMap.get(taskDetailDO.getId()));
                }
            });

            taskDetailMapper.updateBatch(taskDetailDOS);
            List<Long> robotTaskIds = taskDetailDOS.stream()
                    .map(RobotTaskDetailDO::getRobotTaskId)
                    .collect(Collectors.toList());
            List<RobotTaskDO> tasks = taskMapper.selectBatchIds(robotTaskIds);
            tasks.stream().forEach(task -> {
                task.setTaskStatus(RobotTaskStatusEnum.DOING.getType());
            });
            taskMapper.updateBatch(tasks);
        }

        if (TaskTypeEnum.TASK.getType().equals(distribution.getType())) {
            robotCommonTaskService.sendTaskToRobot(distribution);
        }else if (TaskTypeEnum.CHARGE.getType().equals(distribution.getType())) {
            robotCommonTaskService.sendChargeTaskToRobot(distribution, robotChargeLogs);
        }

    }

    /**
     * 机器人完成充电
     *
     * @param robotNo
     */
    private void chargeDone(String robotNo) {
        RobotInformationDO robotInformationDO = robotInformationMapper.selectOne(new LambdaQueryWrapperX<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotNo, robotNo));
        if (!RobotStatusEnum.CHARGE.getType().equals(robotInformationDO.getRobotStatus())) {
            return;
        }
        RobotChargeLogDO robotChargeLogDO = chargeLogMapper.selectOne(new LambdaQueryWrapperX<RobotChargeLogDO>()
                .eq(RobotChargeLogDO::getRobotNo, robotNo)
                .orderByDesc(RobotChargeLogDO::getCreateTime)
                .last("limit 1"));
        if (ObjectUtil.isEmpty(robotChargeLogDO)) {
            return;
        }
        log.info("开始结束充电，并接任务 :{}", robotNo);
        robotChargeLogDO.setEndTime(LocalDateTime.now());

        String pose2dKey = RobotTaskChcheConstant.ROBOT_INFORMATION_POSE_BAT_SOC + robotInformationDO.getMacAddress();
        Object object = redisUtil.get(pose2dKey);
        RobotStatusDataPoseDTO robotStatusDataPoseDTO = JSONUtil.toBean((String) object, RobotStatusDataPoseDTO.class);

        if (ObjectUtil.isNotEmpty(object) && ObjectUtil.isNotEmpty(robotStatusDataPoseDTO)) {
            robotChargeLogDO.setEndElectricity(Integer.valueOf(robotStatusDataPoseDTO.getBat_soc()));
        }
        robotChargeLogDO.setTaskStatus(ChargeTaskStatusEnum.DONE.getType());
        chargeLogMapper.updateBatch(robotChargeLogDO);

        if (ObjectUtil.isEmpty(robotChargeLogDO.getTaskDetailId())) {
            return;
        }
        RobotTaskDetailDO taskDetail = taskDetailMapper.selectById(robotChargeLogDO.getTaskDetailId());
        taskDetail.setTaskStatus(RobotTaskDetailStatusEnum.DONE.getType());
        taskDetailMapper.updateBatch(taskDetail);
        RobotTaskDO robotTask = taskMapper.selectById(taskDetail.getRobotTaskId());
        robotTask.setTaskStatus(RobotTaskStatusEnum.DONE.getType());
        taskMapper.updateBatch(robotTask);
        DeviceInformationDO deviceInformationDO = deviceInformationMapper.selectOne(new LambdaQueryWrapperX<DeviceInformationDO>()
                .eq(DeviceInformationDO::getDeviceNo, robotChargeLogDO.getDeviceNo()));
        deviceInformationDO.setDeviceUseStatus(DeviceUseStatusEnum.IDLE.getType());
        deviceInformationMapper.updateBatch(deviceInformationDO);
        log.info("充电桩设置为空闲 :{}", deviceInformationDO.getDeviceNo());
    }

    /**
     * 任务日志分页
     *
     * @param pageReqVO
     * @return
     */
    @Override
    public PageResult<RobotTaskDetailLogResoVO> logPage(RobotTaskDetailLogVO pageReqVO) {
        PageResult<RobotTaskDetailLogResoVO> dataPage = new PageResult<>();

        IPage mpPage = MyBatisUtils.buildPage(pageReqVO);

        IPage<RobotTaskDetailLogResoVO> page = taskMapper.selectLogPageList(mpPage, pageReqVO);
        dataPage.setTotal(page.getTotal());
        List<RobotTaskDetailLogResoVO> list = page.getRecords();

        List<RobotTaskDetailLogResoVO> targetList = BeanUtil.copyToList(list, RobotTaskDetailLogResoVO.class);
        for (RobotTaskDetailLogResoVO robotTaskRespVO : targetList) {
            String msg = "";
            RobotTaskTypeEnum robotTaskType = RobotTaskTypeEnum.getRobotTaskType(robotTaskRespVO.getTaskType());
            switch (robotTaskType) {
                case TAKE_RELEASE:
                    msg = "取货点 " + robotTaskRespVO.getFromLocationNo() + " 放货点 " + robotTaskRespVO.getToLocationNo();
                    break;
                case PARK:
                    if (ObjectUtil.isNotEmpty(robotTaskRespVO.getToLocationNo())) {
                        msg = "停车点 " + robotTaskRespVO.getToLocationNo();
                    } else {
                        msg = "停车点";
                    }
                    break;
                case CHARGE:
                    if (ObjectUtil.isNotEmpty(robotTaskRespVO.getToLocationNo())) {
                        msg = "充电点 " + robotTaskRespVO.getToLocationNo();
                    } else {
                        msg = "充电点";
                    }
                    break;
                case MOVE:
                    if (ObjectUtil.isNotEmpty(robotTaskRespVO.getToLocationNo())) {
                        msg = "移动点 " + robotTaskRespVO.getToLocationNo();
                    } else {
                        msg = "移动点";
                    }
                    break;
                case TAKE:
                    msg = "取货点 " + robotTaskRespVO.getFromLocationNo();
                    break;
                case RELEASE:
                    msg = "放货点 " + robotTaskRespVO.getToLocationNo();
                    break;
                default:
                    log.error("任务类型不存在");
            }

            robotTaskRespVO.setMsg(msg);
        }
        dataPage.setList(targetList);
        return dataPage;
    }


}
