package cn.estsh.i3plus.ext.mes.pcn.apiservice.serviceimpl.busi.step;

import cn.estsh.i3plus.ext.mes.pcn.api.busi.IProduceSnExtService;
import cn.estsh.i3plus.ext.mes.pcn.pojo.bean.MesProduceSnExt;
import cn.estsh.i3plus.ext.mes.pcn.pojo.bean.MesWorkCenterExt;
import cn.estsh.i3plus.ext.mes.pcn.pojo.model.ObjectCfgExtModel;
import cn.estsh.i3plus.ext.mes.pcn.pojo.repository.MesWorkCenterExtRepository;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.MesPcnExtConstWords;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.MesPcnExtEnumUtil;
import cn.estsh.i3plus.mes.pcn.api.iservice.busi.IStepCommonMethodService;
import cn.estsh.i3plus.mes.pcn.serviceimpl.base.BaseStepService;
import cn.estsh.i3plus.mes.pcn.serviceimpl.busi.dispatch.processcontrol.IPcsCtlCacheService;
import cn.estsh.i3plus.mes.pcn.util.LocaleUtil;
import cn.estsh.i3plus.platform.common.convert.ConvertBean;
import cn.estsh.i3plus.platform.common.tool.TimeTool;
import cn.estsh.i3plus.platform.common.util.MesPcnConstWords;
import cn.estsh.i3plus.platform.plugin.opc.iservice.IOpcUAService;
import cn.estsh.i3plus.platform.plugin.opc.service.OpcUAService;
import cn.estsh.i3plus.pojo.base.bean.DdlPackBean;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.enumutil.MesPcnEnumUtil;
import cn.estsh.i3plus.pojo.base.tool.DdlPreparedPack;
import cn.estsh.i3plus.pojo.hardswitch.bean.OpcUAParam;
import cn.estsh.i3plus.pojo.mes.bean.*;
import cn.estsh.i3plus.pojo.mes.model.ProdOrgModel;
import cn.estsh.i3plus.pojo.mes.model.ProductDataModel;
import cn.estsh.i3plus.pojo.mes.model.StationRequestBean;
import cn.estsh.i3plus.pojo.mes.model.StepResult;
import cn.estsh.i3plus.pojo.mes.repository.*;
import cn.estsh.impp.framework.boot.util.ImppRedis;
import com.alibaba.fastjson.JSON;
import com.google.common.util.concurrent.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: wangjie
 * @CreateDate: 2020/8/31 16:00
 * @Description: 数据采集:采集PLC生产数据
 **/
@Slf4j
@Service
public class SxCollectPlcProductDataStepService extends BaseStepService {

    @Resource(name = MesPcnConstWords.REDIS_RES_PCN)
    private ImppRedis redisMesPcn;

    @Autowired
    private ExecutorService executorService;

    @Autowired
    private IPcsCtlCacheService pcsCtlCacheService;

    @Autowired
    private MesObjectCfgRepository objectCfgRepository;

    @Autowired
    private MesDataObjectRepository dataObjectRepository;

    @Autowired
    private MesPlcRepository plcRepository;

    @Autowired
    private MesWorkCellRepository workCellRepository;

    @Autowired
    private MesWorkCenterExtRepository workCenterExtRepository;

    @Autowired
    private MesProductDataRepository productDataRepository;

    @Autowired
    private IStepCommonMethodService stepCommonMethodService;

    @Autowired
    private IProduceSnExtService produceSnExtService;

    protected static final Map<String, ListenableFuture<Boolean>> listenFutureTaskMap = new ConcurrentHashMap<>();

    @Override
    public StepResult execute(StationRequestBean reqBean) {
        log.info("数据采集生产线[{}]工位[{}]:ws请求参数 [{}]", reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), reqBean);

        try {
            Map<String, MesProdRouteOptParam> codeValueMap = stepCommonMethodService.getStepCfgInfo(reqBean);
            log.info("数据采集生产线[{}]工位[{}]:工步参数 [{}]", reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), codeValueMap);

            List<ObjectCfgExtModel> objectCfgModelList = checkStepParams(codeValueMap, reqBean);

            String plcHandShakeSignal = (!CollectionUtils.isEmpty(codeValueMap) && codeValueMap.containsKey(MesPcnExtConstWords.DATA_COLLECT_PLC_HAND_SHAKE_SIGNAL)) ?
                    codeValueMap.get(MesPcnExtConstWords.DATA_COLLECT_PLC_HAND_SHAKE_SIGNAL).getParamValue() : null;
            String pcnHandShakeSignal = (!CollectionUtils.isEmpty(codeValueMap) && codeValueMap.containsKey(MesPcnExtConstWords.DATA_COLLECT_PCN_HAND_SHAKE_SIGNAL)) ?
                    codeValueMap.get(MesPcnExtConstWords.DATA_COLLECT_PCN_HAND_SHAKE_SIGNAL).getParamValue() : null;
            String plcSn = (!CollectionUtils.isEmpty(codeValueMap) && codeValueMap.containsKey(MesPcnExtConstWords.DATA_COLLECT_PLC_SN)) ?
                    codeValueMap.get(MesPcnExtConstWords.DATA_COLLECT_PLC_SN).getParamValue() : null;
            if (StringUtils.isEmpty(plcHandShakeSignal)) {
                String err = LocaleUtil.transferLanguage(reqBean.getUserInfo(), "缺少工步参数[DATA_COLLECT_PLC_HAND_SHAKE_SIGNAL]");
                sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                foundExThrow(reqBean, err);
            }
            if (StringUtils.isEmpty(pcnHandShakeSignal)) {
                String err = LocaleUtil.transferLanguage(reqBean.getUserInfo(), "缺少工步参数[DATA_COLLECT_PCN_HAND_SHAKE_SIGNAL]");
                sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                foundExThrow(reqBean, err);
            }
            if (StringUtils.isEmpty(plcSn)) {
                String err = LocaleUtil.transferLanguage(reqBean.getUserInfo(), "缺少工步参数[DATA_COLLECT_PLC_SN]");
                sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                foundExThrow(reqBean, err);
            }

            MesPlc plcHandShakeSignalPlc = getPlc(plcHandShakeSignal, MesPcnExtConstWords.DATA_COLLECT_PLC_HAND_SHAKE_SIGNAL, reqBean);
            MesPlc pcnHandShakeSignalPlc = getPlc(pcnHandShakeSignal, MesPcnExtConstWords.DATA_COLLECT_PCN_HAND_SHAKE_SIGNAL, reqBean);
            MesPlc plcSnPlc = getPlc(plcSn, MesPcnExtConstWords.DATA_COLLECT_PLC_SN, reqBean);
            Map<String, MesPlc> dataPlcMap = getDataPlcMap(objectCfgModelList, reqBean);

            ProdOrgModel prodOrgModel = getProdOrgModel(reqBean);

            reqBean.setSerialNumber(null);

            //获取缓存中未处理的数据 之后 return
            Map<String, Object> dataMap = (Map<String, Object>) doHandleCacheProductData(reqBean, 1, reqBean.getWorkCellCode(), null);
            if (!CollectionUtils.isEmpty(dataMap) && !StringUtils.isEmpty(dataMap.get(plcSn))) {
                //数据解析及入库
                insertProductData(reqBean, dataMap, objectCfgModelList, prodOrgModel, plcSnPlc);

                log.info("数据采集生产线[{}]工位[{}]已处理缓存中遗留的数据:[{}]", reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), dataMap.toString());
                return StepResult.getSuccessComplete();
            }

            log.info("数据采集生产线[{}]工位[{}]即将创建异步线程处理数据采集", reqBean.getWorkCenterCode(), reqBean.getWorkCellCode());
            String futureKey = getFutureKey(reqBean);
            cancelRemoveTask(futureKey);

            ListeningExecutorService listenExecutor = MoreExecutors.listeningDecorator(executorService);
            ListenableFuture<Boolean> listenFuture = ListenableFutureTask.create(() ->
                    doCachedDataFromPlc(reqBean, plcHandShakeSignalPlc, pcnHandShakeSignalPlc, plcSnPlc, dataPlcMap, objectCfgModelList, prodOrgModel)
            );

            addTask(futureKey, listenFuture);
            listenExecutor.submit((ListenableFutureTask) listenFuture);

            Futures.addCallback(listenFuture, new FutureCallback<Boolean>() {
                @Override
                public void onSuccess(Boolean result) {
                    if (result) {
                        log.info("数据采集生产线[{}]工位[{}]完成一次数据采集", reqBean.getWorkCenterCode(), reqBean.getWorkCellCode());
                    }
                }

                @Override
                public void onFailure(Throwable t) {
                    log.info("数据采集生产线[{}]工位[{}]数据采集发生错误:[{}]", reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), t.getMessage());
                }
            }, listenExecutor);

            listenFuture.get();

            dataMap = (Map<String, Object>) doHandleCacheProductData(reqBean, 1, reqBean.getWorkCellCode(), null);
            if (!CollectionUtils.isEmpty(dataMap) && !StringUtils.isEmpty(dataMap.get(plcSn))) {
                //数据解析及入库
                insertProductData(reqBean, dataMap, objectCfgModelList, prodOrgModel, plcSnPlc);
            }

            return StepResult.getSuccessComplete();
        } catch (Exception e) {
            String time = TimeTool.getNowTime(true);
            String err = "采集PLC生产数据工步执行失败:" + e.getMessage();
            sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);

            String sOut = "";
            StackTraceElement[] trace = e.getStackTrace();
            for (StackTraceElement s : trace) {
                sOut += "\tat " + s + "\r\n";
            }
            log.info("数据采集生产线[{}]工位[{}]:[{}][{}]", reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), time, sOut);

            String exMsg = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "工位：%s 工步：%s 执行失败 原因: %s"),
                    reqBean.getWorkCellCode(), "sxCollectPlcProductDataStepService", "工位监控程序：" + e.getCause().getMessage() + "\r\n" + sOut);
            redisMesPcn.putHash(reqBean.getOrganizeCode() + ":Exception:" + reqBean.getWorkCenterCode() + ":" + reqBean.getWorkCellCode(),
                    time, exMsg, MesPcnEnumUtil.EXPIRE_TIME.NEVER.getValue());

            return StepResult.getNonComplete(e.getMessage());
        }

    }

    private void insertProductData(StationRequestBean reqBean, Map<String, Object> dataMap, List<ObjectCfgExtModel> objectCfgModelList, ProdOrgModel prodOrgModel, MesPlc plcSnPlc) {

        //数据解析及入库
        Map<String, List<ObjectCfgExtModel>> objectCodeMap = objectCfgModelList.stream().filter(o -> null != o).collect(Collectors.groupingBy(ObjectCfgExtModel::getObjectCode));

        for (String objectCode : objectCodeMap.keySet()) {
            if (StringUtils.isEmpty(objectCode)) {
                continue;
            }
            List<ProductDataModel> list = new ArrayList<>();
            for (ObjectCfgExtModel objectCfg : objectCodeMap.get(objectCode)) {
                if (null == objectCfg) {
                    continue;
                }
                ProductDataModel productDataModel = new ProductDataModel();
                productDataModel.setFieldCode(objectCfg.getFieldCode());
                productDataModel.setFieldName(objectCfg.getFieldName());
                productDataModel.setFieldValue((String) dataMap.get(objectCfg.getPlcCode()));
                list.add(productDataModel);
            }

            MesProductData productData = new MesProductData();
            productData.setOrganizeCode(reqBean.getOrganizeCode());
            productData.setWorkCenterCode(reqBean.getWorkCenterCode());
            productData.setWorkCenterName(prodOrgModel.getWorkCenterName());
            productData.setWorkCellCode(reqBean.getWorkCellCode());
            productData.setWorkCellName(prodOrgModel.getWorkCellName());
            productData.setSerialNumber((String) dataMap.get(plcSnPlc.getPlcCode()));
            productData.setProductSn(productData.getSerialNumber());

            MesProduceSnExt produceSnExt = produceSnExtService.getProduceSnExtBySn(reqBean.getOrganizeCode(), productData.getSerialNumber());
            if (null != produceSnExt) {
                productData.setPartNo(produceSnExt.getPartNo());
                productData.setPartDesc(produceSnExt.getPartNameRdd());
                productData.setOrderNo(!StringUtils.isEmpty(produceSnExt.getLastWorkOrderNo()) ? produceSnExt.getLastWorkOrderNo() : produceSnExt.getWorkOrderNo());
            }
            productData.setEquCode(plcSnPlc.getEquipmentCode());
            productData.setObjectCode(objectCode);
            productData.setObjectName(objectCodeMap.get(objectCode).get(0).getObjectName());
            productData.setLineData(JSON.toJSONString(list));
            productData.setSystemSyncStatus(MesPcnExtEnumUtil.IF_SYNC_STATUS.NO_SYNC.getValue());
            ConvertBean.serviceModelInitialize(productData, reqBean.getUserInfo());
            productDataRepository.insert(productData);
        }

        //删除缓存
        doHandleCacheProductData(reqBean, 3, reqBean.getWorkCellCode(), null);

        reqBean.setSerialNumber((String) dataMap.get(plcSnPlc.getPlcCode()));
    }

    public Boolean doCachedDataFromPlc(StationRequestBean reqBean, MesPlc plcHandShakeSignalPlc, MesPlc pcnHandShakeSignalPlc, MesPlc plcSnPlc, Map<String, MesPlc> dataPlcMap, List<ObjectCfgExtModel> objectCfgModelList, ProdOrgModel prodOrgModel) {
        Boolean result = false;
        Boolean initFlag = true;
        while (pcsCtlCacheService.checkWorkCenterIsRunning(reqBean)) {
            //复位信号
            if (initFlag) {
                Boolean boo1;
                try {
                    boo1 = doWriteOpcParamValue(pcnHandShakeSignalPlc, "3");
                } catch (Exception e) {
                    log.info("数据采集生产线[{}]工位[{}]PCN修改PLC地址位[{}]值为[3]时发生异常:[{}]",
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress(), e.getMessage());
                    String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "生产线[%s]工位[%s]PCN修改PLC地址位[%s]值为[3]时发生异常:[%s]"),
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress(), e.getMessage());
                    sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }
                if (null != boo1 && !boo1) {
                    log.info("数据采集生产线[{}]工位[{}]PCN修改PLC地址位[{}]值为[3]时失败",
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress());
                    String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "生产线[%s]工位[%s]PCN修改PLC地址位[%s]值为[3]时失败"),
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress());
                    sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }

                initFlag = false;

                log.info("数据采集生产线[{}]工位[{}]PCN修改PLC地址位[{}]值为[3]成功",
                        reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress());

                continue;
            }

            try {
                TimeUnit.MILLISECONDS.sleep(500L);
            } catch (InterruptedException e) {
            }

            Integer value1, value2;
            try {
                value1 = Integer.valueOf(doReadOpcParamValue(pcnHandShakeSignalPlc).toString());
                value2 = Integer.valueOf(doReadOpcParamValue(plcHandShakeSignalPlc).toString());
            } catch (Exception e) {
                log.info("数据采集生产线[{}]工位[{}]PCN读取PLC地址位[{}][{}]时发生异常:[{}]",
                        reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress(), plcHandShakeSignalPlc.getTagAddress(), e.getMessage());
                String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "生产线[%s]工位[%s]PCN读取PLC地址位[%s][%s]时发生异常:[%s]"),
                        reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress(), plcHandShakeSignalPlc.getTagAddress(), e.getMessage());
                sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                continue;
            }

            log.info("数据采集生产线[{}]工位[{}]PCN读取PLC地址位[{}][{}]值分别为[{}][{}]",
                    reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress(), plcHandShakeSignalPlc.getTagAddress(), value1, value2);

            if (value1 == 3 && value2 == 0) {
                Boolean boo2;
                try {
                    boo2 = doWriteOpcParamValue(pcnHandShakeSignalPlc, "1");
                } catch (Exception e) {
                    log.info("数据采集生产线[{}]工位[{}]PCN修改PLC地址位[{}]值为[1]时发生异常:[{}]",
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress(), e.getMessage());
                    String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "生产线[%s]工位[%s]PCN修改PLC地址位[%s]值为[1]时发生异常:[%s]"),
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress(), e.getMessage());
                    sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }
                if (null != boo2 && !boo2) {
                    log.info("数据采集生产线[{}]工位[{}]PCN修改PLC地址位[{}]值为[1]时失败",
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress());
                    String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "生产线[%s]工位[%s]PCN修改PLC地址位[%s]值为[1]时失败"),
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress());
                    sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }

                log.info("数据采集生产线[{}]工位[{}]PCN修改PLC地址位[{}]值为[1]成功",
                        reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress());

            } else if (value1 == 1 && value2 == 1) {
                //采集数据
                String serialNumber;
                try {
                    serialNumber = (String) doReadOpcParamValue(plcSnPlc);
                } catch (Exception e) {
                    log.info("数据采集生产线[{}]工位[{}]PCN读取PLC地址位[{}]的值时发生异常:[{}]",
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), plcSnPlc.getTagAddress(), e.getMessage());
                    String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "生产线[%s]工位[%s]PCN读取PLC地址位[%s]的值时发生异常:[%s]"),
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), plcSnPlc.getTagAddress(), e.getMessage());
                    sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }
                if (StringUtils.isEmpty(serialNumber) || serialNumber.equals("0")) {
                    log.info("数据采集生产线[{}]工位[{}]PCN未读取到PLC地址位[{}]的值",
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), plcSnPlc.getTagAddress());
                    String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "生产线[%s]工位[%s]PCN未读取到PLC地址位[%s]的值"),
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), plcSnPlc.getTagAddress());
                    sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }

                log.info("数据采集生产线[{}]工位[{}]PLC地址位[{}]条码[{}]",
                        reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), plcSnPlc.getTagAddress(), serialNumber);

                Map<String, Object> dataMap = new HashMap<>();
                dataMap.put(plcSnPlc.getPlcCode(), serialNumber);

                Boolean itemFalg = true;
                for (String plcCode : dataPlcMap.keySet()) {
                    if (StringUtils.isEmpty(plcCode)) {
                        continue;
                    }
                    MesPlc plc = dataPlcMap.get(plcCode);
                    try {
                        Object value = doReadOpcParamValue(plc);

                        dataMap.put(plcCode, value);
                    } catch (Exception e) {
                        log.info("数据采集生产线[{}]工位[{}]PCN读取PLC地址位[{}]的值时发生异常:[{}]",
                                reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), plc.getTagAddress(), e.getMessage());
                        String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "生产线[%s]工位[%s]PCN读取PLC地址位[%s]的值时发生异常:[%s]"),
                                reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), plc.getTagAddress(), e.getMessage());
                        sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);

                        itemFalg = false;
                        break;
                    }
                }
                if (!itemFalg) {
                    continue;
                }

                log.info("数据采集生产线[{}]工位[{}]PLC地址位[{}]条码[{}]dataMap{}",
                        reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), plcSnPlc.getTagAddress(), serialNumber, dataMap.toString());

                //完成采集
                Boolean boo3;
                try {
                    boo3 = doWriteOpcParamValue(pcnHandShakeSignalPlc, "2");
                } catch (Exception e) {
                    log.info("数据采集生产线[{}]工位[{}]PCN修改PLC地址位[{}]值为[2]时发生异常:[{}]",
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress(), e.getMessage());
                    String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "生产线[%s]工位[%s]PCN修改PLC地址位[%s]值为[2]时发生异常:[%s]"),
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress(), e.getMessage());
                    sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }
                if (null != boo3 && !boo3) {
                    log.info("数据采集生产线[{}]工位[{}]PCN修改PLC地址位[{}]值为[2]时失败",
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress());
                    String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "生产线[%s]工位[%s]PCN修改PLC地址位[%s]值为[2]时失败"),
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress());
                    sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }

                log.info("数据采集生产线[{}]工位[{}]PCN修改PLC地址位[{}]值为[2]成功",
                        reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress());

                //采集数据入缓存
                doHandleCacheProductData(reqBean, 2, reqBean.getWorkCellCode(), dataMap);

            } else if (value1 == 2 && value2 == 0) {
                Boolean boo4;
                try {
                    boo4 = doWriteOpcParamValue(pcnHandShakeSignalPlc, "0");
                } catch (Exception e) {
                    log.info("数据采集生产线[{}]工位[{}]PCN修改PLC地址位[{}]值为[0]时发生异常:[{}]",
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress(), e.getMessage());
                    String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "生产线[%s]工位[%s]PCN修改PLC地址位[%s]值为[0]时发生异常:[%s]"),
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress(), e.getMessage());
                    sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }
                if (null != boo4 && !boo4) {
                    log.info("数据采集生产线[{}]工位[{}]PCN修改PLC地址位[{}]值为[0]时失败",
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress());
                    String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "生产线[%s]工位[%s]PCN修改PLC地址位[%s]值为[0]时失败"),
                            reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress());
                    sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }

                log.info("数据采集生产线[{}]工位[{}]PCN修改PLC地址位[{}]值为[0]成功",
                        reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress());

                result = true;

                break;
            } else {
                log.info("数据采集生产线[{}]工位[{}]PCN正在循环读取PLC地址位[{}][{}]...",
                        reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), pcnHandShakeSignalPlc.getTagAddress(), plcHandShakeSignalPlc.getTagAddress());
            }
        }

        return result;
    }

    private List<ObjectCfgExtModel> checkStepParams(Map<String, MesProdRouteOptParam> codeValueMap, StationRequestBean reqBean) {
        String plcCodeArrStr = (!CollectionUtils.isEmpty(codeValueMap) && codeValueMap.containsKey(MesPcnExtConstWords.DATA_COLLECT_PLC_CODE_ARR)) ?
                codeValueMap.get(MesPcnExtConstWords.DATA_COLLECT_PLC_CODE_ARR).getParamValue() : null;
        if (StringUtils.isEmpty(plcCodeArrStr)) {
            String err = LocaleUtil.transferLanguage(reqBean.getUserInfo(), "缺少工步参数[PLC_CODE_ARR]");
            sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
            foundExThrow(reqBean, err);
        }
        List<String> plcCodeList = getPlcCodeList(plcCodeArrStr.split(","));

        DdlPackBean packBean = DdlPackBean.getDdlPackBean(reqBean.getOrganizeCode());
        DdlPreparedPack.getInPackList(plcCodeList, MesPcnExtConstWords.PLC_CODE, packBean);
        DdlPreparedPack.getIsNotNull(MesPcnExtConstWords.FIELD_CODE, packBean);
        DdlPreparedPack.getIsNotNull(MesPcnExtConstWords.OBJECT_CODE, packBean);
        List<MesObjectCfg> objectCfgDbList = objectCfgRepository.findByHqlWhere(packBean);

        if (CollectionUtils.isEmpty(objectCfgDbList) || objectCfgDbList.size() != plcCodeList.size()) {
            List<String> plcCodeDbList = CollectionUtils.isEmpty(objectCfgDbList) ? new ArrayList<>() :
                    objectCfgDbList.stream().filter(o -> null != o).map(MesObjectCfg::getPlcCode).collect(Collectors.toList());
            List<String> unExistDbList = plcCodeList.stream().filter(o -> (StringUtils.isEmpty(o) && !plcCodeDbList.contains(o))).collect(Collectors.toList());
            String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "PLC代码%s未配置有效的关键数据"), unExistDbList.toString());
            sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
            foundExThrow(reqBean, err);
        }

        List<String> objectCodeList = (objectCfgDbList.stream().filter(o -> null != o).map(MesObjectCfg::getObjectCode).collect(Collectors.toList())).stream().distinct().collect(Collectors.toList());

        DdlPackBean packBean1 = DdlPackBean.getDdlPackBean(reqBean.getOrganizeCode());
        DdlPreparedPack.getInPackList(objectCodeList, MesPcnExtConstWords.OBJECT_CODE, packBean1);
        List<MesDataObject> dataObjectList = dataObjectRepository.findByHqlWhere(packBean1);
        if (CollectionUtils.isEmpty(dataObjectList) || dataObjectList.size() != objectCodeList.size()) {
            List<String> objectCodeDbList = CollectionUtils.isEmpty(dataObjectList) ? null :
                    dataObjectList.stream().filter(o -> null != o).map(MesDataObject::getObjectCode).collect(Collectors.toList());
            List<String> unExistDbList = objectCodeDbList.stream().filter(o -> (StringUtils.isEmpty(o) && !CollectionUtils.isEmpty(objectCodeList) && !objectCodeList.contains(o))).collect(Collectors.toList());
            String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "对象代码%s无效"), unExistDbList.toString());
            sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
            foundExThrow(reqBean, err);
        }

        Map<String, List<MesDataObject>> objectCodeMap = dataObjectList.stream().filter(o -> null != o).collect(Collectors.groupingBy(MesDataObject::getObjectCode));

        List<ObjectCfgExtModel> objectCfgModelList = new ArrayList<>();
        for (MesObjectCfg objectCfg : objectCfgDbList) {
            if (null == objectCfg) {
                continue;
            }
            ObjectCfgExtModel model = new ObjectCfgExtModel();
            BeanUtils.copyProperties(objectCfg, model);
            model.setObjectName(objectCodeMap.get(objectCfg.getObjectCode()).get(0).getObjectName());
            objectCfgModelList.add(model);
        }
        return objectCfgModelList;
    }

    private List<String> getPlcCodeList(String[] plcCodeArr) {
        List<String> list = new ArrayList();
        for (int i = 0; i < plcCodeArr.length; i++) {
            if (!list.contains(plcCodeArr[i])) {
                list.add(plcCodeArr[i]);
            }
        }
        return list;
    }

    public Boolean doWriteOpcParamValue(MesPlc plc, String value) {
        IOpcUAService opcService = new OpcUAService();
        String tagAddress = MessageFormat.format("{0}.{1}.{2}", plc.getChannel(), plc.getDevice(), plc.getTagAddress());
        OpcUAParam opcParam = new OpcUAParam();
        opcParam.setServerUrl(plc.getOpcUrl());
        opcParam.setTagAddress(tagAddress);
        opcParam.setNamespaceIndex(plc.getNameSpaceIndex());
        opcParam.setTagValueType(plc.getDataType());
        opcParam.setTagValue(value);
        Boolean result = opcService.editOpcParamValue(opcParam);
        opcService.disConnection();
        return result;
    }

    public Object doReadOpcParamValue(MesPlc plc) {
        IOpcUAService opcService = new OpcUAService();
        String tagAddress = MessageFormat.format("{0}.{1}.{2}", plc.getChannel(), plc.getDevice(), plc.getTagAddress());
        OpcUAParam opcParam = new OpcUAParam();
        opcParam.setServerUrl(plc.getOpcUrl());
        opcParam.setTagAddress(tagAddress);
        opcParam.setNamespaceIndex(plc.getNameSpaceIndex());
        opcParam.setTagValueType(plc.getDataType());
        Object result = opcService.getOpcParamValue(opcParam);
        opcService.disConnection();
        return result;
    }

    private ProdOrgModel getProdOrgModel(StationRequestBean reqBean) {
        ProdOrgModel prodOrgModel = (ProdOrgModel) doHandleCachePlc(reqBean, 1, reqBean.getWorkCellCode(), null);
        if (null == prodOrgModel) {
            MesWorkCell workCell = workCellRepository.getByProperty(
                    new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_VALID, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.WORK_CENTER_CODE, MesPcnExtConstWords.WORK_CELL_CODE},
                    new Object[]{reqBean.getOrganizeCode(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode()});
            MesWorkCenterExt workCenterExt = workCenterExtRepository.getByProperty(
                    new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_VALID, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.WORK_CENTER_CODE},
                    new Object[]{reqBean.getOrganizeCode(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), reqBean.getWorkCenterCode()});
            if (null != workCell && null != workCenterExt) {
                prodOrgModel = new ProdOrgModel();
                prodOrgModel.setOrganizeCode(reqBean.getOrganizeCode());
                prodOrgModel.setWorkCenterCode(reqBean.getWorkCenterCode());
                prodOrgModel.setWorkCellCode(reqBean.getWorkCellCode());
                prodOrgModel.setWorkCenterName(null != workCenterExt ? workCenterExt.getWorkCenterName() : null);
                prodOrgModel.setWorkCellName(null != workCell ? workCell.getWorkCellName() : null);
                doHandleCacheOrg(reqBean, 2, reqBean.getWorkCellCode(), prodOrgModel);
            } else {
                String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "请检查生产线[%s]工位[%s]的有效性"), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode());
                sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                foundExThrowNoShowMsg();
            }
        }
        return prodOrgModel;
    }

    public MesPlc getPlc(String paramValue, String code, StationRequestBean reqBean) {
        MesPlc plc = (MesPlc) doHandleCachePlc(reqBean, 1, code, null);
        if (null == plc) {
            plc = plcRepository.getByProperty(
                    new String[]{MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_VALID, MesPcnExtConstWords.IS_DELETED, MesPcnExtConstWords.PLC_CODE},
                    new Object[]{reqBean.getOrganizeCode(), CommonEnumUtil.IS_VAILD.VAILD.getValue(), CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue(), paramValue});
            if (null != plc) {
                doHandleCachePlc(reqBean, 2, code, plc);
            } else {
                String err = String.format(LocaleUtil.transferLanguage(reqBean.getUserInfo(), "PLC[%s]在数据库不存在"), paramValue);
                sendMessage(reqBean, null, err, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                foundExThrowNoShowMsg();
            }
        }
        return plc;
    }

    public Object doHandleCacheOrg(StationRequestBean reqBean, Integer flag, String code, ProdOrgModel prodOrgModel) {
        switch (flag) {
            case 1:
                return redisMesPcn.getHash(getOrgKey(reqBean), code);
            case 2:
                return redisMesPcn.putHash(getOrgKey(reqBean), code, prodOrgModel, MesPcnEnumUtil.EXPIRE_TIME.NEVER.getValue());
            case 3:
                redisMesPcn.deleteHash(getOrgKey(reqBean), code);
                return true;
            default:
                return null;
        }
    }

    public Object doHandleCachePlc(StationRequestBean reqBean, Integer flag, String code, MesPlc plc) {
        switch (flag) {
            case 1:
                return redisMesPcn.getHash(getPlcKey(reqBean), code);
            case 2:
                return redisMesPcn.putHash(getPlcKey(reqBean), code, plc, MesPcnEnumUtil.EXPIRE_TIME.NEVER.getValue());
            case 3:
                redisMesPcn.deleteHash(getPlcKey(reqBean), code);
                return true;
            default:
                return null;
        }
    }

    public Object doHandleCacheProductData(StationRequestBean reqBean, Integer flag, String code, Map<String, Object> dataMap) {
        switch (flag) {
            case 1:
                return redisMesPcn.getHash(getProductDataKey(reqBean), code);
            case 2:
                return redisMesPcn.putHash(getProductDataKey(reqBean), code, dataMap, MesPcnEnumUtil.EXPIRE_TIME.NEVER.getValue());
            case 3:
                redisMesPcn.deleteHash(getProductDataKey(reqBean), code);
                return true;
            default:
                return null;
        }
    }

    private Map<String, MesPlc> getDataPlcMap(List<ObjectCfgExtModel> objectCfgModelList, StationRequestBean reqBean) {
        Map<String, MesPlc> dataPlcMap = new HashMap<>();
        for (ObjectCfgExtModel objectCfg : objectCfgModelList) {
            if (null == objectCfg) {
                continue;
            }
            dataPlcMap.put(objectCfg.getPlcCode(), getPlc(objectCfg.getPlcCode(), objectCfg.getPlcCode(), reqBean));
        }
        return dataPlcMap;
    }

    private String getPlcKey(StationRequestBean reqBean) {
        return new StringJoiner(MesPcnExtConstWords.COLON)
                .add(reqBean.getOrganizeCode()).add("PCS_CTL").add("PLC").add(reqBean.getWorkCenterCode()).add(reqBean.getWorkCellCode()).toString();
    }

    private String getOrgKey(StationRequestBean reqBean) {
        return new StringJoiner(MesPcnExtConstWords.COLON)
                .add(reqBean.getOrganizeCode()).add("PCS_CTL").add("ORG").add(reqBean.getWorkCenterCode()).toString();
    }

    private String getProductDataKey(StationRequestBean reqBean) {
        return new StringJoiner(MesPcnExtConstWords.COLON)
                .add(reqBean.getOrganizeCode()).add("PCS_CTL").add("PRODUCT_DATA").add(reqBean.getWorkCenterCode()).toString();
    }

    private String getFutureKey(StationRequestBean reqBean) {
        return new StringJoiner(MesPcnExtConstWords.COLON)
                .add(reqBean.getOrganizeCode()).add("FUTURE").add(reqBean.getWorkCenterCode()).add(reqBean.getWorkCellCode()).toString();
    }

    private static synchronized void cancelRemoveTask(String key) {
        cancelTask(key);
        removeTask(key);
    }

    private static synchronized void cancelTask(String key) {
        ListenableFuture<Boolean> cacheListenableFuture = listenFutureTaskMap.get(key);
        if (cacheListenableFuture != null) {
            cacheListenableFuture.cancel(true);
        }
    }

    private static synchronized void addTask(String key, ListenableFuture<Boolean> listenableFuture) { listenFutureTaskMap.put(key, listenableFuture); }

    private static synchronized void removeTask(String key) { listenFutureTaskMap.remove(key); }

}
