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

import cn.estsh.i3plus.ext.mes.pcn.api.busi.*;
import cn.estsh.i3plus.ext.mes.pcn.apiservice.serviceimpl.step.method.MesWorkOrderCheckCompleteQtyStepService;
import cn.estsh.i3plus.ext.mes.pcn.pojo.context.*;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.MesPcnExtConstWords;
import cn.estsh.i3plus.mes.pcn.actor.shipping.dispatch.IFsmCommonService;
import cn.estsh.i3plus.mes.pcn.serviceimpl.fsm.BaseStepService;
import cn.estsh.i3plus.mes.pcn.util.PojoAttrUtil;
import cn.estsh.i3plus.mes.pcn.util.StringUtil;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.enumutil.MesPcnEnumUtil;
import cn.estsh.i3plus.pojo.mes.bean.MesProdRuleIgnoreCfg;
import cn.estsh.i3plus.pojo.mes.bean.MesProdRuleNosortCfg;
import cn.estsh.i3plus.pojo.mes.bean.MesWorkCenter;
import cn.estsh.i3plus.pojo.mes.model.AttrBean;
import cn.estsh.i3plus.pojo.mes.model.StationRequestBean;
import cn.estsh.i3plus.pojo.mes.model.StationResultBean;
import cn.estsh.i3plus.pojo.mes.model.StepResult;
import cn.estsh.i3plus.pojo.mes.util.MesExtEnumUtil;
import com.alibaba.fastjson.JSONObject;
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 java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @Description : 显示装配件扫描项工步【非排序】
 * @Author : wangjie
 **/
@Slf4j
@Service("mesAssemblyShowNosortStepService")
public class MesAssemblyShowNosortStepService extends BaseStepService {

    @Autowired
    private IMesProductionProcessContextStepService productionProcessContextStepService;

    @Autowired
    private IMesProductionDispatchContextStepService productionDispatchContextStepService;

    @Autowired
    private IMesProdRuleCfgExtService prodRuleCfgExtService;

    @Autowired
    private IMesNumberRuleMatchDispatchService numberRuleMatchDispatchService;

    @Autowired
    private IMesTimeEfficientCfgMatchService timeEfficientCfgMatchService;

    @Autowired
    private IMesWorkOrderExtService workOrderExtService;

    @Autowired
    private IFsmCommonService fsmCommonService;

    @Autowired
    private MesWorkOrderCheckCompleteQtyStepService workOrderCheckCompleteQtyStepService;

    @Override
    public StepResult execute(StationRequestBean reqBean) {

        StationResultBean resultBean = new StationResultBean();

        StepResult stepResult = StepResult.getSuccessComplete();

        //获取上下文信息: 处理加工不可用规则
        MesProductionProcessContext productionProcessContext = productionProcessContextStepService.dispatchProdRuleIgnoreCfgContext(reqBean);

        //配置错误 抛出异常
        if (!productionProcessContext.getSuccess()) stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), productionProcessContext.getMessage());

        //存储生产过程上下文对象
        productionProcessContextStepService.dispatchProductionProcessContext(reqBean, productionProcessContext);

        //从上下文中取出生产线对象
        MesWorkCenter workCenter = productionProcessContext.getWorkCenter();

        //从上下文中取出生产线对象
        MesCellEquipContext cellEquipContext = productionProcessContext.getCurCellEquip();

        //获取上下文加工规则数据信息集合
        List<MesProdRuleContext> prodRuleContextList = productionDispatchContextStepService.getProdRuleDataContext(reqBean);

        //获取上下文产出零件数据信息集合
        List<MesProductionPartContext> productionPartContextList = productionDispatchContextStepService.getProductionPartContext(reqBean);

        //获取上下文进料零件条码信息集合
        List<MesProductionPsInContext> productionPsInContextList = productionDispatchContextStepService.getProductionPsInContext(reqBean);

        //没有【产出零件数据】【进料零件条码信息】,不支持当前工步
        if (CollectionUtils.isEmpty(productionPartContextList) && CollectionUtils.isEmpty(productionPsInContextList))
            stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), String.format("生产线[%s]工位[%s]当前已不存在产成零件或进料条码数据信息,请重置工序!", reqBean.getWorkCenterCode(), reqBean.getWorkCellCode()));

        //根据现有数据【产出零件数据】【进料零件条码信息】比对上下文中已经存在的加工规则数据信息集合, 没有加工规则的数据进行查询
        if (CollectionUtils.isEmpty(prodRuleContextList)) prodRuleContextList = new ArrayList<>();
        Integer initSize = prodRuleContextList.size();

        //封装非排序加工规则
        doHandleProdRuleData(reqBean, resultBean, stepResult, workCenter, productionProcessContext, cellEquipContext, prodRuleContextList, productionPartContextList, productionPsInContextList);

        //验证加工单完成数量, 验证的前提条件: 每腔均已匹配到加工规则
        workOrderCheckCompleteQtyStepService.dispatchWorkOrderCompleteQtyContext(
                reqBean, resultBean, stepResult, productionProcessContext, productionPartContextList, productionPsInContextList);

        //匹配失败需要清除本次扫描/读取信息
        if (!stepResult.isCompleted() && workOrderCheckCompleteQtyStepService.doBusiCheckToDelete(reqBean, stepResult, productionPartContextList, productionPsInContextList))
            return stepResult.nextTriggerEvent(CollectionUtils.isEmpty(productionPsInContextList) ? MesPcnExtConstWords.NEXT_TRIGGER_EVENT_PART_NO : (!StringUtils.isEmpty(stepResult.getObj()) ? MesPcnExtConstWords.NEXT_TRIGGER_EVENT_PART_NO : MesPcnExtConstWords.NEXT_TRIGGER_EVENT_PRODUCT_SN));

        if (prodRuleContextList.size() != initSize) {
            //保存上下文产品加工规则信息集合
            productionDispatchContextStepService.dispatchProdRuleDataContext(reqBean, prodRuleContextList);
            //保存上下文产出零件信息
            if (!CollectionUtils.isEmpty(productionPartContextList)) productionDispatchContextStepService.dispatchProductionPartContext(reqBean, productionPartContextList);
            //保存进料主条码数据
            if (!CollectionUtils.isEmpty(productionPsInContextList)) productionDispatchContextStepService.dispatchProductionPsInContext(reqBean, productionPsInContextList);
        }

        //显示装配件信息
        Boolean isShow = showProductionAssembly(reqBean, resultBean, workCenter, prodRuleContextList);

        return stepSuccessCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.PROCESS.getValue()), stepResult,
                isShow ? "已显示装配件清单,请查看!" : "当前腔匹配的加工规则无装配件清单!",
                MesPcnEnumUtil.PROMPT_SOUND.SUCCESS.getValue());
    }

    //封装非排序加工规则
    private StepResult doHandleProdRuleData(StationRequestBean reqBean, StationResultBean resultBean, StepResult stepResult, MesWorkCenter workCenter, MesProductionProcessContext productionProcessContext, MesCellEquipContext cellEquipContext,
                                            List<MesProdRuleContext> prodRuleContextList, List<MesProductionPartContext> productionPartContextList, List<MesProductionPsInContext> productionPsInContextList) {

        //获取加工不可用规则
        Map<String, List<MesProdRuleIgnoreCfg>> prodRuleIgnoreCfgMap = productionProcessContextStepService.getProdRuleIgnoreCfgContextMap(reqBean, productionProcessContext);

        //根据进料条件获取匹配加工规则数据(只能查询到一条,否则报错)
        if (CollectionUtils.isEmpty(productionPartContextList)) return doHandleProdRuleDataByProductSn(reqBean, resultBean, stepResult, workCenter, productionProcessContext, cellEquipContext, prodRuleContextList, productionPsInContextList, prodRuleIgnoreCfgMap);
            //根据是否存在【产出零件数据】获取剩余待匹配的加工规则数据 (只能查询到一条,否则报错)
        else return doHandleProdRuleDataByProductionPart(reqBean, resultBean, stepResult, workCenter, productionProcessContext, cellEquipContext, prodRuleContextList, productionPartContextList, productionPsInContextList, prodRuleIgnoreCfgMap);

    }

    //根据是否存在【产出零件数据】获取剩余待匹配的加工规则数据 (只能查询到一条,否则报错)
    private StepResult doHandleProdRuleDataByProductionPart(StationRequestBean reqBean, StationResultBean resultBean, StepResult stepResult, MesWorkCenter workCenter, MesProductionProcessContext productionProcessContext,
                                                            MesCellEquipContext cellEquipContext, List<MesProdRuleContext> prodRuleContextList, List<MesProductionPartContext> productionPartContextList, List<MesProductionPsInContext> productionPsInContextList,
                                                            Map<String, List<MesProdRuleIgnoreCfg>> prodRuleIgnoreCfgMap) {

        //加工规则的数据已与产出零件的数量一致
        if (!CollectionUtils.isEmpty(prodRuleContextList) && prodRuleContextList.size() == productionPartContextList.size()) return stepResult;

        //拿到当前最大的foreignKey
        Optional<MesProductionPartContext> maxForeignKeyOptional = productionPartContextList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getForeignKey()))).max(Comparator.comparing(MesProductionPartContext::getForeignKey));
        Integer foreignKey = (null != maxForeignKeyOptional && maxForeignKeyOptional.isPresent()) ? maxForeignKeyOptional.get().getForeignKey() : MesPcnExtConstWords.ZERO;

        //搜集未匹配规则的产出零件
        List<String> outPartNoList = productionPartContextList.stream().filter(o -> (null != o && StringUtils.isEmpty(o.getForeignKey()) && !StringUtils.isEmpty(o.getPartNo()))).map(MesProductionPartContext::getPartNo).collect(Collectors.toList());

        //没有【进料零件条码信息】信息, 根据进料[NULL]条件获取匹配当前产出零件的加工规则数据(只能查询到一条,否则报错)
        if (CollectionUtils.isEmpty(productionPsInContextList)) return doHandleProdRuleDataByProductionPartAndEmptyProductSn(reqBean, resultBean, stepResult, workCenter, productionProcessContext, cellEquipContext, prodRuleContextList, productionPartContextList, outPartNoList, foreignKey, prodRuleIgnoreCfgMap);
            //存在【产出零件数据】信息, 存在【进料零件条码信息】 获取匹配当前产出零件的加工规则数据 (只能查询到一条,否则报错)
        else return doHandleProdRuleDataByProductionPartAndProductSn(reqBean, resultBean, stepResult, workCenter, productionProcessContext, cellEquipContext, prodRuleContextList, productionPartContextList, productionPsInContextList, outPartNoList, foreignKey, prodRuleIgnoreCfgMap);

    }

    //没有【进料零件条码信息】信息, 根据进料[NULL]条件获取匹配当前产出零件的加工规则数据(只能查询到一条,否则报错)
    private StepResult doHandleProdRuleDataByProductionPartAndEmptyProductSn(StationRequestBean reqBean, StationResultBean resultBean, StepResult stepResult, MesWorkCenter workCenter, MesProductionProcessContext productionProcessContext,
                                                                             MesCellEquipContext cellEquipContext, List<MesProdRuleContext> prodRuleContextList, List<MesProductionPartContext> productionPartContextList, List<String> outPartNoList, Integer foreignKey,
                                                                             Map<String, List<MesProdRuleIgnoreCfg>> prodRuleIgnoreCfgMap) {

        //【非排序线】获取产品加工规则 ; 条件进料[NULL]
        List<MesProdRuleNosortCfg> prodRuleNosortCfgList = prodRuleCfgExtService.getProdRuleNosortCfgList(new MesProdRuleContext(reqBean.getOrganizeCode()).equipmentCode(cellEquipContext.getEquipmentCode()).outPartNos(outPartNoList).inPartIsEmpty(true));

        //根据产出零件分组数据
        Map<String, List<MesProdRuleNosortCfg>> prodRuleNosortCfgMap = groupProdRuleNosortCfgList(prodRuleNosortCfgList);

        for (MesProductionPartContext productionPartContext : productionPartContextList) {

            //foreignKey有值代表已经匹配过产品加工规则
            if (null == productionPartContext || productionPartContext.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()) == 0 || !StringUtils.isEmpty(productionPartContext.getForeignKey())) continue;

            List<MesProdRuleNosortCfg> filterList = !CollectionUtils.isEmpty(prodRuleNosortCfgMap) ? prodRuleNosortCfgMap.get(productionPartContext.getPartNo().toUpperCase()) : null;

            if (CollectionUtils.isEmpty(filterList) || filterList.size() > 1) {
                productionPartContextList.forEach(o -> o.busiCheckToDelete());
                return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(), stepResult, String.format(
                        "设备[%s]产出零件[%s]进料零件[空]%s加工规则配置信息!", cellEquipContext.getEquipmentName(), productionPartContext.getPartNo(), CollectionUtils.isEmpty(filterList) ? "未匹配到" : String.format("匹配到[%s]条", filterList.size())));
            }
            //【非排序线】获取产品加工规则对应的装配件信息 -同时- 关联不可用规则
            MesProdRuleContext prodRuleContext = prodRuleCfgExtService.getProdRuleNosortContext(
                    new MesProdRuleContext(reqBean.getOrganizeCode(), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), reqBean.getProcessCode(), productionProcessContext.getCraftCode())
                            .copy(filterList.get(0)).workOrderNo(productionPartContext.getWorkOrderNo()).foreignKey(productionPartContext.foreignKey(foreignKey += 1).getForeignKey()),
                    prodRuleIgnoreCfgMap);
            prodRuleContextList.add(prodRuleContext);

            log.info("工厂{}生产线{}工位{}:FSM STATE DISPATCHER --- DO STEP --- {} EXEC --- 设备:{} 产出零件信息:{} 产品加工规则:{} ---", reqBean.getOrganizeCode(), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(),
                    StringUtil.toLowerCaseFirst(this.getClass().getSimpleName()), cellEquipContext.getEquipmentCode(), JSONObject.toJSONString(productionPartContext), JSONObject.toJSONString(prodRuleContext));

        }

        return stepResult;
    }

    //没有【产出零件数据】信息 , 根据进料条件获取匹配加工规则数据(只能查询到一条,否则报错)
    private StepResult doHandleProdRuleDataByProductSn(StationRequestBean reqBean, StationResultBean resultBean, StepResult stepResult, MesWorkCenter workCenter, MesProductionProcessContext productionProcessContext,
                                                       MesCellEquipContext cellEquipContext, List<MesProdRuleContext> prodRuleContextList, List<MesProductionPsInContext> productionPsInContextList,
                                                       Map<String, List<MesProdRuleIgnoreCfg>> prodRuleIgnoreCfgMap) {

        //【非排序线】获取产品加工规则 ; 分别根据 进料零件有值 与 进料规则不为空 查询 再合并数据返回
        List<MesProdRuleNosortCfg> prodRuleNosortCfgList = getProdRuleNosortCfgList(reqBean, cellEquipContext, productionPsInContextList, null);
        //加工规则关联不可用规则信息
        prodRuleNosortCfgList = prodRuleCfgExtService.getProdRuleNosortCfgList(prodRuleNosortCfgList, prodRuleIgnoreCfgMap);

        //拿到当前最大的foreignKey
        Optional<MesProductionPsInContext> maxForeignKeyOptional = productionPsInContextList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getForeignKey()))).max(Comparator.comparing(MesProductionPsInContext::getForeignKey));
        Integer foreignKey = (null != maxForeignKeyOptional && maxForeignKeyOptional.isPresent()) ? maxForeignKeyOptional.get().getForeignKey() : MesPcnExtConstWords.ZERO;

        for (MesProductionPsInContext productionPsInContext : productionPsInContextList) {

            //foreignKey有值代表已经匹配过产品加工规则
            if (null == productionPsInContext || productionPsInContext.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()) == 0 || !StringUtils.isEmpty(productionPsInContext.getForeignKey())) continue;

            List<MesProdRuleNosortCfg> filterList;
            //搜集进料零件号匹配的产品加工规则
            if (!StringUtils.isEmpty(productionPsInContext.getPartNo())) filterList = filterProdRuleNosortCfgList(prodRuleNosortCfgList, productionPsInContext.getPartNo());
                //进料零件条码匹配进料零件规则
            else filterList = (List<MesProdRuleNosortCfg>) numberRuleMatchDispatchService.matchNumberRule(reqBean.getOrganizeCode(), productionPsInContext.getProductSn(), filterProdRuleNosortCfgList(prodRuleNosortCfgList));

            if (CollectionUtils.isEmpty(filterList) || filterList.size() > 1) {
                if (productionPsInContext.getMessageSource().compareTo(MesExtEnumUtil.CELL_MESSAGE_SOURCE.SCAN.getValue()) == 0) productionPsInContext.busiCheckToDelete();
                else productionPsInContextList.forEach(o -> o.busiCheckToDelete());
                return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(), stepResult, String.format("设备[%s]与进料零件条码%s加工规则配置信息! 进料零件条码[%s]%s",
                        cellEquipContext.getEquipmentName(), CollectionUtils.isEmpty(filterList) ? "未匹配到" : String.format("匹配到[%s]条", filterList.size()), productionPsInContext.getProductSn(),
                        StringUtils.isEmpty(productionPsInContext.getPartNo()) ? "为外协件" : String.format("当前零件号[%s]%s", productionPsInContext.getPartNo(), checkIsOffline(productionPsInContext, reqBean) ? "在当前工位已完成下线" : MesPcnExtConstWords.EMPTY)));
            }

            //时效性验证
            if (!StringUtils.isEmpty(productionPsInContext.getPartNo())) {
                Map<String, Object> result = timeEfficientCfgMatchService.checkSnTimeliness(reqBean.getOrganizeCode(), productionPsInContext.getProductSn(), filterList.get(0).getId(), MesExtEnumUtil.TIME_DATA_SOURCE.DATA_SOURCE20.getValue());

                if (!(Boolean) result.get(MesPcnExtConstWords.RESULT)) {
                    if (productionPsInContext.getMessageSource().compareTo(MesExtEnumUtil.CELL_MESSAGE_SOURCE.SCAN.getValue()) == 0) productionPsInContext.busiCheckToDelete();
                    else productionPsInContextList.forEach(o -> o.busiCheckToDelete());
                    return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog().scanInfo(productionPsInContext.getProductSn()), stepResult, (String) result.get(MesPcnExtConstWords.MESSAGE));
                }
            }

            //【非排序线】获取产品加工规则对应的装配件信息 -同时- 关联不可用规则
            MesProdRuleContext prodRuleContext = new MesProdRuleContext(
                    reqBean.getOrganizeCode(), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), reqBean.getProcessCode(), productionProcessContext.getCraftCode())
                    .copy(filterList.get(0)).productSn(productionPsInContext.getProductSn()).foreignKey(productionPsInContext.foreignKey(foreignKey += 1).getForeignKey());

            prodRuleContext = prodRuleCfgExtService.getProdRuleNosortContext(prodRuleContext, prodRuleIgnoreCfgMap);
            prodRuleContextList.add(prodRuleContext);

            log.info("工厂{}生产线{}工位{}:FSM STATE DISPATCHER --- DO STEP --- {} EXEC --- 设备:{} 进料零件条码:{} 产品加工规则:{} ---", reqBean.getOrganizeCode(), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(),
                    StringUtil.toLowerCaseFirst(this.getClass().getSimpleName()), cellEquipContext.getEquipmentCode(), JSONObject.toJSONString(productionPsInContext), JSONObject.toJSONString(prodRuleContext));

        }

        return stepResult;

    }

    //验证条码是否刚在当前工位下线
    private Boolean checkIsOffline(MesProductionPsInContext productionPsInContext, StationRequestBean reqBean) {
        if (StringUtils.isEmpty(productionPsInContext.getWorkCenterCode()) || StringUtils.isEmpty(productionPsInContext.getWorkCellCode()) || StringUtils.isEmpty(productionPsInContext.getProcessCode())) return false;
        if (!productionPsInContext.getWorkCenterCode().toUpperCase().equals(reqBean.getWorkCenterCode().toUpperCase()) || !productionPsInContext.getWorkCellCode().toUpperCase().equals(reqBean.getWorkCellCode().toUpperCase()) || !productionPsInContext.getProcessCode().toUpperCase().equals(reqBean.getProcessCode().toUpperCase())) return false;
        return true;
    }

    //【非排序线】获取产品加工规则
    private List<MesProdRuleNosortCfg> getProdRuleNosortCfgList(StationRequestBean reqBean, MesCellEquipContext cellEquipContext, List<MesProductionPsInContext> productionPsInContextList, List<String> outPartNoList) {
        //【非排序线】获取产品加工规则 ; 分别根据 进料零件有值 与 进料规则不为空 查询 再合并数据返回,    条件可能携带产出零件
        List<String> inPartNoList = productionPsInContextList.stream().filter(o -> (null != o && StringUtils.isEmpty(o.getForeignKey()) && !StringUtils.isEmpty(o.getPartNo()))).map(MesProductionPsInContext::getPartNo).collect(Collectors.toList());
        Optional<MesProductionPsInContext> optional = productionPsInContextList.stream().filter(o -> (null != o && StringUtils.isEmpty(o.getForeignKey()) && o.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) == 0 && StringUtils.isEmpty(o.getPartNo()))).findFirst();
        List<MesProdRuleNosortCfg> prodRuleNosortCfgList = prodRuleCfgExtService.getProdRuleNosortCfgList(
                !CollectionUtils.isEmpty(inPartNoList) ? new MesProdRuleContext(reqBean.getOrganizeCode()).equipmentCode(cellEquipContext.getEquipmentCode()).inPartNos(inPartNoList).outPartNos(outPartNoList) : null,
                (null != optional && optional.isPresent()) ? new MesProdRuleContext(reqBean.getOrganizeCode()).equipmentCode(cellEquipContext.getEquipmentCode()).inPartIsEmpty(true).inPartRuleIsEmpty(false).outPartNos(outPartNoList) : null);
        return prodRuleNosortCfgList;
    }

    //存在【产出零件数据】信息, 存在【进料零件条码信息】 获取匹配当前产出零件的加工规则数据 (只能查询到一条,否则报错)
    private StepResult doHandleProdRuleDataByProductionPartAndProductSn(StationRequestBean reqBean, StationResultBean resultBean, StepResult stepResult, MesWorkCenter workCenter, MesProductionProcessContext productionProcessContext, MesCellEquipContext cellEquipContext,
                                                                        List<MesProdRuleContext> prodRuleContextList, List<MesProductionPartContext> productionPartContextList, List<MesProductionPsInContext> productionPsInContextList, List<String> outPartNoList, Integer foreignKey,
                                                                        Map<String, List<MesProdRuleIgnoreCfg>> prodRuleIgnoreCfgMap) {

        //【非排序线】获取产品加工规则 ; 分别根据 进料零件有值 与 进料规则不为空 查询 再合并数据返回，  条件携带产出零件
        List<MesProdRuleNosortCfg> prodRuleNosortCfgList = getProdRuleNosortCfgList(reqBean, cellEquipContext, productionPsInContextList, outPartNoList);
        //加工规则关联不可用规则信息
        prodRuleNosortCfgList = prodRuleCfgExtService.getProdRuleNosortCfgList(prodRuleNosortCfgList, prodRuleIgnoreCfgMap);

        //根据产出零件分组数据
        Map<String, List<MesProdRuleNosortCfg>> prodRuleNosortCfgMap = groupProdRuleNosortCfgList(prodRuleNosortCfgList);

        for (MesProductionPartContext productionPartContext : productionPartContextList) {

            //foreignKey有值代表已经匹配过产品加工规则
            if (null == productionPartContext || productionPartContext.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()) == 0 || !StringUtils.isEmpty(productionPartContext.getForeignKey())) continue;

            //获取匹配产出零件的所有加工规则
            List<MesProdRuleNosortCfg> outPartNoProdRuleList = !CollectionUtils.isEmpty(prodRuleNosortCfgMap) ? prodRuleNosortCfgMap.get(productionPartContext.getPartNo().toUpperCase()) : null;

            MesProductionPsInContext productSn = null;

            //判断是否还存在待匹配的主条码信息, 内部循环匹配成功会标记 foreignKey
            Optional<MesProductionPsInContext> optional = productionPsInContextList.stream().filter(o -> (null != o && StringUtils.isEmpty(o.getForeignKey()) && o.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) == 0)).findFirst();
            if (null == optional || !optional.isPresent()) break;

            List<MesProdRuleNosortCfg> innerfilterList = null;
            for (MesProductionPsInContext productionPsInContext : productionPsInContextList) {

                //foreignKey有值代表已经匹配过产品加工规则
                if (null == productionPsInContext || productionPartContext.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()) == 0 || !StringUtils.isEmpty(productionPsInContext.getForeignKey())) continue;

                //搜集进料零件号匹配的产品加工规则
                if (!StringUtils.isEmpty(productionPsInContext.getPartNo())) innerfilterList = filterProdRuleNosortCfgList(outPartNoProdRuleList, productionPsInContext.getPartNo());
                //进料零件条码匹配进料零件规则
                else innerfilterList = (List<MesProdRuleNosortCfg>) numberRuleMatchDispatchService.matchNumberRule(reqBean.getOrganizeCode(), productionPsInContext.getProductSn(), filterProdRuleNosortCfgList(outPartNoProdRuleList));

                if (CollectionUtils.isEmpty(innerfilterList) || innerfilterList.size() > 1) continue;

                //验证进出一致
                if (!StringUtils.isEmpty(productionPsInContext.getPartNo()) && productionPsInContext.getPartNo().toUpperCase().equals(productionPartContext.getPartNo().toUpperCase())) {

                    //进料工单必须与产出工单一致
                    if (!StringUtils.isEmpty(productionPartContext.getWorkOrderNo()) && !StringUtils.isEmpty(productionPsInContext.getWorkOrderNo()) && !productionPsInContext.getWorkOrderNo().toUpperCase().equals(productionPartContext.getWorkOrderNo().toUpperCase())) {
                        if (productionPsInContext.getMessageSource().compareTo(MesExtEnumUtil.CELL_MESSAGE_SOURCE.SCAN.getValue()) == 0) productionPsInContext.busiCheckToDelete();
                        else productionPsInContextList.forEach(o -> o.busiCheckToDelete());
                        return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog().scanInfo(productionPsInContext.getProductSn()), stepResult, String.format("主条码[%s]零件号[%s]关联的加工单[%s]与产出零件[%s]关联的加工单[%s]不一致!",
                                productionPsInContext.getProductSn(), productionPsInContext.getPartNo(), productionPsInContext.getWorkOrderNo(), productionPartContext.getPartNo(), productionPartContext.getWorkOrderNo()));
                    }
                    //进料存在工单且已经过某工序生产, 不能匹配无工单的产出零件
                    if (StringUtils.isEmpty(productionPartContext.getWorkOrderNo()) && !StringUtils.isEmpty(productionPsInContext.getWorkOrderNo()) && !StringUtils.isEmpty(productionPsInContext.getProcessCode())) {
                        if (productionPsInContext.getMessageSource().compareTo(MesExtEnumUtil.CELL_MESSAGE_SOURCE.SCAN.getValue()) == 0) productionPsInContext.busiCheckToDelete();
                        else productionPsInContextList.forEach(o -> o.busiCheckToDelete());
                        return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog().scanInfo(productionPsInContext.getProductSn()), stepResult, String.format("主条码[%s]零件号[%s]已关联加工单[%s]且已经过工序[%s]工艺[%s],不支持匹配当前未关联加工单的产出零件[%s]",
                                productionPsInContext.getProductSn(), productionPsInContext.getPartNo(), productionPsInContext.getWorkOrderNo(), productionPsInContext.getProcessCode(), productionPsInContext.getCraftCode(), productionPartContext.getPartNo()));
                    }

                }

                //时效性验证
                if (!StringUtils.isEmpty(productionPsInContext.getPartNo())) {
                    Map<String, Object> result = timeEfficientCfgMatchService.checkSnTimeliness(reqBean.getOrganizeCode(), productionPsInContext.getProductSn(), innerfilterList.get(0).getId(), MesExtEnumUtil.TIME_DATA_SOURCE.DATA_SOURCE20.getValue());

                    if (!(Boolean)result.get(MesPcnExtConstWords.RESULT)) {
                        if (productionPsInContext.getMessageSource().compareTo(MesExtEnumUtil.CELL_MESSAGE_SOURCE.SCAN.getValue()) == 0) productionPsInContext.busiCheckToDelete();
                        else productionPsInContextList.forEach(o -> o.busiCheckToDelete());
                        return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog().scanInfo(productionPsInContext.getProductSn()), stepResult, (String) result.get(MesPcnExtConstWords.MESSAGE));
                    }
                }

                productionPartContext.foreignKey(productionPsInContext.foreignKey(foreignKey += 1).getForeignKey());
                productSn = productionPsInContext;

                break;
            }

            //【非排序线】获取产品加工规则对应的装配件信息
            if (!CollectionUtils.isEmpty(innerfilterList) && innerfilterList.size() == 1) {

                //【非排序线】获取产品加工规则对应的装配件信息 -同时- 关联不可用规则
                MesProdRuleContext prodRuleContext = prodRuleCfgExtService.getProdRuleNosortContext(
                        new MesProdRuleContext(reqBean.getOrganizeCode(), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), reqBean.getProcessCode(), productionProcessContext.getCraftCode())
                                .copy(innerfilterList.get(0)).productSn(productSn.getProductSn()),
                        prodRuleIgnoreCfgMap);

                prodRuleContext.foreignKey(productionPartContext.getForeignKey());
                if (!StringUtils.isEmpty(productionPartContext.getWorkOrderNo())) prodRuleContext.workOrderNo(productionPartContext.getWorkOrderNo());

                prodRuleContextList.add(prodRuleContext);

                log.info("工厂{}生产线{}工位{}:FSM STATE DISPATCHER --- DO STEP --- {} EXEC --- 设备:{} 产出零件信息:{} 进料零件条码:{} 产品加工规则:{} ---", reqBean.getOrganizeCode(), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(),
                        StringUtil.toLowerCaseFirst(this.getClass().getSimpleName()), cellEquipContext.getEquipmentCode(), JSONObject.toJSONString(productionPartContext), JSONObject.toJSONString(productSn), JSONObject.toJSONString(prodRuleContext));

            }

        }

        //判断是否还存在待匹配的主条码信息
        Optional<MesProductionPsInContext> optional = productionPsInContextList.stream().filter(o -> (null != o && StringUtils.isEmpty(o.getForeignKey()) && o.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) == 0)).findFirst();
        if (null != optional && optional.isPresent()) {
            if (optional.get().getMessageSource().compareTo(MesExtEnumUtil.CELL_MESSAGE_SOURCE.SCAN.getValue()) == 0) optional.get().busiCheckToDelete();
            else productionPsInContextList.forEach(o -> o.busiCheckToDelete());
            String productSnStr = productionPsInContextList.stream().filter(o -> (null != o && StringUtils.isEmpty(o.getForeignKey()) && o.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) == 0))
                    .map(o -> String.format(" 进料零件条码[%s]%s", o.getProductSn(), StringUtils.isEmpty(o.getPartNo()) ? "为外协件" : String.format("当前零件号[%s]%s", o.getPartNo(), checkIsOffline(o, reqBean) ? "在当前工位已完成下线" : MesPcnExtConstWords.EMPTY)))
                    .collect(Collectors.joining(MesPcnExtConstWords.SEMICOLON));
            return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog().scanInfo(productSnStr), stepResult, String.format("设备[%s]产出零件%s与进料零件条码未匹配到唯一的加工规则配置信息!%s", cellEquipContext.getEquipmentName(),
                    productionPartContextList.stream().filter(o -> (null != o && StringUtils.isEmpty(o.getForeignKey()) && o.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) == 0)).map(MesProductionPartContext::getPartNo).collect(Collectors.toList()).toString(),
                    productSnStr));
        }

        return stepResult;

    }

    //搜集进料零件号匹配的产品加工规则
    private List<MesProdRuleNosortCfg> filterProdRuleNosortCfgList(List<MesProdRuleNosortCfg> prodRuleNosortCfgList, String partNo) {
        return CollectionUtils.isEmpty(prodRuleNosortCfgList) ? null : prodRuleNosortCfgList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getInPartNo()) && o.getInPartNo().toUpperCase().equals(partNo.toUpperCase()))).collect(Collectors.toList());
    }

    //搜集进料零件规则有值的产品加工规则
    private List<MesProdRuleNosortCfg> filterProdRuleNosortCfgList(List<MesProdRuleNosortCfg> prodRuleNosortCfgList) {
        return CollectionUtils.isEmpty(prodRuleNosortCfgList) ? null : prodRuleNosortCfgList.stream().filter(o -> (null != o && StringUtils.isEmpty(o.getInPartNo()) && !StringUtils.isEmpty(o.getInPartNoRule()))).collect(Collectors.toList());
    }

    //根据产出零件分组数据
    private Map<String, List<MesProdRuleNosortCfg>> groupProdRuleNosortCfgList(List<MesProdRuleNosortCfg> prodRuleNosortCfgList) {
        return CollectionUtils.isEmpty(prodRuleNosortCfgList) ? null : prodRuleNosortCfgList.stream().filter(o -> null != o).collect(Collectors.groupingBy(o -> o.getOutPartNo().toUpperCase()));
    }

    //显示或者匹配装配件信息 公共调用
    public Boolean showProductionAssembly(StationRequestBean reqBean, StationResultBean resultBean, MesWorkCenter workCenter, List<MesProdRuleContext> prodRuleContextList) {

        //用于获取前端置顶ID
        List<MesProductionAssemblyNosortContext> allAssemblyList = new ArrayList<>();

        for (MesProdRuleContext prodRuleContext : prodRuleContextList) {

            if (null == prodRuleContext || StringUtils.isEmpty(prodRuleContext.getAssemblyDataJson())) continue;

            List<MesProductionAssemblyNosortContext> productionAssemblyNosortContextList = prodRuleContext.getNosortAssemblyDataContext();

            allAssemblyList.addAll(productionAssemblyNosortContextList);

        }

        if (CollectionUtils.isEmpty(allAssemblyList)) return false;

        //获取前端置顶信息 【sourceId&MatchDatetime】
        String topRowInfo = getTopRowInfo(allAssemblyList);

        //是否显示规则列【工位参数】
        Boolean isNeedShowMatchRule = checkIsNeedShowMatchRule(reqBean);

        for (MesProdRuleContext prodRuleContext : prodRuleContextList) {

            if (null == prodRuleContext || StringUtils.isEmpty(prodRuleContext.getAssemblyDataJson())) continue;

            //封装匹配当前设备的装配件信息
            List<MesAssemblyShowContext> assemblyShowContextList = getAssemblyShowContextList(prodRuleContext, workCenter, isNeedShowMatchRule, topRowInfo);
            if (CollectionUtils.isEmpty(assemblyShowContextList)) continue;

            //装配件清单列表标题
            List<AttrBean> attrBeanList = dataAttrList(prodRuleContext.getWorkOrderNo(), isNeedShowMatchRule);

            //封装多表格
            resultBean.addStationResultBeans(new StationResultBean().dataType(MesPcnEnumUtil.STATION_DATA_TYPE.TABLE.getValue()).dataAttrList(attrBeanList).resultList(assemblyShowContextList));

        }

        if (CollectionUtils.isEmpty(resultBean.getStationResultBeans())) return false;

        resultBean.busiType(MesPcnEnumUtil.STATION_BUSI_TYPE.STEP_CUSTOM_CONTENT.getValue()).dataType(MesPcnEnumUtil.STATION_DATA_TYPE.TABLES.getValue());
        this.sendMessage(reqBean, resultBean);

        return true;

    }

    //获取前端置顶信息 【sourceId&MatchDatetime】
    private String getTopRowInfo(List<MesProductionAssemblyNosortContext> allAssemblyList) {
        if (CollectionUtils.isEmpty(allAssemblyList)) return null;
        allAssemblyList = allAssemblyList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getMatchDatetime()))).sorted(Comparator.comparing(MesProductionAssemblyNosortContext::getMatchDatetime).reversed()).collect(Collectors.toList());
        return CollectionUtils.isEmpty(allAssemblyList) ? null : new StringJoiner(MesPcnExtConstWords.AND).add(allAssemblyList.get(0).getSourceId().toString()).add(allAssemblyList.get(0).getMatchDatetime()).toString();
    }

    //是否显示规则列【工位参数】
    private Boolean checkIsNeedShowMatchRule(StationRequestBean reqBean) {
        return fsmCommonService.checkWcpcMapIsMatchTrue(reqBean, MesPcnExtConstWords.ASSEMBLY_SHOW_MR_CFG);
    }

    private String getWorkOrderNo(Map<Integer, MesProductionPartContext> productionPartMap, Integer foreignKey) {
        return (!CollectionUtils.isEmpty(productionPartMap) && productionPartMap.containsKey(foreignKey)) ? productionPartMap.get(foreignKey).getWorkOrderNo() : null;
    }

    //封装匹配当前设备的装配件信息
    private List<MesAssemblyShowContext> getAssemblyShowContextList(MesProdRuleContext prodRuleContext, MesWorkCenter workCenter, Boolean isNeedShowMatchRule, String topRowInfo) {
        List<MesProductionAssemblyContext> productionAssemblyContextList = prodRuleContext.getAssemblyDataContext(workCenter);
        if (CollectionUtils.isEmpty(productionAssemblyContextList)) return null;
        List<MesAssemblyShowContext> assemblyShowContextList = new ArrayList<>();
        for (MesProductionAssemblyContext item : productionAssemblyContextList) {
            //不显示且不扫描/仅目视的不显示出来
            if (null == item || MesExtEnumUtil.ASSEMBLY_MATCH_TYPE.checkIsNoShow(item.getMatchType())) continue;
            MesAssemblyShowContext assemblyShowContext = assemblyShowContext(item, isNeedShowMatchRule, topRowInfo);
            assemblyShowContext.setWorkOrderNo(prodRuleContext.getWorkOrderNo());
            assemblyShowContextList.add(assemblyShowContext);
        }

        assemblyShowContextList = assemblyShowContextList.stream().filter(o -> null != o).sorted(Comparator.comparing(MesAssemblyShowContext::getRouteSeq)).collect(Collectors.toList());

        AtomicReference<Integer> index = new AtomicReference<>(0);
        assemblyShowContextList.forEach(o -> o.index(index.updateAndGet(v -> v + 1)));

        return assemblyShowContextList;
    }

    //封装装配件信息
    private MesAssemblyShowContext assemblyShowContext(MesProductionAssemblyContext productionAssemblyContext, Boolean isNeedShowMatchRule, String topRowInfo) {
        MesAssemblyShowContext assemblyShowContext = new MesAssemblyShowContext();
        BeanUtils.copyProperties(productionAssemblyContext, assemblyShowContext);
        if (isNeedShowMatchRule) assemblyShowContext.setMatchTypeName(showMatchRule(assemblyShowContext));
        assemblyShowContext.setAssemblyStatusName(MesExtEnumUtil.ASSEMBLY_STATUS.valueOfDescription(assemblyShowContext.getAssemblyStatus()));
        if (MesExtEnumUtil.ASSEMBLY_STATUS.ASSEMBLY_STATUS_30.getValue() != assemblyShowContext.getAssemblyStatus())
            assemblyShowContext.setColor(StringUtils.isEmpty(assemblyShowContext.getShowColor()) ? MesExtEnumUtil.COLOR.GREEN.getValue() : assemblyShowContext.getShowColor());
        if (!StringUtils.isEmpty(topRowInfo) && !StringUtils.isEmpty(productionAssemblyContext.getSourceId()) && !StringUtils.isEmpty(productionAssemblyContext.getMatchDatetime()) &&
                topRowInfo.equals(new StringJoiner(MesPcnExtConstWords.AND).add(productionAssemblyContext.getSourceId().toString()).add(productionAssemblyContext.getMatchDatetime()).toString())) assemblyShowContext.isTop();
        return assemblyShowContext;
    }

    private String showMatchRule(MesAssemblyShowContext assemblyShowContext) {
        if (!MesExtEnumUtil.ASSEMBLY_MATCH_TYPE.checkIsNeedShowMatchRule(assemblyShowContext.getMatchType())) return MesExtEnumUtil.ASSEMBLY_MATCH_TYPE.valueOfDescription(assemblyShowContext.getMatchType());
        return MesExtEnumUtil.ASSEMBLY_MATCH_TYPE.valueOfDescription(assemblyShowContext.getMatchType()) + "【" + assemblyShowContext.getMatchRule() + "】";
    }

    //装配件清单列表标题
    private List<AttrBean> dataAttrList(String workOrderNo, Boolean isNeedShowMatchRule) {
        List<AttrBean> attrBeanList = new ArrayList<>();
        PojoAttrUtil.loadPojoAttrs(attrBeanList, MesPcnExtConstWords.INDEX, "序号");
        if (isNeedShowMatchRule) PojoAttrUtil.loadPojoAttrs(attrBeanList, MesPcnExtConstWords.MATCH_TYPE_NAME, "装配方式");
        if (!StringUtils.isEmpty(workOrderNo)) PojoAttrUtil.loadPojoAttrs(attrBeanList, MesPcnExtConstWords.WORK_ORDER_NO, "生产工单编号");
        PojoAttrUtil.loadPojoAttrs(attrBeanList, MesPcnExtConstWords.PART_NO, "产成零件编码");
        PojoAttrUtil.loadPojoAttrs(attrBeanList, MesPcnExtConstWords.ASSEMBLY_PART_NO, "零件编码");
        PojoAttrUtil.loadPojoAttrs(attrBeanList, MesPcnExtConstWords.ASSEMBLY_PART_NAME, "零件名称");
        PojoAttrUtil.loadPojoAttrs(attrBeanList, MesPcnExtConstWords.ASSEMBLY_SN, "零件条码");
        PojoAttrUtil.loadPojoAttrs(attrBeanList, MesPcnExtConstWords.ASSEMBLY_STATUS_NAME, "装配状态");
        return attrBeanList;
    }



}
