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

import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.mqtt.api.path.dto.PathToRobotDTO;
import cn.iocoder.yudao.module.mqtt.api.path.dto.remote.RemotePathPlanningDTO;
import cn.iocoder.yudao.module.mqtt.api.path.dto.remote.RobotModeDTO;
import cn.iocoder.yudao.module.system.api.remote.dto.LoginCheckDTO;
import cn.iocoder.yudao.module.system.api.remote.dto.RemoteRobotStatusDTO;
import cn.iocoder.yudao.module.system.api.remote.dto.RemoteRobotTransferDTO;
import cn.iocoder.yudao.module.system.api.remote.dto.RemoteTaskTransferDTO;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotStatusDataSpeedDTO;
import cn.iocoder.yudao.module.system.api.robot.vo.RobotReactiveStatusDTO;
import cn.iocoder.yudao.module.system.config.mqtt.util.MqttUtils;
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.RobotTaskChcheConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotTopicConstant;
import cn.iocoder.yudao.module.system.controller.admin.log.vo.UserOperationLogSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.remote.vo.ControllerTaskTransferLogSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.remote.vo.RemoteControllerInformationPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.remote.vo.RemoteControllerInformationSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.robot.detail.RobotDoingTaskDTO;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.RobotTaskDetailSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.config.CommonConfigDO;
import cn.iocoder.yudao.module.system.dal.dataobject.log.RobotTaskDetailActionLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.remote.RemoteControllerInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotMapStopDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotTaskDetailDO;
import cn.iocoder.yudao.module.system.dal.mysql.config.CommonConfigMapper;
import cn.iocoder.yudao.module.system.dal.mysql.remote.RemoteControllerInformationMapper;
import cn.iocoder.yudao.module.system.enums.common.ZeroOneEnum;
import cn.iocoder.yudao.module.system.enums.config.CommandConfigTypeEnum;
import cn.iocoder.yudao.module.system.enums.redis.RobotCacheLockEnum;
import cn.iocoder.yudao.module.system.enums.robot.RobotStatusEnum;
import cn.iocoder.yudao.module.system.enums.robot.RobotTaskModelEnum;
import cn.iocoder.yudao.module.system.enums.robot.RobotTaskStatusEnum;
import cn.iocoder.yudao.module.system.enums.robot.RobotTaskTypeEnum;
import cn.iocoder.yudao.module.system.enums.robot.remote.RemoteModeEnum;
import cn.iocoder.yudao.module.system.enums.robot.remote.RemoteOperationModeEnum;
import cn.iocoder.yudao.module.system.enums.robot.task.RobotCommandTypeEnum;
import cn.iocoder.yudao.module.system.enums.robot.task.RobotTaskManualInterventionEnum;
import cn.iocoder.yudao.module.system.enums.robot.task.RobotTaskStageEnum;
import cn.iocoder.yudao.module.system.mq.message.remote.RemoteSendMessage;
import cn.iocoder.yudao.module.system.mq.producer.remote.RemoteControllerProducer;
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.robot.RobotInformationService;
import cn.iocoder.yudao.module.system.service.robot.RobotTaskDetailService;
import cn.iocoder.yudao.module.system.service.robot.RobotTaskService;
import cn.iocoder.yudao.module.system.service.robot.mapstop.RobotMapStopService;
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 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 javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.*;
import java.util.stream.Collectors;

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;


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 RemoteControllerInformationServiceImpl extends ServiceImpl<RemoteControllerInformationMapper, RemoteControllerInformationDO> implements RemoteControllerInformationService {

    @Resource
    private RemoteControllerInformationMapper controllerInformationMapper;

    @Resource
    private UserOperationLogService userOperationLogService;

    @Resource
    private RobotMapStopService mapStopService;

    @Resource
    private RobotTaskDetailActionLogService taskDetailActionLogService;

    @Resource
    private RedissonUtils redissonUtils;

    @Resource
    private RobotInformationService informationService;

    @Resource
    private RedisUtil redisUtil;

    @Autowired
    private MqttUtils mqttUtils;

   /* @Resource
    private CommonApi commonApi;*/

    @Resource
    private CommonConfigMapper configMapper;

    @Resource
    private RobotMaxSpeedService robotMaxSpeedService;

    @Resource
    private RobotTaskDetailService taskDetailService;

    @Resource
    @Lazy
    private RobotTaskService taskService;

    @Resource
    private ControllerTaskTransferLogService controllerTaskTransferLogService;

    @Resource
    private RemoteControllerProducer remoteControllerProducer;

    @Override
    public Long createControllerInformation(RemoteControllerInformationSaveReqVO createReqVO) {
        // 插入
        RemoteControllerInformationDO controllerInformation = BeanUtils.toBean(createReqVO, RemoteControllerInformationDO.class);
        controllerInformationMapper.insert(controllerInformation);
        // 返回
        return controllerInformation.getId();
    }

    @Override
    public void updateControllerInformation(RemoteControllerInformationSaveReqVO updateReqVO) {
        // 校验存在
        validateControllerInformationExists(updateReqVO.getId());
        // 更新
        RemoteControllerInformationDO updateObj = BeanUtils.toBean(updateReqVO, RemoteControllerInformationDO.class);
        controllerInformationMapper.updateById(updateObj);
    }

    @Override
    public void deleteControllerInformation(Long id) {
        // 校验存在
        validateControllerInformationExists(id);
        // 删除
        controllerInformationMapper.deleteById(id);
    }

    private void validateControllerInformationExists(Long id) {
        if (controllerInformationMapper.selectById(id) == null) {
            throw exception(CONTROLLER_INFORMATION_NOT_EXISTS);
        }
    }

    @Override
    public RemoteControllerInformationDO getControllerInformation(Long id) {
        return controllerInformationMapper.selectById(id);
    }

    @Override
    public PageResult<RemoteControllerInformationDO> getControllerInformationPage(RemoteControllerInformationPageReqVO pageReqVO) {
        return controllerInformationMapper.selectPage(pageReqVO);
    }

    /**
     * 保存IP和端口
     *
     * @param loginCheck
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveIpAndPort(LoginCheckDTO loginCheck) {
        RemoteControllerInformationDO information = controllerInformationMapper.selectOne(new LambdaQueryWrapper<RemoteControllerInformationDO>()
                .eq(RemoteControllerInformationDO::getRemoteIp, loginCheck.getRemoteIp())
                .last("limit 1"));
        UserOperationLogSaveReqVO operationLog = null;
        if (ObjectUtil.isEmpty(information)) {
            RemoteControllerInformationDO build = RemoteControllerInformationDO.builder()
                    .remoteIp(loginCheck.getRemoteIp())
                    .systemPort(loginCheck.getSystemPort())
                    .systemIp(loginCheck.getSystemIp())
                    .build();
            controllerInformationMapper.insert(build);

            operationLog = UserOperationLogSaveReqVO.builder()
                    .operateAction("添加远遥设备IP " + loginCheck.getSystemIp() + " 端口 " + loginCheck.getSystemPort())
                    .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();

        } else {
            information.setSystemIp(loginCheck.getSystemIp());
            information.setSystemPort(loginCheck.getSystemPort());
            controllerInformationMapper.updateById(information);

            operationLog = UserOperationLogSaveReqVO.builder()
                    .operateAction("修改远遥设备IP " + loginCheck.getSystemIp() + " 端口 " + loginCheck.getSystemPort())
                    .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();
        }

        userOperationLogService.createUserOperationLog(operationLog);
    }

    /**
     * 查询远遥默认IP和端口
     *
     * @return
     */
    @Override
    public LoginCheckDTO getDefaultIpAndPort(String remoteIp) {
        RemoteControllerInformationDO information = controllerInformationMapper.selectOne(new LambdaQueryWrapper<RemoteControllerInformationDO>()
                .eq(RemoteControllerInformationDO::getRemoteIp, remoteIp)
                .last("limit 1"));
        LoginCheckDTO loginCheck = new LoginCheckDTO();
        if (ObjectUtil.isEmpty(information)) {
            return loginCheck;
        }
        loginCheck.setSystemIp(information.getSystemIp());
        loginCheck.setSystemPort(information.getSystemPort());
        return loginCheck;
    }

    @Override
    public List<RemoteControllerInformationDO> getAllRemoteController() {
        return controllerInformationMapper.selectList(new LambdaQueryWrapper<RemoteControllerInformationDO>()
                .ne(RemoteControllerInformationDO::getRobotNo, ""));
    }

    /**
     * 获取机器人远遥模式/急停/协控状态
     *
     * @param remoteIp
     * @return
     */
    @Override
    public RemoteRobotStatusDTO getRemoteRobotStatus(String remoteIp) {
        RemoteControllerInformationDO information = controllerInformationMapper.selectOne(new LambdaQueryWrapper<RemoteControllerInformationDO>()
                .eq(RemoteControllerInformationDO::getRemoteIp, remoteIp)
                .last("limit 1"));
        if (ObjectUtil.isEmpty(information) || ObjectUtil.isEmpty(information.getRobotNo())) {
            return null;
        }
        RemoteRobotStatusDTO remoteRobotStatus = BeanUtils.toBean(information, RemoteRobotStatusDTO.class);
        List<RobotMapStopDO> robotMapStops = mapStopService.getStopRobotNoByRobotNo(information.getRobotNo());
        if (ObjectUtil.isNotEmpty(robotMapStops)) {
            remoteRobotStatus.setIsStop(ZeroOneEnum.ZERO.getType());
        }

        RobotInformationDO robotInformation = informationService.getInformationByRobotNo(information.getRobotNo());
        if (RobotStatusEnum.STAND_BY.getType().equals(robotInformation.getRobotStatus())) {
            remoteRobotStatus.setRobotStatus(RobotStatusEnum.STAND_BY.getType());
        }
        remoteRobotStatus.setRobotTaskModel(robotInformation.getRobotTaskModel());

        if ((RemoteModeEnum.AUTOMATIC.getType().equals(information.getRemoteMode()) || RemoteModeEnum.FREE.getType().equals(information.getRemoteMode()))
            && RobotStatusEnum.STAND_BY.getType().equals(robotInformation.getRobotStatus())) {
            remoteRobotStatus.setClickException(ZeroOneEnum.ZERO.getType());
        }

        if (RemoteModeEnum.HAND_MOVEMENT.getType().equals(information.getRemoteMode())) {
            List<RobotDoingTaskDTO> robotDoingTaskNo = taskDetailService.getRobotDoingTaskNo(information.getRobotNo());
            if ((ObjectUtil.isNotEmpty(robotDoingTaskNo) && robotDoingTaskNo.size() > 1) || ObjectUtil.isEmpty(robotDoingTaskNo)) {
                return remoteRobotStatus;
            }
            RobotDoingTaskDTO robotDoingTask = robotDoingTaskNo.get(0);

            if (!(RobotTaskStatusEnum.DOING.getType().equals(robotDoingTask.getTaskStatus())
                    && (RobotTaskTypeEnum.TAKE_RELEASE.getType().equals(robotDoingTask.getTaskType())
                    || RobotTaskTypeEnum.TAKE.getType().equals(robotDoingTask.getTaskType())
                    || RobotTaskTypeEnum.RELEASE.getType().equals(robotDoingTask.getTaskType())))) {
                return remoteRobotStatus;
            }

            if (RobotTaskStageEnum.UN_START.getType().equals(robotDoingTask.getTaskStage())
            || RobotTaskStageEnum.GO_TAKE.getType().equals(robotDoingTask.getTaskStage())
            || RobotTaskStageEnum.TAKEING.getType().equals(robotDoingTask.getTaskStage())) {
                remoteRobotStatus.setClickTaskDone(ZeroOneEnum.ZERO.getType());
            }else {
                remoteRobotStatus.setClickTaskDone(ZeroOneEnum.ONE.getType());
            }
        }

        return remoteRobotStatus;
    }

    /**
     * 根据车辆编号查询
     *
     * @param robotNo
     * @return
     */
    @Override
    public RemoteControllerInformationDO getRemoteControllerByRobotNo(String robotNo) {
        RemoteControllerInformationDO information = controllerInformationMapper.selectOne(new LambdaQueryWrapper<RemoteControllerInformationDO>()
                .eq(RemoteControllerInformationDO::getRobotNo, robotNo)
                .last("limit 1"));
        return information;
    }

    /**
     * 远遥切换模式
     *
     * @param remoteMode
     * @param remoteIp
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RemoteRobotTransferDTO robotChangeMode(Integer remoteMode, String remoteIp, String robotNo, int remoteControllerPort, String remoteControllerIp) {
        log.info("切换的模式 :{}, 远遥IP :{}, 车辆编号 :{}, 驾舱端口:{}, 驾舱IP :{}",remoteMode,remoteIp,robotNo,remoteControllerPort,remoteControllerIp);
        RLock lock = redissonUtils.getLock(RobotCacheLockEnum.ROBOT_TASK_DISTRIBUTE_LOCK.getKey());
        if (lock.tryLock()) {
            try {
                log.info("----远遥切换模式----");
                try {
                    //避免有分配中的任务，所以等待0.5秒
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                RemoteRobotTransferDTO dto = doRobotChangeMode(remoteMode, remoteIp, robotNo, remoteControllerPort, remoteControllerIp);
                if (!RemoteModeEnum.AUTOMATIC.getType().equals(remoteMode)) {
                    taskService.chargeDone(robotNo);
                }

                return dto;
            } finally {
                lock.unlock();
            }
        } else {
            log.info("----远遥切换模式----未获取到锁");
            throw exception(REDISSON_NOT_OBTAIN_LOCK);
        }
    }

    /**
     * 任务转移给空闲车辆
     *
     * @param data
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remoteTransferTaskToFreeRobot(RemoteTaskTransferDTO data) {
        RLock lock = redissonUtils.getLock(RobotCacheLockEnum.ROBOT_TASK_DISTRIBUTE_LOCK.getKey());
        if (lock.tryLock()) {
            try {
                log.info("----远遥任务转移开始----");
                try {
                    //避免有分配中的任务，所以等待0.5秒
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                doRemoteTransferTaskToFreeRobot(data);
            } finally {
                lock.unlock();
            }
        } else {
            log.info("----远遥任务转移开始----未获取到锁");
            throw exception(REDISSON_NOT_OBTAIN_LOCK);
        }
    }

    /**
     * 车辆换图修改限速
     *
     * @param pathRobotDTO
     */
    @Override
    public void robotChangeSpeed(PathToRobotDTO pathRobotDTO) {
        RemoteControllerInformationDO information = controllerInformationMapper.selectOne(new LambdaQueryWrapper<RemoteControllerInformationDO>()
                .eq(RemoteControllerInformationDO::getRobotNo, pathRobotDTO.getRobotNo())
                .last("limit 1"));
        if (ObjectUtil.isEmpty(information)) {
            return;
        }
        sendModeToRobot(information.getRobotNo(), RemoteOperationModeEnum.getType(information.getRemoteMode()));
    }

    /**
     * 断开连接
     *
     * @param remoteIp
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RemoteRobotTransferDTO remoteDisconnect(String remoteIp) {
        RemoteControllerInformationDO information = controllerInformationMapper.selectOne(new LambdaQueryWrapper<RemoteControllerInformationDO>()
                .eq(RemoteControllerInformationDO::getRemoteIp, remoteIp)
                .last("limit 1"));
        if (ObjectUtil.isEmpty(information.getRobotNo())) {
            return null;
        }
        checkGraphMatch(information.getRobotNo());

        String robotNo = information.getRobotNo();
        sendRemoteMsgToPP(robotNo, PathPlanningTopicConstant.CLOSE_REMOTE_CONTROLLER, RemoteOperationModeEnum.AUTO.getType());
        sendModeToRobot(robotNo, RemoteOperationModeEnum.AUTO.getType());

        information.setRobotNo(null);
        information.setRemoteMode(RemoteModeEnum.AUTOMATIC.getType());
        information.setTaskDetailId(null);
        information.setCollaborativeControl(ZeroOneEnum.ZERO.getType());
        controllerInformationMapper.updateById(information);

        UserOperationLogSaveReqVO build = UserOperationLogSaveReqVO.builder()
                .operateAction("远遥断开连接 " + information.getRemoteIp())
                .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();
        userOperationLogService.createUserOperationLog(build);

        RemoteSendMessage remoteSendMessage = RemoteSendMessage.builder().robotNo(robotNo).build();
        remoteControllerProducer.sendSmsSendMessage(remoteSendMessage);

        RobotInformationDO robotInformation = informationService.getInformationByRobotNo(robotNo);
        return RemoteRobotTransferDTO.builder()
                        .robotNo(robotInformation.getRobotNo())
                        .robotIp(robotInformation.getRobotIp())
                        .robotPort(robotInformation.getRobotPort()).build();
    }

    @Override
    public void repeatedTransmissionDisconnect(RemoteSendMessage message) {
        String robotNo = message.getRobotNo();
        sendRemoteMsgToPP(robotNo, PathPlanningTopicConstant.CLOSE_REMOTE_CONTROLLER, RemoteOperationModeEnum.AUTO.getType());
        sendModeToRobot(robotNo, RemoteOperationModeEnum.AUTO.getType());
    }

    /**
     * 任务完成/取货完成
     *
     * @param remoteIp
     * @param mode
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remoteTaskDone(String remoteIp, Integer mode) {
        RemoteControllerInformationDO information = checkRemoteControllerHaveBinding(remoteIp);
        List<RobotDoingTaskDTO> robotDoingTaskNo = taskDetailService.getRobotDoingTaskNo(information.getRobotNo());
        if (ObjectUtil.isNotEmpty(robotDoingTaskNo) && robotDoingTaskNo.size() > 1) {
            throw exception(REMOTE_ROBOT_HAVE_MORE_TASK);
        } else if (ObjectUtil.isEmpty(robotDoingTaskNo)) {
            throw exception(REMOTE_ROBOT_HAVE_NOT_TASK);
        }

        //取货完成
        if (ZeroOneEnum.ZERO.getType().equals(mode)) {
            taskDetailService.remoteTaskTakeDone(robotDoingTaskNo.get(0).getId());
        } else {
            //任务完成
            taskDetailService.manuallyCompleted(robotDoingTaskNo.get(0).getId(), " 远遥点击任务完成 ", RobotTaskManualInterventionEnum.REMOTE_TASK_DONE.getType());
            information.setRemoteMode(RemoteModeEnum.FREE.getType());
            information.setTaskDetailId(null);
            information.setCollaborativeControl(ZeroOneEnum.ZERO.getType());
            controllerInformationMapper.updateById(information);
        }

    }

    @Override
    public List<String> getRemoteControllerRobotNos() {
        List<RemoteControllerInformationDO> remoteControllerInformations = controllerInformationMapper.selectList(new LambdaQueryWrapper<RemoteControllerInformationDO>());
        if (ObjectUtil.isEmpty(remoteControllerInformations)) {
            return new ArrayList<>();
        }
        return remoteControllerInformations.stream().map(RemoteControllerInformationDO::getRobotNo).collect(Collectors.toList());
    }

    public RemoteControllerInformationDO checkRemoteControllerHaveBinding(String remoteIp) {
        RemoteControllerInformationDO information = controllerInformationMapper.selectOne(new LambdaQueryWrapper<RemoteControllerInformationDO>()
                .eq(RemoteControllerInformationDO::getRemoteIp, remoteIp)
                .last("limit 1"));
        if (ObjectUtil.isEmpty(information) || ObjectUtil.isEmpty(information.getRobotNo())) {
            throw exception(REMOTE_DEVICE_NOT_SET_UP);
        }
        return information;
    }

    /**
     * 远遥转移任务
     *
     * @param data
     */
    @Transactional(rollbackFor = Exception.class)
    public void doRemoteTransferTaskToFreeRobot(RemoteTaskTransferDTO data) {
        RemoteControllerInformationDO information = checkRemoteControllerHaveBinding(data.getRemoteIp());
        //自动模式判断是否匹配路网
        if (RemoteModeEnum.AUTOMATIC.getType().equals(data.getRemoteMode())) {
            checkGraphMatch(information.getRobotNo());
        }
        //任务没被其他远遥设备选中
        RemoteControllerInformationDO detail = controllerInformationMapper.selectOne(new LambdaQueryWrapper<RemoteControllerInformationDO>()
                .eq(RemoteControllerInformationDO::getTaskDetailId, data.getId())
                .last("limit 1"));
        if (ObjectUtil.isNotEmpty(detail)) {
            throw exception(REMOTE_TASK_HAVE_CHOOSE);
        }
        //任务异常
        RobotTaskDetailDO taskDetail = taskDetailService.getTaskDetail(data.getId());
        String oldRobotNo = taskDetail.getRobotNo();
        List<RobotDoingTaskDTO> robotDoingTaskNo = taskDetailService.getRobotDoingTaskNo(information.getRobotNo());
        if (ObjectUtil.isNotEmpty(robotDoingTaskNo)) {
            throw exception(REMOTE_ROBOT_HAVE_TASK);
        }
        if (ZeroOneEnum.ZERO.getType().equals(taskDetail.getOccurError())) {
            throw exception(REMOTE_TASK_NOT_OCCUR_ERROR);
        }
        if (information.getRobotNo().equals(oldRobotNo)) {
            throw exception(REMOTE_TRANSFER_NOT_SAME_ROBOT);
        }
        taskDetail.setRobotNo(information.getRobotNo());
        if (!RobotTaskTypeEnum.TAKE_RELEASE.getType().equals(taskDetail.getTaskType())
                && !RobotTaskTypeEnum.TAKE.getType().equals(taskDetail.getTaskType())
                && !RobotTaskTypeEnum.RELEASE.getType().equals(taskDetail.getTaskType())) {
            throw exception(REMOTE_TASK_NOT_TAKE_RELEASE);
        }
        RobotTaskDetailSaveReqVO updateReqVO = BeanUtils.toBean(taskDetail, RobotTaskDetailSaveReqVO.class);
        updateReqVO.setOccurError(ZeroOneEnum.ZERO.getType());
        taskDetailService.updateTaskDetail(updateReqVO);

        List<RobotTaskDetailActionLogDO> taskDetailActionLog = taskDetailActionLogService.getTaskDetailActionLogByDetailId(data.getId());
        if (ObjectUtil.isNotEmpty(taskDetailActionLog)) {
            taskDetailActionLog.forEach(v -> {
                v.setRobotNo(information.getRobotNo());
            });
        }
        taskDetailActionLogService.updateTaskDetailActionLogs(taskDetailActionLog);
        //记录
        ControllerTaskTransferLogSaveReqVO createReqLog = new ControllerTaskTransferLogSaveReqVO();
        createReqLog.setRemoteIp(information.getRemoteIp());
        createReqLog.setTaskDetailId(data.getId());
        createReqLog.setTaskStage(taskDetail.getTaskStage());
        createReqLog.setTaskNo(taskDetailService.getTaskNoByDetailId(data.getId()));
        createReqLog.setRobotNo(oldRobotNo);
        createReqLog.setRemoteMode(data.getRemoteMode());
        createReqLog.setTransferRobotNo(information.getRobotNo());
        controllerTaskTransferLogService.createControllerTaskTransferLog(createReqLog);

        information.setRemoteMode(data.getRemoteMode());
        information.setTaskDetailId(data.getId());
        controllerInformationMapper.updateById(information);

        //任务转移重新下发车机和PP
        informationService.rmoteTransferTaskToNewFreeRobo(oldRobotNo, data.getId(), information.getRobotNo());

        //操作信息
        createUserOperateAction(taskDetail, oldRobotNo);
    }

    /**
     * 操作信息
     *
     * @param taskDetail
     * @param oldRobotNo
     * @return
     */
    public void createUserOperateAction(RobotTaskDetailDO taskDetail, String oldRobotNo) {
        String action = "放货任务库位 " + taskDetail.getToLocationNo();
        if (RobotTaskTypeEnum.TAKE_RELEASE.getType().equals(taskDetail.getTaskType())) {
            action = "取货库位任务 " + taskDetail.getFromLocationNo() + " 放货库位任务 " + taskDetail.getToLocationNo();
        } else if (RobotTaskTypeEnum.TAKE.getType().equals(taskDetail.getTaskType())) {
            action = "取货库位任务 " + taskDetail.getFromLocationNo();
        }

        String msg = "远遥将 " + action + "从旧车辆 " + oldRobotNo + " 转移给新车辆 " + taskDetail.getRobotNo();
        UserOperationLogSaveReqVO build = UserOperationLogSaveReqVO.builder()
                .operateAction(msg)
                .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();
        userOperationLogService.createUserOperationLog(build);
    }

    /**
     * @param remoteMode
     * @param remoteIp
     * @param robotNo
     */
    @Transactional(rollbackFor = Exception.class)
    public RemoteRobotTransferDTO doRobotChangeMode(Integer remoteMode, String remoteIp, String robotNo,int remoteControllerPort,String remoteControllerIp) {

        //后续判断下，如果协控中，能不能切换模式

        RemoteControllerInformationDO information = controllerInformationMapper.selectOne(new LambdaQueryWrapper<RemoteControllerInformationDO>()
                .eq(RemoteControllerInformationDO::getRemoteIp, remoteIp)
                .last("limit 1"));

        if (ObjectUtil.isEmpty(information)) {
            throw exception(REMOTE_DEVICE_NOT_SET_UP);
        }

        if (ObjectUtil.isEmpty(information.getRobotNo()) && !RemoteModeEnum.AUTOMATIC.getType().equals(remoteMode)) {
            throw exception(REMOTE_DEVICE_NEED_AUTOMATIC);
        }

        if (!RemoteModeEnum.AUTOMATIC.getType().equals(remoteMode)) {
            try {
                Socket socket = new Socket();
                socket.connect(new InetSocketAddress(remoteControllerIp, remoteControllerPort), 1000);
                socket.close();
            } catch (IOException e) {
                throw exception(REMOTE_ROBOT_CONNECT_FAIL);
            }
        }

        RobotInformationDO robotInformation = informationService.getInformationByRobotNo(robotNo);
        informationService.checkIpAndPort(robotInformation);
        if (ObjectUtil.isEmpty(robotInformation.getRobotIp()) || ObjectUtil.isEmpty(robotInformation.getRobotPort()) ) {
            throw exception(ROBOT_IP_OR_PORT_EMPTY);
        }
        RemoteRobotTransferDTO build =
                RemoteRobotTransferDTO.builder()
                        .robotNo(robotInformation.getRobotNo())
                        .robotIp(robotInformation.getRobotIp())
                        .robotPort(robotInformation.getRobotPort()).build();


        if (ObjectUtil.isNotEmpty(information.getRobotNo()) && !information.getRobotNo().equals(robotNo)) {
            String msg = "目前远遥设备控制的机器人是 " + information.getRobotNo() + " 需要先将此机器人退出远遥";
            throw exception0(REMOTE_DEVICE_CONTROLLER.getCode(), msg, msg);
        }

        //第一次设置为自动模式
        if (ObjectUtil.isEmpty(information.getRobotNo()) && RemoteModeEnum.AUTOMATIC.getType().equals(remoteMode)) {

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

            List<RobotDoingTaskDTO> robotDoingTaskNo = taskDetailService.getRobotDoingTaskNo(robotNo);
            if (ObjectUtil.isNotEmpty(robotDoingTaskNo) && robotDoingTaskNo.size() > 1) {
                throw exception(REMOTE_ROBOT_HAVE_MORE_TASK);
            }

            String operateAction = information.getRemoteIp() + " 开启远遥为自动模式";
            information.setTaskDetailId(null);
            setRemoteChangeLog(information, robotNo, remoteMode, operateAction);
            sendRemoteMsgToPP(robotNo, PathPlanningTopicConstant.START_REMOTE_CONTROLLER, RemoteOperationModeEnum.AUTO.getType());
            sendModeToRobot(robotNo, RemoteOperationModeEnum.AUTO.getType());
            return build;
        }

        if (ObjectUtil.isNotEmpty(information.getRobotNo()) && information.getRobotNo().equals(robotNo)
                && ObjectUtil.isNotEmpty(information.getRemoteMode()) && information.getRemoteMode().equals(remoteMode)) {
            UserOperationLogSaveReqVO operationLog = UserOperationLogSaveReqVO.builder()
                    .operateAction(information.getRemoteIp() + " 重新进入远遥 " + RemoteModeEnum.getMsg(remoteMode))
                    .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();
            userOperationLogService.createUserOperationLog(operationLog);
            sendRemoteMsgToPP(robotNo, PathPlanningTopicConstant.START_REMOTE_CONTROLLER, RemoteOperationModeEnum.getType(information.getRemoteMode()));
            sendModeToRobot(robotNo, RemoteOperationModeEnum.getType(remoteMode));
            return build;
        }

        //自动切手动
        if (RemoteModeEnum.AUTOMATIC.getType().equals(information.getRemoteMode()) && RemoteModeEnum.HAND_MOVEMENT.getType().equals(remoteMode)) {
            List<RobotDoingTaskDTO> robotDoingTaskNo = taskDetailService.getRobotDoingTaskNo(robotNo);
            if (ObjectUtil.isEmpty(robotDoingTaskNo)) {
                throw exception(REMOTE_NOT_HAVE_TASK);
            }
            if (ObjectUtil.isNotEmpty(robotDoingTaskNo) && robotDoingTaskNo.size() > 1) {
                throw exception(REMOTE_ROBOT_HAVE_MORE_TASK);
            }

            String operateAction = information.getRemoteIp() + " 切换远遥模式为 " + RemoteModeEnum.getMsg(remoteMode);
            setRemoteChangeLog(information, robotNo, remoteMode, operateAction);
            sendRemoteMsgToPP(robotNo, PathPlanningTopicConstant.START_REMOTE_CONTROLLER, RemoteOperationModeEnum.MAN.getType());
            sendModeToRobot(robotNo, RemoteOperationModeEnum.MAN.getType());
            return build;
        }

        //自动切自由
        if (RemoteModeEnum.AUTOMATIC.getType().equals(information.getRemoteMode()) && RemoteModeEnum.FREE.getType().equals(remoteMode)) {
            List<RobotDoingTaskDTO> robotDoingTaskNo = taskDetailService.getRobotDoingTaskNo(robotNo);
            if (ObjectUtil.isNotEmpty(robotDoingTaskNo)) {
                throw exception(REMOTE_HAVE_TASK);
            }

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

            if (!RobotStatusEnum.STAND_BY.getType().equals(robotInformation.getRobotStatus())) {
                throw exception(ROBOT_NOT_STAND_BY);
            }

            //校验电量是否充足
            checkElectricity(robotInformation);

            String operateAction = information.getRemoteIp() + " 切换远遥模式为 " + RemoteModeEnum.getMsg(remoteMode);
            setRemoteChangeLog(information, robotNo, remoteMode, operateAction);
            sendRemoteMsgToPP(robotNo, PathPlanningTopicConstant.START_REMOTE_CONTROLLER, RemoteOperationModeEnum.MAN.getType());
            sendModeToRobot(robotNo, RemoteOperationModeEnum.MAN.getType());
            return build;
        }

        //手动切自动
        if (RemoteModeEnum.HAND_MOVEMENT.getType().equals(information.getRemoteMode()) && RemoteModeEnum.AUTOMATIC.getType().equals(remoteMode)) {
            checkGraphMatch(robotNo);

            String operateAction = information.getRemoteIp() + " 切换远遥模式为 " + RemoteModeEnum.getMsg(remoteMode);
            setRemoteChangeLog(information, robotNo, remoteMode, operateAction);
            sendRemoteMsgToPP(robotNo, PathPlanningTopicConstant.START_REMOTE_CONTROLLER, RemoteOperationModeEnum.AUTO.getType());
            sendModeToRobot(robotNo, RemoteOperationModeEnum.AUTO.getType());
            return build;
        }

        //手动切自由
        if (RemoteModeEnum.HAND_MOVEMENT.getType().equals(information.getRemoteMode()) && RemoteModeEnum.FREE.getType().equals(remoteMode)) {
            log.info("手动模式不能切换到自由模式 :{}", remoteIp);
            throw exception(REMOTE_AUTOMATIC_CAN_NOT_CHANGE_TO_FREE);
        }

        //自由切手动
        if (RemoteModeEnum.FREE.getType().equals(information.getRemoteMode()) && RemoteModeEnum.HAND_MOVEMENT.getType().equals(remoteMode)) {
            log.info("自由模式不能切换到手动模式 :{}", remoteIp);
            throw exception(REMOTE_FREE_CAN_NOT_CHANGE_TO_HAND_MOVEMENT);
        }

        //自由切自动
        if (RemoteModeEnum.FREE.getType().equals(information.getRemoteMode()) && RemoteModeEnum.AUTOMATIC.getType().equals(remoteMode)) {
            checkGraphMatch(robotNo);
            String operateAction = information.getRemoteIp() + " 切换远遥模式为 " + RemoteModeEnum.getMsg(remoteMode);
            setRemoteChangeLog(information, robotNo, remoteMode, operateAction);
            sendRemoteMsgToPP(robotNo, PathPlanningTopicConstant.START_REMOTE_CONTROLLER, RemoteOperationModeEnum.AUTO.getType());
            sendModeToRobot(robotNo, RemoteOperationModeEnum.AUTO.getType());
        }
        return build;
    }

    /**
     * 校验车辆是否在路网上
     *
     * @param robotNo
     */
    public void checkGraphMatch(String robotNo) {
        String graphMatchKey = PathPlanningChcheConstant.PATH_PLANNING_GRAPH_MATCH + robotNo;
        Object o = redisUtil.get(graphMatchKey);
        if (ObjectUtil.isEmpty(o) || !Boolean.parseBoolean(String.valueOf(o))) {
            throw exception(REMOTE_NOT_HAVE_PATH_MATCH);
        }

        String speedKey = RobotTaskChcheConstant.ROBOT_SPEED_FORK_HEIGHT + robotNo;
        Object speedObject = redisUtil.get(speedKey);
        if (ObjectUtil.isEmpty(speedObject)) {
            throw exception(REMOTE_NOT_HAVE_SPEED);
        }
        RobotReactiveStatusDTO data = JSON.parseObject(speedObject.toString(), RobotReactiveStatusDTO.class);
        String linearSpeed = Optional.ofNullable(data).map(RobotReactiveStatusDTO::getAgvSpeed).map(RobotStatusDataSpeedDTO::getLinearSpeed).orElse(null);
        String angleSpeed = Optional.ofNullable(data).map(RobotReactiveStatusDTO::getAgvSpeed).map(RobotStatusDataSpeedDTO::getAngleSpeed).orElse(null);
        if (ObjectUtil.isEmpty(linearSpeed) || ObjectUtil.isEmpty(angleSpeed)) {
            throw exception(REMOTE_NOT_HAVE_SPEED);
        }
        BigDecimal zeroSpeed = new BigDecimal("0");
        BigDecimal linearSpeedBigDecimal = new BigDecimal(linearSpeed);
        BigDecimal angleSpeedBigDecimal = new BigDecimal(angleSpeed);
        if (zeroSpeed.compareTo(linearSpeedBigDecimal) < 0 || zeroSpeed.compareTo(angleSpeedBigDecimal) < 0 ){
            throw exception(REMOTE_SPEED_MORE_THAN_ZERO);
        }
    }

    /**
     * 让车机切换模式
     */
    public void sendModeToRobot(String robotNo, String operationMode) {
        RobotModeDTO robotModeDTO = new RobotModeDTO();
        robotModeDTO.setCommandType(RobotCommandTypeEnum.MODE.getType());
        robotModeDTO.setOperationMode(operationMode);
        if (RemoteOperationModeEnum.MAN.getType().equals(operationMode)) {
            String maxSpeed = robotMaxSpeedService.getRobotMaxSpeedByRobotNo(robotNo);
            double v = ObjectUtil.isNotEmpty(maxSpeed) ? Double.parseDouble(maxSpeed) : 0.0;
            robotModeDTO.setMaxSpeed(v);
        }
        String mac = informationService.getMacByRobotNo(robotNo);
//        commonApi.commonMethod(robotModeDTO, RobotTopicConstant.ROBOT_COMMAND_TOPIC + mac);
        mqttUtils.pub(RobotTopicConstant.ROBOT_COMMAND_TOPIC + mac,JSON.toJSONString(robotModeDTO));
        log.info("发送消息让车机 :{} 切换模式 :{}", robotNo, JSON.toJSONString(robotModeDTO));
    }

    /**
     * 发送远遥信息给PP
     *
     * @param robotNo
     */
    public void sendRemoteMsgToPP(String robotNo, String topic, String operationMode) {
        RemotePathPlanningDTO remotePathPlanning = new RemotePathPlanningDTO();
        remotePathPlanning.setRobotNo(robotNo);
        remotePathPlanning.setOperationMode(operationMode);
//        commonApi.commonMethod(remotePathPlanning, topic);
        mqttUtils.pub(topic,JSON.toJSONString(remotePathPlanning));
    }

    /**
     * 远遥切换，添加记录
     */
    public void setRemoteChangeLog(RemoteControllerInformationDO information, String robotNo, Integer remoteMode, String operateAction) {
        information.setRobotNo(robotNo);
        information.setRemoteMode(remoteMode);
//        information.setCollaborativeControl(ZeroOneEnum.ZERO.getType());
        controllerInformationMapper.updateById(information);

        UserOperationLogSaveReqVO operationLog = UserOperationLogSaveReqVO.builder()
                .operateAction(operateAction)
                .nickName(SecurityFrameworkUtils.getLoginUserNickname()).build();

        userOperationLogService.createUserOperationLog(operationLog);
    }


    /**
     * 校验电量是否充足
     *
     * @param information
     */
    private void checkElectricity(RobotInformationDO information) {

        CommonConfigDO commonConfig = configMapper.selectOne(new LambdaQueryWrapper<CommonConfigDO>()
                .eq(CommonConfigDO::getConfigType, CommandConfigTypeEnum.CHARG_CONFIG.getType()));
        //校验机器人是否到达设定的电量
        Boolean canDoTask = informationService.checkRobotElectricity(information, commonConfig);
        if (!canDoTask) {
            log.info("车辆电量不足");
            throw exception(ROBOT_ELECTRICITY_INSUFFICIENT);
        }
    }


}
