WipSetupAction.java

package com.mycim.webapp.actions;

import com.fa.sesa.exception.Assert;
import com.fa.sesa.exception.Errors;
import com.fa.sesa.exception.SystemIllegalArgumentException;
import com.fa.sesa.exception.TransformFunc;
import com.fa.sesa.i18n.I18nUtils;
import com.fa.sesa.threadlocal.LocalContext;
import com.mycim.framework.cache.utils.CacheUtils;
import com.mycim.framework.context.spring.SpringContext;
import com.mycim.framework.utils.MiscUtils;
import com.mycim.framework.utils.beans.BeanUtils;
import com.mycim.framework.utils.beans.PropertyUtils;
import com.mycim.framework.utils.lang.ObjectUtils;
import com.mycim.framework.utils.lang.StringUtils;
import com.mycim.framework.utils.lang.collections.CollectionUtils;
import com.mycim.framework.utils.lang.collections.MapUtils;
import com.mycim.framework.utils.lang.math.NumberUtils;
import com.mycim.framework.utils.lang.time.DateUtils;
import com.mycim.framework.utils.msg.JsonUtils;
import com.mycim.framework.workflow.engine.WorkflowEngineService;
import com.mycim.server.alarm.service.AlarmService;
import com.mycim.server.asm.service.AsmService;
import com.mycim.server.asm.service.WarehouseService;
import com.mycim.server.automonitor.service.LotAutoMonitorInqService;
import com.mycim.server.carrier.service.CarrierService;
import com.mycim.server.constrain.service.ConstrainService;
import com.mycim.server.ctx.exec.service.CtxExecService;
import com.mycim.server.ctx.service.CtxService;
import com.mycim.server.edc.service.EdcService;
import com.mycim.server.ems.service.EmsService;
import com.mycim.server.erp.service.ErpService;
import com.mycim.server.npw.service.EqpMonitorService;
import com.mycim.server.prp.service.ProductService;
import com.mycim.server.prp.service.PrpService;
import com.mycim.server.rcp.service.RecipeService;
import com.mycim.server.reticle.service.ReticleService;
import com.mycim.server.runcard.service.RecoveryRunCardService;
import com.mycim.server.runcard.service.RunCardLotInqService;
import com.mycim.server.runcard.service.RunCardService;
import com.mycim.server.runcard.service.SplitRunCardService;
import com.mycim.server.security.service.SecurityService;
import com.mycim.server.sorter.exec.service.SorterExecInqService;
import com.mycim.server.sorter.service.SorterQueryService;
import com.mycim.server.sorter.service.SorterService;
import com.mycim.server.spc.service.SpcService;
import com.mycim.server.spec.service.ProcessSpecService;
import com.mycim.server.spec.service.SpecService;
import com.mycim.server.wip.service.*;
import com.mycim.server.workorder.service.WorkOrderService;
import com.mycim.utils.WipUtils;
import com.mycim.valueobject.MessageIdList;
import com.mycim.valueobject.ObjectList;
import com.mycim.valueobject.bas.ErrorMsg;
import com.mycim.valueobject.bas.NamedObject;
import com.mycim.valueobject.bas.ObjectVersion;
import com.mycim.valueobject.bas.Relation;
import com.mycim.valueobject.consts.*;
import com.mycim.valueobject.edcspc.Parameter;
import com.mycim.valueobject.ems.*;
import com.mycim.valueobject.ems.pilot.PiLotView;
import com.mycim.valueobject.ocap.OcapCard;
import com.mycim.valueobject.prp.*;
import com.mycim.valueobject.security.User;
import com.mycim.valueobject.sorter.SortJobBean;
import com.mycim.valueobject.sorter.SorterModel;
import com.mycim.valueobject.sys.ReferenceFileDetail;
import com.mycim.valueobject.wip.*;
import com.mycim.valueobject.wip.dto.LotReleaseInfoDto;
import com.mycim.valueobject.wip.dto.ReassignRequestDto;
import com.mycim.webapp.Constants;
import com.mycim.webapp.WebUtils;
import com.mycim.webapp.forms.RootForm;
import com.mycim.webapp.forms.lot.HoldReleaseLotInfoForm;
import com.mycim.webapp.forms.lot.LotInfoForm;
import com.mycim.webapp.forms.lot.SplitMergeInfoForm;
import com.mycim.workflow.valueobject.WorkflowStepModel;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.Override;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;


public class WipSetupAction extends AbstractAction {

    public static final String[] ADJUST_LOT_FIELDS = {"createCategory", "lotType", "priority", "hotFlag",
                                                      "splitMergeFlag", "lotOwner", "dueDate", "customerId",
                                                      "shippingCode", "outerOrderNO", "outOrderType", "lotComments"};

    protected static final String KEY_RECIPE_ID = "recipeId";

    protected static final String KEY_STATUS = "status";

    protected static final String DUMMY_KEY = "DUMMY";

    protected static final String UOM_WAFER = "WAFER";

    protected static final String CR_KEY = "CR";

    protected static final String EDD_KEY = "EDD";

    protected static final String FIFO_KEY = "FIFO";

    protected static final String PRIORITY_KEY = "PRIORITY";

    protected static final String SAME_SETUP_KEY = "SAME_SETUP";

    protected static final String STATION_RRN = "stationRrn";

    protected static final String EQUIPMENT_RRN = "equipmentRrn";

    protected static final String LOT_STATUS = "lotStatus";

    protected static final String DUMMY_FLAG = "dummyFlag";

    protected static final String ON = "on";

    protected static final String TRUE = "1";

    protected static final String FALSE = "0";

    protected static final String LOTID_KEY = "lotId";

    protected static final String LOT_SEARCH_TYPE_KEY = "lotSearchType";

    protected static final String CARRIER_SEARCH_TYPE_KEY = "carrierSearchType";

    protected static final String LOT_READONLY_KEY = "readOnly";

    protected static final String LOT_DEFAULT_SEARCH_TYPE = "LOT";

    protected static final String CARRIER_DEFAULT_SEARCH_TYPE = "LOT_CARRIER";

    protected LotService lotService = SpringContext.getBean(LotService.class);

    protected PrpService prpService = SpringContext.getBean(PrpService.class);

    protected ProductService productService = SpringContext.getBean(ProductService.class);

    protected CtxExecService ctxExecService = SpringContext.getBean(CtxExecService.class);

    protected WipQueryService wipQueryService = SpringContext.getBean(WipQueryService.class);

    protected LotQueryService lotQueryService = SpringContext.getBean(LotQueryService.class);

    protected LotInqService lotInqService = SpringContext.getBean(LotInqService.class);

    protected DiffBatchQueryService diffBatchQueryService = SpringContext.getBean(DiffBatchQueryService.class);

    protected SecurityService securityService = SpringContext.getBean(SecurityService.class);

    protected CarrierService carrierService = SpringContext.getBean(CarrierService.class);

    protected ConstrainService constrainService = SpringContext.getBean(ConstrainService.class);

    protected EmsService emsService = SpringContext.getBean(EmsService.class);

    protected WipWorkflowQueryService wipWorkflowQueryService = SpringContext.getBean(WipWorkflowQueryService.class);

    protected RecipeService recipeService = SpringContext.getBean(RecipeService.class);

    protected WipCheckService wipCheckService = SpringContext.getBean(WipCheckService.class);

    protected WipService wipService = SpringContext.getBean(WipService.class);

    protected AsmService asmService = SpringContext.getBean(AsmService.class);

    protected WarehouseService warehouseService = SpringContext.getBean(WarehouseService.class);

    protected CtxService ctxService = SpringContext.getBean(CtxService.class);

    protected EdcService edcService = SpringContext.getBean(EdcService.class);

    protected ReticleService reticleService = SpringContext.getBean(ReticleService.class);

    protected WorkflowEngineService workflowEngineService = SpringContext.getBean(WorkflowEngineService.class);

    protected AlarmService alarmService = SpringContext.getBean(AlarmService.class);

    protected SpcService spcService = SpringContext.getBean(SpcService.class);

    protected SplitRunCardService splitRunCardService = SpringContext.getBean(SplitRunCardService.class);

    protected RecoveryRunCardService recoveryRunCardService = SpringContext.getBean(RecoveryRunCardService.class);

    protected RunCardService cardService = SpringContext.getBean(RunCardService.class);

    protected EqpMonitorService dmmService = SpringContext.getBean(EqpMonitorService.class);

    protected WorkOrderService workOrderService = SpringContext.getBean(WorkOrderService.class);

    protected SpecService specService = SpringContext.getBean(SpecService.class);

    protected ProcessSpecService processSpecService = SpringContext.getBean(ProcessSpecService.class);

    protected LotReassignService lotReassignService = SpringContext.getBean(LotReassignService.class);

    protected LotRunCardService lotRunCardService = SpringContext.getBean(LotRunCardService.class);

    protected LotRunCardQueryService lotRunCardQueryService = SpringContext.getBean(LotRunCardQueryService.class);

    protected RunCardLotInqService runCardLotInqService = SpringContext.getBean(RunCardLotInqService.class);

    protected LotStatusCheckService lotStatusCheckService = SpringContext.getBean(LotStatusCheckService.class);

    protected ErpService erpService = SpringContext.getBean(ErpService.class);

    protected LotSamplingService lotSamplingService = SpringContext.getBean(LotSamplingService.class);

    protected SorterService sorterService = SpringContext.getBean(SorterService.class);

    protected SorterQueryService sorterQueryService = SpringContext.getBean(SorterQueryService.class);

    protected SorterExecInqService sorterExecInqService = SpringContext.getBean(SorterExecInqService.class);

    protected LotAutoMonitorInqService lotAutoMonitorInqService = SpringContext.getBean(LotAutoMonitorInqService.class);

    @Override
    public NamedObject getInstance(NamedObject instance) {

        NamedObject _instance = null;
        if (instance instanceof Item) {
            _instance = prpService.getItem((Item) instance);
        }
        if (_instance != null) {
            instance = _instance;
        }

        return instance;
    }

    public List<Map> getFutureHoldListbyProduct(Long facilityRrn, long refRrn, Long productRrn, long processRrn,
                                                long lotRrn) {

        List<Map> futureHoldListByProduct = ctxExecService.getFutureHoldListByProduct(refRrn, productRrn, processRrn,
                                                                                      lotRrn);
        setFlowSeqAndStepdesc(facilityRrn, null, futureHoldListByProduct);

        return futureHoldListByProduct;
    }

    public Collection checkSimiliarLot(long eqptRrn, Collection selectedLots) {
        Collection similiarLots = new ArrayList();

        Map criterion = new HashMap();
        criterion.put(LOT_STATUS, new ArrayList(Arrays.asList("WAITING,DISPATCH,HOLD".split(","))));
        criterion.put("objectRrn", (new Long(eqptRrn)).toString());
        criterion.put("objectType", "ENTITY");
        Collection availLots = lotQueryService.getLotList4ExtAtEqpt(criterion);

        for (Iterator itSelected = selectedLots.iterator(); itSelected.hasNext(); ) {
            HashMap selectedLot = (HashMap) itSelected.next();
            String selectedLotId = ((String) selectedLot.get("lotId")).trim();
            for (Iterator itAvail = availLots.iterator(); itAvail.hasNext(); ) {
                HashMap availLot = (HashMap) itAvail.next();
                String availLotId = ((String) availLot.get("lotId")).trim();
                // 循环比较选中的批次和可选批次的批号, 看是否类似, 已选批次存在于可选批次中, 需要排除
                // 位数不同的也不需要比较
                if ((availLotId.equalsIgnoreCase(selectedLotId)) || (availLotId.length() != selectedLotId.length())) {
                } else {
                    // 批次只比较小数点之前的位数
                    String s_availLotId = availLotId.substring(0, availLotId.indexOf("."));
                    String s_selectedLotId = selectedLotId.substring(0, selectedLotId.indexOf("."));
                    // 如果相同, 那就是同一个母批, 不要求比较
                    if (s_availLotId.equalsIgnoreCase(s_selectedLotId)) {
                    } else {
                        // 将批次分成前后两部分, 如果前后都不相同, 表示起码两个位置不同
                        // 只比较前部相同后部不同和前部不同和后不相同两种情况, 且比较length/2个字符
                        int len = s_selectedLotId.length();
                        int mid = len / 2;
                        String f_availLotId = s_availLotId.substring(0, mid);
                        String t_availLotId = s_availLotId.substring(mid);
                        String f_selectedLotId = s_selectedLotId.substring(0, mid);
                        String t_selectedLotId = s_selectedLotId.substring(mid);
                        String result = "";
                        if ((!f_availLotId.equalsIgnoreCase(f_selectedLotId)) &&
                                (t_availLotId.equalsIgnoreCase(t_selectedLotId))) {
                            int diff = 0;
                            for (int i = 0; i < mid; i++) {
                                if (f_availLotId.substring(i, i + 1)
                                                .equalsIgnoreCase(f_selectedLotId.substring(i, i + 1))) {
                                } else {
                                    result = "批次" + selectedLotId + "与待加工的批次" + availLotId + "仅第" + (i + 1) + "位不同";
                                    diff++;
                                }
                            }
                            if (diff == 1) {
                                similiarLots.add(result);
                            }
                        } else if ((f_availLotId.equalsIgnoreCase(f_selectedLotId)) &&
                                (!t_availLotId.equalsIgnoreCase(t_selectedLotId))) {
                            int diff = 0;
                            for (int i = 0; i < len - mid; i++) {
                                if (t_availLotId.substring(i, i + 1)
                                                .equalsIgnoreCase(t_selectedLotId.substring(i, i + 1))) {
                                } else {
                                    result = "批次" + selectedLotId + "与待加工的批次" + availLotId + "仅第" + (i + 1 + mid) +
                                            "位不同";
                                    diff++;
                                }
                            }
                            if (diff == 1) {
                                similiarLots.add(result);
                            }
                        }
                    }
                }
            }
        }
        return similiarLots;
    }

    /**
     * 根据选择recipeId 带出可用腔
     *
     * @param request
     * @param lotInfo
     * @throws Exception
     */
    public void selectRecipeId(HttpServletRequest request, Map lotInfo, int index) {
        String recipeId = WebUtils.getParameter("recipeId" + index, request);
        List<Map> childRecipeList = (List) MapUtils.getObject(lotInfo, "childRecipes");
        boolean hasChildRecipe = childRecipeList != null && childRecipeList.size() > 0;
        if (StringUtils.isEmpty(recipeId)) {
            recipeId = MapUtils.getString(lotInfo, "recipeId");
        }
        String selectedRecipeId = StringUtils.EMPTY;
        if (StringUtils.isNotEmpty(recipeId)) {
            if (StringUtils.equals(WebUtils.getParameter("lotId", request), MapUtils.getString(lotInfo, "lotId"))) {
                setEnableChamberList(request, lotInfo, WebUtils.getParameter("recipeId", request));
            }
            if (CollectionUtils.isNotEmpty(childRecipeList)) {
                int i = 0;
                for (Map map : childRecipeList) {
                    String parentRecipe = MapUtils.getString(lotInfo, "recipeId");
                    String matchRecipeId = recipeService.buildRecipePhysicalId(MapUtils.getString(map, "recipeId"),
                                                                               lotInfo);
                    recipeId = hasChildRecipe && StringUtils.equals(parentRecipe, recipeId) &&
                            i == 0 ? matchRecipeId : recipeId;
                    if (recipeId.equals(matchRecipeId)) {
                        map.put("selectRecipeId", matchRecipeId);
                        selectedRecipeId = matchRecipeId;
                        setEnableChamberList(request, lotInfo, matchRecipeId);
                        break;
                    }
                }
                if (StringUtils.isEmpty(selectedRecipeId)) {
                    String matchRecipeId = recipeService.buildRecipePhysicalId(
                            MapUtils.getString(childRecipeList.get(0), "recipeId"), lotInfo);
                    setEnableChamberList(request, lotInfo, matchRecipeId);
                }
            }
            lotInfo.put("selectedRecipeId", selectedRecipeId);
        }
    }


    /***
     * 根据所选recipe设置可用腔
     *
     * @param request
     * @param lotInfo
     * @param recipeId
     * @throws Exception
     */
    public void setEnableChamberList(HttpServletRequest request, Map lotInfo, String recipeId) {

        if (StringUtils.isEmpty(recipeId)) {
            return;
        }
        Recipe recipe = recipeService.getRecipe(recipeId, LocalContext.getFacilityRrn());

        Collection versions = recipeService.getRecipeVersions(recipe.getInstanceRrn());
        if (versions.size() > 0) {
            ObjectVersion objectVersion = null;
            Iterator iterator = versions.iterator();
            while (iterator.hasNext()) {
                objectVersion = (ObjectVersion) iterator.next();
            }
            RecipeVersion recipeVersion = new RecipeVersion();
            recipeVersion.copyObjectVersion(objectVersion);
            recipeVersion = recipeService.getRecipeVersion(recipeVersion);
            recipeVersion.copyNamedObject(recipe);

            Job job = (Job) request.getAttribute("job");

            List<String> enableChambers = new ArrayList<String>();
            StringBuffer chamberType = new StringBuffer();
            if (!StringUtils.isEmpty(recipeVersion.getChamberTypes())) {
                List<String> recipeChambers = Arrays.asList(
                        StringUtils.split(recipeVersion.getChamberTypes(), RecipeVersion.CHAMBERSEPARATOR));
                for (String chamberKeys : recipeChambers) {
                    String c[] = chamberKeys.split(",");
                    // 所选可用腔组合一个不可用则这个组合不可用
                    boolean enable = true;
                    for (int a = 0; a < c.length; a++) {
                        Entity entity = emsService.getChildChamberEquip(job.getEqptRrn(), c[a]);
                        // 腔体设备存在 且enable log event
                        if (entity == null) {
                            enable = false;
                        }
                    }
                    if (enable) {
                        enableChambers.add(chamberKeys);
                        chamberType.append(chamberKeys);
                    }
                }

            }
            request.setAttribute("chamberJoin", chamberType.toString());
            lotInfo.put("chamberTypes", recipeService.getChamberTypes(enableChambers));

        }
    }

    public void setChildRecipe(HttpServletRequest request, Map lotInfo, Long totalWaferQty) {
        String recipeId = MapUtils.getString(lotInfo, "recipePhysicalId");
        if (StringUtils.isBlank(recipeId)) {
            recipeId = MapUtils.getString(lotInfo, "recipeId");
            lotInfo.put("recipePhysicalId", recipeId);
        }

        HashMap parameters = (HashMap) request.getAttribute(SessionNames.PARAMETERSINFO_KEY);
        String eventId = parameters == null ? null : (String) parameters.get("eventId");
        eventId = eventId == null ? EventName.IDLE_TO_RUN : eventId;

        long lotRrn = MapUtils.getLongValue(lotInfo, "lotRrn");
        long equipmentRrn = MapUtils.getLongValue(lotInfo, "equipmentRrn");


        Collection childRecipes;
        long recipeRrn = getInstanceRrn(recipeId, LocalContext.getFacilityRrn(), ObjectList.RECIPE_KEY);
        String lotStatus = MapUtils.getString(lotInfo, "lotStatus");
        if (LotStatus.isMoveInCheckStatus(lotStatus)) {
            Lot lot = lotQueryService.getLot(lotRrn);
            Map<String, Object> subRecipeMap = wipService.getAvailableSubRecipes(lot, equipmentRrn, eventId, recipeRrn,
                                                                                 totalWaferQty);

            childRecipes = (Collection) subRecipeMap.get("childRecipes");
        } else {
            childRecipes = new ArrayList<>();
        }

        Collection recipeConditions = recipeService.getRecipeCondition(recipeRrn);
        if (childRecipes != null && childRecipes.size() > 0) {
            lotInfo.put("childRecipes", childRecipes);
            if (recipeConditions != null && recipeConditions.size() > 0) {
                RecipeCondition recipeCondition = (RecipeCondition) recipeConditions.iterator().next();
                //                Map             condition       = new HashMap();
                //                condition.put("lotRrn", MapUtils.getString(lotInfo, "lotRrn"));
                //                condition.put("productRrn", recipeCondition.getProductRrn() + "");
                //                condition.put("processRrn", recipeCondition.getProcessRrn() + "");
                //                condition.put("operationRrn", recipeCondition.getOperationRrn() + "");
                //                condition.put("parameterRrn", recipeCondition.getParameterRrn() + "");
                //                condition.put("puType", recipeCondition.getPuType());
                double parameterVal = wipQueryService.getParamValByCondition(MapUtils.getLongValue(lotInfo, "lotRrn"),
                                                                             recipeCondition.getOperationRrn(),
                                                                             recipeCondition.getProductRrn(),
                                                                             recipeCondition.getProcessRrn(),
                                                                             recipeCondition.getParameterRrn(),
                                                                             recipeCondition.getPuType());
                // check has child recipe
                if (!LotStatus.RUNNING.equals(lotStatus)) {
                    for (Iterator it = childRecipes.iterator(); it.hasNext(); ) {
                        Map recipe = (Map) it.next();
                        Double paraMinVal = NumberUtils.toDouble(MapUtils.getString(recipe, "paraMinVal"), 0.0);
                        Double paraMaxVal = NumberUtils.toDouble(MapUtils.getString(recipe, "paraMaxVal"), 0.0);
                        if (parameterVal >= paraMinVal && parameterVal < paraMaxVal) {
                            lotInfo.put("autoCalculateRecipe", recipe.get("recipeId"));
                        }
                    }

                }
            }
        } else {
            // 没有子recipe则设置当前recipe可用chamber
            setEnableChamberList(request, lotInfo, recipeId);
            lotInfo.remove("childRecipes");
        }
        if (StringUtils.isNotBlank((String) lotInfo.get("chamberType")) &&
                StringUtils.isBlank(MapUtils.getString(lotInfo, "recipeId"))) {
            setEnableChamberList(request, lotInfo, recipeId);
        }
    }

    public List<Relation> getEntitiesWithLotSpecial(Lot lot) {
        List<Relation> entities = new ArrayList<Relation>();
        if (StringUtils.equalsIgnoreCase(LotStatus.OUTSOURCING, lot.getLotStatus()) ||
                StringUtils.contains(lot.getOperationId(), "SRC-DUMMY")) {
            return entities;
        }

        LotSpecialEquipContextValue contextValue = ctxExecService.getLotSpecialContextValue(lot);
        if (contextValue != null && StringUtils.isNotBlank(contextValue.getEquipmentRrns())) {
            String equipmentRrnStr = contextValue.getEquipmentRrns();
            entities = emsService.getEntitiesForLotSpecial(equipmentRrnStr);
        }
        if (CollectionUtils.isEmpty(entities)) {
            String eqptGroupRrns = specService.getEqptGroupRrns(lot.getProcessRrn(), lot.getProcessVersion(),
                                                                lot.getRouteRrn(), lot.getOperationRrn());
            if (StringUtils.isNotBlank(eqptGroupRrns)) {
                for (String eqptGroupRrn : eqptGroupRrns.split(",")) {
                    entities.addAll(emsService.getAllEntities(Long.parseLong(eqptGroupRrn)));
                }
            } else {
                Operation operation = prpService.getOperation(lot.getOperationRrn().longValue());
                entities = emsService.getAllEntities(operation.getEntityGroupRrn().longValue());
            }
        }
        List<Relation> distinct = new ArrayList<>();
        for (Relation rel : entities) {
            boolean sameEqpt = false;
            for (Relation rel2 : distinct) {
                if (rel2.getInstanceRrn() == rel.getInstanceRrn()) {
                    sameEqpt = true;
                    break;
                }
            }
            if (!sameEqpt) {
                distinct.add(rel);
            }
        }

        return distinct;
    }

    private Collection myParseString(String tempString, String index) {
        int pos0 = -1;
        int pos1 = 0;
        int pos2 = 0;
        String key;
        String _tempString;
        Collection _keys = new ArrayList();
        if (tempString != null) {
            while (tempString.indexOf("$") > 0) {
                pos1 = tempString.indexOf("$");
                _tempString = tempString.substring(pos0 + 1, pos1);

                if (_tempString.indexOf("|") > 0) {
                    pos2 = _tempString.indexOf("|");

                    if (index.equals("1")) {
                        key = StringUtils.trimToUpperCase(_tempString.substring(pos0 + 1, pos2));
                    } else {
                        key = StringUtils.trimToUpperCase(_tempString.substring(pos2 + 1, pos1));
                    }

                    _keys.add(key);
                    tempString = tempString.substring(pos1 + 1, tempString.length());
                }
            }
        }

        return _keys;
    }

    private void setFlowSeqAndStepdesc(Long facilityRrn, Lot lot, Collection futureHoldList) {

        if (futureHoldList != null) {
            Iterator it = futureHoldList.iterator();
            while (it.hasNext()) {
                Map futureHoldMap = (Map) it.next();
                if (lot != null) {
                    futureHoldMap.put("processVersion", lot.getProcessVersion());
                    futureHoldMap.put("processRrn", lot.getProcessRrn());
                    futureHoldMap.put("productRrn", lot.getProductRrn());
                } else {
                    Long productRrn = MapUtils.getLongValue(futureHoldMap, "productRrn");
                    Long processRrn = MapUtils.getLongValue(futureHoldMap, "processRrn");
                    long processVer = prpService.getProcessVersion(processRrn);
                    futureHoldMap.put("processVersion", processVer);
                    futureHoldMap.put("processRrn", processRrn);
                    futureHoldMap.put("productRrn", productRrn);
                }
                futureHoldMap.put("facilityRrn", facilityRrn);
                futureHoldMap.put("flowSeq", ctxExecService.getFlowSeqByProcessInfo(futureHoldMap));
                futureHoldMap.put("stepDesc", ctxExecService.getOperationDescByProcessInfo(futureHoldMap));
                futureHoldMap.put("stageId", ctxExecService.getStageByProcessInfo(futureHoldMap));
            }
            sortFutureHoldInfosByFlowSeq(futureHoldList);
        }
    }

    private Lot isValidLot(Long facilityRrn, String id) {
        return lotQueryService.getLot(id, facilityRrn);
    }

    private Lot getBondingLot(Long currentLotrrn, Long jobRrn) {

        List<Lot> lots = lotQueryService.getLotsByJobRrn(jobRrn);

        Lot mainLot = lotQueryService.getLot(currentLotrrn);
        Lot anotherLot = new Lot();
        Operation mainOper = prpService.getOperation(mainLot.getOperationRrn());

        if (mainOper.isBondedOperation() && lots != null && lots.size() == 2) {
            Iterator<Lot> it = lots.iterator();

            while (it.hasNext()) {
                anotherLot = it.next();
                if (anotherLot.getLotRrn() != currentLotrrn) {
                    Map tmp = BeanUtils.copyBeanToMap(anotherLot);
                    anotherLot.setFlowSeq(ctxExecService.getFlowSeqByProcessInfo(tmp));
                    anotherLot.setOperationDesc(ctxExecService.getOperationDescByProcessInfo(tmp));
                    break;
                }
            }
        }

        return anotherLot;
    }

    private String getChildLotId(String lotId, Long facilityRrn) {
        String childLotId = "";
        if (lotId.indexOf(".") == -1) {
            lotId = lotId + ".1";
        }
        lotId = lotQueryService.getMaxLotId(lotId.substring(0, lotId.indexOf(".") + 1));
        if (lotId.indexOf(".") == -1) {
            lotId = lotId + ".0";
        }

        String lotNumber = lotId.substring(lotId.indexOf(".") + 1);
        if (StringUtils.isNotBlank(lotNumber)) {
            lotNumber = String.valueOf(Integer.parseInt(lotNumber) + 1);
        } else {
            lotNumber = "1";
        }

        childLotId = lotId.substring(0, lotId.indexOf(".") + 1) + lotNumber;
        // holly modify 20080715 初级排除Carrier与max存在的情况.
        if (this.getInstanceRrn(childLotId, this.getNamedSpace(ObjectList.ENTITY_KEY, facilityRrn),
                                ObjectList.ENTITY_KEY, ObjectList.CARRIER_KEY) > 0) {
            childLotId = lotId.substring(0, lotId.indexOf(".") + 1) + String.valueOf(Integer.parseInt(lotNumber) + 1);
        }
        // MEMS要求个位数, 加0, 类似AAAA.01, 而非AAAA.1
        String newChildLotNumber = childLotId.substring(childLotId.indexOf(".") + 1);
        if (newChildLotNumber.length() == 1) {
            newChildLotNumber = "0" + newChildLotNumber;
        }
        childLotId = childLotId.substring(0, lotId.indexOf(".") + 1) + newChildLotNumber;

        return childLotId;
    }

    private boolean checkSplitCancelMoveIn(HttpServletRequest request) {
        String sourceListValues = request.getParameter("sourceListValues");
        Collection sourceList = parseCollection(sourceListValues);
        String[] values = request.getParameterValues("allLotCancelMovein");
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                if (StringUtils.equalsIgnoreCase("true", values[i])) {
                    return false;
                }
            }
        }

        if (sourceList.size() > 0) {
            return true;
        }
        return false;
    }

    private void splitCancelMoveIn(HttpServletRequest request, Long lotRrn, LotInfoForm theform) throws Exception {
        Long facilityRrn = LocalContext.getFacilityRrn();
        String user = LocalContext.getUserId();
        String childCarrierId = StringUtils.trimToUpperCase(request.getParameter("childCarrierId"));

        Map item = new HashMap();
        item.put("childLotId", StringUtils.upperCase(theform.getSplitChildLotId()));
        item.put("carrierId", WebUtils.getParameterUpperCase("childCarrierId", request));

        String sourceValues = request.getParameter("sourceListValues");
        String targetValues = request.getParameter("targetListValues");
        List<Map> unitsOfParentLot = parseCollection(sourceValues);
        List<Map> targetUnits = parseCollection(targetValues);

        Lot lot = lotQueryService.getLot(lotRrn.longValue());
        lot.setTransPerformedby(user);
        lot.setTransId(TransactionNames.CANCELMOVEIN_KEY);

        carrierService.compareRealCarrierAndCheckAvailabile(facilityRrn, lot.getCarrierId(), childCarrierId);

        Long carrierRrn = carrierService.getCarrierRrnForLot(facilityRrn, new Double(targetUnits.size()),
                                                             childCarrierId);
        item.put("carrierRrn", carrierRrn);

        item.put("technologyRrn", lot.getProcessRrn());
        item.put("routeId", lot.getRouteId());
        item.put("routeId4Show", lot.getRouteId());
        item.put("operationId", lot.getOperationId());
        item.put("operationRrn", new Long(
                getInstanceRrn(lot.getOperationId(), getNamedSpace(ObjectList.OPERATION_KEY, facilityRrn),
                               ObjectList.OPERATION_KEY)));
        Long opRrn = MapUtils.getLong(item, "operationRrn");
        String operationDesc = getInstanceDesc(opRrn.longValue());
        item.put("operationDesc", operationDesc);
        Assert.isTrue(targetUnits.size() > 0, Errors.create().content("Unselected wafer for cancellation!").build());
        // 取选定的Wafer目标值.
        item.put("unitList", targetUnits);

        item.put("splitQty", targetUnits.size());

        HashMap valueMap = new HashMap();
        valueMap.put("lot", lot);
        valueMap.put("childLot", item);
        valueMap.put("unitOfParent", unitsOfParentLot);
        valueMap.put("comment", theform.getLotComments());
        valueMap.put("facilityRrn", facilityRrn);
        valueMap.put("user", user);

        Double remainQty = new Double(unitsOfParentLot.size() + "");
        String userRRN = theform.getUserId().trim();
        String userName = securityService.getUser(Long.parseLong(userRRN)).getUserName();
        String reason = theform.getReasonCode() + " " + theform.getDeptExt().trim().toUpperCase() + " " +
                userName.toUpperCase() + " " + theform.getReason();

        TransReason transReason = new TransReason();
        transReason.setReasonCode(theform.getReasonCode());
        transReason.setReason(reason);
        transReason.setResponsibility(user);
        transReason.setTransQty1(remainQty);
        valueMap.put("transReason", transReason);

        //lotService.splitLotWithCancelIn(valueMap);
    }

    //    protected Equipment validJobSize(Job job) {
    //        Equipment equipment = new Equipment(job.getEqptId(), getNamedSpace(ObjectList.ENTITY_KEY,
    //                                                                           LocalContext
    //                                                                           .getFacilityRrn()),
    //                                            ObjectList.ENTITY_KEY);
    //        if (job.getEqptRrn() != null) {
    //            equipment.setInstanceRrn(job.getEqptRrn());
    //        }
    //        equipment = emsService.getEquipment(equipment);
    //
    //        // delete rubbish job which is create by wfl error
    //        if (wipService.deleteRubbishJob(equipment.getInstanceRrn())) {
    //            equipment = emsService.getEquipment(equipment);
    //        }
    //
    //        return equipment;
    //    }

    private boolean isAvailableSplitRunCardByHoldReason(String holdReason) {
        if (StringUtils.isEmpty(holdReason)) {
            return false;
        }
        if (holdReason.startsWith("(FH)")) {
            holdReason = StringUtils.substringAfter(holdReason, "(FH)");
        } else if (holdReason.startsWith("(FH)")) {
            holdReason = StringUtils.substringAfter(holdReason, "(FH)");
        }

        String runCardId = StringUtils.substringBefore(StringUtils.substringAfter(holdReason, "ID: "), ",");

        NamedObject runCard = new NamedObject(runCardId,
                                              getNamedSpace(ObjectList.ECN_KEY, LocalContext.getFacilityRrn()),
                                              ObjectList.ECN_KEY);
        long runCardRrn = baseService.getNamedObjectRrn(runCard);
        if (runCardRrn <= 0) {
            return false;
        }

        Map<String, Object> lotRunCardInfo = splitRunCardService.getLotRunCardInfo(runCardRrn);
        Map<String, Object> scrInfo = MapUtils.getMap(lotRunCardInfo, "scrInfo");
        if (StringUtils.equals(MapUtils.getString(scrInfo, "ecnStatus"), "CLOSED")) {
            return false;
        }
        return true;
    }

    private boolean checkPositionAvaliable(List units) {
        for (Object unit : units) {
            boolean childPosition = true;
            Map childMap = (Map) unit;
            String unitId = MapUtils.getString(childMap, "unitId");
            int defaultPosition = Unit.getWaferDefaultPostion(unitId);// 截取unitid的尾号
            int actualPosition = MapUtils.getIntValue(childMap, "position");// mapping中的真实位置

            if (defaultPosition != actualPosition) {
                return false;
            }
        }

        return true;
    }

    private void addCancelMoveInLotTrans(List<Map> transInfoList, Map<String, Object> transInfoMap) {
        boolean canAdd = true;
        for (Map transInfo : transInfoList) {
            Lot transLot = (Lot) MapUtils.getObject(transInfo, "lot");
            Lot addLot = (Lot) MapUtils.getObject(transInfoMap, "lot");
            if (transLot.getLotRrn() == addLot.getLotRrn()) {
                canAdd = false;
            }
        }
        if (canAdd) {
            transInfoList.add(transInfoMap);
        }
    }

    private Map<String, Object> buildCancelMoveInTransInfo(Lot lot, String comments, TransReason transReason) {
        Map<String, Object> transInfo = MapUtils.newHashMap();
        transInfo.put("lot", lot);
        transInfo.put("user", LocalContext.getUserId());
        transInfo.put("comments", comments);
        transInfo.put("transReason", transReason);
        transInfo.put("facilityRrn", LocalContext.getFacilityRrn());
        return transInfo;
    }

    private LotReleaseInfoDto buildLotReleaseInfoList(Lot lot, List<Map> releaseReasons, String comments,
                                                      TransReason transReason) {
        LotReleaseInfoDto lotReleaseInfoDto = new LotReleaseInfoDto();
        lotReleaseInfoDto.setLot(lot);
        lotReleaseInfoDto.setReleaseReasonList(releaseReasons);
        lotReleaseInfoDto.setUserId(LocalContext.getUserId());
        lotReleaseInfoDto.setComments(comments);

        TransReason releaseReason = new TransReason();
        BeanUtils.copyProperties(transReason, releaseReason);
        releaseReason.setTransQty1(lot.getInt_qty1().doubleValue());
        releaseReason.setTransQty2(lot.getInt_qty2().doubleValue());
        lotReleaseInfoDto.setTransReason(releaseReason);
        return lotReleaseInfoDto;
    }

    // 不显示pilot single lot的future hold信息 #42577
    private List<Map> checkPilotSingleFutureHold(List<Map> futureHoldListForlot) {
        List<Map> newFutureHoldListForlot = new ArrayList<>();
        for (Map map : futureHoldListForlot) {
            if (!StringUtils.equals(MapUtils.getString(map, "holdCode"), HoldCodeNames.PILOT_HOLD)) {
                newFutureHoldListForlot.add(map);
            }
        }
        return newFutureHoldListForlot;
    }

    class Comp implements Comparator {

        List rules;

        Comp(List rule) {

            this.rules = rule;

        }

        @Override
        public int compare(Object o1, Object o2) {

            HashMap v1 = (HashMap) o1;
            HashMap v2 = (HashMap) o2;

            int seq = 0;
            int res = 0;
            if (rules != null) {

                boolean _isSamed = true;
                for (; seq < rules.size(); seq++) {
                    String key = (String) rules.get(seq);
                    Long v_o1 = null;
                    Long v_o2 = null;
                    if ("priority".equalsIgnoreCase(key)) {
                        v_o1 = new Long((String) v1.get(key));
                        v_o2 = new Long((String) v2.get(key));
                        // queueTimestamp
                    } else {
                        v_o1 = (Long) v1.get(key);
                        v_o2 = (Long) v2.get(key);
                    }

                    if ("priority".equalsIgnoreCase(key) || "dueDate".equalsIgnoreCase(key) ||
                            "estimatedRemainTime".equalsIgnoreCase(key)) {
                        if (v_o1 != null && v_o2 != null) {
                            if (v_o1.longValue() == 0) {
                                // return 1;
                                res = 1;

                                break;
                            }
                            if (v_o2.longValue() == 0) {
                                // return -1;
                                res = 1;

                                break;
                            }
                            if (v_o1.longValue() > v_o2.longValue()) {
                                // return 1;
                                res = 1;

                                break;
                            } else if (v_o1.longValue() < v_o2.longValue()) {
                                // return -1;
                                res = -1;

                                break;
                            }
                        } else {
                            res = 0;

                        }
                    } else {
                        // queueTimestamp
                        if (v_o1 != null && v_o2 != null) {
                            if (v_o1.longValue() > v_o2.longValue()) {
                                // return -1;
                                res = -1;

                                break;
                            } else if (v_o1.longValue() < v_o2.longValue()) {
                                // return 1;
                                res = 1;

                                break;
                            } else {
                            }
                        } else if (v_o1 != null) {
                            // return -1;
                            res = -1;

                            break;
                        }
                    }
                }

            } else {
                // return 0;
            }
            return res;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == this) {
                return true;
            } else {
                return false;
            }
        }

        private double getPriority(HashMap object) {

            double result =
                    ((division(new Long((String) object.get("priority")), (Long) object.get("totalPriority")) * 1) /
                            5) + ((division((Long) object.get("dueDate"), (Long) object.get("totalDueDate")) * 1) / 5) +
                            ((division((Long) object.get("createdTimestamp"), (Long) object.get("totalCreateDate")) *
                                    1) / 5) + ((division((Long) object.get("estimatedRemainTime"),
                                                         (Long) object.get("totalProcessingTime")) * 1) / 5) +
                            ((0 * 1) / 5);
            return result;
        }

        private double division(Long divident, Long divisor) {
            try {
                if (divisor.longValue() != 0) {
                    return ((divident == null) ? 0 : divident.doubleValue()) / divisor.longValue();
                } else {
                    return 0;
                }
            } catch (java.lang.ArithmeticException ex) {
                return 0;
            }
        }

    }

    protected void sortFutureHoldInfosByFlowSeq(Collection futureHoldInfos) {
        Collections.sort((List) futureHoldInfos, new Comparator() {

            @Override
            public int compare(Object paramObject1, Object paramObject2) {
                Map info1 = (Map) paramObject1;
                Map info2 = (Map) paramObject2;
                int flowSeq1 = MapUtils.getIntValue(info1, "flowSeq", 0);
                int flowSeq2 = MapUtils.getIntValue(info2, "flowSeq", 0);
                return (flowSeq1 - flowSeq2);
            }
        });
    }

    protected Map buildLotBaseInfo(Lot lot) {
        long facilityRrn = LocalContext.getFacilityRrn();
        String lotStatus = lot.getLotStatus();
        Integer priority = lot.getPriority();
        String hotflag = lot.getHotFlag();

        Operation operation = new Operation();
        if (StringUtils.equalsIgnoreCase(LotStatus.BANKED, lot.getLotStatus()) ||
                StringUtils.equalsIgnoreCase(LotStatus.OUTSOURCING, lotStatus)){
            operation = prpService.getOperation(lot.getPrevOperationRrn());
            lot.setOperationRrn(lot.getPrevOperationRrn());
        } else {
            operation = prpService.getOperation(lot.getOperationRrn());
        }

        String priorityString = wipQueryService.getHotflagSplicingPriority(NumberUtils.toInt(hotflag), priority,
                                                                           facilityRrn);
        Map resultMap = BeanUtils.copyBeanToMap(lot);
        int cuntScraptUnit = 0;
        if (StringUtils.equalsIgnoreCase(lot.getLotStatus(), LotStatus.SCRAPPED)) {
            cuntScraptUnit = lotQueryService.getTotalQtyForUnscrap(lot.getLotRrn());
        }
        Carrier carrier = carrierService.getCarrier(lot.getCarrierRrn());

        resultMap.put("lotPurpose", lot.getLotComments());
        String eqptGroupIds = specService.getEqptGroupIds(lot.getProcessRrn(), lot.getProcessVersion(),
                                                          lot.getRouteRrn(), lot.getOperationRrn());
        if (StringUtils.isEmpty(eqptGroupIds)) {
            resultMap.put("eqptGroupId", getInstanceId(operation.getEntityGroupRrn()));
        } else {
            resultMap.put("eqptGroupId", eqptGroupIds);
        }

        resultMap.put("processLocation", lotQueryService.getProcessLocation(lot));
        resultMap.put("carrierType", carrier.getObjectSubtype());
        resultMap.put("qty1", lot.getInt_qty1());
        resultMap.put("inputQty1", cuntScraptUnit);
        resultMap.put("workArea",
                      StringUtils.isNotBlank(lot.getWorkArea()) ? lot.getWorkArea() : lotQueryService.getWorkArea(lot));
        resultMap.put("stageId",
                      StringUtils.isNotBlank(lot.getStageId()) ? lot.getStageId() : lotQueryService.getStageId(lot));
        resultMap.put("priority", priorityString);
        resultMap.put("dueDate", DateUtils.formatDate(lot.getDueDate(), DateUtils.DATE_FORMAT4DAYD));
        //reticle
        long reticleFamilyRrn = wipQueryService.getReticleFamilyRrnByLot(lot);
        if (reticleFamilyRrn > 0) {
            String reticles = buildReticlesString(reticleFamilyRrn);
            resultMap.put("reticles", reticles);
        } else {
            resultMap.put("reticles", "");
        }
        //batchID
        resultMap.put("batchId", diffBatchQueryService.getLotBatchId(lot.getLotRrn()));
        resultMap.put("workOrderId", lot.getInnerOrderNO());
        resultMap.put("flipType", lot.getFlipType());

        //#42799 lot info界面点功能按钮后的物料批次号数据丢失
        String materialLotId = warehouseService.getMaterialInfoByLot(lot.getLotRrn()).stream()
                                               .map(lotNumber -> lotNumber.get("lotNumber").toString())
                                               .collect(Collectors.joining(","));
        //需求 #43825 Lot 分批之后,子批Material Lot Id,Lot query 数据栏数据丢失
        if (StringUtils.isBlank(materialLotId)){
            List<Unit> unitList = wipQueryService.getUnitList(lot.getLotRrn());//当前lot下的wafer集合
            if(CollectionUtils.isNotEmpty(unitList)){
                Set<String> lotNumberSet = unitList.stream().map(e -> {
                    //根据当前lot下的每一个wafer去查询其对应的物料信息并去重
                    Map<String, Object> materialInfo = warehouseService.getMaterialInfoByUnit(e.getUnitRrn());
                    if (MapUtils.isNotEmpty(materialInfo)) {
                        return MapUtils.getString(materialInfo, "lotNumber", "");
                    }
                    return "";
                }).collect(Collectors.toSet());
                if(lotNumberSet.contains("")){
                    lotNumberSet.remove("");
                }
                materialLotId=String.join(",",lotNumberSet).trim();
            }
        }

        resultMap.put("materialLotId", materialLotId);
        //添加ocap id
        resultMap.put("ocapId", wipQueryService.getActiveInlineOcapIdByLot(lot.getLotRrn()));
        //Pi-Lot
        //#42799 lot info界面点功能按钮后的piLotViewId数据丢失
        PiLotView piLotView = wipQueryService.getPiLotViewIdByLotRrn(lot.getLotRrn(), lot.getBasedLotRrn());
        if (ObjectUtils.isNotEmpty(piLotView)) {
            resultMap.put("piLotViewId", piLotView.getViewId());
            resultMap.put("viewRrn", piLotView.getViewRrn());
        }
        return resultMap;
    }

    protected boolean isSamePollutionLevel(Long entityGroupRrn, Lot lot) {
        if (entityGroupRrn == null || entityGroupRrn <= 0) {
            return false;
        }

        if (StringUtils.isEmpty(lot.getPollutionLevel()) || StringUtils.equals("0", lot.getPollutionLevel())) {
            return true;
        }

        Set<String> acceptablePolluctionLevels = emsService.getAcceptablePolluctionLevelInEntityGroup(entityGroupRrn);

        boolean isAcceptable = false;
        for (String polluctionLevel : acceptablePolluctionLevels) {
            if (polluctionLevel == null || StringUtils.equals(lot.getPollutionLevel(), polluctionLevel)) {
                isAcceptable = true;
                break;
            }
        }
        return isAcceptable;
    }

    protected List<Relation> getConstrainPairEquipments(Lot lot) {
        List<Relation> equipments = new ArrayList<Relation>();
        long lotRrn = lot.getLotRrn();
        Long operationRrn = lot.getOperationRrn();
        Long routeRrn = lot.getRouteRrn();
        String routeSeq = lot.getRouteSeq();
        String operationSeq = lot.getOperationSeq();
        Long facilityRrn = lot.getFacilityRrn();
        Long processRrn = lot.getProcessRrn();
        Long productRrn = lot.getProductRrn();
        // 1.先查批次是否有直接设置的机限
        EquipmentConstrainPair constrainPairByLot = constrainService.getConstrainPairByLotRrn(lotRrn);
        if (constrainPairByLot == null ||
                EquipmentConstrainPairConstants.STATUS_DISABLE.equals(constrainPairByLot.getStatus())) {// 没设置
            // 或者禁用时都以产品+流程为准
            // 再查看是否通过产品+流程设置机限
            constrainPairByLot = constrainService.getConstrainPairByProductAndProcess(productRrn, processRrn);
        }
        if (constrainPairByLot != null) {
            String status = constrainPairByLot.getStatus();
            if (EquipmentConstrainPairConstants.STATUS_ENABLE.equals(status)) {// 启用状态
                // 获取当前工步的设置
                EquipmentConstrainPairDetail detail = constrainService.getConstrainPairDetailByRunInfo(
                        constrainPairByLot.getConstrainPairRrn(), routeRrn, operationRrn, routeSeq, operationSeq);
                EquipmentConstrainPairDetail benchMark = constrainService.getBenchMarkByConstrainRrn(
                        constrainPairByLot.getConstrainPairRrn());
                // 如果当前就是基准层,那么不卡控设备
                if (benchMark != null && detail != null &&
                        benchMark.getConstrainDetailRrn().equals(detail.getConstrainDetailRrn())) {
                    return equipments;// 不返回机限设备,即是不卡控
                }
                // 获取批次触发记录
                LotConstrainTrigger lotNewTrigger = constrainService.getLotNewTrigger(lotRrn,
                                                                                      constrainPairByLot.getConstrainPairRrn());
                if (lotNewTrigger != null && detail != null) {// 已经触发了
                    String plan = lotNewTrigger.getPlan();
                    String equipmentStr = "";
                    if ("PLANA".equalsIgnoreCase(plan)) {
                        equipmentStr = detail.getPlanA();
                    } else if ("PLANB".equalsIgnoreCase(plan)) {
                        equipmentStr = detail.getPlanB();
                    } else if ("PLANC".equalsIgnoreCase(plan)) {
                        equipmentStr = detail.getPlanC();
                    } else if ("PLAND".equalsIgnoreCase(plan)) {
                        equipmentStr = detail.getPlanD();
                    } else if ("PLANE".equalsIgnoreCase(plan)) {
                        equipmentStr = detail.getPlanE();
                    }
                    if (StringUtils.isNotBlank(equipmentStr)) {// 设置了设备
                        String[] equipmentArry = equipmentStr.split(",");
                        for (String equipmentId : equipmentArry) {
                            Equipment entity = new Equipment(equipmentId,
                                                             getNamedSpace(ObjectList.ENTITY_KEY, facilityRrn),
                                                             ObjectList.ENTITY_KEY);
                            entity = emsService.getEquipment(entity);
                            Relation eqptRelation = new Relation();
                            eqptRelation.setInstanceId(equipmentId);
                            eqptRelation.setInstanceRrn(entity.getInstanceRrn());
                            eqptRelation.setInstanceDesc(entity.getInstanceDesc());
                            equipments.add(eqptRelation);
                        }
                    }
                }
                // 获取设备
            }
        }
        return equipments;
    }

    protected String getHotflagSplicingPriority(Lot lot) {
        return wipQueryService.getHotflagSplicingPriority(NumberUtils.toInt(lot.getHotFlag()), lot.getPriority(),
                                                          LocalContext.getFacilityRrn());
    }

    protected boolean checkEqptRecipeAvailable(List<Map<String, Object>> equipmentRecipes, String recipeId) {
        if (CollectionUtils.isNotEmpty(equipmentRecipes)) {
            for (Map<String, Object> map : equipmentRecipes) {
                String equipmentRecipeId = MapUtils.getString(map, KEY_RECIPE_ID);
                String status = MapUtils.getString(map, KEY_STATUS);
                if (StringUtils.equals(recipeId, equipmentRecipeId) && RecipeStatus.isAvailable(status)) {
                    return true;
                }
            }
        }

        return false;
    }

    protected String parseToString(Collection unitList) {
        Iterator it = unitList.iterator();
        HashMap unit = new HashMap();
        int number = 1;
        int seq = 1;
        StringBuffer sb = new StringBuffer();

        while (it.hasNext()) {
            unit = (HashMap) it.next();

            seq = Integer.parseInt((String) unit.get("position"));

            while ((number < seq) && (number < 9999)) {
                sb.append(" $");
                number++;
            }

            sb.append(unit.get("unitId"));
            sb.append(",");
            sb.append(unit.get("unitRrn"));
            sb.append(",");
            sb.append(unit.get("dummyflag"));
            sb.append(",");
            sb.append(unit.get("available"));
            sb.append(",");
            sb.append(unit.get("lotid"));

            sb.append("$");
            number++;
        }
        if (sb.length() == 0) {
            sb.append("$");
        }
        return sb.substring(0, sb.length() - 1);
    }

    protected List<Map> parseCollection(String unitString) {
        List<Map> unitList = new ArrayList();
        long seq = 0;
        int index_1 = 0;
        int index_2 = 0;
        int index_3 = 0;
        int index_4 = 0;

        if ((unitString == null) || unitString.equalsIgnoreCase("")) {
            return unitList;
        }

        StringTokenizer stn = new StringTokenizer(unitString, "$", false);

        String unit = "";

        String available = "";
        String unitRrn = "";
        String unitId = "";
        String dummyWaferFlag = "";
        String lotId = "";

        while (stn.hasMoreElements()) {
            seq++;
            unit = stn.nextToken();

            index_1 = unit.indexOf(",");
            index_2 = unit.indexOf(",", index_1 + 1);
            index_3 = unit.indexOf(",", index_2 + 1);
            index_4 = unit.indexOf(",", index_3 + 1);

            if ((index_1 == 0) || (index_1 == -1)) {
                unitId = "";
                unitRrn = "";
                dummyWaferFlag = "";
                available = "1";

                // continue;
            } else {
                if (index_1 > 0) {
                    unitId = unit.substring(0, index_1);
                }

                if (index_2 > 0) {
                    unitRrn = unit.substring(index_1 + 1, index_2);
                }

                if (index_3 > 0) {
                    dummyWaferFlag = unit.substring(index_2 + 1, index_3);
                }

                if (index_4 > 0) {
                    available = unit.substring(index_3 + 1, index_4);
                    lotId = unit.substring(index_4 + 1);
                }
            }

            if (unitRrn != null && !unitRrn.trim().equals("")) {
                HashMap map = new HashMap();
                map.put("position", new String("" + seq));
                map.put("unitId", unitId);
                map.put("unitRrn", unitRrn);
                map.put("dummyflag", dummyWaferFlag);
                map.put("available", available);
                map.put("lotid", lotId);
                unitList.add(map);
            }
        }
        return unitList;
    }

    protected Recipe getRecipeByLot(Lot lot) {

        Recipe recipe = null;

        String recipeString = lot.getRecipeString();

        if (StringUtils.isEmpty(recipeString)) {
            recipeString = ctxExecService.getRecipeString(lot);
        }

        if (StringUtils.isBlankCheckNull(recipeString)) {

        } else {
            lot.setRecipeString(recipeString);
            // /recipeId|recipeParam
            int index = recipeString.indexOf("|");
            String recipeRrn = null;
            if (index >= 0) {
                recipeRrn = recipeString.substring(0, index);
                String para = recipeString.substring(index + 1, recipeString.length());
                lot.setRecipeId(getInstanceId(new Long(recipeRrn).longValue()));
                lot.setRecipeParameter(para);
            } else {
                if (!"".equalsIgnoreCase(recipeString.trim())) {
                    recipeRrn = recipeString;
                    lot.setRecipeId(getInstanceId(new Long(recipeString).longValue()));
                }
            }
            recipe = recipeService.getRecipe(Long.parseLong(recipeRrn));
        }

        return recipe;
    }

    protected PlanLot getPlanLot(PlanLot planLot) throws Exception {
        // temp object to store the value object retrived from database
        PlanLot _planLot = lotService.getPlanLot(planLot);

        if (_planLot != null) {
            planLot = _planLot;
        }
        return planLot;
    }

    protected Integer getProcessVersion(long processRrn) throws Exception {
        NamedObject object = new NamedObject(processRrn);
        object = baseService.isExisted(processRrn);
        ObjectVersion version = baseService.getMostActiveVersion(object);
        return new Integer(version.getInstanceVersion());
    }

    protected boolean isProductProcessBined(Long productRrn, long processRrn) {
        List<Long> processRrns = prpService.getProcessRrnsByProductRrn(productRrn);
        return processRrns.contains(processRrn);
    }

    protected Map getStepsInfoByVersion(Lot lot, Long processRrn, Integer processVer, String stepPath) {//TODO 改回原SP,待验证
        Map matchingRules = new HashMap();
        matchingRules.put("productRrn", String.valueOf(lot.getProductRrn()));
        matchingRules.put("processRrn", lot.getProcessRrn());
        matchingRules.put("lotRrn", lot.getLotRrn() + "");

        Map stepsInfo = wipWorkflowQueryService.getStepsInfoByVersion(processRrn, processVer, stepPath, matchingRules);

        return stepsInfo;
    }

    protected String getRecipePhysicalId(Lot lot) {
        String recipePhysicalId = "";
        RecipeVersion recipeVersion = recipeService.getLotRecipe(lot);
        if (recipeVersion != null) {
            recipePhysicalId = recipeVersion.getPpid();
        }
        return recipePhysicalId;
    }

    protected boolean isValidUnitId(String customerWaferId, Long facilityRrn) {
        List unitIds = (List) myParseString(customerWaferId, "1");
        for (int i = 0; i < unitIds.size(); i++) {
            Unit unit = wipQueryService.getUnit(facilityRrn, (String) unitIds.get(i));
            if (unit != null && StringUtils.isNotBlank(unit.getUnitId())) {
                return true;
            }
        }
        return false;
    }

    protected void buildRecipeId(Lot lot) {
        RecipeVersion recipeVersion = recipeService.getLotRecipe(lot);
        if (recipeVersion != null) {
            String physicalId = recipeVersion.getPpid();
            lot.setRecipePhysicalId(physicalId);
        }
    }

    protected String getNextStepDesc(Lot lot) {
        long facilityRrn = LocalContext.getFacilityRrn();
        Map conditionM = new HashMap();
        conditionM.put("facilityRrn", facilityRrn);
        conditionM.put("productRrn", lot.getProductRrn());
        conditionM.put("processRrn", lot.getProcessRrn());
        conditionM.put("processVersion", lot.getProcessVersion());
        conditionM.put("routeRrn", WipUtils.getRouteRrnByProcessStep(lot.getNextStepVersion1()));
        conditionM.put("operationRrn", lot.getNextOperationRrn1());

        String nextOperationDesc1 = ctxExecService.getOperationDescByProcessInfo(conditionM);
        return nextOperationDesc1;
    }

    protected void removeEqptlotCache(Lot lot) {
        Collection availableEquipmentInfos = buildAvailableEquipInfosForLot(lot, LocalContext.getUserRrn());
        for (Object eqpts : availableEquipmentInfos) {
            CacheUtils.remove(MapUtils.getLongValue((Map) eqpts, "equipmentRrn") + "");
        }
    }

    /**
     * @param lot
     * @return
     * @throws Exception
     */
    protected Collection buildAvailableEquipInfosForLot(Lot lot, Long userRrn) {

        long facilityRrn = LocalContext.getFacilityRrn();
        Collection availableEquipmentInfos = new ArrayList();

        String recipeId = lot.getRecipePhysicalId();
        Collection eqptRelations = getEntitiesWithLotSpecial(lot);
        // 判断批次的机限设置 并组装设备列表--->开始
        long lotRrn = lot.getLotRrn();
        List<Relation> constrainPairEquipments = getConstrainPairEquipments(lot);
        if (constrainPairEquipments.size() > 0) {
            eqptRelations = constrainPairEquipments;
        }
        // 判断批次的机限设置 并组装设备列表--->结束
        for (Iterator it = eqptRelations.iterator(); it.hasNext(); ) {
            Relation eqptRelation = (Relation) it.next();
            Equipment equipment = new Equipment();
            equipment.setInstanceRrn(eqptRelation.getInstanceRrn());
            equipment.setInstanceId(eqptRelation.getInstanceId());
            equipment.setInstanceDesc(eqptRelation.getInstanceDesc());

            Map equipmentInfo = checkEquipmentAvailable(lot, userRrn, equipment);
            availableEquipmentInfos.add(equipmentInfo);
        }
        return availableEquipmentInfos;
    }

    protected Map checkEquipmentAvailable(Lot lot, Long userRrn, Equipment equipment) {
        Map equipmentInfo = new HashMap();
        equipmentInfo.put("equipmentId", equipment.getInstanceId());
        equipmentInfo.put("equipmentDesc", equipment.getInstanceDesc());
        equipmentInfo.put("equipmentRrn", equipment.getInstanceRrn());
        // start: 检查批次在当前设备是否有constrain
        ErrorMsg errorMsg = constrainService.checkLotConstrain(equipment, lot);
        boolean overrideConstrain;
        overrideConstrain = wipCheckService.isOverrideConstrain(lot);
        if (overrideConstrain) {
            errorMsg.setError(false);
            errorMsg.setErrorMsg("OK");
        }
        equipmentInfo.put("equipmentConstrain", errorMsg.getError());
        String checkEqptAndRecipeResultMsg = wipCheckService.checkEqptAndRecipeAndReticleAtJobIn(lot,
                                                                                                 equipment.getInstanceRrn(),
                                                                                                 lot.getRecipePhysicalId(),
                                                                                                 userRrn);
        equipmentInfo.put("checkEqptAvailable", StringUtils.isEmpty(checkEqptAndRecipeResultMsg));
        equipmentInfo.put("checkEqptAvailableMsg", checkEqptAndRecipeResultMsg);

        // add edc info
        List<Parameter> edcInfoList = wipQueryService.getLotEdcInfoList(lot, equipment);
        if (CollectionUtils.isNotEmpty(edcInfoList)) {
            equipmentInfo.put("edcInfoList", edcInfoList);
        }

        // check equipment status
        String equipmenStatus = emsService.getEntityCurrentStatus(equipment.getInstanceRrn());
        equipmentInfo.put("equipmentStatus", equipmenStatus);
        if (wipCheckService.checkEqptStatusAvailable(lot, equipment)) {
            equipmentInfo.put("equipmentAvailable", "AVAILABLE");
        } else {
            equipmentInfo.put("equipmentAvailable", "NOT_AVAILABLE");
        }
        return equipmentInfo;
    }

    /**
     * @param request
     * @param readOnly          lotId 和 carrier 输入只读
     * @param lotSearchType     查询的lotType
     * @param carrierSearchType 查询的carrierType
     * @return Lot  可能为  null
     */
    protected Lot initLotBaseInfoForJsp(HttpServletRequest request, boolean readOnly, String lotSearchType,
                                        String carrierSearchType) {
        request.setAttribute(LOT_SEARCH_TYPE_KEY,
                             StringUtils.isBlank(lotSearchType) ? LOT_DEFAULT_SEARCH_TYPE : lotSearchType);
        request.setAttribute(CARRIER_SEARCH_TYPE_KEY,
                             StringUtils.isBlank(carrierSearchType) ? CARRIER_DEFAULT_SEARCH_TYPE : carrierSearchType);
        request.setAttribute(LOT_READONLY_KEY, readOnly);
        long facilityRrn = LocalContext.getFacilityRrn();
        String result;
        boolean carrierFlag = request.getParameter("byCarrierId") != null;
        String carrierId = carrierFlag ? StringUtils.trim(request.getParameter("carrierId")) : StringUtils.EMPTY;
        carrierId = carrierId != null ? carrierId.trim().toUpperCase() : StringUtils.EMPTY;
        Assert.isFalse(carrierFlag && carrierId.length() < 1,
                       Errors.create().content("Cassette id can't be empty!").build());
        if (carrierFlag) {
            Long carrierRrn = this.getInstanceRrn(carrierId, facilityRrn, ObjectList.ENTITY_KEY);
            Assert.isTrue(carrierRrn > 0L, Errors.create().key(MessageIdList.CARRIER_CASSETTE_ID_MISSING)
                                                 .content("Cassette ID is not exist!").build());
            List<String> lotIds = carrierService.getInUseCarrierLotIdList(carrierRrn);
            Assert.isFalse(lotIds == null || lotIds.size() != 1,
                           Errors.create().content("The Cassette have {} lots").args(lotIds == null ? 0 : lotIds.size())
                                 .build());
            result = lotIds.get(0);
        } else {
            result = request.getParameter("lotId") == null ? request.getParameter("instanceId") : StringUtils.trim(
                    request.getParameter("lotId"));
        }
        Assert.isFalse(StringUtils.isBlank(result) && readOnly,
                       Errors.create().key(MessageIdList.LOT_LOTID_OR_CASSETTEID_EMPTY)
                             .content("Lot Id and Cassette Id can't be Empty!</br>").build());
        if (StringUtils.isNotBlank(result)) {
            result = result.toUpperCase();
            // Lot lot = lotQueryService.getLot(result, facilityRrn);
            Lot lot = lotInqService.getLot(result);
            Assert.isFalse(lot == null || lot.getLotRrn() < 1,
                           Errors.create().key(MessageIdList.LOT_ENTER_CORRECT_LOTID)
                                 .content("Please enter the correct lot Id!").build());
            //错误#44134:主辅产品做完Bond.无论触发PostFH 还是正常做完Bond.辅产品在Lot tracking 界面与Lot query 页面不一致
            //修改lot tracking界面qty的数量,再次判断lot.getLotStatus()状态是否为LotStatus.BONDED,是则修改值为去qty
            if(StringUtils.equalsIgnoreCase(lot.getLotStatus(),LotStatus.BONDED)){
                double qty = lotQueryService.getLotWaferCount(lot.getLotRrn());
                lot.setQty1(qty);
            }
            Map lotBaseInfo = buildLotBaseInfo(lot);
            request.setAttribute("lotBaseInfo", lotBaseInfo);
            return lot;
        } else {
            request.setAttribute("lotBaseInfo", new HashMap());
            return null;
        }
    }

    /**
     * There are 4 kinds of future hold in two dimensions
     *
     * @param facilityRrn
     * @param lot
     * @return
     */
    protected List getAllFutureHoldForLot(Long facilityRrn, Lot lot) {
        List futureHoldListForlot = new ArrayList();
        // future hold by lot
        long refRrn = getInstanceRrn(ContextNames.EEN_CONTEXT_HOLDLOT, facilityRrn, ObjectList.CONTEXT_KEY);
        List futureholdList = ctxExecService.getFutureHoldListByLot(refRrn, lot.getLotRrn());

        if (!futureholdList.isEmpty()) {
            futureHoldListForlot.addAll(futureholdList);
        }

        refRrn = getInstanceRrn(ContextNames.EEN_CONTEXT_POST_HOLDLOT, facilityRrn, ObjectList.CONTEXT_KEY);
        futureholdList = ctxExecService.getFutureHoldListByLot(refRrn, lot.getLotRrn());
        if (!futureholdList.isEmpty()) {
            futureHoldListForlot.addAll(futureholdList);
        }

        // future hold by product
        refRrn = getInstanceRrn(ContextNames.EEN_CONTEXT_HOLDPRODUCT, facilityRrn, ObjectList.CONTEXT_KEY);
        Map conditionMap = new HashMap();
        conditionMap.put("refRrn", new Long(refRrn));
        conditionMap.put("productRrn", lot.getProductRrn().toString());
        conditionMap.put("processRrn", lot.getProcessRrn().toString());
        conditionMap.put("lotRrn", new Long(lot.getLotRrn()));

        futureholdList = ctxExecService.getFutureHoldListByProduct(refRrn, lot.getProductRrn(), lot.getProcessRrn(),
                                                                   lot.getLotRrn());
        if (!futureholdList.isEmpty()) {
            futureHoldListForlot.addAll(futureholdList);
        }

        refRrn = getInstanceRrn(ContextNames.EEN_CONTEXT_POST_HOLDPRODUCT, facilityRrn, ObjectList.CONTEXT_KEY);
        conditionMap.put("refRrn", new Long(refRrn));
        futureholdList = ctxExecService.getFutureHoldListByProduct(refRrn, lot.getProductRrn(), lot.getProcessRrn(),
                                                                   lot.getLotRrn());
        if (!futureholdList.isEmpty()) {
            futureHoldListForlot.addAll(futureholdList);
        }
        setFlowSeqAndStepdesc(facilityRrn, lot, futureHoldListForlot);

        futureHoldListForlot = checkPilotSingleFutureHold(futureHoldListForlot);

        return futureHoldListForlot;
    }

    protected Map getStepsInfoByLot(Lot lot, long processRrn, String stepPath) {//TODO 改回原SP,待验证
        Map matchingRules = new HashMap();
        matchingRules.put("productRrn", String.valueOf(lot.getProductRrn()));
        matchingRules.put("processRrn", processRrn);
        matchingRules.put("lotRrn", lot.getLotRrn() + "");
        Map stepsInfo = wipWorkflowQueryService.getStepsInfo(processRrn, stepPath, matchingRules);
        return stepsInfo;
    }

    protected boolean isTechnologyValid4Product(Long productRrn, Long processRrn) {
        List<Map> technologys = prpService.getProductTechnologys(productRrn);
        boolean isValidTechnologyFlag = false;

        if ((technologys == null) || (technologys.size() <= 0)) {
            return isValidTechnologyFlag;
        }
        for (Map item : technologys) {
            if ((item.get("instancerrn") != null) &&
                    ((new Long((String) item.get("instancerrn"))).compareTo(processRrn) == 0)) {
                isValidTechnologyFlag = true;
                break;
            }
        }
        return isValidTechnologyFlag;
    }

    protected long parseRouteRrn(String processStepVersion) {
        long routeRrn = 0;
        if (StringUtils.isNotBlank(processStepVersion)) {

            int firstSep = 0;
            int secondSep = 0;

            firstSep = processStepVersion.indexOf("|");

            secondSep = processStepVersion.indexOf("|", firstSep + 1);
            if (secondSep >= 0) {
                routeRrn = Long.parseLong(processStepVersion.substring(processStepVersion.indexOf("|", 1) + 1,
                                                                       processStepVersion.indexOf(",",
                                                                                                  processStepVersion.indexOf(
                                                                                                          "|", 1))));
            } else {
                routeRrn = Long.parseLong(processStepVersion.substring(0, processStepVersion.indexOf(",")));
            }
        }

        return routeRrn;

    }

    protected boolean checkOperationInFacility(Long operationRrn, Long facilityRrn) {

        boolean inSameFab = false;

        NamedObject object = baseService.getNamedObject(new NamedObject(operationRrn.longValue()));

        if (object != null && object.getNamedSpace() != null) {

            String _temp = getNamedSpace(ObjectList.OPERATION_KEY, facilityRrn);

            if (object.getNamedSpace().equalsIgnoreCase(_temp)) {

                inSameFab = true;
            }

        }
        return inSameFab;
    }

    protected List<Map<String, Object>> dispatch(List<Map<String, Object>> lotList, long equipmentRrn) {
        // there are 5 dispatch rule:
        // 1. Priority based on plan lot
        // 2. Earliest Due Date
        // 3. FIFO
        // 4. Shortest Remaining time (processing time)
        // 5. Same equipment setup. --not available in mimos
        // check percentum in each dispatch rule

        List<Map> rules = emsService.getEquipmentDispatchingRule(equipmentRrn);

        List<String> dispatchRules = new ArrayList();

        for (Map item : rules) {

            if ((item.get("ruleid") != null) && item.get("ruleid").equals(WipSetupAction.PRIORITY_KEY)) {
                dispatchRules.add("priority");

            }

            if ((item.get("ruleid") != null) && item.get("ruleid").equals(WipSetupAction.EDD_KEY)) {
                dispatchRules.add("dueDate");

            }

            if ((item.get("ruleid") != null) && item.get("ruleid").equals(WipSetupAction.FIFO_KEY)) {
                dispatchRules.add("queueTimestamp");

            }

            if ((item.get("ruleid") != null) && item.get("ruleid").equals(WipSetupAction.CR_KEY)) {
                dispatchRules.add("estimatedRemainTime");

            }
            if ((item.get("ruleid") != null) && item.get("ruleid").equals(WipSetupAction.SAME_SETUP_KEY)) {
                dispatchRules.add("sameSetup");

            }

        }

        Comp comp = new Comp(dispatchRules);

        lotList.sort(comp);

        return lotList;
    }

    protected List<Map> getFutureHoldListbyLot(long refRrn, Lot lot) {
        Long facilityRrn = LocalContext.getFacilityRrn();
        List<Map> futureholdListbyLot = ctxExecService.getFutureHoldListByLot(refRrn, lot.getLotRrn());
        if (CollectionUtils.isNotEmpty(futureholdListbyLot)) {
            for (Iterator<?> iterator = futureholdListbyLot.iterator(); iterator.hasNext(); ) {
                Map<?, ?> map = (Map<?, ?>) iterator.next();

                if (StringUtils.contains(MapUtils.getString(map, "holdCode"), HoldCodeNames.SRCHOLD_KEY) &&
                        isAvailableSplitRunCardByHoldReason(MapUtils.getString(map, "holdReason"))) {
                    iterator.remove();
                }
            }
        }

        setFlowSeqAndStepdesc(facilityRrn, lot, futureholdListbyLot);

        futureholdListbyLot = checkPilotSingleFutureHold(futureholdListbyLot);

        return futureholdListbyLot;
    }

    protected List<Map> getPreOrPostFutureHoldForLot(Long facilityRrn, Lot lot, String holdlotKey,
                                                     String holdProductKey) throws Exception {

        List<Map> futureHoldListForlot = new ArrayList();

        // future hold by lot
        long refRrn = this.getInstanceRrn(holdlotKey, this.getNamedSpace(ObjectList.CONTEXT_KEY, facilityRrn),
                                          ObjectList.CONTEXT_KEY);
        List<Map> futureholdList = ctxExecService.getFutureHoldListByLot(refRrn, lot.getLotRrn());

        if (!futureholdList.isEmpty()) {
            futureHoldListForlot.addAll(futureholdList);
        }
        // future hold by product
        refRrn = baseService.getNamedObjectRrn(holdProductKey,
                                               baseService.getNamedSpace(lot.getFacilityRrn().longValue(),
                                                                         ObjectList.CONTEXT_KEY),
                                               ObjectList.CONTEXT_KEY);
        Map conditionMap = new HashMap();
        conditionMap.put("refRrn", new Long(refRrn));
        conditionMap.put("productRrn", lot.getProductRrn().toString());
        conditionMap.put("processRrn", lot.getProcessRrn().toString());
        conditionMap.put("routeRrn", lot.getRouteRrn().toString());
        conditionMap.put("operationRrn", lot.getOperationRrn().toString());
        conditionMap.put("lotRrn", new Long(lot.getLotRrn()));
        futureholdList = ctxExecService.getFutureHoldListByProduct(refRrn, lot.getProductRrn(), lot.getProcessRrn(),
                                                                   lot.getLotRrn());
        if (!futureholdList.isEmpty()) {
            futureHoldListForlot.addAll(futureholdList);
        }

        setFlowSeqAndStepdesc(facilityRrn, lot, futureHoldListForlot);

        futureHoldListForlot = checkPilotSingleFutureHold(futureHoldListForlot);

        return futureHoldListForlot;

    }

    protected String checkRecipeTimeOutMsg(Collection lots) {
        String recipeTimeOutMsg = "";
        for (Iterator it = lots.iterator(); it.hasNext(); ) {
            HashMap item = (HashMap) it.next();
            Lot lot = lotQueryService.getLot(MapUtils.getLongValue(item, "lotRrn"));
            RecipeContextValue contextValue = ctxExecService.getRecipeContextValue(lot);
            if (contextValue != null && StringUtils.isNotBlank(contextValue.getCheckUnit()) &&
                    contextValue.getLastUsedTime() > 0 && contextValue.getFrequency() > 0) {

                long lastUsedTime = DateUtils.addTimeByUnit(contextValue.getLastUsedTime(), contextValue.getCheckUnit(),
                                                            contextValue.getFrequency());
                long nowTime = System.currentTimeMillis();
                long diffTime = nowTime - lastUsedTime;

                if (diffTime > 0) {
                    recipeTimeOutMsg +=
                            contextValue.getTimeOutAction() + "|批次" + lot.getLotId() + "的RECIPE" + lot.getRecipeId() +
                                    "已经超过" + diffTime / 1000 / 60 + "(分钟)未使用!";
                }
            }
        }
        return recipeTimeOutMsg;
    }

    protected void validLotInEqp(long eqptRrn, List<Lot> lots) {
        for (Lot lot : lots) {
            validLotInEqp(eqptRrn, lot);
        }
    }

    protected void validLotInEqp(long eqptRrn, Lot lot) {
        boolean state = false;
        List<Relation> entitygroup = new ArrayList<>();
        String eqptGroupRrns = specService.getEqptGroupRrns(lot.getProcessRrn(), lot.getProcessVersion(),
                                                            lot.getRouteRrn(), lot.getOperationRrn());
        if (StringUtils.isNotBlank(eqptGroupRrns)) {
            List<Long> eqptGroupRrnList = Arrays.stream(eqptGroupRrns.split(",")).map(s -> Long.parseLong(s))
                                                .collect(Collectors.toList());
            for (Long eqptGroupRrn : eqptGroupRrnList) {
                entitygroup.addAll(emsService.getAllEntities(eqptGroupRrn));
            }
        } else {
            long operationRrn = lot.getOperationRrn();
            Operation operation = prpService.getOperation(operationRrn);
            long entityGroupRrn = operation == null ? 0L : operation.getEntityGroupRrn();
            entitygroup = emsService.getAllEntities(entityGroupRrn);
        }

        // 检测当前设备是否在当前步骤可操作的设备组中
        for (Relation relation : entitygroup) {
            if (eqptRrn == relation.getInstanceRrn()) {
                state = true;
                break;
            }
        }

        if (lotService.isLotSpecialEqpt(eqptRrn, lot.getLotRrn())) {
            state = true;
        }

        if (lotService.isLotSpecial(lot.getLotRrn(), lot.getOperationRrn())) {
            state = true;
        }

        Assert.isTrue(state, Errors.create().content("该批次所在步骤为{}({})," + "当前设备{}不能操作该步骤,请更换设备!")
                                   .args(getInstanceId(lot.getOperationRrn()), getInstanceDesc(lot.getOperationRrn()),
                                         getInstanceId(eqptRrn)).build());
    }

    protected boolean isTrackUnitFlag(Lot lot) {
        Operation operation = prpService.getOperation(lot.getOperationRrn());
        String flag = operation.getTrackUnitFlag();
        if ((flag != null) && flag.equals(ON)) {
            return true;
        } else {
            return false;
        }
    }

    protected Collection parseToSimplifyCollection(String unitString) {
        Collection unitList = new ArrayList();
        long seq = 0;
        int index_1 = 0;
        int index_2 = 0;
        int index_3 = 0;
        int index_4 = 0;

        if ((unitString == null) || unitString.equalsIgnoreCase("")) {
            return unitList;
        }
        StringTokenizer stn = new StringTokenizer(unitString, "$", false);
        String unit = "";
        String available = "";
        String unitRrn = "";
        String unitId = "";
        String dummyWaferFlag = "";
        String lotId = "";

        while (stn.hasMoreElements()) {
            seq++;
            unit = stn.nextToken();

            if ((unit == null) || unit.trim().equals("")) {
                continue;
            }

            index_1 = unit.indexOf(",");
            index_2 = unit.indexOf(",", index_1 + 1);
            index_3 = unit.indexOf(",", index_2 + 1);
            index_4 = unit.indexOf(",", index_3 + 1);

            unitId = unit.substring(0, index_1);
            unitRrn = unit.substring(index_1 + 1, index_2);
            dummyWaferFlag = unit.substring(index_2 + 1, index_3);
            available = unit.substring(index_3 + 1, index_4);
            lotId = unit.substring(index_4 + 1);

            if ((index_1 != 0) && (index_1 != -1) && (unitId != null) && !unitId.equalsIgnoreCase("N/A") &&
                    (unitRrn != null) && !unitRrn.equalsIgnoreCase("null")) {
                HashMap map = new HashMap();

                map.put("position", new String("" + seq));
                map.put("unitId", unitId);
                map.put("unitRrn", unitRrn);
                map.put("dummyflag", dummyWaferFlag);
                map.put("available", available);
                map.put("lotid", lotId);
                unitList.add(map);
            }
        }

        return unitList;
    }

    /**
     * @param unitList
     * @return String
     * @Description 将集合对象转换成json字符串(包含空位置信息的json, 一般用于前台ext的展示)
     * @author Aiden
     */
    protected String parseToJsonString(List<Map> unitList) {
        List<Map> newUnits = new ArrayList<Map>(unitList);
        Collections.sort(newUnits, new Comparator<Map>() {

            // 将wafer重新排序
            @Override
            public int compare(Map map1, Map map2) {
                int position1 = MapUtils.getIntValue(map1, "position");
                int position2 = MapUtils.getIntValue(map2, "position");
                if (position1 > position2) {
                    return 1;
                } else if (position1 < position2) {
                    return -1;
                } else {
                    return 0;
                }
            }
        });
        Iterator it = newUnits.iterator();
        Map unit = new HashMap();
        int number = 1;// 指wafer位置从1开始
        int seq = 1;
        List<Map> units = new ArrayList<Map>();
        while (it.hasNext()) {
            Map<String, Object> unitMap = null;
            unit = (HashMap) it.next();
            seq = MapUtils.getIntValue(unit, "position");
            while ((number < seq)) {
                unitMap = new HashMap();
                unitMap.put("position", number + "");
                unitMap.put("unitId", "");
                unitMap.put("unitRrn", "");
                unitMap.put("lotid", "");
                unitMap.put("chooseFlag", "");
                unitMap.put("markNumber", "");
                unitMap.put("markNumberComment", "");
                units.add(unitMap);
                number++;
            }
            unitMap = new HashMap();
            unitMap.put("position", number + "");
            unitMap.put("unitId", unit.get("unitId"));
            unitMap.put("unitRrn", unit.get("unitRrn"));
            unitMap.put("lotid", unit.get("lotid"));
            unitMap.put("chooseFlag", unit.get("chooseFlag"));
            unitMap.put("markNumber", unit.get("markNumber"));
            unitMap.put("markNumberComment", unit.get("markNumberComment"));
            units.add(unitMap);
            number++;
        }
        int listSize = units.size();// 已有
        for (int i = listSize + 1; i <= 25; i++) {
            Map<String, Object> unitMap = new HashMap();
            unitMap.put("position", i + "");
            unitMap.put("unitId", "");
            unitMap.put("unitRrn", "");
            unitMap.put("lotid", "");
            unitMap.put("chooseFlag", "");
            unitMap.put("markNumber", "");
            unitMap.put("markNumberComment", "");
            units.add(unitMap);
        }
        return JsonUtils.toString(units);
    }

    protected String initEmptyCarrier() {
        List<Map<String, Object>> emptyUnits = new ArrayList<Map<String, Object>>();
        int number = 25;// 默认槽位25片
        for (int i = 1; i <= number; i++) {
            Map<String, Object> unitMap = new HashMap<String, Object>();
            unitMap.put("position", i + "");
            unitMap.put("unitId", "");
            unitMap.put("unitRrn", "");
            unitMap.put("lotid", "");
            emptyUnits.add(unitMap);

        }
        String jsonStr = JsonUtils.toString(emptyUnits);
        return jsonStr;
    }

    protected String getValidateId(String objectId, String type) {
        long facilityRrn = LocalContext.getFacilityRrn();

        if (objectId.equals("")) {
            return objectId;
        }

        objectId = objectId.trim().toUpperCase();
        Assert.isFalse(getInstanceRrn(objectId, facilityRrn, type) <= 0,
                       Errors.create().key(MessageIdList.ID_NOT_EXIST).content("The {}: {} does not exist!")
                             .args(type, objectId).build());

        return objectId;
    }

    protected void verifyChildLot(String childLotId, String carrierId, List<Map> cLots) {
        Assert.isFalse((childLotId == null) || childLotId.equalsIgnoreCase(""),
                       Errors.create().key(MessageIdList.CHILD_LOT_ID_EMPTY).content("The Child Lot ID is empty!").build());

        for (Map item : cLots) {
            Assert.isFalse(item.get("childLotId").equals(childLotId),
                           Errors.create().key(MessageIdList.CHILD_LOT_ID_EXISTS).content("The Child Lot ID already existed!").build());

            Assert.isFalse(item.get("carrierId").equals(carrierId),
                           Errors.create().content("The Carrier has already been specified!").build());
        }
    }

    protected void verifyChildLotId(SplitMergeInfoForm theform, List<Map> childLots) {
        String childLotId = theform.getLotId();


        Assert.isFalse((childLotId == null) || childLotId.equalsIgnoreCase(""),
                       Errors.create().key(MessageIdList.CHILD_LOT_ID_EMPTY).content("The Child Lot ID is empty!").build());

        childLotId = childLotId.toUpperCase();
        theform.setLotId(childLotId);

        for (Map item : childLots) {
            Assert.isFalse(((String) item.get("childLotId")).equalsIgnoreCase(childLotId),
                           Errors.create().key(MessageIdList.CHILD_LOT_ID_EXISTS).content("The Child Lot ID already existed!").build());
        }
    }

    protected void validateLotId(String lotId) {
        long facilityRrn = LocalContext.getFacilityRrn();
        Lot lot = lotQueryService.getLot(lotId, facilityRrn);

        Assert.isFalse((lot != null) && (lot.getLotRrn() > 0),
                       Errors.create().content("This lot ID is already existed!Lot ID='{}'").args(lotId).build());
    }

    protected void builditemChidLotInfo(HttpServletRequest request, SplitMergeInfoForm theform, LotInfoForm lotInfoForm,
                                        Lot lot, Map item) {

        long facilityRrn = LocalContext.getFacilityRrn();
        theform.setCarrierId(StringUtils.trimToUpperCase(theform.getCarrierId()));

        Lot tmpLot = new Lot();
        BeanUtils.copyProperties(lot, tmpLot);

        carrierService.compareRealCarrierAndCheckAvailabile(facilityRrn, lot.getCarrierId(), theform.getCarrierId());

        long carrierRrn = carrierService.getCarrierRrnForLot(facilityRrn, theform.getQty1(), theform.getCarrierId());

        //check sortJob
        checkWaitJobs(carrierRrn, 0L, 0L, null);

        item.put("childLotId", theform.getLotId());
        item.put("carrierId", theform.getCarrierId());
        item.put("carrierRrn", carrierRrn);
        item.put("splitQty", theform.getQty1());
        if (theform.getQty1() != null) {
            item.put("intSplitQty", theform.getQty1().intValue() + "");
        }
        item.put("technologyRrn", lot.getProcessRrn());

        if (lotInfoForm.getSplitType().equals("permanent") && theform.getChangeProcessFlag().equals("0")) {
            item.put("wflStepPath", null);
        } else {
            item.put("wflStepPath", request.getParameter("stepNumber"));
        }

        item.put("routeId", theform.getRouteId());
        item.put("routeId4Show", theform.getRouteId());
        item.put("operationId", theform.getOperationId());
        item.put("operationRrn", new Long(
                getInstanceRrn(theform.getOperationId(), getNamedSpace(ObjectList.OPERATION_KEY, facilityRrn),
                               ObjectList.OPERATION_KEY)));
        Long opRrn = (Long) item.get("operationRrn");
        String operationDesc = getInstanceDesc(opRrn.longValue());
        item.put("operationDesc", operationDesc);
        if (StringUtils.isNotEmptyTrim(WebUtils.getParameter("targetListValues", request))) {
            item.put("unitList", request.getParameter("targetListValues"));// 取选定的Wafer目标值.
        } else {
            item.put("unitList", theform.getTargetListValues());// 取选定的Wafer目标值.
        }
    }

    protected Long getDefaultTechnology(Long productRrn) {
        Long processRrn = 0L;
        boolean firstProcess = true;
        List<Map> productTechnologys = prpService.getProductTechnologys(productRrn);
        for (Map processMap : productTechnologys) {
            if (processMap != null) {
                if (StringUtils.isNotBlank(MapUtils.getJsonString(processMap, "default")) &&
                        StringUtils.equalsIgnoreCase("y", MapUtils.getJsonString(processMap, "default"))) {
                    // 获取默认流程
                    processRrn = MapUtils.getLong(processMap, "instancerrn");
                    break;
                } else {
                    // 获取第一个流程
                    if (firstProcess) {
                        firstProcess = false;
                        processRrn = MapUtils.getLong(processMap, "instancerrn");
                    }
                }
            }
        }
        Assert.isFalse(processRrn == 0L,
                       Errors.create().key(MessageIdList.PRODUCT_EMPTYPROCESS).content("product dose not have process")
                             .build());
        return processRrn;
    }

    protected void copyLotForm(SplitMergeInfoForm theform, LotInfoForm lotInfoForm, Lot lot) {

        theform.setCacheLotInfo(WebUtils.getCacheObj2String(lotInfoForm));
        Map unitsOfParentLot = WebUtils.getCacheString2Obj(theform.getCacheUnitsOfParentLot()) ==
                null ? new HashMap() : ((Map) WebUtils.getCacheString2Obj(theform.getCacheUnitsOfParentLot()));
        unitsOfParentLot.put(lot.getLotId() + "unitsOfParentLot", lotInfoForm.getSourceListValues());
        theform.setCacheUnitsOfParentLot(WebUtils.getCacheObj2String(unitsOfParentLot));
        PropertyUtils.copyProperties(theform, lot);
    }

    /**
     * @param jsonStr
     * @return List<Map < String, Object>>
     * @Description 获得移除空位后的wafer信息
     */
    protected List<Map<String, Object>> parseJsonStrAndRemoveIdlePostion(String jsonStr) {
        List<Map<String, Object>> unitList = new ArrayList<Map<String, Object>>();
        List list = JsonUtils.toObject(jsonStr, List.class);
        List<Map<String, Object>> tempUnits = (List) list;
        for (Map<String, Object> unitMap : tempUnits) {
            String unitId = MapUtils.getString(unitMap, "unitId");
            String unitRrn = MapUtils.getString(unitMap, "unitRrn");
            String lotid = MapUtils.getString(unitMap, "lotid");
            if (StringUtils.isNotBlank(unitId) && StringUtils.isNotBlank(unitRrn) && StringUtils.isNotBlank(lotid)) {
                unitList.add(unitMap);
            }
        }
        return unitList;
    }

    protected void replaceChildRecipeId(Map lotInfo) {
        List childRecipeList = (List) MapUtils.getObject(lotInfo, "childRecipes");
        if (childRecipeList != null) {
            for (Object obj : childRecipeList) {
                Map map = (Map) obj;
                String matchRecipeId = recipeService.buildRecipePhysicalId(MapUtils.getString(map, "recipeId"),
                                                                           lotInfo);
                map.put("recipePhysicalId", matchRecipeId);
            }
        }
    }

    protected Operation getOperation(Operation operation) {
        return prpService.getOperation(operation.getInstanceRrn());
    }

    protected void checkParameters(ReassignRequestDto reassignRequestDto) {
        Assert.isFalse(CollectionUtils.isEmpty(reassignRequestDto.getReassignLotIds()),
                       Errors.create().content("Reassign Lots can not be empty!").build());
        Assert.isFalse(StringUtils.isEmpty(reassignRequestDto.getDepartment()),
                       Errors.create().content("Department can not be empty!").build());
        Assert.isFalse(StringUtils.isEmpty(reassignRequestDto.getResponsibilityUserId()) ||
                               reassignRequestDto.getResponsibilityUserRrn() <= 0,
                       Errors.create().content("User Id can not be empty!").build());
        Assert.isFalse(StringUtils.isEmpty(reassignRequestDto.getReason()),
                       Errors.create().content("Reason can not be empty!").build());
    }

    protected String transUnit(String sourceList, String targetList) {
        List<Map<String, Object>> source = parseJsonStrAndRemoveIdlePostion(sourceList);
        List<Map> target = (List) parseJsonStrAndRemoveIdlePostion(targetList);
        target.addAll(source);
        return parseToJsonString(target);
    }

    protected void sourceAndChidUnits(List source, Map item, List<Map> parentLotRunCardCreateTime) {
        List buildItem = new ArrayList();
        // Collection buildItemTwo = new ArrayList();
        List childUnits = (List) item.get("units");

        String childLotId = MapUtils.getString(item, "childLotId");
        Lot childLot = lotQueryService.getLot(childLotId, LocalContext.getFacilityRrn());
        Timestamp childLotCreateTime = childLot.getCreatedTimestamp();
        if (parentLotRunCardCreateTime != null && parentLotRunCardCreateTime.size() > 0) {
            for (Map parentRunCardCreatedTime : parentLotRunCardCreateTime) {
                Timestamp createdTime = (Timestamp) MapUtils.getObject(parentRunCardCreatedTime, "createdTime");
                String runCardId = MapUtils.getString(parentRunCardCreatedTime, "runCardId");
                Assert.isFalse(createdTime != null && childLotCreateTime.before(createdTime),
                               Errors.create().key(MessageIdList.LOT_PARENTLOT_HAS_UNFINISHED_RUNCARD)
                                     .content("Parent lot has unfinished runcard({}). Forbid of merger!")
                                     .args(runCardId).build());
            }
        }

        List<Map> childLotRunCardCreatedTimeList = lotRunCardQueryService.getSumbitAndActiveRunCardCreateTime(
                childLot.getLotRrn());
        Assert.isFalse(childLotRunCardCreatedTimeList != null && childLotRunCardCreatedTimeList.size() > 0,
                       Errors.create().key(MessageIdList.LOT_PARENTLOT_HAS_UNFINISHED_RUNCARD)
                             .content("Child lot: {} has unfinished runcard. Forbid of merger!")
                             .args(childLot.getLotId()).build());

        // 检查子批和目批片号与对应的位置是否一致
        Assert.isTrue(checkPositionAvaliable(source),
                      Errors.create().content("Unit Id for parent lot don't match the position!").build());
        Assert.isTrue(checkPositionAvaliable(childUnits),
                      Errors.create().content("Unit Id for child lot don't match the " + "position!").build());

        List sourcePosition = new ArrayList();
        for (Object childUnit : childUnits) {
            boolean childPosition = true;
            Map childMap = (Map) childUnit;
            String unitId = MapUtils.getString(childMap, "unitId");
            int defaultPosition = Unit.getWaferDefaultPostion(unitId);
            if (defaultPosition > 0) {
                for (Iterator iterator = source.iterator(); iterator.hasNext(); ) {
                    Map sourceMap = (Map) iterator.next();
                    int position = MapUtils.getIntValue(sourceMap, "position");
                    if (defaultPosition == position) {// 说明子批的原来的位置被占用了,不能再合批
                        childPosition = false;
                    }

                    // 再次check unit id唯一性
                    Assert.isFalse(StringUtils.equals(unitId, MapUtils.getString(sourceMap, "unitId")),
                                   Errors.create().content("Parent lot's unit id is same with child lot'd unit id!")
                                         .build());
                }
            } else {// 子批默认位置为空
                childPosition = false;
            }
            Assert.isTrue(childPosition, Errors.create().content("Haven't avaliable position for child lot!").build());
            childMap.put("position", defaultPosition + "");
            buildItem.add(childMap);
        }
        source.addAll(buildItem);
    }

    protected void checkLotStepForMerge(Lot parentLot, Lot childLot) {
        Assert.isFalse(!parentLot.getOperationRrn().equals(childLot.getOperationRrn()) ||
                               !StringUtils.equals(parentLot.getRouteId(), childLot.getRouteId()),
                       Errors.create().key(MessageIdList.MERGELOT_LOTS_STEPID_DIFFERENT)
                             .content("Lots with different step Id.").build());
    }

    protected void setCache4WFL(HttpServletRequest request, ActionForm form) {
        RootForm theform = (RootForm) form;
        theform.setCacheCurrentlots(WebUtils.getCacheObj2String(request.getAttribute("currentlots")));
        theform.setCacheJob(WebUtils.getCacheObj2String(request.getAttribute(SessionNames.JOB_KEY)));
        theform.setCacheParametersInfo(
                WebUtils.getCacheObj2String(request.getAttribute(SessionNames.PARAMETERSINFO_KEY)));
        theform.setCacheCollection(WebUtils.getCacheObj2String(request.getAttribute(SessionNames.COLLECTION_KEY)));
        theform.setCacheSorterJob(WebUtils.getCacheObj2String(request.getAttribute(SessionNames.SORTER_JOB)));
        request.setAttribute("runcardLotId", request.getAttribute("runcardLotId"));
    }

    protected long getSourceUnitRrn(long targetUnitRrn) throws Exception {
        long sourceUnitRrn = 0L;
        BondingMapping bondingMapping = wipService.getBondingMapping(targetUnitRrn);
        if (bondingMapping != null) {
            sourceUnitRrn = bondingMapping.getSourceUnitRrn();
        }
        return sourceUnitRrn;
    }

    protected String listToString(List<Map> unitList) {
        StringBuilder sb = new StringBuilder();
        Iterator it = unitList.iterator();

        int number = 1;
        int seq;
        HashMap unit;
        while (it.hasNext()) {
            unit = (HashMap) it.next();
            seq = Integer.parseInt((String) unit.get("position"));

            while ((number < seq) && (number < 51)) {
                sb.append(" $");
                number++;
            }

            sb.append(unit.get("unitId"));
            sb.append(",");
            sb.append(unit.get("unitRrn"));
            sb.append(",");
            sb.append(unit.get("dummyflag"));
            sb.append(",");
            sb.append(unit.get("available"));
            sb.append(",");
            sb.append(unit.get("lotid"));

            sb.append("$");
            number++;
        }

        return sb.toString();
    }

    protected void validateTask(Map params, String task) {

        String taskAction = "";

        if ("abortjob".equalsIgnoreCase(task)) {
            taskAction = "/abortjob.do";
        } else if ("changeprocess".equalsIgnoreCase(task)) {
            taskAction = "/wip/processswitch.jsp";
        } else if ("edc".equalsIgnoreCase(task)) {
            taskAction = "/edcstart.do";
        } else if ("autoedc".equalsIgnoreCase(task)) {
            taskAction = "/autoedcstart.do";
        } else if ("holdlot".equalsIgnoreCase(task)) {
            taskAction = "/wip/processswitch.jsp";
        } else if ("logevent".equalsIgnoreCase(task)) {
            taskAction = "/logeventedit.do";
        } else if ("movein".equalsIgnoreCase(task)) {
            taskAction = "/movein.do";
        } else if ("moveout".equalsIgnoreCase(task)) {
            taskAction = "/moveout.do";
        } else if ("processswitch".equalsIgnoreCase(task)) {
            taskAction = "/wip/processswitch.jsp";
        } else if ("rework".equalsIgnoreCase(task)) {
            taskAction = "/wip/processswitch.jsp";
        } else if ("showinstruction".equalsIgnoreCase(task)) {
            taskAction = "/showinstruction.do";
        } else if ("shiplot".equalsIgnoreCase(task)) {
            taskAction = "/shiplottask.do";
        } else if ("movenext".equalsIgnoreCase(task)) {
            taskAction = "/processswitch.do?next=true&processFlag=nextStep&bgMove=y";
        }
        Assert.isFalse(params != null && params.get("Task_Method_ID") != null &&
                               !params.get("Task_Method_ID").equals(taskAction),
                       Errors.create().key(MessageIdList.SYSTEM_TRANSACTION_NOT_TOKEN).content("验证token失败!").build());
    }

    protected List<ContextValue> getInstruction(String byType, Map item, String actionPoint) {

        Long productRrn = MapUtils.getLongValue(item, "productRrn");
        Long processRrn = MapUtils.getLongValue(item, "processRrn");
        Long routeRrn = MapUtils.getLongValue(item, "routeRrn");
        Long operationRrn = MapUtils.getLongValue(item, "operationRrn");

        if (StringUtils.isEmpty(actionPoint) || StringUtils.equalsIgnoreCase(actionPoint, ActionPointList.MOVEIN_KEY)) {
            actionPoint = ActionPointList.MOVEIN_KEY;
        } else {
            actionPoint = ActionPointList.MOVEOUT_KEY;
        }

        if ("product".equals(byType)) {
            return ctxExecService.getInstructionByProduct(productRrn, processRrn, routeRrn, operationRrn, actionPoint);
        } else if ("process".equals(byType)) {
            return ctxExecService.getInstructionByProcess(processRrn, routeRrn, operationRrn, actionPoint);
        } else if ("lot".equals(byType)) {
            Lot lot = lotQueryService.getLot(MapUtils.getString(item, "lotId"), LocalContext.getFacilityRrn());
            return ctxExecService.getInstructionByLot(lot.getLotRrn(), lot.getRouteSeq(), lot.getOperationSeq(),
                                                      productRrn, processRrn, routeRrn, operationRrn, actionPoint);
        }
        return null;
    }

    protected String getUOM1(Long operationRrn) {
        String UOM1 = "";

        Operation oper = prpService.getOperation(operationRrn);

        if (oper != null && oper.getUnitOfMeasure1() != null) {
            UOM1 = oper.getUnitOfMeasure1();
        }

        return UOM1;
    }

    protected Map buildProcessLotInfo(Lot lot) {
        Map lotInfo = new HashMap();
        lotInfo.put("lotId", lot.getLotId());
        lotInfo.put("lotRrn", new Long(lot.getLotRrn()));
        lotInfo.put("productId", lot.getProductId());
        lotInfo.put("processId", lot.getProcessId());
        lotInfo.put("lotStatus", lot.getLotStatus());
        lotInfo.put("stageId", lot.getStageId());
        lotInfo.put("operationId", lot.getOperationId());
        lotInfo.put("operationDesc", lot.getOperationDesc());
        lotInfo.put("recipeId", lot.getRecipeId());
        try {
            lotInfo.put("recipePhysicalId", getRecipePhysicalId(lot));
        } catch (Exception e) {
            lotInfo.put("recipePhysicalId", "");
        }
        lotInfo.put("qty1", lot.getQty1());
        return lotInfo;
    }

    protected void buildLotinfo(Job job, List lots) {
        int i = 0;
        for (Iterator it = lots.iterator(); it.hasNext(); ) {
            HashMap item = (HashMap) it.next();
            Lot lot = lotQueryService.getLot(MapUtils.getLongValue(item, "lotRrn"));

            List<Relation> reticles = wipQueryService.getAvailableReticlesForTrackIn(lot, job.getEqptRrn());
            String isReticleRequired = TRUE;
            if (CollectionUtils.isEmpty(reticles)) {
                reticles = new ArrayList<>();
                isReticleRequired = FALSE;
            }

            item.put("isReticleRequired", isReticleRequired);
            item.put("reticleString" + i, reticles);

            i++;

            RecipeContextValue recipe = ctxExecService.getRecipeContextValue(lot);
            Long borRrn = 0L;
            if (recipe != null) {
                String borString = recipe.getResultValue5();
                if (StringUtils.isNotEmpty(borString)) {
                    borRrn = Long.parseLong(borString);
                }
                item.put("parame", recipe.getParameter());
                if (recipe.getWiringRrn() != null && !getInstanceId(recipe.getWiringRrn()).equalsIgnoreCase("N/A")) {
                    item.put("wiringId", getInstanceId(recipe.getWiringRrn()));
                }
                item.put("yield", StringUtils.EMPTY);
            }

            // For BOR
            //Long borRrn = ctxExecService.getBomRrn(lot);
            if (borRrn > 0) {
                item.put("borRrn", borRrn);
                item.put("borId", getInstanceId(borRrn));
            }

            String stageId = ctxExecService.getStageId(lot);
            if (StringUtils.isNotBlank(stageId)) {
                item.put("stageId", stageId);
            }
        }
    }

    /**
     * 取消moveIn init方法
     *
     * @param theform
     * @param request
     * @throws Exception
     */
    protected void cancelMoveInInit(LotInfoForm theform, HttpServletRequest request) throws Exception {

        Long facilityRrn = LocalContext.getFacilityRrn();

        // Populate the new valid format id
        Lot lotInfo = new Lot();
        String lotId = theform.getLotId().trim().toUpperCase();
        request.setAttribute("lotId", lotId);
        long jobrrn = 0;
        Assert.isTrue(lotId != null && !lotId.trim().equals(""), Errors.create().content("No such Lot exist!").build());
        lotInfo = isValidLot(facilityRrn, lotId.trim().toUpperCase());

        Assert.notNull(lotInfo, Errors.create().content("No such Lot exist!").build());

        String equipmentRrns = request.getParameter("avaliableEqtRrn");
        request.setAttribute("equipmentRrns", equipmentRrns);

        Long lotRrn = lotInfo.getLotRrn();
        Lot curLot = lotQueryService.getLot(lotRrn.longValue());

        //卡控post future hold被hold住的lot
        checkPostFutureHold(lotRrn);

        String lotStatus = lotQueryService.getLotStatus(lotRrn.longValue());
        jobrrn = wipQueryService.getJobRrn(lotRrn.longValue());
        Long jobRrn = new Long(jobrrn);
        if (("RUNNING".equals(lotStatus) || "DISPATCH".equals(lotStatus) || "PROCESSED".equals(lotStatus)) &&
                jobRrn != null) {
            org.apache.commons.beanutils.PropertyUtils.copyProperties(theform, lotInfo);
            theform.setLot_rrn(lotRrn);
            theform.setJobRrn(jobRrn);
            theform.setWflStepPath(curLot.getWflStepPath());
            theform.setOperationSeq(lotInfo.getOperationSeq());
            request.setAttribute("dueDate", lotInfo.getDueDate());

            Lot anotherLot = getBondingLot(lotRrn, jobRrn);
            if (anotherLot != null && anotherLot.getLotRrn() > 0) {
                request.setAttribute("anotherLot", anotherLot);
            }

            theform.setSourceListValues(parseToString(wipQueryService.getUnitListByLot(lotRrn)));
            theform.setTargetListValues("");
            String childLotId = getChildLotId(lotInfo.getLotId(), facilityRrn);
            theform.setSplitChildLotId(childLotId);
            Map conditionMap = BeanUtils.copyBeanToMap(curLot);
            theform.setOperationDesc(ctxExecService.getOperationDescByProcessInfo(conditionMap));

            theform.setHotFlagPriorityStr(
                    wipQueryService.getHotflagSplicingPriority(NumberUtils.toInt(theform.getHotFlag(), 0),
                                                               theform.getPriority(), facilityRrn));
            NamedObject obj = new NamedObject(curLot.getCarrierRrn());
            obj = baseService.getNamedObject(obj);

            request.setAttribute("baseCarrierType", obj.getObjectSubtype());

        } else {
            throw new SystemIllegalArgumentException(
                    Errors.create().key(MessageIdList.LOT_STATUS_MUST_RUNNING_OR_DISPATCH)
                          .content("lot status must running or dispatch").build());
        }
    }

    /**
     * Cancel MoveIn 处理
     **/
    protected ActionForward cancelMoveInHandle(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                                               HttpServletResponse response) throws Exception {
        String user = LocalContext.getUserId();
        Long facilityRrn = LocalContext.getFacilityRrn();
        LotInfoForm theform = (LotInfoForm) form;

        Long lotRrn = theform.getLot_rrn();
        Long jobRrn = theform.getJobRrn();
        String comments = theform.getLotComments();

        // 没有部分取消作业 永远不会走
        if (checkSplitCancelMoveIn(request)) {
            //todo 暂时没有部分取消作业
            //splitCancelMoveIn(request, lotRrn, theform);
            Boolean flag = (Boolean) request.getAttribute("_cancelMoveIn");
            if (flag != null && flag.booleanValue()) {
                request.getRequestDispatcher("/lotLocation4CSECAction.do?action=lotlocation&lotId=" +
                                                     String.valueOf(request.getAttribute("_lotId")))
                       .forward(request, response);
                return null;
            }
            return new ActionForward(mapping.getInput());
        }

        String userRRN = StringUtils.trimToUpperCase(theform.getUserId());
        Assert.isFalse(StringUtils.isEmpty(theform.getUserId()),
                       Errors.create().content("Responsibility cannot be empty! ").build());

        String userName = securityService.getUser(Long.parseLong(userRRN)).getUserName();
        Assert.isFalse(comments != null && comments.getBytes("UTF-8").length > 128,
                       Errors.create().content("The input content exceeds the length limit!").build());

        String reason =
                theform.getReasonCode() + " " + theform.getDeptExt().trim().toUpperCase() + " " + userName + " " +
                        theform.getReason();

        Assert.isFalse(reason != null && reason.getBytes("UTF-8").length > 1024,
                       Errors.create().content("The input content exceeds the length limit!").build());

        Lot lot = lotQueryService.getLot(lotRrn.longValue());

        if (lot.getJobRrn() != null && lot.getJobRrn().longValue() > 0) {
            List<Lot> lotList = lotQueryService.getLotsByJobRrn(lot.getJobRrn());
            List<Map> transInfo = new ArrayList<Map>();
            for (Lot _lot : lotList) {
                _lot = lotQueryService.getLot(_lot.getLotRrn());
                //涉及数据权限问题,有些功能比如RRC的cancleMovein的job为0,会查出其它没有权限的批次,导致getLot的结果为空
                if (_lot == null || _lot.getLotRrn() <= 0L) {
                    continue;
                }
                TransReason transReason = new TransReason();
                transReason.setReasonCode(theform.getReasonCode());
                transReason.setReason(reason);
                transReason.setResponsibility(user);
                transReason.setTransQty1(_lot.getQty1());
                transReason.setTransQty2(_lot.getQty2());

                if (!StringUtils.equalsIgnoreCase(_lot.getLotStatus(), LotStatus.WAITING) && _lot.getJobRrn() != null &&
                        _lot.getJobRrn() > 0) {

                    Map<String, Object> tranMap = buildCancelMoveInTransInfo(_lot, comments, transReason);
                    addCancelMoveInLotTrans(transInfo, tranMap);
                }

                Lot anotherLot = getBondingLot(_lot.getLotRrn(), jobRrn);
                anotherLot = lotQueryService.getLot(anotherLot.getLotId(), facilityRrn);
                if (anotherLot != null && anotherLot.getLotRrn() > 0 &&
                        !StringUtils.equalsIgnoreCase(anotherLot.getLotStatus(), LotStatus.WAITING) &&
                        anotherLot.getJobRrn() != null && anotherLot.getJobRrn() > 0) {

                    String comment = "Auto cancel movein with :" + _lot.getLotId() + ";";
                    Map<String, Object> tranMap = buildCancelMoveInTransInfo(anotherLot, comment, transReason);
                    addCancelMoveInLotTrans(transInfo, tranMap);
                }

                List<Lot> batchLotList = diffBatchQueryService.getBatchLotList(_lot.getLotRrn());
                if (CollectionUtils.isNotEmpty(batchLotList)) {
                    for (Lot batchLot : batchLotList) {
                        if (batchLot.getLotRrn() != _lot.getLotRrn() &&
                                LotStatus.isCanCancelMoveIn(batchLot.getLotStatus())) {
                            Map<String, Object> tranMap = buildCancelMoveInTransInfo(batchLot, comments, transReason);
                            addCancelMoveInLotTrans(transInfo, tranMap);
                        }
                    }
                }

                CacheUtils.remove(lot.getEqptRrn() + "");
            }

            doCancelMoveIn(transInfo);
        }

        cancelMoveInRunCardHandle(lot, theform, reason, userName, facilityRrn);

        cancelMoveInReturn(theform, request, response);
        return WebUtils.NULLActionForward;
    }

    /**
     * 执行cancel move in 特殊情况重写该方法
     *
     * @author finatice.yang
     * @date 2021/7/23
     * @version 6.0.0
     **/
    protected void doCancelMoveIn(List<Map> transInfo) {
        List<String> lotRrns = new ArrayList<>();
        for (Map map : transInfo) {
            Lot lot = (Lot) MapUtils.getObject(map, "lot");
            lotRrns.add(StringUtils.toString(lot.getLotRrn()));
        }

        lotService.cancelMoveIn(transInfo, lotRrns);
    }

    /**
     * Cancel MoveIn RunCard处理(RunCard CancelMoveIn 重写)
     *
     * @param lot
     * @param theform
     * @param reason
     * @param userName
     * @param facilityRrn
     */
    protected void cancelMoveInRunCardHandle(Lot lot, LotInfoForm theform, String reason, String userName,
                                             Long facilityRrn) {
    }

    /**
     * Cancel MoveIn 之后返回(子类重写)
     *
     * @param theform
     * @param request
     * @param response
     * @throws Exception
     */
    protected void cancelMoveInReturn(LotInfoForm theform, HttpServletRequest request,
                                      HttpServletResponse response) throws Exception {

    }

    /**
     * Hold 处理
     */
    protected ActionForward holdHandle(ActionMapping mapping, ActionForm form, HttpServletRequest request) {
        HoldReleaseLotInfoForm theform = (HoldReleaseLotInfoForm) form;

        long facilityRrn = LocalContext.getFacilityRrn();
        String lotId = theform.getLotId();
        long userRrn = LocalContext.getUserRrn();
        String userId = LocalContext.getUserId();

        User user = securityService.getUser(userId, facilityRrn);

        Lot lot = lotQueryService.getLot(lotId, facilityRrn);

        Assert.isFalse(lot.getLotRrn() <= 0,
                       Errors.create().key(MessageIdList.LOT_LOTRRN_NOT_FOUND).content("Cannot Find Lot!").build());

        Assert.isFalse((theform.getReason() == null) || ("".equalsIgnoreCase(theform.getReason())),
                       Errors.create().key(MessageIdList.ERROR_REASON_IS_EMPTY).content("Reason is empty!").build());


        String reason = theform.getDepartment() + " Reason:" + "Holdcode is " + theform.getReasonCode() + ". " +
                theform.getReason();
        TransReason transReason = new TransReason();
        transReason.setReasonCode(theform.getReasonCode());
        transReason.setReason(reason);
        transReason.setTransQty1(lot.getQty1());
        transReason.setTransQty2(lot.getQty2());
        transReason.setResponsibility(userId);

        List<Lot> holdLotList = new ArrayList<>();
        List<Lot> batchLotList = diffBatchQueryService.getBatchLotList(lot.getLotRrn());
        if (CollectionUtils.isNotEmpty(batchLotList)) {
            for (Lot batchLot : batchLotList) {
                Assert.state(LotStatus.isCanHold(batchLot.getLotStatus()),
                             Errors.create().key(MessageIdList.LOT_STATUS_CANT_NOT_BE_HOLD)
                                   .content("Lot status must be 'HOLD' or 'WAITING'!").build());
                holdLotList.add(batchLot);
            }
        } else {
            Assert.state(LotStatus.isCanHold(lot.getLotStatus()),
                         Errors.create().key(MessageIdList.LOT_STATUS_CANT_NOT_BE_HOLD)
                               .content("Lot status must be 'HOLD' or 'WAITING'!").build());
            holdLotList.add(lot);
        }
        List<String> lockLotRrns = holdLotList.stream().map(holdLot -> StringUtils.toString(holdLot.getLotRrn()))
                                              .collect(Collectors.toList());
        lotService.holdLot(holdLotList, user, transReason, lockLotRrns);

        //清除派工页面缓存
        Collection availableEquipmentInfos = buildAvailableEquipInfosForLot(lot, userRrn);
        for (Object temp : availableEquipmentInfos) {
            Map equipmentInfo = (Map) temp;
            CacheUtils.remove(MapUtils.getString(equipmentInfo, "equipmentRrn"));
        }
        return mapping.findForward(Constants.LOTLOCATION_KEY);
    }

    /**
     * Hold Running 处理
     */
    protected ActionForward holdRunningHandle(ActionMapping mapping, ActionForm form, HttpServletRequest request) {
        HoldReleaseLotInfoForm theform = (HoldReleaseLotInfoForm) form;

        long facilityRrn = LocalContext.getFacilityRrn();
        String lotId = theform.getLotId();
        long userRrn = LocalContext.getUserRrn();
        String userId = LocalContext.getUserId();

        User user = securityService.getUser(userId, facilityRrn);

        Lot lot = lotQueryService.getLot(lotId, facilityRrn);

        Assert.isFalse(lot.getLotRrn() <= 0,
                       Errors.create().key(MessageIdList.LOT_LOTRRN_NOT_FOUND).content("Cannot Find Lot!").build());

        Assert.isFalse((theform.getReason() == null) || ("".equalsIgnoreCase(theform.getReason())),
                       Errors.create().key(MessageIdList.ERROR_REASON_IS_EMPTY).content("Reason is empty!").build());

        String reason = theform.getDepartment() + " Reason:" + "Holdcode is " + theform.getReasonCode() + ". " +
                theform.getReason();
        TransReason transReason = new TransReason();
        transReason.setReasonCode(theform.getReasonCode());
        transReason.setReason(reason);
        transReason.setTransQty1(lot.getQty1());
        transReason.setTransQty2(lot.getQty2());
        transReason.setResponsibility(userId);

        List<Lot> holdLotList = new ArrayList<>();
        List<Lot> batchLotList = diffBatchQueryService.getBatchLotList(lot.getLotRrn());
        if (CollectionUtils.isNotEmpty(batchLotList)) {
            for (Lot batchLot : batchLotList) {
                Assert.state(LotStatus.isCanRunningHold(batchLot.getLotStatus()),
                             Errors.create().key(MessageIdList.LOT_STATUS_CANT_NOT_BE_HOLD)
                                   .content("Lot status must be 'RUNNING' or 'RUNNINGHOLD'!").build());
                holdLotList.add(batchLot);
            }
        } else {
            Assert.state(LotStatus.isCanRunningHold(lot.getLotStatus()),
                         Errors.create().key(MessageIdList.LOT_STATUS_CANT_NOT_BE_HOLD)
                               .content("Lot status must be 'RUNNING' or 'RUNNINGHOLD'!").build());
            holdLotList.add(lot);
        }
        List<String> lockLotRrns = holdLotList.stream().map(holdLot -> StringUtils.toString(holdLot.getLotRrn()))
                                              .collect(Collectors.toList());
        lotService.holdRunningLot(holdLotList, user, transReason, lockLotRrns);

        //清除派工页面缓存
        Collection availableEquipmentInfos = buildAvailableEquipInfosForLot(lot, userRrn);
        for (Object temp : availableEquipmentInfos) {
            Map equipmentInfo = (Map) temp;
            CacheUtils.remove(MapUtils.getString(equipmentInfo, "equipmentRrn"));
        }

        //TODO CacheHelper相关更新
        return mapping.findForward(Constants.LOTLOCATION_KEY);
    }

    /**
     * Release 处理
     */
    protected ActionForward releaseHandle(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                                          HttpServletResponse response) {
        HoldReleaseLotInfoForm theform = (HoldReleaseLotInfoForm) form;
        // List<Lot> lockLots = new ArrayList<Lot>();
        boolean superReleaseFlag = StringUtils.isNotEmpty((String) request.getParameter("superrelease"));
        long facilityRrn = LocalContext.getFacilityRrn();
        String lotId = theform.getLotId();
        long userRrn = LocalContext.getUserRrn();
        String userId = LocalContext.getUserId();
        String[] newPasswords = request.getParameterValues("newPassword");
        String fieldFlag = request.getParameter("fieldFlags");
        String[] fieldFlags = fieldFlag.split(",");

        Lot lot = lotQueryService.getLot(lotId, facilityRrn);

        Assert.isFalse(lot.getLotRrn() <= 0,
                       Errors.create().key(MessageIdList.LOT_LOTRRN_NOT_FOUND).content("Cannot Find Lot!").build());

        Assert.isTrue(StringUtils.equals(lot.getLotStatus(), LotStatus.HOLD),
                      Errors.create().key(MessageIdList.LOT_STATUS_NOT_ALLOW).content("Lot status not allow").build());

        PiLotView piLotView = wipQueryService.getPiLotViewIdByLotRrn(lot.getLotRrn(), lot.getBasedLotRrn());

        checkRunCard(lot);

        List<Map> holdReasons = wipQueryService.getHoldReasons(lot.getLotRrn());
        List<Map> releaseReasons = new ArrayList();
        List<Map> unReleaseReasons = new ArrayList();

        //是否存在trackOutHold释放
        boolean trackOutHoldExisted = false;
        //工步变更检查flag
        boolean lotStepChanged = false;
        // 是否存在Bonding绑定错误
        boolean sapphireBondedError = false;
        for (int i = 0; i < holdReasons.size(); i++) {
            Map holdReason = holdReasons.get(i);
            String reasonCode = MapUtils.getString(holdReason, "reasonCode");

            if (StringUtils.equals(TransReason.reasonCode.BONDERROR.name(), reasonCode)) {
                sapphireBondedError = true;
            }

            if (StringUtils.equals("0", fieldFlags[i])) {
                if (StringUtils.contains(MapUtils.getString(holdReason, "reasonCode"), HoldCodeNames.SRCHOLD_KEY)) {
                    List<LotRunCard> runcardList = cardService.getActivedLotRuncardByLot(lot.getLotRrn());
                    for (LotRunCard runcard : runcardList) {
                        Assert.isFalse(StringUtils.contains(MapUtils.getString(holdReason, "reason"),
                                                            "Current Lot WaferCnt is not equal to SRC Lot " +
                                                                    "WaferCnt,Please Check!") &&
                                               StringUtils.equals(lot.getFlowSeq(), runcard.getSplitFlowSeq()),
                                       Errors.create().key(MessageIdList.RC_WITHDRAW_FIRST)
                                             .content("Please withdraw the RC {} first!")
                                             .args(getInstanceId(runcard.getRunCardRrn())).build());


                        Assert.isFalse(StringUtils.equals(lot.getFlowSeq(), runcard.getSplitFlowSeq()) &&
                                               StringUtils.contains(MapUtils.getString(holdReason, "reason"),
                                                                    getInstanceId(runcard.getRunCardRrn())),
                                       Errors.create().key(MessageIdList.LOT_RELEASE_SRCHOLD_DENY)
                                             .content("Can't release SRCHOLD!").build());
                    }
                }

                // Pilot分批后  卡控子批未合并不允许释放HOlD  #41886
                if (StringUtils.contains(MapUtils.getString(holdReason, "reasonCode"), "MERGED")) {
                    Assert.isFalse(Objects.nonNull(piLotView) && StringUtils.equalsIgnoreCase(piLotView.getSingleLot(),
                                                                                              PiLotStatusEnum.NO.toString()),
                                   Errors.create().key(MessageIdList.PILOT_SPLIT_HOLD_RELEASER)
                                         .content("Please release after merging child lot and parent lot!").build());
                }
                // 增加ocap hold卡控
                Assert.isFalse(StringUtils.contains(reasonCode, HoldCodeNames.OCAP_HOLD) &&
                                       wipQueryService.isValidOcapHold(lot.getLotRrn(),
                                                                       MapUtils.getString(holdReason, "reason")),
                               Errors.create().content("Unable to release OCAP Hold!").build());

                Assert.isFalse(HoldCodeNames.AUTO_MERGE_HOLD_KEY.equalsIgnoreCase(
                                       MapUtils.getString(holdReason, "reasonCode")),
                               Errors.create().content("Can't release AUTO_MERGE_HOLD!").build());

                boolean hasModule = reasonCode.indexOf(HoldCodeNames.MODULE_QTIMEHOLD_KEY) > -1;
                if (hasModule) {
                    ReferenceFileDetail referenceFileDetail = getReferenceFileDetail(ReferenceDetailNames.HOLD_CODE,
                                                                                     reasonCode,
                                                                                     LocalContext.getFacilityRrn());
                    Assert.nonNull(referenceFileDetail, Errors.create().content(
                            "Please configure option {} in the $HOLD_CODE reference detail").args(reasonCode).build());
                }

                if (StringUtils.equals("TRACKOUTHOLD", MapUtils.getString(holdReason, "reasonCategory"))) {
                    trackOutHoldExisted = true;
                }
                String holdPassword =
                        holdReason.get("holdPassword") != null ? ((String) holdReason.get("holdPassword")).trim() : "";
                String newPassword = newPasswords[i] != null ? newPasswords[i].trim() : "";

                Assert.isTrue(StringUtils.equals(holdPassword, newPassword),
                              Errors.create().content("Invalid Release Password!").build());

                releaseReasons.add(holdReason);
            } else {
                unReleaseReasons.add(holdReason);
            }

        }

        Assert.isFalse(releaseReasons.size() <= 0,
                       Errors.create().key(MessageIdList.LOT_RELEASE_HOLD_EMPTY).content("please select release hold!")
                             .build());

        Assert.isFalse(trackOutHoldExisted == true && unReleaseReasons.size() > 0,
                       Errors.create().key(MessageIdList.LOT_TRACKOUT_HOLD_CANNOT_RELEASE)
                             .content("Before Trackout hold released, Please release other hold first!").build());

        if (trackOutHoldExisted) {
            lotStepChanged = lotService.checkLotStepChange(lot.getLotRrn());
        }

        HashMap releaseInfo = new HashMap();
        releaseInfo.put("lotRrn", new Long(lot.getLotRrn()).toString());
        releaseInfo.put("lotId", lot.getLotId());
        releaseInfo.put("releaseReasons", releaseReasons);
        releaseInfo.put("transPerformedBy", userId);

        String holdCode = StringUtils.EMPTY;
        if (releaseReasons.size() == 1) {
            holdCode = MapUtils.getString(releaseReasons.get(0), "reasonCode") + StringUtils.COMMA_SIGN;
        } else {
            for (Map<String, String> releaseReason: releaseReasons) {
                holdCode += MapUtils.getString(releaseReason, "reasonCode") + StringUtils.COMMA_SIGN;
            }
        }
        StringBuilder stringBuilder = new StringBuilder(
                theform.getReasonGroup() + ",Release Code: " + theform.getReasonCode() + ",Hold Code: " + holdCode +
                        "Reason:");

        releaseInfo.put("transComments", stringBuilder.toString());
        releaseInfo.put("operation", lot.getOperationId());

        TransReason transReason = new TransReason();
        transReason.setReasonCode(theform.getReasonCode());

        transReason.setReason(theform.getLotComments());
        transReason.setTransQty1(lot.getQty1());
        transReason.setTransQty2(lot.getQty2());
        transReason.setResponsibility(userId);
        transReason.setReason(theform.getReason());

        releaseInfo.put("transReason", transReason);
        releaseInfo.put("superFlag", "0");
        // lockLots.add(lot);
        // checkAndCreateLotsTransLock(LocalContext.getUserRrn(), TransactionNames.LOCK_RELEASE_LOT, lockLots,
        //                             "release in LotReleaseAction by " + userId);
        lotService.releaseLot(releaseInfo);
        // removeLotLockWhenTransError(LocalContext.getUserRrn(), lockLots, TransactionNames.LOCK_RELEASE_LOT,
        //                             "release in LotReleaseAction by " + userId);


        //清除派工页面缓存
        Collection availableEquipmentInfos = buildAvailableEquipInfosForLot(lot, userRrn);
        for (Object temp : availableEquipmentInfos) {
            Map equipmentInfo = (Map) temp;
            CacheUtils.remove(MapUtils.getString(equipmentInfo, "equipmentRrn"));
        }

        releaseSpcHold(lot, releaseReasons);

        if (sapphireBondedError || (trackOutHoldExisted && unReleaseReasons.isEmpty() && !lotStepChanged)) {
            //查询最新批次信息
            lot = lotQueryService.getLot(lot.getLotRrn());
            return dispatchLotAfterRelease(mapping, request, response, userId, facilityRrn, lot);
        }

        splitRunCardService.doStartSplitRunCard(LocalContext.getUserId(), lot);
        lotRunCardService.autoMergeRunCardLot(lot.getLotRrn(), LocalContext.getUserId());
        return mapping.findForward(Constants.LOTLOCATION_KEY);
    }

    /**
     * 检查Lot 是否属于RunCard.(重写 LotReleaseAction)
     */
    protected void checkRunCard(Lot lot) {
    }

    /**
     * releasePostFH 拼接参数返回manual/auto工作流 并不是真正的release操作
     **/
    protected ActionForward postFutureReleaseHandle(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                                                    HttpServletResponse response) throws ServletException, IOException {
        HoldReleaseLotInfoForm theform = (HoldReleaseLotInfoForm) form;
        Map map = new HashMap();
        map.put("lotId", theform.getLotId());
        map.put("reasonCode", theform.getReasonCode());
        map.put("lotComments", theform.getLotComments());
        map.put("reason", theform.getReason());
        String fieldFlag = request.getParameter("fieldFlags");
        map.put("fieldFlag", fieldFlag);
        String[] fieldFlags = fieldFlag.split(",");
        List<FutureHoldLot> postFutureHoldLotList = (List<FutureHoldLot>) request.getAttribute("postFutureHoldLotList");
        map.put("postFutureHoldLotList", postFutureHoldLotList);

        Map<String, Object> parameters = wipService.getWorkflowParametersForRelease(map);
        Lot lot = (Lot) MapUtils.getObject(parameters, "lot");
        Job job = (Job) MapUtils.getObject(parameters, "job");
        request.setAttribute(SessionNames.JOB_KEY, job);

        List<Lot> lotList = new ArrayList<>();
        lotList.add(lot);
        List<Map> handleLotInfos = new ArrayList<>();
        Map handleLotInfo = new HashMap<>();
        handleLotInfo.put("Lot", lot);
        handleLotInfo.put("handleInfo", new HashMap<>());
        handleLotInfos.add(handleLotInfo);
        request.setAttribute(SessionNames.HANDLE_LOT_INFO_KEY, handleLotInfos);
        request.setAttribute(SessionNames.COLLECTION_KEY, lotList);
        request.setAttribute(SessionNames.PARAMETERSINFO_KEY, parameters);

        if (WipUtils.AUTO.equals(postFutureHoldLotList.get(0).getAttributeData2())) {
            lotService.updateLotJobRrn(lot.getLotRrn(), job.getJobRrn());
            lotService.doMoveJob(parameters);
            request.getRequestDispatcher(
                           mapping.findForward("jobmanagement4lot").getPath() + "&lotId=" + lot.getLotId())
                   .forward(request, response);
        } else {
            request.getRequestDispatcher(mapping.findForward("workflow").getPath()).forward(request, response);
        }
        return null;
    }

    /**
     * release在主产品debond后被postFH的辅产品lot
     **/
    protected ActionForward bondedLotPostFHReleaseHandle(ActionMapping mapping, ActionForm form,
                                                         HttpServletRequest request, HttpServletResponse response) {
        //release该lot
        HoldReleaseLotInfoForm theform = (HoldReleaseLotInfoForm) form;
        long facilityRrn = LocalContext.getFacilityRrn();
        String lotId = theform.getLotId();
        String userId = LocalContext.getUserId();
        Lot lot = lotQueryService.getLot(lotId, facilityRrn);
        String fieldFlag = request.getParameter("fieldFlags");
        String[] fieldFlags = fieldFlag.split(",");

        //release信息
        List<Map> holdReasons = wipQueryService.getHoldReasons(lot.getLotRrn());
        List<Map> releaseReasons = new ArrayList<>();
        for (int i = 0; i < holdReasons.size(); i++) {
            if ("0".equals(fieldFlags[i])) {
                releaseReasons.add(holdReasons.get(i));
            }
        }

        HashMap releaseInfo = new HashMap();
        releaseInfo.put("lotRrn", new Long(lot.getLotRrn()).toString());
        releaseInfo.put("lotId", lot.getLotId());
        releaseInfo.put("releaseReasons", releaseReasons);
        releaseInfo.put("transPerformedBy", userId);
        releaseInfo.put("transComments", theform.getLotComments());
        releaseInfo.put("operation", lot.getOperationId());

        TransReason transReason = new TransReason();
        transReason.setReasonCode(theform.getReasonCode());
        transReason.setReason(theform.getLotComments());
        transReason.setTransQty1(lot.getQty1());
        transReason.setTransQty2(lot.getQty2());
        transReason.setResponsibility(userId);
        transReason.setReason(theform.getReason());

        releaseInfo.put("transReason", transReason);
        releaseInfo.put("superFlag", "0");
        lotService.releaseLot(releaseInfo);

        FutureHoldLot fhLotInfo = wipService.getFutureHoldLotList(lot.getLotRrn(), EenActionType.POST_FUTURE_HOLD)
                                            .get(0);
        //删除futurehold表的lot信息
        wipService.releaseFutureHoldLot(lot.getLotRrn(), EenActionType.POST_FUTURE_HOLD, releaseInfo);

        //判断是否release掉所有hold 之后再完成跳步
        List<FutureHoldLot> fhLotList = wipService.getFutureHoldLotList(lot.getLotRrn(),
                                                                        EenActionType.POST_FUTURE_HOLD);
        if (CollectionUtils.isEmpty(fhLotList)) {
            //被bond批次跳步到debond工步 更新被bond批次信息
            List<HoldDebondLot> holdDebondLot = wipService.getHoldDebondLotBySourceLotRrn(lot.getLotRrn());
            Long debondToStepRrn = holdDebondLot.get(0).getDebondToStepRrn();
            Map nodeMap = wipQueryService.getNodeInfoByWflTree(lot, debondToStepRrn, false);
            String routeId = MapUtils.getString(nodeMap, "routeId");
            lot.setRouteId(routeId);
            long routeRrn = MapUtils.getLongValue(nodeMap, "routeRrn");
            lot.setRouteRrn(new Long(routeRrn));
            String operationId = MapUtils.getString(nodeMap, "operationId");
            lot.setOperationId(operationId);
            long operationRrn = MapUtils.getLongValue(nodeMap, "operationRrn");
            lot.setOperationRrn(new Long(operationRrn));
            String stepPath = MapUtils.getString(nodeMap, "stepInfo");

            Map sourceLotMap = builDebondLot(LocalContext.getFacilityRrn(), userId, lot, stepPath);
            lotService.changeLotProcess(sourceLotMap);

            //删除hold_debonded_lot信息
            wipService.deleteHoldDebondLotBySourceLot(lot.getLotRrn());
        }

        return mapping.findForward(Constants.LOTLOCATION_KEY);
    }

    //判断是否有母批。解除spcHold
    protected void releaseSpcHold(Lot lot, List<Map> releaseReasons) {

        if (lot.getLotRrn() != lot.getBasedLotRrn()) {
            int countPilotLot = wipQueryService.countPilotLotInfo(lot.getLotRrn());
            if (countPilotLot > 0) {
                PilotLotInfo pilotLotInfo = wipQueryService.getPilotLotInfo(lot.getLotRrn());
                if (StringUtils.equals(pilotLotInfo.getAutoReleaseSpcHold(), PilotContextValue.YES)) {
                    Lot baseLot = lotQueryService.getLot(lot.getBasedLotRrn());

                    List<Map> baseHoldReasons = wipQueryService.getHoldReasons(baseLot.getLotRrn());

                    List<Map> baseReleaseReasons = new ArrayList<>();
                    String childLot = "";
                    if (StringUtils.equalsIgnoreCase("CN", I18nUtils.getCurrentLanguage().toString())) {
                        childLot = "子批:";
                    } else {
                        childLot = "childLot:";
                    }
                    for (Map childHoldReasonsMap : releaseReasons) {
                        String childReasonCode = MapUtils.getString(childHoldReasonsMap, "reasonCode");
                        String childReason = MapUtils.getString(childHoldReasonsMap, "reason");
                        for (Map holdReasonsMap : baseHoldReasons) {
                            String reasonCode = MapUtils.getString(holdReasonsMap, "reasonCode");
                            String reason = MapUtils.getString(holdReasonsMap, "reason");
                            String childLotId = StringUtils.substringAfter(reason, childLot);
                            reason = StringUtils.substringBefore(reason, childLot);
                            if (StringUtils.equals(reason, childReason) &&
                                    StringUtils.equals(reasonCode, childReasonCode) &&
                                    StringUtils.equals(lot.getLotId(), childLotId)) {
                                baseReleaseReasons.add(holdReasonsMap);
                            }
                        }
                    }

                    if (baseReleaseReasons.size() > 0) {
                        String spcMsg = "";
                        if (StringUtils.equalsIgnoreCase("CN", I18nUtils.getCurrentLanguage().toString())) {
                            spcMsg = "先行片释放SPC HOLD,自动释放母批SPC HOLD";
                        } else {
                            spcMsg = "The SPC HOLD is released, and the parent batch SPC HOLD is " +
                                    "automatically released.";
                        }
                        HashMap baseReleaseInfo = new HashMap();
                        baseReleaseInfo.put("lotRrn", new Long(baseLot.getLotRrn()).toString());
                        baseReleaseInfo.put("lotId", baseLot.getLotId());
                        baseReleaseInfo.put("releaseReasons", releaseReasons);
                        baseReleaseInfo.put("transPerformedBy", "SYSTEM");
                        baseReleaseInfo.put("transComments", spcMsg);
                        baseReleaseInfo.put("operation", baseLot.getOperationId());

                        TransReason baseTransReason = new TransReason();
                        baseTransReason.setReasonCode("RELEASESPCHOLD");
                        baseTransReason.setReason(spcMsg);
                        baseTransReason.setTransQty1(baseLot.getQty1());
                        baseTransReason.setTransQty2(baseLot.getQty2());
                        baseTransReason.setResponsibility("SYSTEM");

                        baseReleaseInfo.put("transReason", baseTransReason);
                        baseReleaseInfo.put("superFlag", "0");

                        lotService.releaseLot(baseReleaseInfo);
                    }
                }
            }

        }
    }

    protected ActionForward dispatchLotAfterRelease(ActionMapping mapping, HttpServletRequest request,
                                                    HttpServletResponse response, String user, Long facilityRrn,
                                                    Lot lot) {
        List lots = new ArrayList();
        List<Lot> lockLots = new ArrayList<Lot>();
        lockLots.add(lot);
        lots.add(new Long(lot.getLotRrn()));
        Job job = new Job();
        job.setJobComments("");
        job.setJobStatus(JobStatus.WAITING);
        job.setTransPerformedby(user);
        //批次出站暂停-点击终止后会清空eqptRrn,现在从批次历史中拿到eqptRrn
        if (lot.getEqptRrn() == null || lot.getEqptRrn() == 0L) {
            List lotHistory = lotQueryService.qryLotHistoryExp(lot.getLotRrn());
            if (lotHistory != null && !lotHistory.isEmpty()) {
                int stepSequence = 0;
                long eqptRrn = 0L;
                for (Iterator iterator = lotHistory.iterator(); iterator.hasNext(); ) {
                    HashMap object = (HashMap) iterator.next();
                    Collection operationList = (Collection) object.get(object.get("operationRrn"));
                    for (Iterator iterator2 = operationList.iterator(); iterator2.hasNext(); ) {
                        HashMap lotTransmap = (HashMap) iterator2.next();
                        int tempStepSequence = MapUtils.getIntValue(lotTransmap, "step_sequence");
                        long tempEqptRrn = MapUtils.getLongValue(lotTransmap, "eqpt_rrn", 0L);
                        if (tempStepSequence > stepSequence && tempEqptRrn > 0) {
                            eqptRrn = tempEqptRrn;
                        }
                    }
                    lot.setEqptRrn(eqptRrn);
                }
            }
        }
        job.setEqptRrn(lot.getEqptRrn());
        job.setOperationRrn(lot.getOperationRrn());
        // checkAndCreateLotsTransLock(LocalContext.getUserRrn(), TransactionNames.LOCK_MOVENEXT, lots,
        //                             "dispatchLotAfterRelease in HoldReleaseLotSaveAction");
        job = wipService.createJob(job, lots);

        HashMap parameters = new HashMap();
        parameters.put("eqptRrn", lot.getEqptRrn().toString());
        parameters.put("operationRrn", lot.getOperationRrn().toString());
        long wflRrn = getInstanceRrn("MOVEOUT_AFTER_RELEASE_STD", facilityRrn, ObjectList.WFL_KEY);
        parameters.put("recipeRrn", job.getRecipeString());
        parameters.put("jobRrn", job.getJobRrn() + "");
        parameters.put("WFL_RRN", wflRrn + "");
        parameters.put("RUNSTEP_FLAG", "1");
        parameters.put("ROOT_WFL_EXEC_RRN", lot.getExecutionRrn().toString());
        parameters.put("PARENT_WFL_EXEC_RRN", "0");
        parameters.put("RUNPARENTSTEP_FLAG", "0");
        parameters.put("WFL_EXEC_RRN", "0");

        if (((String) parameters.get("WFL_EXEC_RRN")).equals("") ||
                ((String) parameters.get("WFL_EXEC_RRN")).equals("0")) {
            long _workflowRrn = MiscUtils.parseSQL(new Long((String) parameters.get("WFL_RRN")));

            String _multiRootInstanceRrn = (String) parameters.get("ROOT_WFL_EXEC_RRN");
            long _parentInstanceRrn = MiscUtils.parseSQL(new Long((String) parameters.get("PARENT_WFL_EXEC_RRN")));

            long executionRrn = -1;

            executionRrn = workflowEngineService.createExecutionRrn(_workflowRrn, _multiRootInstanceRrn,
                                                                    _parentInstanceRrn, null, parameters);
            parameters.put("WFL_EXEC_RRN", executionRrn + "");
        }

        request.setAttribute("job", job);
        request.setAttribute("parametersInfo", parameters);
        request.removeAttribute("release4moveoutafter");
        WebUtils.dispatch(request, response, mapping.findForward("workflow").getPath());
        return WebUtils.NULLActionForward;
    }

    /**
     * Release Running 处理
     */
    protected ActionForward releaseRunningHandle(ActionMapping mapping, ActionForm form, HttpServletRequest request,
                                                 HttpServletResponse response) {
        HoldReleaseLotInfoForm theform = (HoldReleaseLotInfoForm) form;

        boolean superReleaseFlag = StringUtils.isNotEmpty((String) request.getParameter("superrelease"));
        long facilityRrn = LocalContext.getFacilityRrn();
        String lotId = theform.getLotId();
        long userRrn = LocalContext.getUserRrn();
        String userId = LocalContext.getUserId();
        String[] newPasswords = request.getParameterValues("newPassword");
        String fieldFlag = request.getParameter("fieldFlags");
        String[] fieldFlags = fieldFlag.split(",");
        // List<Lot> lockLots = new ArrayList<Lot>();
        Lot lot = lotQueryService.getLot(lotId, facilityRrn);
        Assert.isFalse(lot.getLotRrn() <= 0,
                       Errors.create().key(MessageIdList.LOT_LOTRRN_NOT_FOUND).content("Cannot Find Lot!").build());

        Assert.isTrue(StringUtils.equals(lot.getLotStatus(), LotStatus.RUNNINGHOLD),
                      Errors.create().key(MessageIdList.LOT_STATUS_NOT_ALLOW).content("Lot status not allow").build());

        List<Map> holdReasons = wipQueryService.getHoldReasons(lot.getLotRrn());
        List<Map> releaseReasons = new ArrayList();
        List<Map> unReleaseReasons = new ArrayList();

        //是否存在trackOutHold释放
        boolean trackOutHoldExisted = false;
        //工步变更检查flag
        boolean lotStepChanged = false;
        // 是否存在Bonding绑定错误
        boolean sapphireBondedError = false;
        for (int i = 0; i < holdReasons.size(); i++) {
            Map holdReason = holdReasons.get(i);
            String reasonCode = MapUtils.getString(holdReason, "reasonCode");

            if (StringUtils.equals(TransReason.reasonCode.BONDERROR.name(), reasonCode)) {
                sapphireBondedError = true;
            }

            if (StringUtils.equals("0", fieldFlags[i])) {
                if (StringUtils.contains(reasonCode, HoldCodeNames.RRCHOLD_KEY)) {
                    List<LotRunCard> runcardList = new ArrayList<LotRunCard>();
                    runcardList = cardService.getActivedLotRuncardByLot(lot.getLotRrn());

                    for (LotRunCard runcard : runcardList) {
                        Assert.isFalse(StringUtils.equals(lot.getFlowSeq(), runcard.getSplitFlowSeq()) &&
                                               StringUtils.contains(MapUtils.getString(holdReason, "reason"),
                                                                    getInstanceId(runcard.getRunCardRrn())),
                                       Errors.create().key(MessageIdList.LOT_RELEASE_RRCHOLD_DENY)
                                             .content("Can't release RRCHOLD").build());
                    }
                }

                // 增加ocap hold卡控
                if(StringUtils.contains(reasonCode, HoldCodeNames.OCAP_HOLD)) {
                    Assert.state(
                            !wipQueryService.isValidOcapHold(lot.getLotRrn(), MapUtils.getString(holdReason, "reason")),
                            Errors.create().content("Unable to release OCAP Hold!").build());

                    String ocapId = StringUtils.substringAfter(MapUtils.getString(holdReason, "reason"), "OCAP ID: ");
                    OcapCard ocapCard = wipQueryService.getOcapCardByUniqueKey(LocalContext.getFacilityRrn(), ocapId, null);
                    if (Objects.nonNull(ocapCard) && Objects.nonNull(ocapCard.getOcapMainLotRrn()) &&
                            ocapCard.getOcapMainLotRrn().longValue() != lot.getLotRrn() &&
                            StringUtils.equals(VersionStatus.ACTIVE_KEY, ocapCard.getStatus())) {
                        Lot mainLot = lotInqService.getLot(ocapCard.getOcapMainLotRrn());
                        EdcLotContextValue dto = ctxExecService.getEdcLotContextValue(mainLot, null, ActionPointList.MOVEOUT_KEY);
                        Long parameterSetRrn = 0L;
                        if (dto != null && dto.getParameterSetRrn() > 0) {
                            parameterSetRrn = dto.getParameterSetRrn();
                        }

                        dto = ctxExecService.getEdcLotContextValue(lot, null, ActionPointList.MOVEOUT_KEY);
                        Assert.state(dto == null || dto.getParameterSetRrn().longValue() <= 0 ||
                                             dto.getParameterSetRrn().longValue() != parameterSetRrn.longValue(),
                                     Errors.create().content("need operator ocap main lot:{}").args(mainLot.getLotId()).build());
                    }
                }

                if (StringUtils.equals("TRACKOUTHOLD", MapUtils.getString(holdReason, "reasonCategory"))) {
                    trackOutHoldExisted = true;
                }
                String holdPassword =
                        holdReason.get("holdPassword") != null ? ((String) holdReason.get("holdPassword")).trim() : "";
                String newPassword = newPasswords[i] != null ? newPasswords[i].trim() : "";

                Assert.isTrue(StringUtils.equals(holdPassword, newPassword),
                              Errors.create().content("Invalid Release Password!").build());

                releaseReasons.add(holdReason);
            } else {
                unReleaseReasons.add(holdReason);
            }
        }

        Assert.isFalse(releaseReasons.size() <= 0,
                       Errors.create().key(MessageIdList.LOT_RELEASE_HOLD_EMPTY).content("please select release hold!")
                             .build());

        Assert.isFalse(trackOutHoldExisted && unReleaseReasons.size() > 0,
                       Errors.create().key(MessageIdList.LOT_TRACKOUT_HOLD_CANNOT_RELEASE)
                             .content("Before Trackout hold released, Please release other hold first!").build());

        if (trackOutHoldExisted) {
            lotStepChanged = lotService.checkLotStepChange(lot.getLotRrn());
        }

        TransReason transReason = new TransReason();
        transReason.setReasonCode(theform.getReasonCode());
        transReason.setReason(theform.getLotComments());
        transReason.setResponsibility(userId);
        transReason.setReason(theform.getReason());

        String holdCode = StringUtils.EMPTY;
        if (releaseReasons.size() == 1) {
            holdCode = MapUtils.getString(releaseReasons.get(0), "reasonCode") + StringUtils.COMMA_SIGN;
        } else {
            for (Map<String, String> releaseReason: releaseReasons) {
                holdCode += MapUtils.getString(releaseReason, "reasonCode") + StringUtils.COMMA_SIGN;
            }
        }
        StringBuilder stringBuilder = new StringBuilder(
                theform.getReasonGroup() + ",Release Code: " + theform.getReasonCode() + ",Hold Code: " + holdCode +
                        "Reason: " + theform.getReason());
        transReason.setReason(stringBuilder.toString());

        List<LotReleaseInfoDto> releaseInfoDtoList = new ArrayList<>();
        releaseInfoDtoList.add(buildLotReleaseInfoList(lot, releaseReasons, theform.getLotComments(), transReason));
        if (CollectionUtils.isEmpty(unReleaseReasons)) {
            List<BatchLotStore> batchLotStoreList = diffBatchQueryService.getBatchStoreListByLot(lot.getLotRrn());
            for (BatchLotStore batchLotStore : batchLotStoreList) {
                if (lot.getLotRrn() == batchLotStore.getLotRrn().longValue()) {
                    continue;
                }
                Lot batchLot = lotQueryService.getLot(batchLotStore.getLotRrn());
                List<Map> batchReleaseReasonList = wipQueryService.getHoldReasons(batchLot.getLotRrn());
                LotReleaseInfoDto releaseInfoDto = buildLotReleaseInfoList(batchLot, batchReleaseReasonList,
                                                                           theform.getLotComments(), transReason);
                releaseInfoDtoList.add(releaseInfoDto);
            }
        }

        List<String> lotRrns = new ArrayList<>();
        for (LotReleaseInfoDto releaseInfoDto : releaseInfoDtoList) {
            lotRrns.add(StringUtils.toString(releaseInfoDto.getLot().getLotRrn()));
        }

        lotService.releaseRunningLot(releaseInfoDtoList, lotRrns);

        //清除派工页面缓存
        Collection availableEquipmentInfos = buildAvailableEquipInfosForLot(lot, userRrn);
        for (Object temp : availableEquipmentInfos) {
            Map equipmentInfo = (Map) temp;
            CacheUtils.remove(MapUtils.getString(equipmentInfo, "equipmentRrn"));
        }
        //TODO CacheHelper相关更新
        recoveryRunCardService.doStartRecoveryRunCard(LocalContext.getUserId(), lot);
        return mapping.findForward(Constants.LOTLOCATION_KEY);
    }

    protected void checkRCHoldCode(String holdCode) {
        Assert.isFalse(StringUtils.contains(holdCode, HoldCodeNames.SRCHOLD_KEY),
                       Errors.create().content("Hold Code can not contains '{}'").args(HoldCodeNames.SRCHOLD_KEY)
                             .build());

        Assert.isFalse(StringUtils.contains(holdCode, HoldCodeNames.RRCHOLD_KEY),
                       Errors.create().content("Hold Code can not contains '{}'").args(HoldCodeNames.RRCHOLD_KEY)
                             .build());
    }

    protected List<Map<String, Object>> getWflPathoperation(Lot lot) throws Exception {
        Integer routeVersion = lot.getRouteVersion();

        WorkflowStepModel stepModel = new WorkflowStepModel(lot.getRouteRrn(), routeVersion);

        stepModel.setStepId(lot.getOperationId());

        return prpService.getWflPath(stepModel.getWflRrn(), stepModel.getWflVersion(), stepModel.getStepId());
    }

    protected List<Map<String, Object>> getWflPathRoute(Lot lot) {
        long workflowRrn = getInstanceRrn(lot.getProcessId(), lot.getFacilityRrn(), ObjectList.WFL_KEY);

        int workflowVersion = baseService.getActiveVersion(workflowRrn, true);

        WorkflowStepModel stepModel = new WorkflowStepModel(workflowRrn, workflowVersion);

        stepModel.setStepId(lot.getRouteId());

        return prpService.getWflPath(stepModel.getWflRrn(), stepModel.getWflVersion(), stepModel.getStepId());
    }

    protected String buildReticlesString(Long reticleFamilyRrn) {
        StringBuilder rectileInfo = new StringBuilder();

        List<Relation> rectiles = reticleService.getReticles(reticleFamilyRrn);
        for (Relation relation : rectiles) {
            rectileInfo.append(relation.getInstanceId()).append(" ");
        }

        return rectileInfo.toString();
    }

    protected ActionForward showRunningHoldInfoDetail(ActionMapping mapping, HttpServletRequest request, long jobRrn) {
        List holdInfos = new ArrayList<>();
        List<Map> joblots = wipQueryService.getJobList(jobRrn, ObjectList.JOB_KEY);

        for (Map lotMap : joblots) {
            if (StringUtils.equalsIgnoreCase(lotQueryService.getLotStatus(MapUtils.getLongValue(lotMap, "lotRrn")),
                                             LotStatus.RUNNINGHOLD)) {
                holdInfos.addAll(wipQueryService.getHoldReasons(MapUtils.getLongValue(lotMap, "lotRrn")));
            }
        }
        request.setAttribute("holdInfo", holdInfos);
        request.setAttribute(SessionNames.COLLECTION_KEY, joblots);
        return mapping.findForward("runningHold");
    }

    protected List<Map> rePositionUnitList(List<Map> unitList) {
        List<Map> newUnits = new ArrayList<Map>(unitList);
        Collections.sort(newUnits, new Comparator<Map>() {
            @Override
            public int compare(Map map1, Map map2) {
                int position1 = MapUtils.getIntValue(map1, "position");
                int position2 = MapUtils.getIntValue(map2, "position");
                if (position1 > position2) {
                    return 1;
                } else if (position1 < position2) {
                    return -1;
                } else {
                    return 0;
                }
            }
        });
        Iterator it = newUnits.iterator();
        Map unit = new HashMap();
        int number = 1;
        int seq = 1;
        List<Map> units = new ArrayList<Map>();
        while (it.hasNext()) {
            Map<String, Object> unitMap = null;
            unit = (HashMap) it.next();
            seq = MapUtils.getIntValue(unit, "position");
            while ((number < seq)) {
                unitMap = new HashMap();
                unitMap.put("position", number + "");
                unitMap.put("unitId", "");
                units.add(unitMap);
                number++;
            }
            unit.put("position", number + "");
            units.add(unit);
            number++;
        }
        int listSize = units.size();
        for (int i = listSize + 1; i <= 25; i++) {
            Map<String, Object> unitMap = new HashMap();
            unitMap.put("position", i + "");
            unitMap.put("unitId", "");
            units.add(unitMap);
        }
        return units;
    }

    protected void checkPostFutureHold(long lotRrn) {
        boolean postFutureHold = false;
        List<FutureHoldLot> futureHoldLotList = wipService.getFutureHoldLotList(lotRrn, EenActionType.POST_FUTURE_HOLD);
        if (CollectionUtils.isNotEmpty(futureHoldLotList)) {
            postFutureHold = true;
        }
        Assert.isFalse(postFutureHold,
                       Errors.create().key(MessageIdList.LOTS_STATUS_POST_FUTURE_HOLD).content("Lot status not allow")
                             .build());
    }

    /**
     * 未执行的任务 检查源晶舟和目标晶舟是否存在未执行的Sort任务
     */
    protected void checkWaitJobs(long sourceCarrierRrn, long targetCarrierRrn, Long targetCarrierRrn2,
                                 TransformFunc error) {
        Assert.isFalse(sorterQueryService.haveWaitJobForCarrier(sourceCarrierRrn),
                       error != null ? error : Errors.create().key(MessageIdList.SOURCE_CARRIER_BE_OCCUPIED).build());
        if (targetCarrierRrn > 0) {
            Assert.isFalse(sorterQueryService.haveWaitJobForCarrier(targetCarrierRrn),
                           error != null ? error : Errors.create().key(MessageIdList.TARGET_CARRIER_BE_OCCUPIED)
                                                         .build());
        }
        if (targetCarrierRrn2 != null && targetCarrierRrn2 > 0) {
            Assert.isFalse(sorterQueryService.haveWaitJobForCarrier(targetCarrierRrn2),
                           error != null ? error : Errors.create().key(MessageIdList.TARGET_CARRIER_BE_OCCUPIED)
                                                         .build());
        }
    }

    protected Map builDebondLot(Long facilityRrn, String userId, Lot lot, String stepPath) {
        Map tempInfo = new HashMap();
        Map changeProcessInfo = new HashMap();
        changeProcessInfo.put("lotRrn", new Long(lot.getLotRrn()));
        changeProcessInfo.put("lotId", lot.getLotId());
        changeProcessInfo.put("lotStatus", lot.getLotStatus());
        changeProcessInfo.put("qty", "" + lot.getQty1());
        changeProcessInfo.put("lotOwner", lot.getLotOwner());
        changeProcessInfo.put("currOperation", lot.getOperationId());
        changeProcessInfo.put("transPerformedBy", userId);
        changeProcessInfo.put("productRrn", new Long(
                getInstanceRrn(lot.getProductId(), getNamedSpace(ObjectList.PRODUCT_KEY, facilityRrn),
                               ObjectList.PRODUCT_KEY)));
        changeProcessInfo.put("productId", lot.getProductId());

        changeProcessInfo.put("processRrn", lot.getProcessRrn());
        Lot _lot = new Lot();
        _lot.setProductRrn(MapUtils.getLong(changeProcessInfo, "productRrn"));
        tempInfo = getStepsInfoByLot(_lot, lot.getProcessRrn(), stepPath);
        changeProcessInfo.put("stageId", tempInfo.get("stageId"));
        changeProcessInfo.put("layerId", tempInfo.get("layerId"));
        changeProcessInfo.put("executionRrn", tempInfo.get("executionRrn"));
        changeProcessInfo.put("operationRrn", tempInfo.get("operationRrn"));

        Long operationRrn = (Long) tempInfo.get("operationRrn");
        changeProcessInfo.put("operationId", getInstanceId(((Long) tempInfo.get("operationRrn")).longValue()));

        changeProcessInfo.put("operationVer", tempInfo.get("operationVer"));
        changeProcessInfo.put("processStepVersion", tempInfo.get("processStepVersion"));
        changeProcessInfo.put("processVersion", tempInfo.get("processVersion"));

        changeProcessInfo.put("processStepIdVersion", tempInfo.get("processStepIdVersion"));
        changeProcessInfo.put("processStepVersion4wfl", tempInfo.get("processStepVersion4wfl"));
        changeProcessInfo.put("nextoperationRrn1", tempInfo.get("nextoperationRrn1"));
        changeProcessInfo.put("nextoperationRrn2", tempInfo.get("nextoperationRrn2"));
        changeProcessInfo.put("nextprocessStepVersion1", tempInfo.get("nextprocessStepVersion1"));
        changeProcessInfo.put("nextprocessStepIdVersion1", tempInfo.get("nextprocessStepIdVersion1"));
        changeProcessInfo.put("nextprocessStepVersion2", tempInfo.get("nextprocessStepVersion2"));
        changeProcessInfo.put("nextprocessStepIdVersion2", tempInfo.get("nextprocessStepIdVersion2"));
        String temp = (String) tempInfo.get("processStepVersion");
        String route = "";

        if (temp != null) {
            int i = 0;
            int j = 0;
            i = temp.indexOf("|");

            // find first '|'
            if (i != -1) {
                j = temp.indexOf("|", i + 1);

                // find se '|'
                // string is aaa|bbb|cccc
                if (j > 0) {
                    route = temp.substring(i + 1, j);
                } else if (i > 0) { // aaa|bbb
                    route = temp.substring(0, i);
                }
            }
        }
        route = route.replace(',', '.');
        changeProcessInfo.put("route", route);

        tempInfo = prpService.getOperationInfo((Long) tempInfo.get("operationRrn"),
                                               (Integer) tempInfo.get("operationVer"));
        changeProcessInfo.put("trackUnitFlag", tempInfo.get("trackUnitFlag"));
        changeProcessInfo.put("keepUnitHistoryFlag", tempInfo.get("keepUnitHistoryFlag"));

        // get Context info
        tempInfo = new HashMap();
        tempInfo.put("productRrn", changeProcessInfo.get("productRrn"));
        // tempInfo.put("routeId", null);
        tempInfo.put("routeRrn", WipUtils.parseRouteRrn((String) changeProcessInfo.get("processStepVersion")) + "");
        tempInfo.put("recipeRrn", null);
        tempInfo.put("operationRrn", changeProcessInfo.get("operationRrn"));
        tempInfo.put("technologyRrn", changeProcessInfo.get("processRrn"));
        tempInfo.put("lotRrn", changeProcessInfo.get("lotRrn"));

        Map resultInfo = ctxExecService.getBorbyContext(tempInfo);

        changeProcessInfo.put("borRrn", resultInfo.get("borRrn"));

        changeProcessInfo.put("reticleRrn", tempInfo.get("reticleRrn")); // to do

        changeProcessInfo.put("transPerformedBy", userId);
        changeProcessInfo.put("transComments", "");
        changeProcessInfo.put("sourceLot", lot);

        changeProcessInfo.put("facilityRrn", facilityRrn);

        return changeProcessInfo;
    }

    /**
     * Check flip for lot and flow
     *
     * @param lot 批次
     * @param psi 流程上的情境值
     */
    protected void checkFlip4LotAndFlow(Lot lot, ProcessSpecItemDto psi) {
        String flipType = Objects.isNull(psi) ? StringUtils.EMPTY : psi.getFlipType();

        // A 可以跳A,A to B, A to B 可以跳 A
        boolean state = StringUtils.equalsIgnoreCase(lot.getFlipType(), flipType) ||
                StringUtils.startsWith(flipType, lot.getFlipType()) ||
                StringUtils.startsWith(lot.getFlipType(), flipType);

        Assert.state(state, Errors.create().key(MessageIdList.LOT_FLOW_NOT_SAME).content(
                                          "Flip attribute {} of Lot {} is inconsistent with Flip attribute {} of " +
                                                  "corresponding work step on target Flow!").args(lot.getFlipType(),
                                                                                                  lot.getLotId(),
                                                                                                  flipType)
                                  .build());
    }

    protected Boolean checkLotIsDummyFlagLot(Long lotRrn) {
        return lotAutoMonitorInqService.checkLotIsAutoMonitorLot(lotRrn) ||
                lotRunCardQueryService.checkLotInRunCard(lotRrn);
    }

    protected SorterModel checkSorterModelAndCreateInLineSorterJob(Lot lot, Equipment equipment){
        return sorterExecInqService.checkSorterModelAndCreateInLineSorterJob(lot,
                                                                             equipment.getInstanceId(), null);
    }

    protected void endSortJob(SortJobBean sortJobBean){
        sorterService.endSortJob(sortJobBean);
    }

    protected SortJobBean getSortJobListByCarrierRrn(Lot lot){
        return sorterQueryService.getSortJobListByCarrierRrn(lot.getCarrierRrn());
    }

    protected void checkCstRemoved(Set<Long> lotRrns){
        lotService.checkCstRemoved(lotRrns);
    }
}