package com.shkj.wcs.plc;

import com.shkj.common.constant.HttpStatus;
import com.shkj.common.constant.RedisConstant;
import com.shkj.common.core.domain.Result;
import com.shkj.common.utils.DateUtils;
import com.shkj.plc.sdk.device.PlcHelper;
import com.shkj.plc.sdk.device.PlcOperate;
import com.shkj.system.service.ISysParameterService;
import com.shkj.wcs.bo.WcsPlcConnectQueryBo;
import com.shkj.wcs.domain.WcsPlcConnect;
import com.shkj.wcs.domain.WcsPlcProperty;
import com.shkj.wcs.mapper.WcsPlcConnectMapper;
import com.shkj.wcs.service.IWcsPlcConnectService;
import com.shkj.wcs.service.IWcsPlcPropertyService;
import com.shkj.wcs.third.rcs.RCSUtil;
import com.shkj.wcs.third.stacker.StkCallUtil;
import com.shkj.wcs.vo.WcsPlcConnectVo;
import com.shkj.wms.bo.STKMoveBo;
import com.shkj.wms.bo.UpdPlanErrorBo;
import com.shkj.wms.bo.UpdPlanLocationBo;
import com.shkj.wms.bo.WcsAddStockCallbackBo;
import com.shkj.wms.domain.BaseLocation;
import com.shkj.wms.domain.BusinOutInPlan;
import com.shkj.wms.domain.BusinPlcException;
import com.shkj.wms.domain.BusinPlcStatus;
import com.shkj.wms.enums.OutOrderStatusEnum;
import com.shkj.wms.enums.PlanStatusEnum;
import com.shkj.wms.enums.PlcPropertyEnum;
import com.shkj.wms.service.IBusinDataTaskService;
import com.shkj.wms.service.IBusinOutInPlanService;
import com.shkj.wms.service.IBusinPlcExceptionService;
import com.shkj.wms.service.IPutInOutStorageService;
import com.shkj.wms.utils.JsonUtil;
import com.shkj.wms.utils.WmsCallWcsUtil;
import com.shkj.wms.vo.OutStockDetailVo;
import com.shkj.wms.vo.PlanOrderLocationVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.*;

import  com.shkj.plc.sdk.device.PLCReadAndWrite;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import oshi.jna.platform.mac.SystemB;


@Slf4j
@Service("PlcEventTask")
@EnableScheduling
public class PlcEventTask {

    @Autowired
    IWcsPlcConnectService iWcsPlcConnectService;

    @Resource
    RedisTemplate redisTemplate;

    @Autowired
    RCSUtil rcsUtil;

    @Autowired
    IWcsPlcPropertyService iWcsPlcPropertyService;

    @Autowired
    IBusinDataTaskService iBusinDataTaskService;

    @Autowired
    IPutInOutStorageService iPutInOutStorageService;

    @Autowired
    ISysParameterService isysParameterService;

    @Resource
    WcsPlcConnectMapper wcsPlcConnectMapper;

    @Autowired
    IBusinOutInPlanService  iBusinOutInPlanService;

    @Autowired
    WmsCallWcsUtil wmsCallWcsUtil;

    @Autowired
    StkCallUtil stkCallUtil;

    @Autowired
    IBusinPlcExceptionService iBusinPlcExceptionService;

    @Autowired
    PlcInit plcInit;

    @Scheduled(fixedDelay = 8000)
    public void init() {
        //查询需要连接的PLC,初始化连接对象
        List<WcsPlcConnectVo> plcList = iWcsPlcConnectService.selectWcsPlcConnectList(new WcsPlcConnect().setIsActive("1"));

        if (plcList.size() == 0) {
            return;
        }

        for (WcsPlcConnectVo conn : plcList) {
            if (conn.getPlcIp().contains("155.180")){
                plcTask(plcInit.getPlcOperate_stk(), conn, conn.getPointTables());
            }

            if (conn.getPlcIp().contains("155.170")){
                plcTask(plcInit.getPlcOperate_dps(), conn, conn.getPointTables());
            }
        }
    }


    //输送线心跳
    @Scheduled(fixedDelay = 4000)
    public void writePlcheartbeatDps(){
        //根据连接的plcId获取属性
        PlcOperate plcOperate = plcInit.getPlcOperate_dps();
        PLCReadAndWrite plcReadAndWrite = new PLCReadAndWrite();
        Map<String, Object> readPointMap =  new HashMap<>();
        String heartbeat="DB320.0.0";
        readPointMap.put(heartbeat,"Boolean");
        Map<String, Object> pointMap = plcReadAndWrite.readPlcData(plcOperate, JsonUtil.toJSONString(readPointMap));

        //读取心跳，并判断是写入true还是false;
        Boolean heartbeatV = false;

        if (pointMap.containsKey(heartbeat)) {
            heartbeatV = Boolean.valueOf(pointMap.get(heartbeat).toString());
        }
        heartbeatV = (heartbeatV == true) ? false : true;
        Map<String, Object> writeHbMap = new HashMap<>(1);
        writeHbMap.put("Boolean", heartbeatV);
        Map<String, Object> allHbMap = new HashMap<>(1);
        allHbMap.put(heartbeat, writeHbMap);
        Boolean heartType = plcReadAndWrite.execWrite(plcOperate, JsonUtil.toJSONString(allHbMap));
        if(!heartType){
            log.info("dps心跳写入失败，重连服务器");
            plcInit.connect();
        }
    }
    //堆垛机心跳
    @Scheduled(fixedDelay = 4000)
    public void writePlcheartbeatStk(){
        //根据连接的plcId获取属性
        PlcOperate plcOperate = plcInit.getPlcOperate_stk();
        PLCReadAndWrite plcReadAndWrite = new PLCReadAndWrite();
        Map<String, Object> readPointMap =  new HashMap<>();
        String heartbeat="DB20.0.4";
        readPointMap.put(heartbeat,"Boolean");
        Map<String, Object> pointMap = plcReadAndWrite.readPlcData(plcOperate, JsonUtil.toJSONString(readPointMap));

        //读取心跳，并判断是写入true还是false;
        Boolean heartbeatV = false;

        if (pointMap.containsKey(heartbeat)) {
            heartbeatV = Boolean.valueOf(pointMap.get(heartbeat).toString());
        }
        heartbeatV = (heartbeatV == true) ? false : true;
        Map<String, Object> writeHbMap = new HashMap<>(1);
        writeHbMap.put("Boolean", heartbeatV);
        Map<String, Object> allHbMap = new HashMap<>(1);
        allHbMap.put(heartbeat, writeHbMap);
        Boolean heartType = plcReadAndWrite.execWrite(plcOperate, JsonUtil.toJSONString(allHbMap));
        if(!heartType){
            log.info("stk心跳写入失败，重连服务器");
            plcInit.connect();
        }
    }

    public void plcTask(PlcOperate plcOperate, WcsPlcConnectVo conn, String pointTables) {

            Long plcId = conn.getId();
            //根据连接的plcId获取属性
            PLCReadAndWrite plcReadAndWrite = new PLCReadAndWrite();
            Map<String, Object> pointMap = plcReadAndWrite.readPlcData(plcOperate, pointTables);
       //     log.info("连接的IP:"+conn.getPlcIp()+":设备："+conn.getConnectName()+"   读取的点位内容："+pointMap);

            List<WcsPlcProperty> propertyList = (List<WcsPlcProperty>)redisTemplate.opsForValue().get(RedisConstant.redisPlcProperty + plcId);
            if(propertyList == null || propertyList.size()==0){
                propertyList = iWcsPlcPropertyService.getWcsPlcPropertyByPlcId(plcId);
            }
            //任务ID
            Long taskId = 0L;
            // 设备类型：2输送线  1 堆垛机
            switch (conn.getDevType()) {
                //输送线
                case "2":
                    dpsRealTimeTask(propertyList, pointMap, plcReadAndWrite,plcOperate );
                    doExecMsg(propertyList,pointMap,"dps");
                    doDevStatus(propertyList,pointMap);
                    break;
                case "1":
                    //堆垛机
                    stkRealTimeTask(propertyList, pointMap, plcReadAndWrite,plcOperate,pointTables );
                    doExecMsg(propertyList,pointMap,"stk");
                    doDevStatus(propertyList,pointMap);
                    break;
                default:
                    log.info("无匹配的设备类型");
                    break;
            }


    }


    /**
     * 处理设备报警信息
     * @param propertyList
     * @param pointMap
     */
    private void doExecMsg(List<WcsPlcProperty> propertyList,Map<String, Object> pointMap,String devType){

        try {
            BusinPlcException businPlcException=null;
            List<BusinPlcException> execList = new ArrayList<>();
            for (WcsPlcProperty property : propertyList) {
                String pointType = property.getPointType();
                String devpName = property.getDevpName();
                String address = property.getAddress();
                businPlcException = new BusinPlcException();
                if (pointType.equals("99")){
                    if (pointMap.containsKey(address) && Boolean.valueOf((pointMap.get(address).toString()))){
                        businPlcException.setPlcPoint(address);
                        businPlcException.setExecMsg(devpName);
                        businPlcException.setExecDate(DateUtils.getTime());
                        businPlcException.setExecDev(devType);
                        execList.add(businPlcException);
                    }
                }
               // iBusinPlcExceptionService.saveBatch(execList);
            }
            redisTemplate.opsForValue().set(RedisConstant.redisPlcExecMsg+devType,execList );
        }catch (Exception e ){
         log.error("处理报警信息异常{}"+e.getMessage());
        }

    }


    /**
     * 处理设备状态
     * @param propertyList
     * @param pointMap
     */
    private void doDevStatus(List<WcsPlcProperty> propertyList,Map<String, Object> pointMap){
        try {
           /* BusinPlcStatus businPlcStatus =(BusinPlcStatus)redisTemplate.opsForValue().get(RedisConstant.redisPlcStatus);
            if (businPlcStatus == null){
                businPlcStatus = new BusinPlcStatus();
            }*/
            Long plcId =0L;
            BusinPlcStatus  businPlcStatus = new BusinPlcStatus();
            for (WcsPlcProperty property : propertyList) {
                plcId = property.getPlcId();

                String pointType = property.getPointType();
                String address = property.getAddress();
                String devCode = property.getRelPoint();
                if (pointType.equals("run")){
                    if (pointMap.containsKey(address) && Boolean.valueOf((pointMap.get(address).toString()))){
                        if (devCode.equals("dps")){
                            businPlcStatus.setDpsRun(pointMap.get(address).toString());
                        }else if (devCode.equals("stk")){
                            businPlcStatus.setStkRun(pointMap.get(address).toString());
                        }else if (devCode.equals("dts")){
                            businPlcStatus.setDtsRun(pointMap.get(address).toString());
                        }
                    }
                }

                if (pointType.equals("wait")){
                    if (pointMap.containsKey(address) && Boolean.valueOf((pointMap.get(address).toString()))){
                        if (devCode.equals("dps")){
                            businPlcStatus.setDpsWait(pointMap.get(address).toString());
                        }else if (devCode.equals("stk")){
                            businPlcStatus.setStkWait(pointMap.get(address).toString());
                        }else if (devCode.equals("dts")){
                            businPlcStatus.setDtsWait(pointMap.get(address).toString());
                        }
                    }
                }

                if (pointType.equals("pause")){
                    if (pointMap.containsKey(address) && Boolean.valueOf((pointMap.get(address).toString()))){
                        if (devCode.equals("dps")){
                            businPlcStatus.setDpsPause(pointMap.get(address).toString());
                        }else if (devCode.equals("stk")){
                            businPlcStatus.setStkPause(pointMap.get(address).toString());
                        }else if (devCode.equals("dts")){
                            businPlcStatus.setDtsPause(pointMap.get(address).toString());
                        }
                    }
                }

                if (pointType.equals("fault")){
                    if (pointMap.containsKey(address) && Boolean.valueOf((pointMap.get(address).toString()))){
                        if (devCode.equals("dps")){
                            businPlcStatus.setDpsFault(pointMap.get(address).toString());
                        }else if (devCode.equals("stk")){
                            businPlcStatus.setStkFault(pointMap.get(address).toString());
                        }else if (devCode.equals("dts")){
                            businPlcStatus.setDtsFault(pointMap.get(address).toString());
                        }
                    }
                }
            }
            //log.info("处理设备状态的Id:"+plcId);
            if (plcId.toString().equals("48")){
                redisTemplate.opsForValue().set(RedisConstant.redisStkStatus,businPlcStatus );
            }else {
                redisTemplate.opsForValue().set(RedisConstant.redisPlcStatus,businPlcStatus );
            }

        }catch (Exception e ){
            log.error("处理设备状态{}"+e.getMessage());
        }
    }


    public void dpsRealTimeTask(List<WcsPlcProperty> propertyList,Map<String, Object> pointMap, PLCReadAndWrite plcReadAndWrite,PlcOperate plcOperate ) {
        try {
            String oneAllotAddress = "", oneAllotTaskId = "", oneAllotTaskIdVt = "";
            String twoAllotAddress = "", twoAllotTaskId = "", twoAllotTaskIdVt = "";

            String  autoAddress = "";

            //货物高度wms的值
            String cargoHeight ="";
            //托盘条码地址
            String onePalletAddress = "",twoPalletAddress="";

            //请求入库
            String oneReqPutin = "",twoReqPutin = "";
            //请求入库任务ID
            String oneReqPutInTaskId = "",twoReqPutInTaskId = "";

            //货位高度
            String oneHignType = "",twoHignType = "";
            String oneHignTypeVT = "",twoHignTypeVT = "";

            /**
             * 失败原因 超长  左超宽 右超宽 超高
             */
            String oneOverlength="",oneLwide="",oneRwide="",oneHigh="";
            String twoOverlength="",twoLwide="",twoRwide="",twoHigh="";

            Boolean oneOverlengthV=false,oneLwideV=false,oneRwideV=false,oneHighV=false;
            Boolean twoOverlengthV=false,twoLwideV=false,twoRwideV=false,twoHighV=false;

            String oneWcsErrExit ="",twoWcsErrExit ="";
            String oneWcsErrExitVT="",twoWcsErrExitVT ="";
            for (WcsPlcProperty plcProperty : propertyList) {
                String pointType = plcProperty.getPointType();
                //请求分配
                if (pointType.equals("oneAllot")) {
                    oneAllotAddress = plcProperty.getAddress();
                }else  if (pointType.equals("twoAllot")) {
                    twoAllotAddress = plcProperty.getAddress();
                } else if (pointType.equals("oneAllotTask")) {
                    oneAllotTaskId = plcProperty.getAddress();
                    oneAllotTaskIdVt = plcProperty.getValueType();
                } else if (pointType.equals("twoAllotTask")) {
                    twoAllotTaskId = plcProperty.getAddress();
                    twoAllotTaskIdVt = plcProperty.getValueType();
                } else if (pointType.equals("atuo")) {
                    autoAddress = plcProperty.getAddress();
                } else if (pointType.equals("oneInBar")) {
                    onePalletAddress = plcProperty.getAddress();
                } else if (pointType.equals("twoInBar")) {
                    twoPalletAddress = plcProperty.getAddress();
                } else if (pointType.equals("oneReqIn")) {
                    oneReqPutin = plcProperty.getAddress();
                } else if (pointType.equals("twoReqIn")) {
                    twoReqPutin = plcProperty.getAddress();
                } else if (pointType.equals("oneInTaskId")) {
                    oneReqPutInTaskId = plcProperty.getAddress();
                } else if (pointType.equals("twoInTaskId")) {
                    twoReqPutInTaskId = plcProperty.getAddress();
                } else if (pointType.equals("oneHighType")) {
                    //货物高度
                    oneHignType = plcProperty.getAddress();
                    oneHignTypeVT = plcProperty.getValueType();
                } else if (pointType.equals("twoHighType")) {
                    //货物高度
                    twoHignType = plcProperty.getAddress();
                    twoHignTypeVT = plcProperty.getValueType();
                } else if (pointType.equals("oneOverlength")) {
                    oneOverlength =plcProperty.getAddress();
                } else if (pointType.equals("oneLwide")) {
                    oneLwide =plcProperty.getAddress();
                } else if (pointType.equals("oneRwide")) {
                    oneRwide =plcProperty.getAddress();
                } else if (pointType.equals("oneHigh")){
                    oneHigh  =plcProperty.getAddress();
                } else if (pointType.equals("twoOverlength")) {
                    twoOverlength =plcProperty.getAddress();
                } else if (pointType.equals("twoLwide")) {
                    twoLwide =plcProperty.getAddress();
                } else if (pointType.equals("twoRwide")) {
                    twoRwide =plcProperty.getAddress();
                } else if (pointType.equals("twoHigh")){
                    twoHigh  =plcProperty.getAddress();
                } else if (pointType.equals("ontExit")){
                    oneWcsErrExit  =plcProperty.getAddress();
                    oneWcsErrExitVT = plcProperty.getValueType();
                } else if (pointType.equals("twoExit")){
                    twoWcsErrExit  =plcProperty.getAddress();
                    twoWcsErrExitVT = plcProperty.getValueType();
                }
            }

            // 电柜状态是否是自动，如果是自动则继续流程
            if (pointMap.containsKey(autoAddress) && Boolean.valueOf(pointMap.get(autoAddress).toString())) {
                Long taskIdOne = 0L;
                //1楼是否请求分配
                if (pointMap.containsKey(oneAllotAddress) && Boolean.valueOf(pointMap.get(oneAllotAddress).toString())) {
                    //请求入库分配时，判断是否有正在执行的出库任务，如果有则返回
                    Object taskOutId = redisTemplate.opsForValue().get(RedisConstant.redisTaskOut);
                    if (taskOutId !=null){
                        log.info("1楼入库分配请求时，有出库任务，不可分配，返回");
                        return;
                    }

                    //读取托盘条码内容
                    String barcodeValueOne = pointMap.get(onePalletAddress).toString();
                    log.info("1楼请求入库时，读取的条码内容："+barcodeValueOne);
                    //根据托盘条码返回正在执行的任务号
                    Result<PlanOrderLocationVo> orderResult = iBusinOutInPlanService.getOrderNoByContainerCode(barcodeValueOne, "in");
                    log.info("1根据条码获取的任务信息："+orderResult.getData());
                    if (Result.isOk(orderResult)) {
                        PlanOrderLocationVo data = orderResult.getData();
                        taskIdOne = data.getOrderNo();
                        cargoHeight = data.getType();//获取托盘或模具类型
                    }
                    else {
                        redisTemplate.opsForValue().set(RedisConstant.redisPutInExeMsg+orderResult.getMsg(),orderResult.getMsg());
                        log.info("获取任务信息失败："+orderResult.getMsg()+"托盘条码："+barcodeValueOne);
                        //回填任务号
                        Map<String, Integer> writeMap = new HashMap<>(1);
                        writeMap.put(oneWcsErrExitVT, 1);
                        Map<String, Object> allMap = new HashMap<>(1);
                        allMap.put(oneWcsErrExit, writeMap);
                        Boolean errBoolean = plcReadAndWrite.execWrite(plcOperate, JsonUtil.toJSONString(allMap));
                        if (errBoolean){
                            log.info("1楼获取单号入库失败，写入点位成功{}",allMap);
                        }else {
                            log.info("1楼获取单号入库失败，写入点位失败{}",allMap);
                        }
                    }
                    log.info("1楼入库分配任务ID："+taskIdOne + "条码号："+ barcodeValueOne);
                    log.info("1楼入库分配货位类型："+cargoHeight);
                    String redisBarcodeOne = String.valueOf(redisTemplate.opsForValue().get(RedisConstant.redisContainer+taskIdOne));

                    //根据扫码的托盘，判断是否已经下任务了
                    if (taskIdOne != 0L && redisBarcodeOne !=null && !redisBarcodeOne.equals(barcodeValueOne)) {
                        //回填任务号
                        Map<String, Long> writeMap = new HashMap<>(1);
                        writeMap.put(oneAllotTaskIdVt, taskIdOne);
                        Map<String, Object> allMap = new HashMap<>(1);
                        allMap.put(oneAllotTaskId, writeMap);

                        Map<String, String> cargoHeightMap = new HashMap<>(1);
                        cargoHeightMap.put(oneHignTypeVT, cargoHeight);
                        allMap.put(oneHignType, cargoHeightMap);

                        Map<String, Integer> wcsErrwriteMap = new HashMap<>(1);
                        wcsErrwriteMap.put(oneWcsErrExitVT, 0);
                        allMap.put(oneWcsErrExit,wcsErrwriteMap);
                        log.info("1楼按下入库按钮时，给输送线写命令数据：{}"+allMap);

                        Boolean taskBoolean = plcReadAndWrite.execWrite(plcOperate, JsonUtil.toJSONString(allMap));
                        if (taskBoolean) {
                            //分配任务成功时，记录已下发入库任务，不可在下发出库或入库任务
                            redisTemplate.opsForValue().set(RedisConstant.redisTaskIn,taskIdOne);
                            //更改入库计划为1
                            iBusinOutInPlanService.updPlanStatus(taskIdOne, PlanStatusEnum.DOING.getValue());
                        }
                        //改为入库ID
                        redisTemplate.opsForValue().set(RedisConstant.redisContainer, taskIdOne, 10, TimeUnit.SECONDS);
                    }
                }
                //2楼是否请求分配
                if (pointMap.containsKey(twoAllotAddress) && Boolean.valueOf(pointMap.get(twoAllotAddress).toString())) {
                    Long taskIdTwo=0L;
                    //请求入库分配时，判断是否有正在执行的出库任务，如果有则返回
                    Object taskOutId = redisTemplate.opsForValue().get(RedisConstant.redisTaskOut);
                    if (taskOutId !=null){
                        log.info("2楼入库分配请求时，有出库任务，不可分配，返回");
                        return;
                    }
                    //读取托盘条码内容
                    String barcodeValueTwo = pointMap.get(twoPalletAddress).toString();
                    log.info("1楼请求入库时，读取的条码内容："+barcodeValueTwo);
                    //根据托盘条码返回正在执行的任务号
                    Result<PlanOrderLocationVo> orderResult = iBusinOutInPlanService.getOrderNoByContainerCode(barcodeValueTwo, "in");
                    log.info("2楼根据条码获取的任务信息："+orderResult.getData());
                    if (Result.isOk(orderResult)) {
                        PlanOrderLocationVo data = orderResult.getData();
                        taskIdTwo = data.getOrderNo();
                        cargoHeight = data.getType();//获取托盘或模具类型
                    }else {
                        redisTemplate.opsForValue().set(RedisConstant.redisPutInExeMsg+orderResult.getMsg(),orderResult.getMsg());
                        log.info("2楼获取任务信息失败："+orderResult.getMsg()+"托盘条码："+barcodeValueTwo);
                        //回填任务号
                        Map<String, Integer> writeMap = new HashMap<>(1);
                        writeMap.put(twoWcsErrExitVT, 1);
                        Map<String, Object> allMap = new HashMap<>(1);
                        allMap.put(twoWcsErrExit, writeMap);
                        Boolean errBoolean = plcReadAndWrite.execWrite(plcOperate, JsonUtil.toJSONString(allMap));
                        if (errBoolean){
                            log.info("2楼获取单号入库失败，写入点位成功{}",allMap);
                        }else {
                            log.info("2楼获取单号入库失败，写入点位失败{}",allMap);
                        }
                    }
                    log.info("2楼入库分配任务ID："+taskIdTwo + "条码号："+ barcodeValueTwo);
                    log.info("2楼入库分配货位类型："+cargoHeight);
                    String redisBarcodeTwo = String.valueOf(redisTemplate.opsForValue().get(RedisConstant.redisContainer+taskIdTwo));

                    //根据扫码的托盘，判断是否已经下任务了
                    if (taskIdTwo != 0L && redisBarcodeTwo!=null && !redisBarcodeTwo.equals(barcodeValueTwo)) {
                        //回填任务号
                        Map<String, Long> writeMap = new HashMap<>(1);
                        writeMap.put(twoAllotTaskIdVt, taskIdTwo);
                        Map<String, Object> allMap = new HashMap<>(1);
                        allMap.put(twoAllotTaskId, writeMap);

                        Map<String, String> cargoHeightMap = new HashMap<>(1);
                        cargoHeightMap.put(twoHignTypeVT, cargoHeight);
                        allMap.put(twoHignType, cargoHeightMap);

                        Map<String, Integer> wcsErrwriteMap = new HashMap<>(1);
                        wcsErrwriteMap.put(twoWcsErrExitVT, 0);
                        allMap.put(twoWcsErrExit,wcsErrwriteMap);
                        log.info("2楼按下入库按钮时，给输送线写命令数据：{}"+allMap);


                        Boolean taskBoolean = plcReadAndWrite.execWrite(plcOperate, JsonUtil.toJSONString(allMap));
                        if (taskBoolean) {
                            //分配任务成功时，记录已下发入库任务，不可在下发出库任务
                            redisTemplate.opsForValue().set(RedisConstant.redisTaskIn,taskIdTwo);
                            //更改入库计划为1
                            iBusinOutInPlanService.updPlanStatus(taskIdTwo, PlanStatusEnum.DOING.getValue());
                        }
                        //改为入库ID
                        redisTemplate.opsForValue().set(RedisConstant.redisContainer, taskIdTwo, 10, TimeUnit.SECONDS);
                    }
                }
                //获取1楼入库失败的内容
                if(pointMap.containsKey(oneOverlength)){
                    oneOverlengthV = Boolean.valueOf(pointMap.get(oneOverlength).toString());
                }
                if(pointMap.containsKey(oneLwide)){
                    oneLwideV = Boolean.valueOf(pointMap.get(oneLwide).toString());
                }

                if(pointMap.containsKey(oneRwide)){
                    oneRwideV = Boolean.valueOf(pointMap.get(oneRwide).toString());
                }

                if(pointMap.containsKey(oneHigh)){
                    oneHighV = Boolean.valueOf(pointMap.get(oneHigh).toString());
                }

                if (oneOverlengthV || oneLwideV || oneRwideV || oneHighV) {
                    //读取失败原因
                    String failedReasonValue ="";
                    if (oneOverlengthV){
                        failedReasonValue="超长";
                    }
                    if (oneLwideV){
                        failedReasonValue="左超宽";
                    }
                    if (oneRwideV){
                        failedReasonValue="右超宽";
                    }
                    if (oneHighV){
                        failedReasonValue="超高";
                    }
                    log.info("1楼入库失败原因："+failedReasonValue);
                    //读取失败任务号
                    Long failedTaskIdValueOne = Long.valueOf(pointMap.get(oneAllotTaskId).toString());

                    //根据回填入库失败原因
                    UpdPlanErrorBo updPlanErrorBo = new UpdPlanErrorBo();
                    updPlanErrorBo.setErrorMsg(failedReasonValue);
                    updPlanErrorBo.setOrderNo(failedTaskIdValueOne);
                    iBusinOutInPlanService.updErrMsg(updPlanErrorBo);

                    String redisFailedTaskIdValueOne = String.valueOf(redisTemplate.opsForValue().get(RedisConstant.redisDPSFailedTaskIdOne));

                    log.info("1楼入库失败redis任务ID："+redisFailedTaskIdValueOne);
                    log.info("1楼入库失败PLC任务ID："+failedTaskIdValueOne);
                    //入库失败，更改入库状态为0
                    if (failedTaskIdValueOne != 0L && !redisFailedTaskIdValueOne.equals(failedTaskIdValueOne)) {
                        redisTemplate.opsForValue().set(RedisConstant.redisDPSFailedTaskIdOne, failedTaskIdValueOne);
                        //更改入库计划3,取消
                        iBusinOutInPlanService.updPlanStatus(failedTaskIdValueOne, PlanStatusEnum.CANCEL.getValue());
                        //清理预占库存
                        iBusinOutInPlanService.updateSysStockByPlanOrderNo(failedTaskIdValueOne.toString());
                    }
                }


                //获取2楼入库失败的内容
                if(pointMap.containsKey(twoOverlength)){
                    twoOverlengthV = Boolean.valueOf(pointMap.get(twoOverlength).toString());
                }
                if(pointMap.containsKey(twoLwide)){
                    twoLwideV = Boolean.valueOf(pointMap.get(twoLwide).toString());
                }

                if(pointMap.containsKey(twoRwide)){
                    twoRwideV = Boolean.valueOf(pointMap.get(twoRwide).toString());
                }

                if(pointMap.containsKey(twoHigh)){
                    twoHighV = Boolean.valueOf(pointMap.get(twoHigh).toString());
                }

                if (twoOverlengthV || twoLwideV || twoRwideV || twoHighV) {
                    //读取失败原因
                    String failedReasonValue ="";
                    if (twoOverlengthV){
                        failedReasonValue="超长";
                    }
                    if (twoLwideV){
                        failedReasonValue="左超宽";
                    }
                    if (twoRwideV){
                        failedReasonValue="右超宽";
                    }
                    if (twoHighV){
                        failedReasonValue="超高";
                    }
                    log.info("2楼入库失败原因："+failedReasonValue);
                    //读取失败任务号
                    Long failedTaskIdValueTwo = Long.valueOf(pointMap.get(twoAllotTaskId).toString());

                    //根据回填入库失败原因
                    UpdPlanErrorBo updPlanErrorBo = new UpdPlanErrorBo();
                    updPlanErrorBo.setErrorMsg(failedReasonValue);
                    updPlanErrorBo.setOrderNo(failedTaskIdValueTwo);
                    iBusinOutInPlanService.updErrMsg(updPlanErrorBo);

                    String redisFailedTaskIdValueTwo = String.valueOf(redisTemplate.opsForValue().get(RedisConstant.redisDPSFailedTaskIdTwo));

                    log.info("2楼入库失败redis任务ID："+redisFailedTaskIdValueTwo);
                    log.info("2楼入库失败PLC任务ID："+failedTaskIdValueTwo);
                    //入库失败，更改入库状态为0
                    if (failedTaskIdValueTwo != 0L && !redisFailedTaskIdValueTwo.equals(failedTaskIdValueTwo)) {
                        redisTemplate.opsForValue().set(RedisConstant.redisDPSFailedTaskIdTwo, failedTaskIdValueTwo);
                        //更改入库计划3,取消
                        iBusinOutInPlanService.updPlanStatus(failedTaskIdValueTwo, PlanStatusEnum.CANCEL.getValue());
                        //清理预占库存
                        iBusinOutInPlanService.updateSysStockByPlanOrderNo(failedTaskIdValueTwo.toString());
                    }
                }

                //1楼是否请求入库：
                if (pointMap.containsKey(oneReqPutin) && Boolean.valueOf(pointMap.get(oneReqPutin).toString())) {
                    //请求入库，给堆垛机下发命令
                     Long oneTaskId= Long.valueOf((String) pointMap.get(oneReqPutInTaskId));
                    //从redis中验证是否已经下发任务
                    Long redisTaskId = (Long) redisTemplate.opsForValue().get(RedisConstant.redisReqWare + oneTaskId);
                    if (oneTaskId.equals(redisTaskId)) {
                        return;
                    }
                    //执行dps任务，给堆垛机下发命令
                    dpsTask(oneTaskId,1) ;
                }
                //2楼是否请求入库
                if (pointMap.containsKey(twoReqPutin) && Boolean.valueOf(pointMap.get(twoReqPutin).toString())) {
                    //请求入库，给堆垛机下发命令
                    Long twotaskId = Long.valueOf((String) pointMap.get(twoReqPutInTaskId));
                    //从redis中验证是否已经下发任务
                    Long redisTaskId = (Long) redisTemplate.opsForValue().get(RedisConstant.redisReqWare + twotaskId);
                    if (twotaskId.equals(redisTaskId)) {
                        return;
                    }
                    //执行dps任务，给堆垛机下发命令
                    dpsTask(twotaskId,2) ;
                }
            }
        }catch (Exception e){
            log.error("dpsRealTimeTask:"+e.getMessage());
        }
    }


    /**
     * 堆垛机的完成状态处理
     * @param propertyList
     * @param pointMap
     * @param plcReadAndWrite
     * @param plcOperate
     * @param pointTables
     */
    public void stkRealTimeTask(List<WcsPlcProperty> propertyList,Map<String, Object> pointMap, PLCReadAndWrite plcReadAndWrite,PlcOperate plcOperate,String pointTables){
        try {
            if (pointMap.isEmpty()){
                plcInit.connect();
                pointMap = plcReadAndWrite.readPlcData(plcOperate, pointTables);
                log.info("堆垛机为空时，重新读取的属性值{} ",pointMap);
            }
            //实时监测堆垛机完成状态，调用库存处理逻辑
            Long taskId=0L;
            String readTaskPoint = null;
            String readfinishPoint = null;
            int readValue = 0;
            for (WcsPlcProperty wcsPlcProperty : propertyList) {
                if (wcsPlcProperty.getPointType().equals("4")) {
                    readfinishPoint = wcsPlcProperty.getAddress();
                } else if (wcsPlcProperty.getPointType().equals("111")) {
                    readTaskPoint = wcsPlcProperty.getAddress();
                }
            }
            if (pointMap.containsKey(readfinishPoint)) {
                readValue = Integer.parseInt(pointMap.get(readfinishPoint).toString());
            }
            if (pointMap.containsKey(readTaskPoint)) {
                taskId = Long.valueOf((String)pointMap.get(readTaskPoint));
            }

            log.info("堆垛机完成出入库状态："+readValue+"---taksID="+taskId);

            //判断redis中是否已有此任务号，如果有则不再执行；
            if (readValue == 1) {
                Result<Boolean> booleanResult =null;
                //判断任务号是否是系统编号，或者是否已完成的，则给堆垛机回填完成信号是0
                BusinOutInPlan orderNoByOrderNo = iBusinOutInPlanService.getOrderNoByOrderNo(taskId.toString(), PlanStatusEnum.DOING.getValue());
                if (orderNoByOrderNo ==null || orderNoByOrderNo.getPlanStatus().equals(PlanStatusEnum.FINISH.getValue())){
                    log.info("根据任务号未查询到任务或者已经完成，不做处理，写入堆垛机完成状态为0");
                    booleanResult=Result.ok();
                }else {
                    log.info("正常执行处理库存业务逻辑"+orderNoByOrderNo);
                    //调用入库完成库存逻辑接口
                    WcsAddStockCallbackBo bo = new WcsAddStockCallbackBo();
                    bo.setOrderNo(taskId);
                    booleanResult = iBusinOutInPlanService.wcsActualAddStock(bo);
                }

                //处理成功 返回状态，写入状态位为0
                if (Result.isOk(booleanResult)){
                    Map<String, Object> writeMap = new HashMap<>(1);
                    writeMap.put("INTEGER", 0);
                    Map<String, Object> allMap = new HashMap<>(1);
                    allMap.put(readfinishPoint, writeMap);
                    if (plcReadAndWrite.execWrite(plcOperate, JsonUtil.toJSONString(allMap))) {
                        //堆垛机完成状态后，把redis中的ID删除，代表此任务已经完成
                        Object taskInId =redisTemplate.opsForValue().get(RedisConstant.redisTaskIn);
                        Object taskOutId = redisTemplate.opsForValue().get(RedisConstant.redisTaskOut);

                        if (taskOutId !=null && String.valueOf(taskOutId).equals(taskId.toString())){
                            log.info("出库完成时，删除redis中的出库任务状态记录");
                            redisTemplate.delete(RedisConstant.redisTaskOut);
                        }

                        if (taskInId != null && String.valueOf(taskInId).equals(taskId.toString())){
                            log.info("入库完成时，删除redis中的入库任务状态记录");
                            redisTemplate.delete(RedisConstant.redisTaskIn);
                        }

                        log.info("给堆垛机下发完成信号为0,成功："+taskId);
                        //请求成功，记录已下发
                        redisTemplate.delete(RedisConstant.redisReqWare+taskId);
                    }else {
                        log.info("给堆垛机下发完成信号为0,失败："+taskId);
                    }
                }
            }

        }catch (Exception e){
            log.error("stkRealTimeTask:"+e.getMessage());
        }
    }


    /**
     * 输送线请求入库
     * @param taskId
     * @return
     */
    public  Boolean dpsTask(Long taskId,Integer reqLayer){

        //下发任务时，先查询是否有正在执行的入库任务或出库任务，如果有，则不下发入库任务
        Object taskInId = redisTemplate.opsForValue().get(RedisConstant.redisTaskIn);
        if (taskInId !=null ){
            log.info("有入库任务在执行，无法下发入库任务:"+taskInId);
            return false;
        }

        Object taskOutId = redisTemplate.opsForValue().get(RedisConstant.redisTaskOut);
        if (taskOutId !=null ){
            log.info("有出库任务在执行，无法下发入库任务:"+taskOutId);
            return false;
        }

        PLCReadAndWrite plcReadAndWrite = new PLCReadAndWrite();
        //根据任务ID获取对应的货位信息
        Result<BaseLocation> baseLocationResult = iBusinOutInPlanService.wcsGetLocationByOrderNo(taskId.toString());
        BaseLocation baseLocation = baseLocationResult.getData();
        String toLayer = baseLocation.getLayer();
        String toColumn = baseLocation.getLocationColumn();
        String toRow = baseLocation.getLocationRow();
        log.info("单号："+taskId+"--> 请求入库，入库排层列："+toRow+toLayer+toColumn);
        //获取堆垛机
        WcsPlcConnectQueryBo plcConnect = new WcsPlcConnectQueryBo();
        plcConnect.setDevType("1");
        WcsPlcConnectVo wcsPlcConnectVo = wcsPlcConnectMapper.selectPlcConnectBydevType(plcConnect);
        Long stkPlcId = wcsPlcConnectVo.getId();
        PlcOperate stkOperate = plcInit.getPlcOperateByIp(wcsPlcConnectVo.getPlcIp());
        String pointTables = wcsPlcConnectVo.getPointTables();

        Map<String, Object> pointMap = plcReadAndWrite.readPlcData(stkOperate, pointTables);

        List<WcsPlcProperty> propertyList = iWcsPlcPropertyService.getWcsPlcPropertyByPlcId(stkPlcId);
        String plcTaskId = "";
        String taskIdvalueType ="";
        String plcToRow = "";
        String toRowValueType="";
        String plcToColumn = "";
        String toColumnValueType = "";
        String plcToLayer = "";
        String toLayerValueType="";

        String plcPlatform="";
        String plcPlatformValueType="";

        String plcOrderType = "";
        String plcOrderTypeValue = "";

        //堆垛机完成状态
        String readfinish ="";
        String readfinishValueType ="";

        String stkAutoManual="";
        String stkGround="";
        String stkidle="";
        String stkRun="";

        for (WcsPlcProperty stkProperty : propertyList) {
            //获取要堆垛机要写入的点位
            if (stkProperty.getPointType().equals("101")) {
                plcTaskId = stkProperty.getAddress();
                taskIdvalueType = stkProperty.getValueType();
            } else if (stkProperty.getPointType().equals("108")) {
                plcToRow = stkProperty.getAddress();
                toRowValueType = stkProperty.getValueType();
            } else if (stkProperty.getPointType().equals("109")) {
                plcToColumn = stkProperty.getAddress();
                toColumnValueType = stkProperty.getValueType();
            } else if (stkProperty.getPointType().equals("110")) {
                plcToLayer = stkProperty.getAddress();
                toLayerValueType = stkProperty.getValueType();
            } else if (stkProperty.getPointType().equals("102")) {
                plcOrderType = stkProperty.getAddress();
                plcOrderTypeValue=stkProperty.getValueType();
            }else if (stkProperty.getPointType().equals("103")) {
                plcPlatform  =stkProperty.getAddress();
                plcPlatformValueType=stkProperty.getValueType();
            }else if (stkProperty.getPointType().equals("4")) {
                readfinish = stkProperty.getAddress();
                readfinishValueType=stkProperty.getValueType();
            }
            else if (stkProperty.getPointType().equals("10")) {
                //等于1时堆垛机自动，等于0时堆垛机手动
                stkAutoManual = stkProperty.getAddress();
            }else if (stkProperty.getPointType().equals("11")) {
                //等于1时地面柜自动，等于0时地面柜手动
                stkGround = stkProperty.getAddress();
            }else if (stkProperty.getPointType().equals("12")) {
                //等于1时堆垛机空闲，等于0时堆垛机正忙
                stkidle = stkProperty.getAddress();
            }else if (stkProperty.getPointType().equals("13")) {
                //等于1时堆垛机已激活（运行）
                stkRun = stkProperty.getAddress();
            }
        }

        Boolean stkRunV = false;
        if (pointMap.containsKey(stkRun)) {
            stkRunV = Boolean.valueOf(pointMap.get(stkRun).toString());
        }
        log.info("堆垛机运行状态："+stkRunV);
        if (!stkRunV){
            log.info(taskId+"请求入库时，堆垛机尚未运行");
            return false;
        }


        Boolean stkAutoManualV=null;
        if (pointMap.containsKey(stkAutoManual)) {
            stkAutoManualV = Boolean.valueOf(pointMap.get(stkAutoManual).toString());
        }
        log.info(taskId+"堆垛机自动状态："+stkAutoManualV);
        if(!stkAutoManualV ){
            log.info(taskId+"请求入库时，堆垛机手动状态");
            return false;
        }

        Boolean stkGroundV= false;
        if (pointMap.containsKey(stkGround)) {
            stkGroundV = Boolean.valueOf(pointMap.get(stkGround).toString());
        }
        log.info(taskId+"地面柜自动状态："+stkGroundV);
        if(!stkGroundV){
            log.info(taskId+"请求入库时，堆垛机地面柜手动状态");
            return false;
        }

        Boolean stkidleV = false;
        if (pointMap.containsKey(stkidle)) {
            stkidleV = Boolean.valueOf(pointMap.get(stkidle).toString());
        }
        log.info(taskId+"堆垛机是状态:"+stkidleV);
        //1空闲 0繁忙
        if(!stkidleV){
            log.info(taskId+"请求入库时，堆垛机繁忙");
            return false;
        }

         //给堆垛机写命令
        Map<String, Object> allMap = new HashMap<>(1);
        Map<String, Object> plcTaskIdMap = new HashMap<>(1);
        plcTaskIdMap.put(taskIdvalueType, taskId);
        allMap.put(plcTaskId, plcTaskIdMap);
        Map<String, Object> plcToRowMap = new HashMap<>(1);
        plcToRowMap.put(toRowValueType, toRow);
        allMap.put(plcToRow, plcToRowMap);
        Map<String, Object> plcToColumnMap = new HashMap<>(1);
        plcToColumnMap.put(toColumnValueType, toColumn);
        allMap.put(plcToColumn, plcToColumnMap);
        Map<String, Object> plcToLayerMap = new HashMap<>(1);
        plcToLayerMap.put(toLayerValueType, toLayer);
        allMap.put(plcToLayer, plcToLayerMap);

        Map<String, Object> plcOrderTypeMap = new HashMap<>(1);
        plcOrderTypeMap.put(plcOrderTypeValue, 1);
        allMap.put(plcOrderType, plcOrderTypeMap);

        //入库起始站台
        Map<String, Object> plcPlatformMap = new HashMap<>(1);
        plcPlatformMap.put(plcPlatformValueType,reqLayer);
        allMap.put(plcPlatform,plcPlatformMap);
        //堆垛机完成状态,下命令时写入0，未完成状态
        Map<String, Object> readfinishMap = new HashMap<>(1);
        readfinishMap.put(readfinishValueType, 0);
        allMap.put(readfinish, readfinishMap);


        log.info(taskId+"请求入库：任务下发之前"+JsonUtil.toJSONString(allMap));
        if (plcReadAndWrite.execWrite(stkOperate, JsonUtil.toJSONString(allMap))) {
            log.info(taskId+"请求入库，任务下发成功：");
            //请求成功，记录已下发
            redisTemplate.opsForValue().set(RedisConstant.redisReqWare + taskId, taskId,24,TimeUnit.HOURS);
            //请求分配任务ID dps
            redisTemplate.delete(RedisConstant.redisContainer+taskId);
            return true;
        }else {
            log.info(taskId+"请求入库任务下发失败");
            return false;
        }
    }
}
