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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
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.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.mqtt.api.path.dto.RobotDimensionsDTO;
import cn.iocoder.yudao.module.mqtt.api.path.task.TaskRobotNoLimittationAreaDTO;
import cn.iocoder.yudao.module.mqtt.api.path.task.TaskToPathPlanningDTO;
import cn.iocoder.yudao.module.mqtt.api.task.dto.RobotAcceptTaskDTO;
import cn.iocoder.yudao.module.mqtt.api.task.dto.RobotRcsHeartBeatDTO;
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.processor.RequestProcessor;
import cn.iocoder.yudao.module.system.api.robot.dto.FloorZoneDTO;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotStatusDTO;
import cn.iocoder.yudao.module.system.api.robot.websocket.RobotStatusDataPoseDTO;
import cn.iocoder.yudao.module.system.api.robot.websocket.RobotInformationVO;
import cn.iocoder.yudao.module.system.config.mqtt.util.MqttUtils;
import cn.iocoder.yudao.module.system.config.poperties.ZnConfigConstant;
import cn.iocoder.yudao.module.system.constant.CommonConstant;
import cn.iocoder.yudao.module.system.constant.path.PathPlanningChcheConstant;
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.config.vo.CommonConfigVO;
import cn.iocoder.yudao.module.system.controller.admin.log.vo.UserOperationLogSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.robot.camera.RobotCameraAddVO;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.*;
import cn.iocoder.yudao.module.system.controller.admin.statistics.dto.RobotStatusClassificationDTO;
import cn.iocoder.yudao.module.system.controller.admin.tool.dto.CleanAgvDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.config.CommonConfigDO;
import cn.iocoder.yudao.module.system.dal.dataobject.houselocation.WareHouseLocationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.houselocation.WareHouseTakePointDO;
import cn.iocoder.yudao.module.system.dal.dataobject.information.DeviceInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.informationmapassociation.InformationMapAssociationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.log.RobotTaskDetailActionLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.positionmap.PositionMapDO;
import cn.iocoder.yudao.module.system.dal.dataobject.positionmap.PositionMapItemDO;
import cn.iocoder.yudao.module.system.dal.dataobject.remote.RemoteControllerInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.*;
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.positionmap.PositionMapItemMapper;
import cn.iocoder.yudao.module.system.dal.mysql.positionmap.PositionMapMapper;
import cn.iocoder.yudao.module.system.dal.mysql.remote.RemoteControllerInformationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.*;
import cn.iocoder.yudao.module.system.dal.mysql.wait.MoveToWaitMapper;
import cn.iocoder.yudao.module.system.enums.common.ZeroOneEnum;
import cn.iocoder.yudao.module.system.enums.device.DeviceTypeEnum;
import cn.iocoder.yudao.module.system.enums.device.DeviceUseStatusEnum;
import cn.iocoder.yudao.module.system.enums.item.PositionMapItemEnum;
import cn.iocoder.yudao.module.system.enums.item.UseStatusEnum;
import cn.iocoder.yudao.module.system.enums.path.PathTaskTypeEnum;
import cn.iocoder.yudao.module.system.enums.path.PathTaskTypeToRobotEnum;
import cn.iocoder.yudao.module.system.enums.redis.RobotCacheLockEnum;
import cn.iocoder.yudao.module.system.enums.robot.LocationLockEnum;
import cn.iocoder.yudao.module.system.enums.robot.LocationUseStatusEnum;
import cn.iocoder.yudao.module.system.enums.robot.ReleaseTakeEnum;
import cn.iocoder.yudao.module.system.enums.robot.RobotStatusEnum;
import cn.iocoder.yudao.module.system.enums.robot.RobotTaskDetailStatusEnum;
import cn.iocoder.yudao.module.system.enums.robot.RobotTaskModelEnum;
import cn.iocoder.yudao.module.system.enums.robot.actionlog.ActionStatusEnum;
import cn.iocoder.yudao.module.system.enums.robot.actionlog.CommandIdEnum;
import cn.iocoder.yudao.module.system.enums.robot.charge.ChargeModelEnum;
import cn.iocoder.yudao.module.system.enums.robot.information.RobotStatisticsTypeEnum;
import cn.iocoder.yudao.module.system.enums.robot.task.RobotCommandTypeEnum;
import cn.iocoder.yudao.module.system.enums.robot.task.RobotTaskStageEnum;
import cn.iocoder.yudao.module.system.service.houselocation.WareHouseTakePointService;
import cn.iocoder.yudao.module.system.service.informationmapassociation.InformationMapAssociationService;
import cn.iocoder.yudao.module.system.service.log.RobotTaskDetailActionLogService;
import cn.iocoder.yudao.module.system.service.log.UserOperationLogService;
import cn.iocoder.yudao.module.system.service.positionmap.PositionMapItemService;
import cn.iocoder.yudao.module.system.service.robot.camera.RobotCameraService;
import cn.iocoder.yudao.module.system.service.robot.mapstop.RobotMapStopService;
import cn.iocoder.yudao.module.system.service.robot.pathplanning.RobotPathPlanningService;
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.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

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 陈宾顺
 */
@Service
@Validated
@Slf4j
public class RobotInformationServiceImpl extends ServiceImpl<RobotInformationMapper, RobotInformationDO> implements RobotInformationService {

    @Resource
    private RobotInformationMapper informationMapper;

    @Resource
    private PositionMapMapper positionMapMapper;
    @Resource
    private RobotModelMapper modelMapper;

    /*@Resource
    private CommonApi commonApi;*/

    @Autowired
    private MqttUtils mqttUtils;

    @Resource
    private UserOperationLogService userOperationLogService;

    @Resource
    private RobotTaskDetailActionLogService taskDetailActionLogService;

    @Resource
    private DeviceInformationMapper deviceInformationMapper;

    @Resource
    private RobotTaskDetailMapper taskDetailMapper;

    @Resource
    private PositionMapItemService positionMapItemService;

    @Resource
    private RobotTaskMapper taskMapper;

    @Autowired
    private RobotPathPlanningService robotPathPlanningService;

    @Resource
    private PositionMapItemMapper positionMapItemMapper;

    @Resource
    private RedisUtil redisUtil;
    @Resource
    private RequestProcessor processor;
    @Resource
    private InformationMapAssociationService informationMapAssociationService;
    // -- 获取所有的设备信息key
    public static final String key = RobotTaskChcheConstant.ROBOT_GET_ROBOT_INFO;
    @Autowired
    private WareHouseLocationMapper wareHouseLocationMapper;

    @Resource
    private RedissonUtils redissonUtils;

    @Resource
    private RobotCameraService cameraService;

    @Resource
    private RobotChargeLogMapper chargeLogMapper;

    @Resource
    private MoveToWaitMapper moveToWaitMapper;

    @Resource
    private RemoteControllerInformationMapper controllerInformationMapper;

    @Resource
    private RobotMapStopMapper mapStopMapper;

    @Autowired
    private ZnConfigConstant znConfigConstant;

    @Resource
    @Lazy
    private RobotMapStopService mapStopService;

    @Resource
    private WareHouseTakePointService houseTakePointService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createInformation(RobotInformationSaveReqVO createReqVO) {

        RobotInformationDO existRobotInformationDO =
                informationMapper.selectByMacAndIdNotIn(createReqVO.getMacAddress(), null);
        if (ObjectUtil.isNotEmpty(existRobotInformationDO)) {
            throw exception(ROBOT_MAC_ADDRESS_EXISTS);
        }

        HashSet<String> set = new HashSet<>();
        set.add(createReqVO.getRobotNo());
        List<RobotInformationDO> robots = informationMapper.selectByRobotNos(set);
        if (ObjectUtil.isNotEmpty(robots)) {
            throw exception(ROBOT_ROBOT_NO_EXISTS);
        }

        RobotInformationDO existRobotIp = informationMapper.selectOne(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotIp, createReqVO.getRobotIp())
                .last("limit 1"));
        if (ObjectUtil.isNotEmpty(existRobotIp)) {
            throw exception(ROBOT_ROBOT_IP_EXISTS);
        }

        if (ObjectUtil.isNotEmpty(createReqVO.getCameraAddVOList())) {
            List<RobotCameraAddVO> cameraAddVOList = createReqVO.getCameraAddVOList();
            cameraService.checkCameraExist(cameraAddVOList);
            cameraService.createCameraList(cameraAddVOList, createReqVO.getRobotNo());
        }
        RobotModelDO robotModelDO = modelMapper.selectById(createReqVO.getRobotModelId());
        createReqVO.setRobotModelNumber(robotModelDO.getRobotModelNumber());
        createReqVO.setUrl(robotModelDO.getUrl());
        createReqVO.setChargeType(robotModelDO.getChargeType());
        // 插入
        RobotInformationDO information = BeanUtils.toBean(createReqVO, RobotInformationDO.class);

        checkIpAndPort(information);

        informationMapper.insert(information);
        redisUtil.set(RobotTaskChcheConstant.ROBOT_GET_ROBOTNO_BY_MAC + information.getMacAddress(), information.getRobotNo());
        redisUtil.set(RobotTaskChcheConstant.ROBOT_GET_MAC_BY_NO + information.getRobotNo(), information.getMacAddress());
        // 新增AGV于地图关系表 -----
        if (CollUtil.isNotEmpty(createReqVO.getFloorAreaJson())) {
            List<InformationMapAssociationDO> list = new ArrayList<>();
            for (Long mapId : createReqVO.getFloorAreaJson()) {
                InformationMapAssociationDO item = new InformationMapAssociationDO();
                item.setRobotInformationId(information.getId());
                item.setPositionMapId(mapId);
                list.add(item);
            }
            informationMapAssociationService.saveBatch(list);
        }

        UserOperationLogSaveReqVO operationLog = UserOperationLogSaveReqVO.builder()
                .operateAction("新增车辆 " + createReqVO.getRobotNo())
                .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();
        userOperationLogService.createUserOperationLog(operationLog);

        sendRobotConfigToPP();

        redisUtil.del(key);
        // 返回
        return information.getId();
    }

    @Override
    public void checkIpAndPort(RobotInformationDO updateObj) {
        if (ObjectUtil.isNotEmpty(updateObj.getRobotPort())) {
            String hexString = Integer.toHexString(updateObj.getRobotPort().intValue());
            if (hexString.length() != 4) {
                throw exception(ROBOT_PORT_NOT_COMPLIANCE);
            }
        }
        if (ObjectUtil.isNotEmpty(updateObj.getRobotIp())) {
            boolean validSubnet = isValidIpAddress(updateObj.getRobotIp());
            if (!validSubnet) {
                throw exception(ROBOT_IP_NOT_COMPLIANCE);
            }
        }
    }

    /**
     * 车辆状态分类统计
     *
     * @return
     */
    @Override
    public RobotStatusClassificationDTO robotStatusClassification() {
        List<RobotInformationDO> robotInformationDOS = informationMapper.selectList();
        if (ObjectUtil.isEmpty(robotInformationDOS)) {
            return null;
        }

        int idleNum = 0;
        int doingTaskNum = 0;
        int chargeNum = 0;
        int faultNum = 0;
        int total = robotInformationDOS.size();

        for (RobotInformationDO robotInformation : robotInformationDOS) {
            String errorLevelKey = RobotTaskChcheConstant.ROBOT_ERROR_LEVEL + robotInformation.getMacAddress();
            Object errorLevel = redisUtil.get(errorLevelKey);
            if (ObjectUtil.isNotEmpty(errorLevel)) {
                int level = Integer.parseInt(errorLevel.toString());
                if (level > 3) {
                    faultNum = faultNum + 1;
                    continue;
                }
            }

            if (RobotStatusEnum.STAND_BY.getType().equals(robotInformation.getRobotStatus())) {
                idleNum = idleNum + 1;
            } else if (RobotStatusEnum.DOING.getType().equals(robotInformation.getRobotStatus())) {
                doingTaskNum = doingTaskNum + 1;
            } else if (RobotStatusEnum.CHARGE.getType().equals(robotInformation.getRobotStatus())) {
                chargeNum = chargeNum + 1;
            }
        }

        int idle = idleNum * 100 / total;
        int doingTask = doingTaskNum * 100 / total;
        int charge = chargeNum * 100 / total;
        int fault = faultNum * 100 / total;

        return RobotStatusClassificationDTO.builder()
                .idle(idle)
                .idleNum(idleNum)
                .doingTask(doingTask)
                .doingTaskNum(doingTaskNum)
                .charge(charge)
                .chargeNum(chargeNum)
                .fault(fault)
                .faultNum(faultNum).build();
    }

    /**
     * 查询车辆所在的地图id
     *
     * @param robotNo
     * @return
     */
    @Override
    public Long getRobotMapIdByRobotNo(String robotNo) {
        FloorZoneDTO floorZoneDTO = getFloorZoneDTOByRobotNo(robotNo);
        if (ObjectUtil.isEmpty(floorZoneDTO) && ObjectUtil.isNotEmpty(floorZoneDTO.getFloor()) && ObjectUtil.isNotEmpty(floorZoneDTO.getArea())) {
            return null;
        }
        PositionMapDO positionMap = positionMapMapper.selectOne(new LambdaQueryWrapper<PositionMapDO>()
                .eq(PositionMapDO::getArea, floorZoneDTO.getArea())
                .eq(PositionMapDO::getFloor, floorZoneDTO.getFloor())
                .last("limit 1"));
        if (ObjectUtil.isEmpty(positionMap)) {
            return null;
        }
        return positionMap.getId();
    }

    @Override
    public void sendRobotConfigToPP() {
        List<RobotDimensionsDTO> robotDimensionsList = informationMapper.selectRobotDimensions();
        if (ObjectUtil.isNotEmpty(robotDimensionsList)) {
            for (RobotDimensionsDTO robotDimensionsDTO : robotDimensionsList) {
                robotDimensionsDTO.setCheckShortestDistance(znConfigConstant.getCheckShortestDistance());
                robotDimensionsDTO.setCheckLongestDistance(znConfigConstant.getCheckLongestDistance());
            }
        }
        mqttUtils.pub(PathPlanningTopicConstant.SEND_ROBOT_DIMENSIONS, JSON.toJSONString(robotDimensionsList));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void stopRobot(String robotNo) {
        RobotInformationDO robotInformationDO = informationMapper.selectOne(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotNo, robotNo)
                .last("limit 1"));

        RobotAcceptTaskDTO robotTask = new RobotAcceptTaskDTO();
        robotTask.setOrderId(robotInformationDO.getTaskDetailId() + "");
        robotTask.setTopic(RobotTopicConstant.ROBOT_TASK_MOVE_TOPIC + robotInformationDO.getMacAddress());
        robotTask.setExecutionType(ExecutionTypeEnum.PAUSE.getType());
        mqttUtils.pub(robotTask.getTopic(), JSON.toJSONString(robotTask));
        mapStopService.rcsStopRobotAddLog(robotNo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void continueRobot(String robotNo) {
        RobotInformationDO robotInformationDO = informationMapper.selectOne(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotNo, robotNo)
                .last("limit 1"));

        RobotAcceptTaskDTO robotTask = new RobotAcceptTaskDTO();
        robotTask.setOrderId(robotInformationDO.getTaskDetailId() + "");
        robotTask.setTopic(RobotTopicConstant.ROBOT_TASK_MOVE_TOPIC + robotInformationDO.getMacAddress());
        robotTask.setExecutionType(ExecutionTypeEnum.CONTINUE.getType());
        mqttUtils.pub(robotTask.getTopic(), JSON.toJSONString(robotTask));
        mapStopService.rcsDeleteStopRobotAddLog(robotNo);
    }

    @Override
    public void initRobotInformation() {
        informationMapper.initRobotInformation();
    }

    @Override
    public void lockRobot(String robotNo) {
        informationMapper.updateRobotTaskModel(robotNo,ZeroOneEnum.ZERO.getType());
    }


    /**
     * 校验IP地址是否合法
     *
     * @param ipAddress IP地址字符串
     * @return 是否合法
     */
    public static boolean isValidIpAddress(String ipAddress) {
        String ipPattern = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$";
        return ipAddress.matches(ipPattern);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateInformation(RobotInformationSaveReqVO updateReqVO) {
        // 校验存在
        validateInformationExists(updateReqVO.getId());
        String str = "";
        RobotInformationDO robotInformationDO = informationMapper.selectById(updateReqVO.getId());

        if (RobotStatusEnum.STAND_BY.getType().equals(robotInformationDO.getRobotStatus())) {
            PositionMapItemDO positionMapItem = positionMapItemMapper.selectOne(new LambdaQueryWrapper<PositionMapItemDO>()
                    .eq(PositionMapItemDO::getRobotNo, robotInformationDO.getRobotNo())
                    .eq(PositionMapItemDO::getUseStatus, UseStatusEnum.USEING.getType())
                    .eq(PositionMapItemDO::getType, PositionMapItemEnum.STOP.getType())
                    .last("limit 1"));
            if (ObjectUtil.isNotEmpty(positionMapItem) && ObjectUtil.isEmpty(updateReqVO.getFloorAreaJson())) {
                log.info("车辆 :{} ,停靠在 :{},", robotInformationDO.getRobotNo(), positionMapItem.getPositionMapId());
                throw exception(ROBOT_PART_AREA);
            } else if (ObjectUtil.isNotEmpty(positionMapItem) && ObjectUtil.isNotEmpty(updateReqVO.getFloorAreaJson())
                    && !updateReqVO.getFloorAreaJson().contains(positionMapItem.getPositionMapId())) {
                throw exception(ROBOT_PART_AREA);
            }
        }

        /*RemoteControllerInformationDO remoteControllerInformation = controllerInformationMapper.selectOne(new LambdaQueryWrapper<RemoteControllerInformationDO>()
                .eq(RemoteControllerInformationDO::getRobotNo, robotInformationDO.getRobotNo())
                .last("limit 1"));
        if (ObjectUtil.isNotEmpty(remoteControllerInformation)) {
            throw exception(ROBOT_DOING_REMOTE);
        }*/

        if (!updateReqVO.getRobotNo().equals(robotInformationDO.getRobotNo())
                || !updateReqVO.getMacAddress().equals(robotInformationDO.getMacAddress())) {
            List<RobotTaskDO> list = taskMapper.selectDoingTaskByRobotNo(robotInformationDO.getRobotNo());

            if (ObjectUtil.isNotEmpty(list)) {
                throw exception(ROBOT_HAVE_DOING_TASK);
            }
        }

        if (RobotStatusEnum.STOP_FREE.getType().equals(updateReqVO.getRobotStatus())
                || RobotStatusEnum.STOP_DOING.getType().equals(updateReqVO.getRobotStatus())) {
            updateReqVO.setRobotStatus(robotInformationDO.getRobotStatus());
        } else if (!updateReqVO.getRobotStatus().equals(robotInformationDO.getRobotStatus())) {
            str = str + " 修改车辆状态: " + RobotStatusEnum.getMsg(updateReqVO.getRobotStatus());
        }

        if (!updateReqVO.getRobotNo().equals(robotInformationDO.getRobotNo())) {
            RobotInformationDO existRobotInformationDO =
                    informationMapper.selectByRobotNoAndIdNotIn(updateReqVO.getRobotNo(), updateReqVO.getId());
            if (ObjectUtil.isNotEmpty(existRobotInformationDO)) {
                throw exception(ROBOT_ROBOT_NO_EXISTS);
            }
        }

        if (!updateReqVO.getMacAddress().equals(robotInformationDO.getMacAddress())) {
            RobotInformationDO existRobotInformationDO =
                    informationMapper.selectByMacAndIdNotIn(updateReqVO.getMacAddress(), updateReqVO.getId());
            if (ObjectUtil.isNotEmpty(existRobotInformationDO)) {
                throw exception(ROBOT_MAC_ADDRESS_EXISTS);
            }
        }

        //校验IP
        RobotInformationDO existRobotIp = informationMapper.selectOne(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotIp, updateReqVO.getRobotIp())
                .ne(RobotInformationDO::getId, updateReqVO.getId())
                .last("limit 1"));
        if (ObjectUtil.isNotEmpty(existRobotIp)) {
            throw exception(ROBOT_ROBOT_IP_EXISTS);
        }

        cameraService.deleteCameraByRobotNo(robotInformationDO.getRobotNo());

        if (ObjectUtil.isNotEmpty(updateReqVO.getCameraAddVOList())) {
            List<RobotCameraAddVO> cameraAddVOList = updateReqVO.getCameraAddVOList();
            cameraService.checkCameraExist(cameraAddVOList);
            cameraService.createCameraList(cameraAddVOList, updateReqVO.getRobotNo());
        }
        // 更新
        RobotInformationDO updateObj = BeanUtils.toBean(updateReqVO, RobotInformationDO.class);

        checkIpAndPort(updateObj);

        if (!updateReqVO.getRobotNo().equals(robotInformationDO.getRobotNo())) {
            updateRobotNo(robotInformationDO.getRobotNo(), updateReqVO.getRobotNo());
        }

        informationMapper.updateById(updateObj);


        // 更新AGV于地图关系表 -----
        List<List<Long>> list = CollectionUtils.compareLists(
                (CollUtil.isEmpty(robotInformationDO.getFloorAreaJson()) ? Collections.emptyList() : new ArrayList<>(robotInformationDO.getFloorAreaJson())),
                (CollUtil.isEmpty(updateReqVO.getFloorAreaJson()) ? Collections.emptyList() : new ArrayList<>(updateReqVO.getFloorAreaJson())),
                ObjectUtil::equal);
        informationMapAssociationService.saveOrUpdateOrDel(updateReqVO.getId(), list);
        redisUtil.set(RobotTaskChcheConstant.ROBOT_GET_ROBOTNO_BY_MAC + updateObj.getMacAddress(), updateObj.getRobotNo());
        redisUtil.set(RobotTaskChcheConstant.ROBOT_GET_MAC_BY_NO + updateObj.getRobotNo(), updateObj.getMacAddress());

        redisUtil.del(key);


        if (!robotInformationDO.getRobotTaskModel().equals(updateReqVO.getRobotTaskModel())
                && RobotTaskModelEnum.REJECTION.getType().equals(updateReqVO.getRobotTaskModel())) {
            str = ",且设置车辆锁定";
        } else if (!robotInformationDO.getRobotTaskModel().equals(updateReqVO.getRobotTaskModel())
                && RobotTaskModelEnum.NORMAL.getType().equals(updateReqVO.getRobotTaskModel())) {
            str = ",且设置车辆非锁定";
        }

        if (!robotInformationDO.getMacAddress().equals(updateReqVO.getMacAddress())
                || !robotInformationDO.getRobotNo().equals(updateReqVO.getRobotNo())) {
            //地图相关
            String floorAreaKey = RobotTaskChcheConstant.ROBOT_FLOOR_AREA + robotInformationDO.getMacAddress();
            Object o = redisUtil.get(floorAreaKey);
            if (ObjectUtil.isNotEmpty(o)) {
                FloorZoneDTO floorZone = JSON.parseObject((String) o, FloorZoneDTO.class);
                String oldFloorArea = floorZone.getFloor() + CommonConstant.SYMBOL + floorZone.getArea();
                redisUtil.hdel(oldFloorArea, robotInformationDO.getRobotNo());
                redisUtil.del(floorAreaKey);
                if (ObjectUtil.isNotEmpty(o)) {
                    String newFloorAreaKey = RobotTaskChcheConstant.ROBOT_FLOOR_AREA + updateReqVO.getMacAddress();
                    redisUtil.set(newFloorAreaKey, JSON.toJSONString(floorZone));
                    Map<String, Object> map = new HashMap<>();
                    String value = floorZone.getFloor() + CommonConstant.SYMBOL + floorZone.getArea();
                    map.put(updateReqVO.getRobotNo(), value);
                    redisUtil.hmset(value, map);
                }
            }
        }

        UserOperationLogSaveReqVO operationLog = UserOperationLogSaveReqVO.builder()
                .operateAction("更新车辆信息" + str)
                .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();
        userOperationLogService.createUserOperationLog(operationLog);

        sendRobotConfigToPP();
    }

    /**
     * 跟新车辆编号
     *
     * @param oldRobotNo
     * @param newRobotNo
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateRobotNo(String oldRobotNo, String newRobotNo) {

        deviceInformationMapper.updateRobotNo(oldRobotNo, newRobotNo);

        controllerInformationMapper.updateRobotNo(oldRobotNo, newRobotNo);

        RobotChargeLogDO robotChargeLog = chargeLogMapper.selectOne(new LambdaQueryWrapperX<RobotChargeLogDO>()
                .eq(RobotChargeLogDO::getRobotNo, oldRobotNo)
                .orderByDesc(RobotChargeLogDO::getCreateTime)
                .last("limit 1"));
        if (ObjectUtil.isNotEmpty(robotChargeLog)) {
            robotChargeLog.setRobotNo(newRobotNo);
            chargeLogMapper.updateById(robotChargeLog);
        }

        //这表暂时不改
//        robotWorkingHoursStatisticsMapper
        mapStopMapper.updateRobotNo(oldRobotNo, newRobotNo);

        moveToWaitMapper.updateRobotNo(oldRobotNo, newRobotNo);

        positionMapItemMapper.updateRobotNo(oldRobotNo, newRobotNo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteInformation(Long id) {
        // 校验存在
        validateInformationExists(id);
        RobotInformationDO robotInformationDO = informationMapper.selectById(id);

        RemoteControllerInformationDO remoteControllerInformation = controllerInformationMapper.selectOne(new LambdaQueryWrapper<RemoteControllerInformationDO>()
                .eq(RemoteControllerInformationDO::getRobotNo, robotInformationDO.getRobotNo())
                .last("limit 1"));
        if (ObjectUtil.isNotEmpty(remoteControllerInformation)) {
            throw exception(ROBOT_DOING_REMOTE);
        }

        cameraService.deleteCameraByRobotNo(robotInformationDO.getRobotNo());

        List<RobotTaskDO> list = taskMapper.selectDoingTaskByRobotNo(robotInformationDO.getRobotNo());

        if (ObjectUtil.isNotEmpty(list)) {
            throw exception(ROBOT_HAVE_DOING_TASK);
        }

        redisUtil.del(RobotTaskChcheConstant.ROBOT_GET_ROBOTNO_BY_MAC + robotInformationDO.getMacAddress());
        redisUtil.del(RobotTaskChcheConstant.ROBOT_GET_MAC_BY_NO + robotInformationDO.getRobotNo());

        UserOperationLogSaveReqVO operationLog = UserOperationLogSaveReqVO.builder()
                .operateAction("删除车辆 " + robotInformationDO.getRobotNo())
                .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();
        userOperationLogService.createUserOperationLog(operationLog);
        // 删除
        informationMapper.deleteById(id);

        PositionMapItemDO positionMapItem = positionMapItemMapper.selectOne(new LambdaQueryWrapper<PositionMapItemDO>()
                .eq(PositionMapItemDO::getRobotNo, robotInformationDO.getRobotNo())
                .eq(PositionMapItemDO::getType, PositionMapItemEnum.STOP.getType())
                .last("limit 1"));
        if (ObjectUtil.isNotEmpty(positionMapItem)) {
            positionMapItem.setRobotNo(null);
            positionMapItem.setUseStatus(UseStatusEnum.FREE.getType());
            positionMapItemMapper.updateById(positionMapItem);
        }

        //释放暂停记录
        releaseRobotStop(robotInformationDO);

        sendRobotConfigToPP();

        String pose2dKey = RobotTaskChcheConstant.ROBOT_INFORMATION_POSE_BAT + robotInformationDO.getMacAddress();
        redisUtil.del(pose2dKey);

        redisUtil.del(key);
        //地图相关
        String floorAreaKey = RobotTaskChcheConstant.ROBOT_FLOOR_AREA + robotInformationDO.getMacAddress();
        Object o = redisUtil.get(floorAreaKey);
        if (ObjectUtil.isNotEmpty(o)) {
            FloorZoneDTO floorZone = JSON.parseObject((String) o, FloorZoneDTO.class);
            String oldFloorArea = floorZone.getFloor() + CommonConstant.SYMBOL + floorZone.getArea();
            redisUtil.hdel(oldFloorArea, robotInformationDO.getRobotNo());
            redisUtil.del(floorAreaKey);
        }

        deleteRobot(robotInformationDO.getRobotNo());
    }

    private void deleteRobot(String robotNo) {
        deviceInformationMapper.clearRobotNo(robotNo);
        positionMapItemMapper.clearRobotNo(robotNo);
    }

    public void releaseRobotStop(RobotInformationDO robotInformationDO) {
        List<RobotMapStopDO> robotMapStops = mapStopMapper.selectList(new LambdaQueryWrapperX<RobotMapStopDO>()
                .eq(RobotMapStopDO::getRobotNo, robotInformationDO.getRobotNo()));
        if (ObjectUtil.isEmpty(robotMapStops)) {
            return;
        }
        RobotAcceptTaskDTO robotSimulationPose = new RobotAcceptTaskDTO();
        robotSimulationPose.setExecutionType(RobotCommandTypeEnum.CONTINUE.getType());
        List<String> robotNoList = Collections.singletonList(robotInformationDO.getRobotNo());
        sendEmergencyStopOrRecoveryToRobot(robotSimulationPose, robotNoList, "删除车辆时恢复车辆");
        mapStopMapper.deleteRobotMapStopByRobotNos(robotNoList);

        Long positionMapId = null;
        for (RobotMapStopDO robotMapStopDO : robotMapStops) {
            if (ObjectUtil.isNotEmpty(robotMapStopDO.getPositionMapId()) && robotMapStopDO.getPositionMapId() > 0) {
                positionMapId = robotMapStopDO.getPositionMapId();
            }
        }

        if (ObjectUtil.isNotEmpty(positionMapId)) {
            List<RobotMapStopDO> mapStopDOList = mapStopMapper.selectList(new LambdaQueryWrapperX<RobotMapStopDO>()
                    .eq(RobotMapStopDO::getPositionMapId, positionMapId));
            //说明此时地图上没有其他被暂停的车辆了
            if (ObjectUtil.isNotEmpty(mapStopDOList) && mapStopDOList.size() == 1) {
                PositionMapDO positionMapDO = positionMapMapper.selectById(positionMapId);
                mapStopMapper.deleteRobotMapStopByMapId(positionMapId);
                UserOperationLogSaveReqVO operationLogs = UserOperationLogSaveReqVO.builder()
                        .operateAction(positionMapDO.getFloor() + "层" + positionMapDO.getArea() + " 地图上所有车辆已经恢复,所以把地图也设置为恢复")
                        .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();
                userOperationLogService.createUserOperationLog(operationLogs);
            }
        }
    }

    private void validateInformationExists(Long id) {
        if (informationMapper.selectById(id) == null) {
            throw exception(ROBOT_INFORMATION_NOT_EXISTS);
        }
    }

    @Override
    public RobotInformationRespVO getInformation(Long id) {
        RobotInformationDO robotInformationDO = informationMapper.selectById(id);
        RobotInformationRespVO bean = BeanUtils.toBean(robotInformationDO, RobotInformationRespVO.class);
        setPositionMapList(bean);
        String pose2dKey = RobotTaskChcheConstant.ROBOT_INFORMATION_POSE_BAT + bean.getMacAddress();
        Object object = redisUtil.get(pose2dKey);
        RobotStatusDataPoseDTO robotStatusDataPoseDTO = JSONUtil.toBean((String) object, RobotStatusDataPoseDTO.class);

        String floorAreaKey = RobotTaskChcheConstant.ROBOT_FLOOR_AREA + robotInformationDO.getMacAddress();
        Object floorAreaObject = redisUtil.get(floorAreaKey);
        FloorZoneDTO floorZoneDTO = JSONUtil.toBean((String) floorAreaObject, FloorZoneDTO.class);
        if (ObjectUtil.isNotEmpty(object) && ObjectUtil.isNotEmpty(robotStatusDataPoseDTO)) {
            String soc = getSocByMac(robotInformationDO.getMacAddress(), ZeroOneEnum.ONE.getType());
            if (ObjectUtil.isNotEmpty(soc)) {
                bean.setElectricity(soc);
            }
            bean.setFloor(floorZoneDTO.getFloor());
            bean.setArea(floorZoneDTO.getArea());
        }

        List<RobotCameraAddVO> cameraAddVOList = cameraService.getCameraByRobotNo(robotInformationDO.getRobotNo());
        bean.setCameraAddVOList(cameraAddVOList);

        List<RobotMapStopDO> stopRobotNoByRobotNo = mapStopService.getStopRobotNoByRobotNo(robotInformationDO.getRobotNo());
        if (ObjectUtil.isEmpty(stopRobotNoByRobotNo)) {
            return bean;
        } else if (RobotStatusEnum.STAND_BY.getType().equals(robotInformationDO.getRobotStatus())) {
            robotInformationDO.setRobotStatus(RobotStatusEnum.STOP_FREE.getType());
        } else {
            robotInformationDO.setRobotStatus(RobotStatusEnum.STOP_DOING.getType());
        }

        return bean;
    }

    public void setPositionMapList(RobotInformationRespVO bean) {
        Set<Long> ids = bean.getFloorAreaJson();
        if (ObjectUtil.isNotEmpty(ids)) {
            List<PositionMapDO> list = positionMapMapper.selectByIds(ids);
            List<RobotPositionMapVO> targetList = BeanUtil.copyToList(list, RobotPositionMapVO.class);
            bean.setPositionMapList(targetList);
        }
    }

    @Override
    public PageResult<RobotInformationPageRespVO> getInformationPage(RobotInformationPageReqVO pageReqVO) {
        List<RobotInformationDO> list = informationMapper.selectList(new LambdaQueryWrapperX<RobotInformationDO>()
                .eqIfPresent(RobotInformationDO::getRobotModelId, pageReqVO.getRobotModelId())
                .eqIfPresent(RobotInformationDO::getRobotModelNumber, pageReqVO.getRobotModelNumber())
                .likeIfPresent(RobotInformationDO::getRobotNo, pageReqVO.getRobotNo())
                .eqIfPresent(RobotInformationDO::getRobotTaskModel, pageReqVO.getRobotTaskModel())
                .eqIfPresent(RobotInformationDO::getMacAddress, pageReqVO.getMacAddress())
                .eqIfPresent(RobotInformationDO::getRobotStatus, pageReqVO.getRobotStatus())
                .betweenIfPresent(RobotInformationDO::getCreateTime, pageReqVO.getCreateTime())
                .orderByDesc(RobotInformationDO::getId));

        PageResult<RobotInformationPageRespVO> dataPage = new PageResult<>();
        Long total = ObjectUtil.isEmpty(list) ? 0L : list.size();
        dataPage.setTotal(total);
        List<RobotInformationPageRespVO> targetList = BeanUtil.copyToList(list, RobotInformationPageRespVO.class);

        List<RobotMapStopDO> allRobotMapStops = mapStopService.getAllRobotMapStops(ZeroOneEnum.ONE.getType());
        List<String> stopRobotNos = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(allRobotMapStops)) {
            stopRobotNos = allRobotMapStops.stream().map(RobotMapStopDO::getRobotNo).collect(Collectors.toList());
        }

        for (RobotInformationPageRespVO v : targetList) {
            setMsgAndRobotStatus(v, stopRobotNos);
        }

        if (ObjectUtil.isNotEmpty(pageReqVO.getRobotStatisticsType())) {
            List<RobotInformationPageRespVO> resultList = new ArrayList<>();
            for (RobotInformationPageRespVO v : targetList) {

                String cargoDetectedKey = RobotTaskChcheConstant.ROBOT_CARGO_DETECTED + v.getMacAddress();
                Object cargoDetected = redisUtil.get(cargoDetectedKey);
                if (ObjectUtil.isNotEmpty(cargoDetected) && RobotStatusCodeConstant.CARGO_DETECTED.equals(Boolean.parseBoolean(String.valueOf(cargoDetected)))) {
                    //说明取货完成
                    v.setCargoDetected("有货");
                } else {
                    v.setCargoDetected("空车");
                }
                if (RobotStatisticsTypeEnum.STANDBY.getType().equals(pageReqVO.getRobotStatisticsType())
                        && v.getRobotTaskStatus().equals(0)) {
                    resultList.add(v);
                    continue;
                }

                if (RobotStatisticsTypeEnum.INTASK.getType().equals(pageReqVO.getRobotStatisticsType())
                        && v.getRobotTaskStatus().equals(1)) {
                    resultList.add(v);
                    continue;
                }

                if (RobotStatisticsTypeEnum.CHARGE.getType().equals(pageReqVO.getRobotStatisticsType())
                        && v.getRobotTaskStatus().equals(2)) {
                    resultList.add(v);
                    continue;
                }

                if (RobotStatisticsTypeEnum.DOLOCK.getType().equals(pageReqVO.getRobotStatisticsType())
                        && v.getRobotTaskModel().equals(0)) {
                    resultList.add(v);
                    continue;
                }

                if (RobotStatisticsTypeEnum.OFFLINE.getType().equals(pageReqVO.getRobotStatisticsType())
                        && v.getOnlineStatus().equals(0)) {
                    resultList.add(v);
                    continue;
                }

                if (RobotStatisticsTypeEnum.FAULT.getType().equals(pageReqVO.getRobotStatisticsType())
                        && v.getRobotEssenceStatus().equals(2)) {
                    resultList.add(v);
                }
            }
            dataPage.setList(resultList);
        } else {
            dataPage.setList(targetList);
        }

        return dataPage;
    }

    /**
     * 设置状态和信息
     *
     * @param v
     */
    private void setMsgAndRobotStatus(RobotInformationPageRespVO v, List<String> stopRobotNos) {

        if (ObjectUtil.isNotEmpty(stopRobotNos) && stopRobotNos.contains(v.getRobotNo())) {
            v.setIsStop(ZeroOneEnum.ONE.getType());
        }

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

        String robotDoingActionKey = RobotTaskChcheConstant.ROBOT_QUERY_DOING_ACTION + v.getMacAddress();
        Object action = redisUtil.get(robotDoingActionKey);

        String floorAreaKey = RobotTaskChcheConstant.ROBOT_FLOOR_AREA + v.getMacAddress();
        Object floorAreaObject = redisUtil.get(floorAreaKey);
        FloorZoneDTO floorZoneDTO = JSONUtil.toBean((String) floorAreaObject, FloorZoneDTO.class);

        if (ObjectUtil.isNotEmpty(object) && ObjectUtil.isNotEmpty(robotStatusDataPoseDTO)) {
            String soc = getSocByMac(v.getMacAddress(), ZeroOneEnum.ONE.getType());
            if (ObjectUtil.isNotEmpty(soc)) {
                v.setElectricity(soc);
            }
            v.setFloor(floorZoneDTO.getFloor());
            v.setArea(floorZoneDTO.getArea());
            v.setOnlineStatus(1);
        }

        v.setRobotEssenceStatus(v.getRobotTaskModel());
        //设置异常
        String errorLevelKey = RobotTaskChcheConstant.ROBOT_ERROR_LEVEL + v.getMacAddress();
        Object errorLevel = redisUtil.get(errorLevelKey);
        if (ObjectUtil.isNotEmpty(errorLevel)) {
            v.setRobotEssenceStatus(2);
        }

        if (ObjectUtil.isNotEmpty(errorLevel)) {
            v.setRobotCodeLevel(Integer.valueOf(errorLevel.toString()).intValue());
        }

        String errorMsgKey = RobotTaskChcheConstant.ROBOT_ERROR_MSG + v.getMacAddress();
        Object errorMsg = redisUtil.get(errorMsgKey);
        if (ObjectUtil.isNotEmpty(errorMsg)) {
            v.setRobotCodeMsg(errorMsg.toString());
        }

        if (RobotStatusEnum.STAND_BY.getType().equals(v.getRobotStatus())) {
            //待命中
            v.setRobotTaskStatus(0);
            v.setMsg("车辆正在待命中");
        } else if (RobotStatusEnum.CHARGE.getType().equals(v.getRobotStatus())) {
            //充电中
            v.setRobotTaskStatus(2);
            v.setMsg("车辆正在充电");
        } else {
            //任务中
            v.setRobotTaskStatus(1);
            if (RobotStatusEnum.DOING.getType().equals(v.getRobotStatus()) && ObjectUtil.isNotEmpty(action)) {
                v.setRobotStatus(1);
                v.setMsg(String.valueOf(action));
            }
        }

    }

    /**
     * 统计车辆待命/任务中/离线
     *
     * @return
     */
    @Override
    public RobotInformationStatisticsVO statisticsInformation() {
        RobotInformationStatisticsVO info = new RobotInformationStatisticsVO();

        RobotInformationDO query = new RobotInformationDO();
        List<RobotInformationDO> existRobot = informationMapper.queryAllByLimit(query);
        if (ObjectUtil.isEmpty(existRobot)) {
            return info;
        }
        Integer standby = 0;
        Integer inTask = 0;
        Integer doLock = 0;
        Integer offline = 0;
        Integer charge = 0;
        Integer fault = 0;
        //0：待命、1：任务中、2：锁定、3：离线、4:充电中、5:故障
        for (RobotInformationDO v : existRobot) {
            String pose2dKey = RobotTaskChcheConstant.ROBOT_INFORMATION_POSE_BAT + v.getMacAddress();
            Object object = redisUtil.get(pose2dKey);

            if (ObjectUtil.isEmpty(object)) {
                //离线
                offline++;
            }

            if (RobotTaskModelEnum.REJECTION.getType().equals(v.getRobotTaskModel())) {
                //锁定
                doLock++;
            }
            //设置异常
            String errorLevelKey = RobotTaskChcheConstant.ROBOT_ERROR_LEVEL + v.getMacAddress();
            Object errorLevel = redisUtil.get(errorLevelKey);
            if (ObjectUtil.isNotEmpty(errorLevel) && Integer.valueOf(errorLevel.toString()).intValue() >= 3) {
                fault++;
            }

            if (RobotStatusEnum.STAND_BY.getType().equals(v.getRobotStatus())) {
                //待命中
                standby++;
            } else if (RobotStatusEnum.CHARGE.getType().equals(v.getRobotStatus())) {
                //充电中
                charge++;
            } else if (RobotStatusEnum.DOING.getType().equals(v.getRobotStatus())) {
                //任务中
                inTask++;
            }
        }
        info.setStandby(standby);
        info.setInTask(inTask);
        info.setDoLock(doLock);
        info.setOffline(offline);
        info.setCharge(charge);
        info.setFault(fault);
        info.setTotal(existRobot.size());

        return info;
    }

    /**
     * @param str  type 1:是mac， 0 :是车辆编号
     * @param type
     * @return
     */
    @Override
    public String getSocByMac(String str, Integer type) {
        if (ZeroOneEnum.ZERO.getType().equals(type)) {
            str = getMacByRobotNo(str);
        }
        String socKey = RobotTaskChcheConstant.ROBOT_INFORMATION_SOC + str;
        Object o = redisUtil.get(socKey);
        if (ObjectUtil.isEmpty(o)) {
            return null;
        }
        return o.toString();
    }

    @Override
    public List<RobotInfoStatusVO> getAGVListStatusInfo() {
        List<RobotInfoStatusVO> list = new ArrayList<>();
        // -- 先从缓存中 获取AGV状态信息 - 如果没有的话 - 再从数据库中获取 - 并且缓存到redis中
        List<RobotInformationDO> robotInformationDOS = informationMapper.selectList();
        if (CollUtil.isEmpty(robotInformationDOS)) {
            return list;
        }
        for (RobotInformationDO informationDO : robotInformationDOS) {
            RobotInfoStatusVO vo = new RobotInfoStatusVO();
            vo.setMacAddress(informationDO.getMacAddress());
            //0：待命、1：任务中、2：锁定、3：离线、4:充电中、5:故障
            String pose2dKey = RobotTaskChcheConstant.ROBOT_INFORMATION_POSE_BAT + informationDO.getMacAddress();
            Object object = redisUtil.get(pose2dKey);
            if (ObjectUtil.isEmpty(object)) {
                //离线
                vo.setOffline(1);
            }
            if (RobotTaskModelEnum.REJECTION.getType().equals(informationDO.getRobotTaskModel())) {
                //锁定
                vo.setDoLock(1);
            }
            //设置异常
            String errorLevelKey = RobotTaskChcheConstant.ROBOT_ERROR_LEVEL + informationDO.getMacAddress();
            Object errorLevel = redisUtil.get(errorLevelKey);
            if (ObjectUtil.isNotEmpty(errorLevel) && Integer.parseInt(errorLevel.toString()) >= 3) {
                vo.setFault(1);
            }
            if (RobotStatusEnum.STAND_BY.getType().equals(informationDO.getRobotStatus())) {
                //待命中
                vo.setStandby(1);
            } else if (RobotStatusEnum.CHARGE.getType().equals(informationDO.getRobotStatus())) {
                //充电中
                vo.setCharge(1);
            } else {
                //任务中
                vo.setInTask(1);
            }
            list.add(vo);
        }
        return list;
    }

    /**
     * @return
     */
    @Override
    public List<RobotInformationDO> getCanUseRobot() {
        RobotInformationDO query = new RobotInformationDO();
        query.setRobotTaskModel(RobotTaskModelEnum.NORMAL.getType());
        return informationMapper.queryAllByLimit(query);
    }

    @Override
    public List<RobotInformationDO> getAllRobot() {
        return informationMapper.selectList(new LambdaQueryWrapper<RobotInformationDO>());
    }

    /**
     * 根据mac查询机器人编号
     *
     * @param mac
     * @return
     */
    @Override
    public String getRobotNoByMac(String mac) {
        if (ObjectUtil.isEmpty(mac)) {
            return "";
        }
        String robotNo = (String) redisUtil.get(RobotTaskChcheConstant.ROBOT_GET_ROBOTNO_BY_MAC + mac);

        if (ObjectUtil.isNotEmpty(robotNo)) {
            return robotNo;
        }

        RobotInformationDO robotInformationDO = informationMapper.selectOne(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getMacAddress, mac)
                .last("limit 1"));
        if (ObjectUtil.isNotEmpty(robotInformationDO)) {
            redisUtil.set(RobotTaskChcheConstant.ROBOT_GET_ROBOTNO_BY_MAC + mac, robotInformationDO.getRobotNo());
            return robotInformationDO.getRobotNo();
        }
        return "";
    }

    @Override
    public void test(RobotStatusDTO robotStatusDataDTO) {
        TenantContextHolder.setTenantId(1L);
        if (ObjectUtil.isEmpty(robotStatusDataDTO) || ObjectUtil.isEmpty(robotStatusDataDTO.getMac())) {
            return;
        }
        // -- 通过mac 地址获取车辆信息 - (并且加入到缓存中)
        Map<String, RobotInformationVO> robotInformationVOS = this.getAllRobotByRedis();
        RobotInformationVO robotInformationVO = robotInformationVOS.get(robotStatusDataDTO.getMac());
        if (robotInformationVO == null) {
            robotInformationVO = this.getRobotByRedis(robotStatusDataDTO.getMac());
        }

        RobotStatusDataPoseDTO robotStatusDataPoseDTO = new RobotStatusDataPoseDTO();
        robotStatusDataPoseDTO.setX(robotStatusDataDTO.getData().getPose2d().getX());
        robotStatusDataPoseDTO.setY(robotStatusDataDTO.getData().getPose2d().getY());
        robotStatusDataPoseDTO.setYaw(robotStatusDataDTO.getData().getPose2d().getYaw());
        robotStatusDataPoseDTO.setFloor(robotStatusDataDTO.getData().getFloor_zone().getFloor());
        robotStatusDataPoseDTO.setArea(robotStatusDataDTO.getData().getFloor_zone().getArea());
//        robotStatusDataPoseDTO.setBatSoc(robotStatusDataDTO.getData().getPose2d().getBatSoc());

        robotInformationVO.setPose2d(robotStatusDataPoseDTO);

        // 模拟请求
        processor.handleRequest(robotStatusDataDTO.getData().getFloor_zone().getFloor() + CommonConstant.SYMBOL + robotStatusDataDTO.getData().getFloor_zone().getArea(),
                robotStatusDataDTO.getMac(), JSONUtil.toJsonStr(robotInformationVO));

    }

    @Override
    public Map<String, RobotInformationVO> getAllRobotByRedis() {
        Object o = redisUtil.get(key);
        if (ObjectUtil.isNotEmpty(o)) {
            // 化作列表
            List<RobotInformationVO> list = JSONUtil.toList(o.toString(), RobotInformationVO.class);
            return list.stream().collect(Collectors.toMap(RobotInformationVO::getMacAddress, Function.identity(), (v1, v2) -> v1));
        } else {
            List<RobotInformationDO> allRobotInfoList = this.list();
            List<RobotInformationVO> list = BeanUtil.copyToList(allRobotInfoList, RobotInformationVO.class);
            redisUtil.set(key, JSONUtil.toJsonStr(list));
            return list.stream().collect(Collectors.toMap(RobotInformationVO::getMacAddress, Function.identity(), (v1, v2) -> v1));
        }
    }

    @Override
    public RobotInformationVO getRobotByRedis(String macAddress) {
        String addressKey = RobotTaskChcheConstant.ROBOT_ADDRESS + macAddress;
        if (ObjectUtil.isNotEmpty(redisUtil.get(addressKey))) {
            return new RobotInformationVO();
        }
        // todo -- 如果说有一个设备的mac 一直都不在库里 会出现缓存穿透的问题 - 后续如果出现这种情况的话 - 考虑将null值也存入到缓存中
        RobotInformationDO item = this.getOne(new LambdaQueryWrapper<RobotInformationDO>().eq(RobotInformationDO::getMacAddress, macAddress));
        if (ObjectUtil.isEmpty(item)) {
            redisUtil.set(addressKey, macAddress, 30 * 1000);
        }
        // -- 将获取到的存入到redis中
        if (ObjectUtil.isNotEmpty(item)) {
            RobotInformationVO vo = BeanUtil.toBean(item, RobotInformationVO.class);
            List<RobotInformationVO> list = new ArrayList<>();
            Object o = redisUtil.get(key);
            if (ObjectUtil.isNotEmpty(o)) {
                list = JSONUtil.toList(o.toString(), RobotInformationVO.class);
            }
            list.add(vo);
            redisUtil.set(key, JSONUtil.toJsonStr(list));
            return vo;
        }
        return new RobotInformationVO();
    }

    @Override
    public String getMacByRobotNo(String robotNo) {
        if (ObjectUtil.isEmpty(robotNo)) {
            return "";
        }
        String mac = (String) redisUtil.get(RobotTaskChcheConstant.ROBOT_GET_MAC_BY_NO + robotNo);

        if (ObjectUtil.isNotEmpty(mac)) {
            return mac;
        }

        RobotInformationDO robotInformationDO = informationMapper.selectOne(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotNo, robotNo)
                .last("limit 1"));
        if (ObjectUtil.isNotEmpty(robotInformationDO)) {
            redisUtil.set(RobotTaskChcheConstant.ROBOT_GET_MAC_BY_NO + robotNo, robotInformationDO.getMacAddress());
            return robotInformationDO.getMacAddress();
        }
        return "";
    }

    @Override
    public List<RobotInformationRespVO> getAllRobotAndErrorMsg() {
        RobotInformationDO query = new RobotInformationDO();
        List<RobotInformationDO> robotInformationDOS = informationMapper.queryAllByLimit(query);
        if (ObjectUtil.isEmpty(robotInformationDOS)) {
            return new ArrayList<>();
        }

        List<RobotInformationRespVO> list = BeanUtils.toBean(robotInformationDOS, RobotInformationRespVO.class);
        for (RobotInformationRespVO v : list) {
            String errorMsgKey = RobotTaskChcheConstant.ROBOT_ERROR_MSG + v.getMacAddress();
            Object errorMsg = redisUtil.get(errorMsgKey);
            if (ObjectUtil.isNotEmpty(errorMsg)) {
                v.setErrorMsg(String.valueOf(errorMsg));
            }
        }

        return list;
    }

    @Override
    public List<RobotInformationDO> getListByMapId(Long mapId) {
        return informationMapper.getListByMapId(mapId);
    }

    @Override
    public RobotInformationDO getRobotInformationByDetailId(String taskDetailId) {
        RobotInformationDO robotInformationDO = informationMapper.selectOne(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getTaskDetailId, taskDetailId)
                .last("limit 1"));
        return robotInformationDO;
    }

    @Override
    public void rcsHeartBeat() {
        RobotInformationDO query = new RobotInformationDO();
        List<RobotInformationDO> robotInformations = informationMapper.queryAllByLimit(query);
        for (RobotInformationDO robotInformationDO : robotInformations) {
            String topic = RobotTopicConstant.RCS_HEART_BEAT + robotInformationDO.getMacAddress();
//            commonApi.commonMethod(new RobotRcsHeartBeatDTO(), topic);
            mqttUtils.pub(topic, JSON.toJSONString(new RobotRcsHeartBeatDTO()));
        }
    }

    /**
     * 清除交管
     *
     * @param robotNo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cleanTrafficManagement(String robotNo) {
        UserOperationLogSaveReqVO operationLog = UserOperationLogSaveReqVO.builder()
                .operateAction("清除交管 " + robotNo)
                .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();
        userOperationLogService.createUserOperationLog(operationLog);
        RobotTaskDetailActionLogDO log = closeTask(robotNo);
        if (ObjectUtil.isEmpty(log)) {
            return;
        }
        RobotTaskDetailDO robotTaskDetail = taskDetailMapper.selectById(log.getTaskDetailId());
        //移动、充电任务需手动关闭
        if (PathTaskTypeEnum.CHARGE.getType().equals(log.getCommandType())
                || PathTaskTypeEnum.AUTO_CHARGE.getType().equals(log.getCommandType())) {
            deviceInformationMapper.setDeviceReleaseByLastUser(robotNo, DeviceUseStatusEnum.IDLE.getType());
        } else if (PathTaskTypeEnum.MOVE_TO_WAIT_STOP.getType().equals(log.getCommandType())) {
            if (ObjectUtil.isNotEmpty(robotTaskDetail)) {
                positionMapItemService.setMapItemIdle(robotTaskDetail.getToLocationId());
            }
        } else if (PathTaskTypeEnum.MOVE_TO_WAIT.getType().equals(log.getCommandType())) {
            if (ObjectUtil.isNotEmpty(robotTaskDetail.getReleaseId())) {
                positionMapItemService.setMapItemIdle(robotTaskDetail.getReleaseId());
            }
        } else if (PathTaskTypeEnum.TAKE_RELEASE.getType().equals(log.getCommandType())
                || PathTaskTypeEnum.TAKE.getType().equals(log.getCommandType())
                || PathTaskTypeEnum.RELEASE.getType().equals(log.getCommandType())) {
            takeReleaseCleanTrafficManagement(robotTaskDetail);
        }

        RobotInformationDO robotInformationDO = informationMapper.selectOne(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotNo, robotNo)
                .last("limit 1"));
        robotInformationDO.setRobotStatus(RobotStatusEnum.STAND_BY.getType());
//        robotInformationDO.setRobotTaskModel(RobotTaskModelEnum.REJECTION.getType());
        informationMapper.updateById(robotInformationDO);
    }

    public RobotTaskDetailActionLogDO closeTask(String robotNo) {
        CleanAgvDTO build = CleanAgvDTO.builder().robotNo(robotNo).build();
//        commonApi.commonMethod(build, PathPlanningTopicConstant.CLEAN_AGV);
        mqttUtils.pub(PathPlanningTopicConstant.CLEAN_AGV, JSON.toJSONString(build));
        RobotTaskDetailActionLogDO log = taskDetailActionLogService.getLastTaskByRobotNo(robotNo, CommandIdEnum.TASK.getType());
        if (ObjectUtil.isEmpty(log) || ActionStatusEnum.DONE.getType().equals(log.getActionStatus())
                || ActionStatusEnum.CLOSE.getType().equals(log.getActionStatus())) {
            return null;
        }
        String mac = getMacByRobotNo(robotNo);
        robotCloseTaskDetail(log.getTaskDetailId() + "", mac, log.getCommandType());
        return log;
    }

    /**
     * 恢复任务
     *
     * @param robotNo
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doTaskContinue(String robotNo, Boolean isRobot) {

        RobotInformationDO robotInformationDO = informationMapper.selectOne(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotNo, robotNo)
                .last("limit 1"));
        if (ObjectUtil.isEmpty(robotInformationDO.getTaskDetailId())) {
            throw exception(ROBOT_LAST_TASK_NO_EXISTS);
        }

        RobotTaskDetailActionLogDO actionLog = taskDetailActionLogService.getLastActionLogByDetailId(robotInformationDO.getTaskDetailId());
        if (ObjectUtil.isEmpty(actionLog)) {
            throw exception(ROBOT_LAST_TASK_NO_EXISTS);
        }

        if (!actionLog.getRobotNo().equals(actionLog.getOriginalRobotNo())) {
            throw exception0(TASK_ASSIGN_OTHER_ROBOT.getCode(), TASK_ASSIGN_OTHER_ROBOT.getMsg() + actionLog.getRobotNo(),
                    TASK_ASSIGN_OTHER_ROBOT.getMsg() + actionLog.getRobotNo());
        }

        if (RobotTaskModelEnum.REJECTION.getType().equals(robotInformationDO.getRobotTaskModel())) {
            throw exception(ROBOT_REJECTION);
        }

        String mac = getMacByRobotNo(robotNo);
        robotCloseTaskDetail(actionLog.getTaskDetailId() + "", mac, actionLog.getCommandType());

        checkTaskDone(actionLog.getTaskDetailId() + "");

//        CleanAgvDTO build = CleanAgvDTO.builder().robotNo(robotNo).build();
//        commonApi.commonMethod(build, PathPlanningTopicConstant.CLEAN_AGV);
//        mqttUtils.pub(PathPlanningTopicConstant.CLEAN_AGV, JSON.toJSONString(build));

        RobotClosePathPlantingDTO closePathPlanting = RobotClosePathPlantingDTO.builder()
                .robotNo(robotNo)
                .id(actionLog.getTaskDetailId() + "")
                .build();
//        commonApi.commonMethod(closePathPlanting, PathPlanningTopicConstant.KILL_TASK);
        mqttUtils.pub(PathPlanningTopicConstant.KILL_TASK, JSON.toJSONString(closePathPlanting));

        Object o = checkTaskDetailExist(actionLog.getTaskDetailId());

        /*String taskStatusKey = RobotTaskChcheConstant.ROBOT_TASK_STATUS + robotInformationDO.getMacAddress();
        Object taskStatus = redisUtil.get(taskStatusKey);
        if (ObjectUtil.isEmpty(taskStatus) || !RobotStatusCodeConstant.CAN_DO_TASK.equals(Boolean.parseBoolean(String.valueOf(taskStatus)))) {
            log.info("车机上报不允许接任务 :{}", robotNo);
            throw exception(ROBOT_DO_TASK_FAIL);
        }*/

        TaskToPathPlanningDTO pathPlanning = JSONUtil.toBean((String) o, TaskToPathPlanningDTO.class);

        if (isRobot) {
            resendToPP(pathPlanning, actionLog, robotInformationDO);
        } else {
            RLock lock = redissonUtils.getLock(RobotCacheLockEnum.ROBOT_TASK_DISTRIBUTE_LOCK.getKey());
            if (lock.tryLock()) {
                try {
                    resendToPP(pathPlanning, actionLog, robotInformationDO);
                } finally {
                    lock.unlock();
                }
            } else {
                log.info("下发任务给路径规划未获取到锁");
                throw exception(REDISSON_NOT_OBTAIN_LOCK);
            }
        }

        UserOperationLogSaveReqVO operationLog = UserOperationLogSaveReqVO.builder()
                .operateAction("恢复任务 " + robotNo)
                .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();
        userOperationLogService.createUserOperationLog(operationLog);
    }

    /**
     * 校验任务是否还存在缓存
     *
     * @param taskDetailId
     * @return
     */
    public Object checkTaskDetailExist(Long taskDetailId) {
        String key = PathPlanningChcheConstant.PATH_PLANNING_TASK + taskDetailId;
        Object o = redisUtil.get(key);
        if (ObjectUtil.isEmpty(o)) {
            throw exception(ROBOT_LAST_TASK_DELETE);
        }
        return o;
    }

    /**
     * 将任务重新发给PP
     *
     * @param pathPlanning
     * @param actionLog
     */
    private void resendToPP(TaskToPathPlanningDTO pathPlanning, RobotTaskDetailActionLogDO actionLog, RobotInformationDO robotInformationDO) {
        TaskRobotNoLimittationAreaDTO taskRobotNoLimittationAreaDTO =
                robotPathPlanningService.getRobotNoLimitationArea(Collections.singletonList(robotInformationDO)).get(0);

        List<TaskRobotNoLimittationAreaDTO> robotNoLimitions = Collections.singletonList(taskRobotNoLimittationAreaDTO);
        pathPlanning.setRobotNoLimitationAreaDTOS(robotNoLimitions);

        resendToPPData(pathPlanning, actionLog, robotInformationDO, false);

        List<TaskToPathPlanningDTO> pathPlanningList = new ArrayList<>();
        pathPlanningList.add(pathPlanning);

        String plantingKey = PathPlanningChcheConstant.PATH_PLANNING_TASK + pathPlanning.getOrderId();
        redisUtil.set(plantingKey, JSON.toJSONString(pathPlanning), znConfigConstant.getTask().getTaskCacheTime());

        log.info("111111恢复任务下发给PP :{}", JSON.toJSONString(pathPlanningList));
        mqttUtils.pub(PathPlanningTopicConstant.TASK_ASSIGNMENT_REQUEST, JSON.toJSONString(pathPlanningList));
    }

    private void resendToPPData(TaskToPathPlanningDTO pathPlanning, RobotTaskDetailActionLogDO actionLog, RobotInformationDO robotInformationDO, Boolean isRemote) {
        if (PathTaskTypeEnum.MOVE_TO_WAIT.getType().equals(actionLog.getCommandType())
                || PathTaskTypeEnum.MOVE_TO_WAIT_STOP.getType().equals(actionLog.getCommandType())) {
            moveToWaitTask(pathPlanning, robotInformationDO.getRobotNo());
        } else if (PathTaskTypeEnum.CHARGE.getType().equals(actionLog.getCommandType())
                || PathTaskTypeEnum.AUTO_CHARGE.getType().equals(actionLog.getCommandType())) {
            chargeTask(pathPlanning, robotInformationDO);
        } else if (PathTaskTypeEnum.TAKE_RELEASE.getType().equals(actionLog.getCommandType())) {
            takeReleaseTask(pathPlanning, isRemote);
        } else if (PathTaskTypeEnum.TAKE.getType().equals(actionLog.getCommandType())) {
            takeTask(pathPlanning, isRemote);
        } else if (PathTaskTypeEnum.RELEASE.getType().equals(actionLog.getCommandType())) {
            releaseTask(pathPlanning);
        } else if (PathTaskTypeEnum.MOVE.getType().equals(actionLog.getCommandType())
                || PathTaskTypeEnum.MOVE_TO_POINT.getType().equals(actionLog.getCommandType())) {

        }
    }

    /**
     * 仅放货
     *
     * @param pathPlanning
     */
    private void releaseTask(TaskToPathPlanningDTO pathPlanning) {
//        RobotTaskDetailDO robotTaskDetail = checkTaskDone(pathPlanning.getOrderId());
//        releaseCheck(robotTaskDetail.getToLocationId(), robotTaskDetail.getRobotTaskId());
    }

    /**
     * 仅取货
     *
     * @param pathPlanning
     */
    private void takeTask(TaskToPathPlanningDTO pathPlanning, Boolean isRemote) {
//        RobotTaskDetailDO robotTaskDetail = checkTaskDone(pathPlanning.getOrderId());
//        takeCheck(robotTaskDetail.getFromLocationId(), robotTaskDetail.getRobotTaskId(), isRemote);
    }

    public RobotTaskDetailDO checkTaskDone(String id) {
        RobotTaskDetailDO robotTaskDetail = taskDetailMapper.selectById(id);
        if (RobotTaskStageEnum.DONE.getType().equals(robotTaskDetail.getTaskStage())
                || RobotTaskStageEnum.MANUALLY_COMPLETED.getType().equals(robotTaskDetail.getTaskStage())) {
            throw exception(ROBOT_LAST_TASK_NO_EXISTS);
        }
        return robotTaskDetail;
    }

    /**
     * 取放货
     *
     * @param pathPlanning
     */
    private void takeReleaseTask(TaskToPathPlanningDTO pathPlanning, Boolean isRemote) {

        RobotTaskDetailDO robotTaskDetail = checkTaskDone(pathPlanning.getOrderId());

        if (RobotTaskStageEnum.UN_START.getType().equals(robotTaskDetail.getTaskStage())
                || RobotTaskStageEnum.GO_TAKE.getType().equals(robotTaskDetail.getTaskStage())
                || RobotTaskStageEnum.TAKEING_EXCEPTION.getType().equals(robotTaskDetail.getTaskStage())
                || RobotTaskStageEnum.TAKEING.getType().equals(robotTaskDetail.getTaskStage())) {
            return;
        }

        String robotNo = pathPlanning.getRobotNoLimitationAreaDTOS().get(0).getRobotNo();
        String mac = getMacByRobotNo(robotNo);
        String cargoDetectedKey = RobotTaskChcheConstant.ROBOT_CARGO_DETECTED + mac;
        Object cargoDetected = redisUtil.get(cargoDetectedKey);
        if (ObjectUtil.isNotEmpty(cargoDetected) && RobotStatusCodeConstant.CARGO_DETECTED.equals(Boolean.parseBoolean(String.valueOf(cargoDetected)))) {
            //说明取货完成
            takeDone(pathPlanning, robotTaskDetail);
        } else if (RobotTaskStageEnum.GO_RELEASE.getType().equals(robotTaskDetail.getTaskStage())
                || RobotTaskStageEnum.RELEASEING.getType().equals(robotTaskDetail.getTaskStage())
                || RobotTaskStageEnum.RELEASEING_EXCEPTION.getType().equals(robotTaskDetail.getTaskStage())) {
            takeDone(pathPlanning, robotTaskDetail);
        } else if (RobotTaskStageEnum.CLOSE.getType().equals(robotTaskDetail.getTaskStage())
                || RobotTaskStageEnum.EXCEPTION.getType().equals(robotTaskDetail.getTaskStage())) {
            WareHouseLocationDO wareHouseLocation = wareHouseLocationMapper.selectById(robotTaskDetail.getFromLocationId());
            if (ZeroOneEnum.ZERO.getType().equals(wareHouseLocation.getLocationUseStatus())) {
                takeDone(pathPlanning, robotTaskDetail);
            }
        }


        /*else {
            takeCheck(robotTaskDetail.getFromLocationId(), robotTaskDetail.getRobotTaskId(), isRemote);
        }
        releaseCheck(robotTaskDetail.getToLocationId(), robotTaskDetail.getRobotTaskId());*/
    }

    public void takeDone(TaskToPathPlanningDTO pathPlanning, RobotTaskDetailDO robotTaskDetail) {
        pathPlanning.setTakeLevel(null);
        pathPlanning.setTakeGroupId(null);
        pathPlanning.setTakeLocationNumber(null);
        pathPlanning.setTakePointId(null);
        pathPlanning.setTakeOffsetHeight(null);
        pathPlanning.setTakeOffsetHeight(null);
        pathPlanning.setTaskType(PathTaskTypeToRobotEnum.DROP_OFF_GOODS.getType());
        if(ReleaseTakeEnum.isLocation(robotTaskDetail.getTakeType())) {
            WareHouseLocationDO wareHouseLocation = wareHouseLocationMapper.selectById(robotTaskDetail.getFromLocationId());
            if (wareHouseLocation.getTaskId().equals(robotTaskDetail.getTakeId()) && LocationLockEnum.NO.getType().equals(wareHouseLocation.getLocationLock())) {
                wareHouseLocation.setSkuInfo(null);
                wareHouseLocation.setSkuNumber(0L);
                wareHouseLocation.setLocationLock(LocationLockEnum.YES.getType());
                wareHouseLocation.setLocationUseStatus(LocationUseStatusEnum.NO.getType());
                wareHouseLocationMapper.updateById(wareHouseLocation);
            }
        } else if (ReleaseTakeEnum.TO_POINT.getType().equals(robotTaskDetail.getTakeType())) {
            WareHouseTakePointDO houseTakePoint = houseTakePointService.getHouseTakePoint(robotTaskDetail.getFromLocationId());
            houseTakePoint.setPointStatus(ZeroOneEnum.ZERO.getType());
            houseTakePointService.updateById(houseTakePoint);
        }

    }

    /**
     * 校验放货任务
     *
     * @param toLocationId
     * @param takeId
     */
    private void releaseCheck(Long toLocationId, Long takeId) {
        WareHouseLocationDO wareHouseLocation = wareHouseLocationMapper.selectById(toLocationId);
        if (ZeroOneEnum.ONE.getType().equals(wareHouseLocation.getLocationUseStatus())) {
            throw exception(TASK_RELEASE_LOCATION_NOT_EMPTY);
        }
        if (!wareHouseLocation.getTaskId().equals(takeId)) {
            throw exception(TASK_RELEASE_LOCATION_HAVE_OTHER_TASK);
        }
        List<WareHouseLocationDO> locations = wareHouseLocationMapper.selectList(new LambdaQueryWrapperX<WareHouseLocationDO>()
                .eq(WareHouseLocationDO::getMapItemId, wareHouseLocation.getMapItemId())
                .ne(WareHouseLocationDO::getId, toLocationId));
        if (ObjectUtil.isEmpty(locations)) {
            return;
        }
        List<WareHouseLocationDO> bigNumbers = locations.stream()
                .filter(v -> v.getLocationNumber() > wareHouseLocation.getLocationNumber()
                        && ZeroOneEnum.ZERO.getType().equals(v.getLocationUseStatus()))
                .collect(Collectors.toList());
        if (ObjectUtil.isNotEmpty(bigNumbers)) {
            throw exception(TASK_RELEASE_LOCATION_LOWER_LEVELS_EMPTY);
        }
    }

    /**
     * 取货校验
     *
     * @param id
     * @param takeId
     */
    private void takeCheck(Long id, Long takeId, Boolean isRemote) {
        WareHouseLocationDO wareHouseLocation = wareHouseLocationMapper.selectById(id);
        if (ZeroOneEnum.ZERO.getType().equals(wareHouseLocation.getLocationUseStatus())) {
            throw exception(TASK_TAKE_LOCATION_EMPTY);
        }

        if (!wareHouseLocation.getTaskId().equals(takeId) && !isRemote) {
            throw exception(TASK_TAKE_LOCATION_HAVE_OTHER_TASK);
        }

        List<WareHouseLocationDO> locations = wareHouseLocationMapper.selectList(new LambdaQueryWrapperX<WareHouseLocationDO>()
                .eq(WareHouseLocationDO::getMapItemId, wareHouseLocation.getMapItemId())
                .ne(WareHouseLocationDO::getId, id));
        if (ObjectUtil.isEmpty(locations)) {
            return;
        }
        List<WareHouseLocationDO> bigNumbers = locations.stream()
                .filter(v -> v.getLocationNumber() < wareHouseLocation.getLocationNumber()
                        && ZeroOneEnum.ONE.getType().equals(v.getLocationUseStatus()))
                .collect(Collectors.toList());
        if (ObjectUtil.isNotEmpty(bigNumbers)) {
            throw exception(TASK_TAKE_LOCATION_UPPER_LEVELS_NOT_EMPTY);
        }

    }

    /**
     * 充电任务
     *
     * @param pathPlanning
     * @param robot
     */
    private void chargeTask(TaskToPathPlanningDTO pathPlanning, RobotInformationDO robot) {
        List<DeviceInformationDO> deviceInformations = deviceInformationMapper.selectList(new LambdaQueryWrapperX<DeviceInformationDO>()
                .eq(DeviceInformationDO::getDeviceEnable, ZeroOneEnum.ONE.getType())
                .eq(DeviceInformationDO::getDeviceUseStatus, DeviceUseStatusEnum.IDLE.getType())
                .eq(DeviceInformationDO::getDeviceType, DeviceTypeEnum.CHARGING_STATION.getType()));
        if (ObjectUtil.isEmpty(deviceInformations)) {
            log.info("没有空闲的充电桩 :{}", robot.getRobotNo());
            throw exception(ROBOT_NOT_FOUND_FREE_CHARGING_STATION);
        }

        DeviceInformationDO deviceInformationDO = null;
        for (DeviceInformationDO v : deviceInformations) {
            if (ObjectUtil.isNotEmpty(v.getLastUser()) && v.getLastUser().equals(robot.getRobotNo())) {
                deviceInformationDO = v;
                break;
            }
            if (v.getDeviceAttribute().equals(robot.getChargeType())
                    && robot.getFloorAreaJson().contains(v.getPositionMapId()) && DeviceUseStatusEnum.IDLE.getType().equals(v.getDeviceUseStatus())) {
                deviceInformationDO = v;
            }
        }

        if (ObjectUtil.isEmpty(deviceInformationDO)) {
            log.info("当前机器人查不到对应的充电桩类型、或者机器人不能在此区域充电 :{}", robot.getRobotNo());
            throw exception(ROBOT_NOT_FOUND_FREE_CHARGING_STATION);
        }

        pathPlanning.setReleaseGroupId("POINT_" + deviceInformationDO.getPositionMapItemId());
        pathPlanning.setReleasePointId(String.valueOf(deviceInformationDO.getPositionMapItemId()));
    }

    /**
     * 移动到等待点
     *
     * @param pathPlanning
     */
    private void moveToWaitTask(TaskToPathPlanningDTO pathPlanning, String robotNo) {

        PositionMapItemDO positionMapItems = positionMapItemMapper.selectOne(new LambdaQueryWrapperX<PositionMapItemDO>()
                .eq(PositionMapItemDO::getType, PositionMapItemEnum.STOP.getType())
                .eq(PositionMapItemDO::getRobotNo, robotNo)
                .last("limit 1"));

        if (ObjectUtil.isEmpty(positionMapItems)) {
            positionMapItems = positionMapItemMapper.selectOne(new LambdaQueryWrapperX<PositionMapItemDO>()
                    .eq(PositionMapItemDO::getType, PositionMapItemEnum.STOP.getType())
                    .eq(PositionMapItemDO::getUseStatus, UseStatusEnum.FREE.getType())
                    .last("limit 1"));
        }

        if (ObjectUtil.isEmpty(positionMapItems)) {
            log.info("------没有空闲的停车点----- :{}", robotNo);
            throw exception(ROBOT_NOT_FOUND_WAIT_ITEM);
        }

        pathPlanning.setWaitIds(Collections.singletonList(positionMapItems.getId() + ""));
    }


    /**
     * 取放货清除交管
     *
     * @param robotTaskDetail
     */
    private void takeReleaseCleanTrafficManagement(RobotTaskDetailDO robotTaskDetail) {
        if (RobotTaskStageEnum.UN_START.getType().equals(robotTaskDetail.getTaskStage())
                || RobotTaskStageEnum.GO_TAKE.getType().equals(robotTaskDetail.getTaskStage())) {
            log.info("清除交管--任务阶段是前往取货/待执行/前往放货 :{}", robotTaskDetail.getRobotNo());
            robotTaskDetail.setTaskStatus(RobotTaskDetailStatusEnum.NEW.getType());
//            robotTaskDetail.setRobotNo(null);
            taskDetailMapper.updateById(robotTaskDetail);
        }
    }

    /**
     * 车机关闭任务
     *
     * @param id
     * @param mac
     * @param orderType
     */
    @Override
    public void robotCloseTaskDetail(String id, String mac, String orderType) {
        RobotAcceptTaskDTO robotTask = new RobotAcceptTaskDTO();
        robotTask.setOrderId(id);
        robotTask.setOrderType(orderType);
        robotTask.setTopic(RobotTopicConstant.ROBOT_TASK_MOVE_TOPIC + mac);
        robotTask.setExecutionType(ExecutionTypeEnum.CANCEL.getType());
//        commonApi.commonMethod(robotTask, robotTask.getTopic());
        mqttUtils.pub(robotTask.getTopic(), JSON.toJSONString(robotTask));
    }

    /**
     * 获取楼层区域对应的机器人编号
     *
     * @param floor
     * @param area
     * @return
     */
    @Override
    public List<String> getRobotByFloorAndArea(String floor, String area) {

        List<RobotInformationDO> robots = getAllRobot();
        if (ObjectUtil.isEmpty(robots)) {
            return new ArrayList<>();
        }

        List<String> robotNos = robots.stream().map(RobotInformationDO::getRobotNo).collect(Collectors.toList());

        List<String> list = new ArrayList<>();
        String floorArea = floor + CommonConstant.SYMBOL + area;
        Map<Object, Object> hmget = redisUtil.hmget(floorArea);
        if (ObjectUtil.isEmpty(hmget)) {
            return new ArrayList<>();
        }

        hmget.forEach((k, v) -> {
            if (robotNos.contains(k)) {
                list.add((String) k);
            }
        });

        return list;
    }

    /**
     * 车辆一键暂停或启动
     *
     * @param robotSimulationPose
     * @param robotNos
     * @param str
     */
    @Override
    public void sendEmergencyStopOrRecoveryToRobot(RobotAcceptTaskDTO robotSimulationPose, List<String> robotNos, String str) {
        List<RobotInformationDO> robotInformationDOS = informationMapper.selectList(new LambdaQueryWrapperX<RobotInformationDO>()
                .in(RobotInformationDO::getRobotNo, robotNos));
        for (RobotInformationDO robotInformationDO : robotInformationDOS) {
            robotSimulationPose.setOrderId(robotInformationDO.getTaskDetailId() + "");
            mqttUtils.pub(RobotTopicConstant.ROBOT_TASK_MOVE_TOPIC + robotInformationDO.getMacAddress(), JSON.toJSONString(robotSimulationPose));
            UserOperationLogSaveReqVO operationLog = UserOperationLogSaveReqVO.builder()
                    .operateAction(str + robotInformationDO.getRobotNo())
                    .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();
            userOperationLogService.createUserOperationLog(operationLog);
        }
    }

    @Override
    public List<RobotTaskDO> selectDoingTaskByRobotNo(String robotNo) {
        List<RobotTaskDO> list = taskMapper.selectDoingTaskByRobotNo(robotNo);
        return list;
    }

    @Override
    public RobotInformationDO getInformationByRobotNo(String robotNo) {
        return informationMapper.selectOne(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotNo, robotNo)
                .last("limit 1"));
    }

    /**
     * 校验机器人是否到达设定的电量
     * false 不能做任务
     *
     * @param robotInformation
     * @param commonConfig
     * @return
     */
    @Override
    public Boolean checkRobotElectricity(RobotInformationDO robotInformation, CommonConfigDO commonConfig) {

        if (ObjectUtil.isEmpty(commonConfig) || ObjectUtil.isEmpty(commonConfig.getConfigStr())) {
            log.info("没有配置电量,不能做任务");
            return false;
        }

        CommonConfigVO chargeConfig = JSONUtil.toBean(commonConfig.getConfigStr(), CommonConfigVO.class);

        String soc = getSocByMac(robotInformation.getMacAddress(), ZeroOneEnum.ONE.getType());
        if (ObjectUtil.isEmpty(soc)) {
            log.info("车机没有电量信息 :{}", robotInformation.getRobotNo());
            return false;
        }

        if (ObjectUtil.isNotEmpty(chargeConfig) && ObjectUtil.isNotEmpty(chargeConfig.getStartAutoCharge())) {
            BigDecimal robotRemainingElectricity = new BigDecimal(soc);
            BigDecimal robotEndElectricity = new BigDecimal(chargeConfig.getStartAutoCharge() + "");
            if (robotRemainingElectricity.compareTo(robotEndElectricity) < 0) {
                log.info("机器人的电量少于自动充电电量，不能接任务 :{}", robotInformation.getRobotNo());
                return false;
            }
        }

        return checkElectricity(chargeConfig, robotInformation, soc);
    }

    @Override
    public List<RobotInformationDO> getRobotInformationByRobotNos(List<String> robotNos) {
        return informationMapper.selectList(new LambdaQueryWrapperX<RobotInformationDO>()
                .in(RobotInformationDO::getRobotNo, robotNos));
    }

    @Override
    public FloorZoneDTO getFloorZoneDTOByRobotNo(String robotNo) {
        RobotInformationDO robotInformationDO = getInformationByRobotNo(robotNo);
        String floorAreaKey = RobotTaskChcheConstant.ROBOT_FLOOR_AREA + robotInformationDO.getMacAddress();
        Object floorAreaObject = redisUtil.get(floorAreaKey);
        if (ObjectUtil.isEmpty(floorAreaObject)) {
            return null;
        }
        FloorZoneDTO floorZoneDTO = JSONUtil.toBean((String) floorAreaObject, FloorZoneDTO.class);
        return floorZoneDTO;
    }

    /**
     * 切换任务
     *
     * @param id      任务id
     * @param robotNo 切换的车辆编号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rmoteTransferTaskToNewFreeRobo(String oldRobotNo, Long id, String robotNo) {
        RobotTaskDetailActionLogDO actionLog = taskDetailActionLogService.getOriginalLastTaskByRobotNo(oldRobotNo);
        if (ObjectUtil.isEmpty(actionLog)) {
            log.info("车辆前一个任务不存在或已经完成");
            throw exception(ROBOT_LAST_TASK_NO_EXISTS);
        }
        if (!oldRobotNo.equals(actionLog.getRobotNo())) {
            log.info("此任务已经被转移到车辆 :{}", actionLog.getRobotNo());
            throw exception0(TASK_ASSIGN_OTHER_ROBOT.getCode(), TASK_ASSIGN_OTHER_ROBOT.getMsg() + actionLog.getRobotNo(),
                    TASK_ASSIGN_OTHER_ROBOT.getMsg() + actionLog.getRobotNo());
        }

        if (ActionStatusEnum.DONE.getType().equals(actionLog.getActionStatus())) {
            throw exception(TASK_CHECK_TASK_STATUS);
        }

        Object o = checkTaskDetailExist(actionLog.getTaskDetailId());

        RobotInformationDO robotInformationDO = informationMapper.selectOne(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotNo, robotNo)
                .last("limit 1"));

        if (RobotStatusEnum.DOING.getType().equals(robotInformationDO.getRobotStatus())) {
            throw exception(ROBOT_HAVE_TASK);
        }

        if (RobotTaskModelEnum.REJECTION.getType().equals(robotInformationDO.getRobotTaskModel())) {
            throw exception(ROBOT_REJECTION);
        }

        String taskStatusKey = RobotTaskChcheConstant.ROBOT_TASK_STATUS + robotInformationDO.getMacAddress();
        Object taskStatus = redisUtil.get(taskStatusKey);
        if (ObjectUtil.isEmpty(taskStatus) || !RobotStatusCodeConstant.CAN_DO_TASK.equals(Boolean.parseBoolean(String.valueOf(taskStatus)))) {
            log.info("车机上报不允许接任务 :{}", robotNo);
            throw exception(ROBOT_DO_TASK_FAIL);
        }

        String mac = getMacByRobotNo(oldRobotNo);
        robotCloseTaskDetail(actionLog.getTaskDetailId() + "", mac, actionLog.getCommandType());

        CleanAgvDTO build = CleanAgvDTO.builder().robotNo(robotNo).build();
//        commonApi.commonMethod(build, PathPlanningTopicConstant.CLEAN_AGV);
        mqttUtils.pub(PathPlanningTopicConstant.CLEAN_AGV, JSON.toJSONString(build));

        TaskToPathPlanningDTO pathPlanning = JSONUtil.toBean((String) o, TaskToPathPlanningDTO.class);

        robotInformationDO.setTaskDetailId(id);
        robotInformationDO.setRobotStatus(RobotStatusEnum.DOING.getType());
        informationMapper.updateById(robotInformationDO);

        TaskRobotNoLimittationAreaDTO taskRobotNoLimittationAreaDTO =
                robotPathPlanningService.getRobotNoLimitationArea(Collections.singletonList(robotInformationDO)).get(0);

        List<TaskRobotNoLimittationAreaDTO> robotNoLimitions = Arrays.asList(taskRobotNoLimittationAreaDTO);
        pathPlanning.setRobotNoLimitationAreaDTOS(robotNoLimitions);

        resendToPPData(pathPlanning, actionLog, robotInformationDO, true);

        String plantingKey = PathPlanningChcheConstant.PATH_PLANNING_TASK + pathPlanning.getOrderId();
        redisUtil.set(plantingKey, JSON.toJSONString(pathPlanning), znConfigConstant.getTask().getTaskCacheTime());

        List<TaskToPathPlanningDTO> pathPlanningList = new ArrayList<>();
        pathPlanningList.add(pathPlanning);
        log.info("远遥任务转移, 任务下发给PP :{}", JSON.toJSONString(pathPlanningList));
//        commonApi.commonMethod(pathPlanningList, PathPlanningTopicConstant.TASK_ASSIGNMENT_REQUEST);
        mqttUtils.pub(PathPlanningTopicConstant.TASK_ASSIGNMENT_REQUEST, JSON.toJSONString(pathPlanningList));
    }

    /**
     * 设置车辆空闲
     *
     * @param robotNo
     */
    @Override
    public void setRobotFree(String robotNo) {
        RobotInformationDO robotInformationDO = informationMapper.selectOne(new LambdaQueryWrapper<RobotInformationDO>()
                .eq(RobotInformationDO::getRobotNo, robotNo)
                .last("limit 1"));
        robotInformationDO.setRobotStatus(RobotStatusEnum.STAND_BY.getType());
        informationMapper.updateById(robotInformationDO);
    }

    private Boolean checkElectricity(CommonConfigVO chargeConfig, RobotInformationDO robot, String soc) {
        String chargeModelKey = RobotTaskChcheConstant.ROBOT_CHARGE_MODEL + robot.getRobotNo();
        Object chargeModelCache = redisUtil.get(chargeModelKey);
        log.info("充电机器人编号:{} ,信息: {}", robot.getRobotNo());

        //车子剩余电量
        BigDecimal robotRemainingElectricity = new BigDecimal(soc);
        //设置离开的电量
        BigDecimal robotEndElectricity = new BigDecimal("10");

        if (ObjectUtil.isNotEmpty(robot.getAutoCharge()) && ObjectUtil.isNotEmpty(chargeModelCache)
                && !ChargeModelEnum.FULL.getType().equals((Integer) chargeModelCache)) {
            robotEndElectricity = new BigDecimal(String.valueOf(robot.getAutoCharge()));
        } else if (ObjectUtil.isNotEmpty(chargeModelCache) && ChargeModelEnum.FULL.getType().equals((Integer) chargeModelCache)) {
            robotEndElectricity = new BigDecimal(znConfigConstant.getFullElectricity());
        } else if (ObjectUtil.isNotEmpty(chargeModelCache) && ChargeModelEnum.CHANCE.getType().equals((Integer) chargeModelCache)
                && ObjectUtil.isNotEmpty(chargeConfig.getChanceChargeEnd())) {
            robotEndElectricity = new BigDecimal(String.valueOf(chargeConfig.getChanceChargeEnd()));
        } else if (ObjectUtil.isNotEmpty(chargeConfig.getEndAutoCharge())) {
            robotEndElectricity = new BigDecimal(String.valueOf(chargeConfig.getEndAutoCharge()));
        }

        if (robotRemainingElectricity.compareTo(robotEndElectricity) >= 0) {
            return true;
        } else {
            log.info("机器人正在充电,还没达到充电设置的电量,暂不能接任务:{} ", robot.getRobotNo());
        }

        return false;
    }

}
