CarrierManagerImpl.java

package com.mycim.server.carrier.manager.impl;

import com.fa.sesa.exception.Assert;
import com.fa.sesa.exception.Errors;
import com.fa.sesa.exception.SystemIllegalArgumentException;
import com.fa.sesa.threadlocal.LocalContext;
import com.mycim.framework.jdbc.Page;
import com.mycim.framework.oid.IDGenerators;
import com.mycim.framework.oid.type.IDNames;
import com.mycim.framework.oid.type.SequenceNames;
import com.mycim.framework.utils.beans.BeanUtils;
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.server.base.manager.EventManager;
import com.mycim.server.base.manager.NamedObjectManager;
import com.mycim.server.base.manager.TransactionLogManager;
import com.mycim.server.carrier.dao.CarrierDAO;
import com.mycim.server.carrier.dao.DoorDAO;
import com.mycim.server.carrier.dao.PodDAO;
import com.mycim.server.carrier.manager.CarrierManager;
import com.mycim.server.carrier.manager.DoorManager;
import com.mycim.server.carrier.manager.PcdManager;
import com.mycim.server.carrier.manager.PodManager;
import com.mycim.server.ctx.exec.manager.ProcessLocationContextManager;
import com.mycim.server.ems.manager.EntityManager;
import com.mycim.server.reticle.manager.LocationInqManager;
import com.mycim.server.status.manager.StatusManager;
import com.mycim.server.system.manager.ReferenceFileManager;
import com.mycim.server.wip.manager.*;
import com.mycim.utils.CheckRegexUtils;
import com.mycim.utils.WipUtils;
import com.mycim.valueobject.LotConstants;
import com.mycim.valueobject.MessageIdList;
import com.mycim.valueobject.ObjectList;
import com.mycim.valueobject.SystemConstant;
import com.mycim.valueobject.bas.NamedObject;
import com.mycim.valueobject.bas.TransactionLog;
import com.mycim.valueobject.consts.*;
import com.mycim.valueobject.ems.Carrier;
import com.mycim.valueobject.ems.PcdAssembly;
import com.mycim.valueobject.ems.PcdClean;
import com.mycim.valueobject.runcard.util.RunCardConstants;
import com.mycim.valueobject.runcard.util.RunCardUtils;
import com.mycim.valueobject.wip.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author yanbing.chen
 * @date 2019/10/10
 * @since 1.8
 **/
@Service
@Transactional
public class CarrierManagerImpl implements CarrierManager {

    private static final String CSTTYPE_SEPERATER_KEY = "-";

    private static final Pattern PCDPATERN = Pattern.compile("(\\w{1,}\\-\\w{1,})");

    private static final Long SLOTMAXCOUNT = new Long(25);

    @Autowired
    CarrierDAO carrierDAO;

    @Autowired
    DoorDAO doorDAO;

    @Autowired
    PodDAO podDAO;

    @Autowired
    NamedObjectManager objectManager;

    @Autowired
    TransactionLogManager transactionLogManager;

    @Autowired
    EntityManager entityManager;

    @Autowired
    PcdManager pcdManager;

    @Autowired
    StatusManager statusManager;

    @Autowired
    EventManager eventManager;

    @Autowired
    PodManager podManager;

    @Autowired
    DoorManager doorManager;

    @Autowired
    LotManager lotManager;

    @Autowired
    LotQueryManager lotQueryManager;

    @Autowired
    ReferenceFileManager referenceFileManager;

    @Autowired
    ProcessLocationContextManager processLocationContextManager;

    @Autowired
    UnitQueryManager unitQueryManager;

    @Autowired
    RunCardQueryManager runCardQueryManager;

    @Autowired
    LotInqManager lotInqManager;

    @Autowired
    LocationInqManager locationInqManager;

    public void insertCarrierMappingH(Long transRrn, Long carrierMappingRrn) {
        carrierDAO.insertCarrierMappingH(transRrn, carrierMappingRrn);
    }

    /**
     * 交换晶舟
     *
     * @param transRrn        事务号
     * @param transSeq        事务seq
     * @param lot             晶舟上的批次
     * @param newCarrierRrn   新晶舟RRN
     * @param carrierMappings 批次中的晶圆
     */
    @Override
    public void exchangeCarrier(Long transRrn, Long transSeq, Lot lot, Long newCarrierRrn, List<Map> carrierMappings) {
        Long oldCarrierRrn = lot.getCarrierRrn();
        String carrierMappingRrn = IDGenerators.get(IDNames.SEQUENCE).generateId(SequenceNames.MODULE_SEQ_OBJECT_RRN);

        // String lotId = getInUseCarrierLotIdList(oldCarrierRrn).iterator().next();
        String lotId = lot.getLotId();
        Carrier carrier = new Carrier(newCarrierRrn);
        String carrierId = getCarrier(carrier).getInstanceId(); // 新晶舟ID

        // update New Carrier
        insertCarrierMapping(transRrn, newCarrierRrn, carrierMappings, Long.valueOf(carrierMappingRrn));
        carrier.setInstanceRrn(oldCarrierRrn);
        carrier = getCarrier(carrier); // 旧晶舟

        // 更新新晶舟数据
        updateCarrier(transRrn, carrier.getPollutionLevel(), newCarrierRrn, Long.valueOf(carrierMappingRrn), lotId, "",
                      PcdStatus.IN_USE_KEY);

        deleteCarrierMapping(transRrn, lot.getCarrierMapRrn());

        // 旧晶舟的处理
        PcdAssembly oldPcdAssembly = pcdManager.getPcdAssembly(oldCarrierRrn);
        String oldCarrierChangeStatus = (oldPcdAssembly == null) ? PcdStatus.FREE_KEY : PcdStatus.ASSEMBLY_KEY;

        if (StringUtils.equalsIgnoreCase(PcdStatus.WAIT_CLEAN_KEY, carrier.getCarrierStatus())) {
            carrierDAO.updateCarrierForInit(oldCarrierRrn, oldCarrierChangeStatus);
            carrierDAO.insertCarrierH(oldCarrierRrn, "Init cassette", carrierId,
                                      new TransactionLog(transRrn, LocalContext.getUserRrn()));
            PcdClean cleanInfo = pcdManager.getPcdCleanInfo(oldCarrierRrn);
            if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                pcdManager.insertPcdCleanH(transRrn, cleanInfo);
            }

            carrierDAO.updateCarrierForInit(oldCarrierRrn, PcdStatus.WAIT_CLEAN_KEY);
            carrierDAO.insertCarrierH(oldCarrierRrn, "Init cassette", carrierId,
                                      new TransactionLog(transRrn, LocalContext.getUserRrn()));
            if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                pcdManager.insertPcdCleanH(transRrn, cleanInfo);
            }
        } else {
            carrierDAO.updateCarrierForInit(oldCarrierRrn, oldCarrierChangeStatus);
            carrierDAO.insertCarrierH(oldCarrierRrn, "Init cassette", carrierId,
                                      new TransactionLog(transRrn, LocalContext.getUserRrn()));
            PcdClean cleanInfo = pcdManager.getPcdCleanInfo(oldCarrierRrn);
            if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                pcdManager.insertPcdCleanH(transRrn, cleanInfo);
            }
        }

        // update Lot And Units
        List<Long> inUseLotRrns = carrierDAO.getInUseCarrierLotRrnList(oldCarrierRrn);
        carrierDAO.updateLotForCarrierRrn(inUseLotRrns, newCarrierRrn);
        carrierDAO.updateUnitForCarrierRrn(oldCarrierRrn, newCarrierRrn);
        carrierDAO.updateUnitForCarrierMapping(carrierMappings);
        lot.setCarrierMapRrn(Long.valueOf(carrierMappingRrn));
        lot.setCarrierRrn(newCarrierRrn);
        createLotTransHistoryForExchanegCarrier(transRrn, transSeq, lot);
    }
    /**
     * 交换晶舟 For Src
     *
     * @param transRrn        事务号
     * @param transSeq        事务seq
     * @param lot             晶舟上的Src批次
     * @param targetCarrierRrn   新晶舟RRN
     * @param carrierMappings 批次中的晶圆
     */
    private void exchangeCarrier4Src(Long transRrn, Long transSeq, Lot lot, Long sourceCarrierRrn, Long sourceCarrierMapRrn, Long targetCarrierRrn, List<Map> carrierMappings, boolean needDealSourceCarrier) {
        String newCarrierMappingStr = IDGenerators.get(IDNames.SEQUENCE).generateId(SequenceNames.MODULE_SEQ_OBJECT_RRN);
        long newCarrierMappingRrn = Long.parseLong(newCarrierMappingStr);

        Carrier targetCarrier = getCarrier(new Carrier(targetCarrierRrn));  // 目标晶舟
        String targetCarrierId = targetCarrier.getInstanceId(); // 目标晶舟的ID
        Carrier sourceCarrier = getCarrier(new Carrier(sourceCarrierRrn)); // 源晶舟

        //1、 将交换后的晶圆数据 以新的carrierMappingRrn插入到 CARRIER_MAPPING 及其历史表中
        insertCarrierMapping(transRrn, targetCarrierRrn, carrierMappings, newCarrierMappingRrn);
        //2、 更新 目标晶舟 数据
        updateCarrier(transRrn, sourceCarrier.getPollutionLevel(), targetCarrierRrn, newCarrierMappingRrn, StringUtils.EMPTY, StringUtils.EMPTY, PcdStatus.IN_USE_KEY);
        //3、 删除原CarrierMapping映射关系的数据(并没有实际删除)
        deleteCarrierMapping(transRrn, sourceCarrierMapRrn);
        //4、 源晶舟的处理
        //MAINRC 和 原LOT使用同一个Cassette,即使MAINRC的UNIT交换出去以后,但是原LOT的UNIT还在,所以不能修改Cassette的状态
        PcdAssembly oldPcdAssembly = pcdManager.getPcdAssembly(sourceCarrierRrn);
        String sourceCarrierChangeStatus = (oldPcdAssembly == null) ? PcdStatus.FREE_KEY : PcdStatus.ASSEMBLY_KEY;
        PcdClean cleanInfo = null;
        if (needDealSourceCarrier) {
            carrierDAO.updateCarrierForInit(sourceCarrierRrn, sourceCarrierChangeStatus);//MAINRC不能执行
            carrierDAO.insertCarrierH(sourceCarrierRrn, "Init cassette", targetCarrierId,
                                      new TransactionLog(transRrn, LocalContext.getUserRrn()));
            cleanInfo = pcdManager.getPcdCleanInfo(sourceCarrierRrn);
            if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                pcdManager.insertPcdCleanH(transRrn, cleanInfo);
            }
        }
        if (StringUtils.equalsIgnoreCase(PcdStatus.WAIT_CLEAN_KEY, sourceCarrier.getCarrierStatus())) {
            if (needDealSourceCarrier) {
                carrierDAO.updateCarrierForInit(sourceCarrierRrn, PcdStatus.WAIT_CLEAN_KEY);//MAINRC不能执行
                carrierDAO.insertCarrierH(sourceCarrierRrn, "Init cassette", targetCarrierId,
                                          new TransactionLog(transRrn, LocalContext.getUserRrn()));
                if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
                    pcdManager.insertPcdCleanH(transRrn, cleanInfo);
                }
            }
        }
        // else {
            // if (needDealSourceCarrier) carrierDAO.updateCarrierForInit(sourceCarrierRrn, sourceCarrierChangeStatus);//MAINRC不能执行
            // carrierDAO.insertCarrierH(sourceCarrierRrn, "Init cassette", targetCarrierId,
            //                           new TransactionLog(transRrn, LocalContext.getUserRrn()));
            // PcdClean cleanInfo = pcdManager.getPcdCleanInfo(sourceCarrierRrn);
            // if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
            //     pcdManager.insertPcdCleanH(transRrn, cleanInfo);
            // }
        // }

        //5、 update SRC Lot And Units
        List<Long> inUseLotRrns = carrierDAO.getInUseCarrierLotRrnList(sourceCarrierRrn);
        //母RC的LOT表中的数据不能修改,子RC的LOT表中的数据需要修改。
        Assert.state(CollectionUtils.isNotEmpty(inUseLotRrns), Errors.create().key(MessageIdList.UNIT_DATA_ERROR).build());
        for (Long lotRrn : inUseLotRrns){
            Lot rcLot = lotQueryManager.getLot(lotRrn);
            if (LotStatus.isRunCardHold(rcLot.getLotStatus())) {
                rcLot = lotQueryManager.getLot(RunCardUtils.buildMainRcLotId(rcLot.getLotId()),
                                               rcLot.getFacilityRrn());
            }
            carrierDAO.updateLotForCarrierRrn(rcLot.getLotRrn(), targetCarrierRrn);
            carrierDAO.updateSrcLotForCarrierRrn(rcLot.getLotRrn(), targetCarrierRrn, targetCarrierId);
        }
        carrierDAO.updateUnitListForCarrierRrn(carrierMappings, sourceCarrierRrn, targetCarrierRrn);//如果是MAINRC,将指定的UNIT的carrierRRN修改,不能连原LOT的一起修改。
        lot.setCarrierMapRrn(newCarrierMappingRrn);
        lot.setCarrierRrn(targetCarrierRrn);
        createLotTransHistoryForExchanegCarrier(transRrn, transSeq, lot);
    }

    @Override
    public Carrier getCarrier(Carrier carrier) {
        NamedObject namedObject = objectManager.getNamedObject(carrier);
        if (namedObject == null || !StringUtils.equalsIgnoreCase(ObjectList.CARRIER_KEY, namedObject.getObjectType())) {
            return null;
        }
        carrier.copyNamedObject(namedObject);

        carrier = carrierDAO.getCarrier(carrier);
        // 补充getInstanceId
        if (carrier.getMcsLastOperatorRrn() != null) {
            carrier.setMcsLastOperator(objectManager.getInstanceId(carrier.getMcsLastOperatorRrn()));
        }
        return carrier;
    }

    @Override
    public Carrier getCarrier(Long facilityRrn, String carrierId) {
        Carrier carrier = getCarrier(
                new Carrier(carrierId, objectManager.getNamedSpace(facilityRrn, ObjectList.FACILITY_KEY),
                            ObjectList.ENTITY_KEY));
        Assert.isTrue(Objects.nonNull(carrier) && carrier.getInstanceRrn() > 0,
                      Errors.create().content("Cassette Id: {} is not Exist").args(carrierId).build());
        return carrier;
    }

    @Override
    public Map<String, Object> getCarrierInfoDetailById(String carrierId, String namedSpace) {
        Map<String, Object> dataMap = carrierDAO.qryCarrierInfoDetailById(carrierId, namedSpace);
        PcdAssembly pcdAssembly = pcdManager.getPcdAssembly(MapUtils.getLong(dataMap, "carrierRrn"), null, null);
        if (pcdAssembly != null) {
            dataMap.put("podId", pcdAssembly.getPodId());
            dataMap.put("doorId", pcdAssembly.getDoorId());

            if (pcdManager.isPcdCleaningTimeIsLessThan3Days(pcdAssembly.getPodRrn())) {
                dataMap.put("podCleanRedFlag", "1");
            }
            if (pcdManager.isPcdCleaningTimeIsLessThan3Days(pcdAssembly.getDoorRrn())) {
                dataMap.put("doorCleanRedFlag", "1");
            }
        }
        if (pcdManager.isPcdCleaningTimeIsLessThan3Days(MapUtils.getLong(dataMap, "carrierRrn"))) {
            dataMap.put("carrierCleanRedFlag", "1");
        }
        return dataMap;
    }

    @Override
    public Map<String, Object> qryCarrierListByPage(Map<String, Object> argMap) {
        int pageSize = 20;
        int startRow = MapUtils.getInteger(argMap, "startRow", 0);
        if (argMap.get("pageSize") != null) {
            pageSize = MapUtils.getInteger(argMap, "pageSize", pageSize);
        }
        if (MapUtils.getLong(argMap, "facility") > 0) {
            argMap.put("namedSpace",
                       objectManager.getNamedSpace(MapUtils.getLong(argMap, "facility"), ObjectList.ENTITY_KEY));
        }

        if (StringUtils.isNotBlank(MapUtils.getString(argMap, "carrierId"))) {
            argMap.put("carrierId",
                       StringUtils.replace(MapUtils.getString(argMap, "carrierId"), "*", "%").toString().toUpperCase());
        }
        // 查询数据集和数据总量
        Page page = new Page((startRow / pageSize) + 1, pageSize);
        page = carrierDAO.getCarrierListByPage(page, argMap);
        int totalCount = (int) page.getTotalItems();
        List<Map> eqptInfoList = (List<Map>) page.getResults();


        for (Map dataMap : eqptInfoList) {
            // 替换location
            String location = MapUtils.getString(dataMap, "location");
            if (StringUtils.isNotBlank(location) && StringUtils.equalsIgnoreCase("$EQPT_LOCATION", location)) {
                dataMap.put("location", "");
            }

            PcdAssembly pcdAssembly = pcdManager.getPcdAssembly(MapUtils.getLong(dataMap, "carrierRrn"), null, null);
            if (pcdAssembly != null) {
                dataMap.put("podId", pcdAssembly.getPodId());
                dataMap.put("doorId", pcdAssembly.getDoorId());

                if (pcdManager.isPcdCleaningTimeIsLessThan3Days(pcdAssembly.getPodRrn())) {
                    dataMap.put("podCleanRedFlag", "1");
                }
                if (pcdManager.isPcdCleaningTimeIsLessThan3Days(pcdAssembly.getDoorRrn())) {
                    dataMap.put("doorCleanRedFlag", "1");
                }
            }
            if (pcdManager.isPcdCleaningTimeIsLessThan3Days(MapUtils.getLong(dataMap, "carrierRrn"))) {
                dataMap.put("carrierCleanRedFlag", "1");
            }
            // carrier query 增加 查询location、pLocation
            String carrierId = MapUtils.getString(dataMap, "carrierId", StringUtils.EMPTY);
            List<Lot> lotList = lotInqManager.getLotListByCarrierId(carrierId);
            if (CollectionUtils.isNotEmpty(lotList)) {
                Set<String> locationIds = new HashSet<>();
                Set<String> pLocationIds = new HashSet<>();
                for (Lot lot : lotList) {
                    locationIds.add(lot.getLocation());
                    pLocationIds.add(lot.getpLocation());
                }
                String locationId = locationIds.stream().collect(Collectors.joining(StringUtils.COMMA_SIGN));
                String pLocationId = pLocationIds.stream().collect(Collectors.joining(StringUtils.COMMA_SIGN));
                dataMap.put("locationId", locationId);
                dataMap.put("pLocationId", pLocationId);
            }
        }

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("data", eqptInfoList);
        dataMap.put("totalCount", totalCount);
        return dataMap;
    }

    @Override
    public Carrier getCarrier(long carrierRrn) {
        return carrierDAO.getCarrier(carrierRrn);
    }

    @Override
    public void deleteCarrierEntity(String carrierId, Long facilityRrn, String user) {
        String namedSpace = objectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY);
        long carrierRrn = 0L;
        carrierRrn = objectManager.getNamedObjectRrn(carrierId, namedSpace, ObjectList.ENTITY_KEY,
                                                     ObjectList.CARRIER_KEY);

        Assert.isFalse(carrierRrn <= 0,
                       Errors.create().key(MessageIdList.CARRIER_CASSETTE_ID_NOT_EXIST).content("Cassette号不存在!")
                             .build());

        // carrier存在,检查carrier当前信息,判断是否可以删除
        Carrier carrier = this.getCarrier(carrierRrn);
        Assert.isFalse(checkCarrierDelete(carrierRrn),
                       Errors.create().key(MessageIdList.CARRIER_IN_HISTORY_CANNOT_DELETE)
                             .content("Carrier cannot delete a wafer that has been loaded").build());

        // 目前判断不是FREE状态不能删除
        if (StringUtils.equalsIgnoreCase(PcdStatus.FREE_KEY, carrier.getCarrierStatus())) {
            // 删除carrier
            carrier.setTransId("DELETE");
            carrier.setCarrierStatus(PcdStatus.DELETED_KEY);
            carrier.setTransPerformedby(user);

            deleteCarrier(carrier);
        } else {
            Assert.isFalse(StringUtils.equalsIgnoreCase(PcdStatus.HOLD_KEY, carrier.getCarrierStatus()),
                           Errors.create().key(MessageIdList.CARRIER_IN_HOLD_STATUS).content("晶舟在hold 状态,不能删除!")
                                 .build());
            throw new SystemIllegalArgumentException(
                    Errors.create().key(MessageIdList.CARRIER_IN_USE_CANNOT_DELETE).content("Carrier正在使用中,不能删除!")
                          .build());
        }
    }

    @Override
    public void deleteCarrier(Long transRrn, Carrier carrier, TransactionLog transactionLog) {
        carrierDAO.insertCarrierH(carrier.getInstanceRrn(), StringUtils.EMPTY, carrier.getInstanceId(), transactionLog);
        carrierDAO.deleteCarrier(carrier);
        long instanceRrn = objectManager.deleteNamedObject(carrier, transRrn);
        pcdManager.deletePcdCleanInfo(carrier.getInstanceRrn(), transRrn);
        //delete entity
        entityManager.deleteEntityOnly(instanceRrn);
        //delete entityDocument
        entityManager.deleteEntityDocs(instanceRrn);
        //delete entityConstraint
        entityManager.deleteEntityConstraints(instanceRrn);
    }

    @Override
    public void deleteCarrier(Carrier carrier) {
        // get the transaction log
        TransactionLog transactionLog = transactionLogManager.startTransactionLog(carrier.getTransPerformedby(),
                                                                                  carrier.getTransId());

        // delete the named object.
        long transRrn = transactionLog.getTransRrn();
        deleteCarrier(transRrn, carrier, transactionLog);

        // insert into Transaction Log
        transactionLogManager.markTransactionLog(transactionLog);
    }

    @Override
    public void addCarrierEntity(Map<String, Object> attrMap, Long facilityRrn, String user) {

        String allowableEventId = MapUtils.getString(attrMap, "carrierAllowableEvent").toUpperCase().trim();
        String engineerGroupId = MapUtils.getString(attrMap, "carrierEngineerGroupID").toUpperCase().trim();
        long allowableEventRrn = objectManager.getNamedObjectRrn(allowableEventId,
                                                                 objectManager.getNamedSpace(facilityRrn,
                                                                                             ObjectList.ALLOWABLEEVENTSET_KEY),
                                                                 ObjectList.ALLOWABLEEVENTSET_KEY.toString());
        long engineerGroupRrn = objectManager.getNamedObjectRrn(engineerGroupId,
                                                                objectManager.getNamedSpace(facilityRrn,
                                                                                            ObjectList.USERGROUP_KEY),
                                                                ObjectList.USERGROUP_KEY);

        Assert.isFalse(allowableEventRrn < 1,
                       Errors.create().key(MessageIdList.EVENT_ALLOWABLE_EVENT_MISSING).content("允许事件集无效!").build());
        Assert.isFalse(StringUtils.isNotBlank(engineerGroupId) && engineerGroupRrn < 1,
                       Errors.create().key(MessageIdList.ENGINEERGROUP_ENGINEER_GROUP_MISSING).content("工程师组无效!")
                             .build());

        Carrier carrier = new Carrier();
        carrier.setAllowableEventsId(allowableEventId);
        carrier.setAllowableEventsRrn(allowableEventRrn);
        carrier.setCarrierCode(MapUtils.getString(attrMap, "carrierColor"));
        carrier.setInstanceId(MapUtils.getString(attrMap, "carrierId").toUpperCase().trim());
        carrier.setInstanceDesc(MapUtils.getString(attrMap, "carrierDesc"));
        carrier.setSlotCount(MapUtils.getLong(attrMap, "carrierSlotCount"));
        carrier.setAvailableSlotCount(MapUtils.getLong(attrMap, "carrierSlotCount"));
        carrier.setInstanceStatus("ACTIVE");
        carrier.setNamedSpace(objectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY));
        carrier.setObject(ObjectList.ENTITY_KEY);
        carrier.setObjectType(ObjectList.CARRIER_KEY);
        carrier.setPollutionLevel(MapUtils.getString(attrMap, "carrierPollutionLevel"));
        carrier.setCarrierStatus(PcdStatus.FREE_KEY);
        carrier.setFlagType(MapUtils.getString(attrMap, "carrierCategory"));

        String locationId = StringUtils.trimToUpperCase(MapUtils.getString(attrMap, "carrierlocation"));
        if (StringUtils.isNotBlank(locationId)) {
            long locationRrn = objectManager.getNamedObjectRrn(locationId, ObjectList.LOCATION_KEY,
                                                               objectManager.getNamedSpace(facilityRrn,
                                                                                           ObjectList.LOCATION_KEY));

            Assert.isFalse(locationRrn <= 0,
                           Errors.create().key(MessageIdList.ENTITY_LOCATION_MISSING).content("位置无效!").build());
            carrier.setLocationRrn(locationRrn);

        }

        carrier.setTransId(TransactionNames.CREATE_KEY);
        carrier.setTransPerformedby(user);
        carrier.setObjectSubtype(MapUtils.getString(attrMap, "carrierType"));
        carrier.setMaintenanceEngineerId(engineerGroupId);
        carrier.setMaintenanceEngineerRrn(engineerGroupRrn);
        carrier.setFacilityRrn(facilityRrn);
        carrier.setCleanCycle(MapUtils.getDoubleValue(attrMap, "carrierCleanCycle"));

        insertCarrier(carrier);
    }

    @Override
    public long insertCarrier(Carrier carrier) {
        long instanceRrn = 0;
        // named object inserting and transaction start to log
        TransactionLog transactionLog = transactionLogManager.startTransactionLog(carrier.getTransPerformedby(),
                                                                                  carrier.getTransId());
        instanceRrn = objectManager.insertNamedObject(carrier, transactionLog.getTransRrn());
        carrier.setInstanceRrn(instanceRrn);
        pcdManager.insertPcdNamedObjectExt(instanceRrn, carrier.getFlagType(), carrier.getDmmType());
        entityManager.insertParentEntity(carrier, transactionLog.getTransRrn(), instanceRrn);
        entityManager.insertEntityHistory(carrier, transactionLog.getTransRrn(), instanceRrn);
        //Use the reconstructed insert and insert history method
        carrierDAO.insertCarrier(carrier);
        carrierDAO.insertCarrierH(instanceRrn, StringUtils.EMPTY, carrier.getInstanceId(), transactionLog);

        pcdManager.addPcdCleanInfo(instanceRrn, transactionLog.getTransRrn(), carrier.getFacilityRrn(),
                                   ObjectList.CARRIER_KEY, carrier.getCleanCycle());

        // mark transaction end flag
        transactionLogManager.markTransactionLog(transactionLog);
        return instanceRrn;
    }

    @Override
    public void updateCarrierEntity(Map<String, Object> attrMap, Long facilityRrn, String user) {
        String allowableEventId = MapUtils.getString(attrMap, "carrierAllowableEvent").toUpperCase().trim();
        String engineerGroupId = MapUtils.getString(attrMap, "carrierEngineerGroupID").toUpperCase().trim();
        long allowableEventRrn = objectManager.getNamedObjectRrn(allowableEventId,
                                                                 objectManager.getNamedSpace(facilityRrn,
                                                                                             ObjectList.ALLOWABLEEVENTSET_KEY),
                                                                 ObjectList.ALLOWABLEEVENTSET_KEY.toString());

        long engineerGroupRrn = objectManager.getNamedObjectRrn(engineerGroupId,
                                                                objectManager.getNamedSpace(facilityRrn,
                                                                                            ObjectList.USERGROUP_KEY),
                                                                ObjectList.USERGROUP_KEY);

        Assert.isFalse(allowableEventRrn <= 0,
                       Errors.create().key(MessageIdList.EVENT_ALLOWABLE_EVENT_MISSING).content("允许事件集无效!").build());
        Assert.isFalse(StringUtils.isNotBlank(engineerGroupId) && engineerGroupRrn < 1,
                       Errors.create().key(MessageIdList.ENGINEERGROUP_ENGINEER_GROUP_MISSING).content("工程师组无效!")
                             .build());

        Long carrierRrn = MapUtils.getLong(attrMap, "carrierRrn");

        Carrier carrier = getCarrier(carrierRrn);

        carrier.setAllowableEventsId(allowableEventId);
        carrier.setAllowableEventsRrn(allowableEventRrn);
        carrier.setCarrierCode(MapUtils.getString(attrMap, "carrierColor"));
        carrier.setInstanceDesc(MapUtils.getString(attrMap, "carrierDesc"));
        carrier.setPollutionLevel(MapUtils.getString(attrMap, "carrierPollutionLevel"));
        carrier.setFlagType(MapUtils.getString(attrMap, "carrierCategory"));

        String locationId = StringUtils.trimToUpperCase(MapUtils.getString(attrMap, "carrierlocation"));
        if (StringUtils.isNotBlank(locationId)) {
            long locationRrn = objectManager.getNamedObjectRrn(locationId, ObjectList.LOCATION_KEY,
                                                               objectManager.getNamedSpace(facilityRrn,
                                                                                           ObjectList.LOCATION_KEY));
            Assert.isFalse(locationRrn <= 0,
                           Errors.create().key(MessageIdList.ENTITY_LOCATION_MISSING).content("位置无效!").build());
            carrier.setLocationRrn(locationRrn);
        }

        carrier.setTransId(TransactionNames.MODIFY_KEY);
        carrier.setTransPerformedby(user);
        carrier.setObjectSubtype(MapUtils.getString(attrMap, "carrierType"));
        carrier.setMaintenanceEngineerId(engineerGroupId);
        carrier.setMaintenanceEngineerRrn(engineerGroupRrn);
        carrier.setSlotCount(MapUtils.getLong(attrMap, "carrierSlotCount"));
        carrier.setAvailableSlotCount(MapUtils.getLong(attrMap, "carrierAvailableSlotCount"));

        TransactionLog transactionLog = transactionLogManager.startTransactionLog(user, carrier.getTransId());
        long transRrn = transactionLog.getTransRrn();

        if (pcdManager.countPcdCategoryByRrn(carrier.getInstanceRrn()) > 0) {
            // 已经存在数据
            pcdManager.updatePcdNamedObjectExt(carrier.getInstanceRrn(), carrier.getFlagType());
        } else {
            pcdManager.insertPcdNamedObjectExt(carrier.getInstanceRrn(), carrier.getFlagType(), carrier.getDmmType());
        }
        carrier.setLastUpdateTimestamp(transactionLog.getTransStartTimestamp());
        carrier.setLastUpdateUserRrn(LocalContext.getUserRrn());
        carrierDAO.updateCarrier(carrier, transRrn);
        PcdClean carrierCleanInfo = new PcdClean();
        carrierCleanInfo.setInstanceRrn(carrier.getInstanceRrn());
        carrierCleanInfo.setInstanceId(carrier.getInstanceId());
        carrierCleanInfo.setCleanCycle(MapUtils.getDoubleValue(attrMap, "carrierCleanCycle"));
        carrierCleanInfo.setNamedSpace(objectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY));
        pcdManager.updatePcdCleanInfo(transRrn, carrierCleanInfo);

        // insert into Transaction Log
        transactionLogManager.markTransactionLog(transactionLog);
    }

    @Override
    public Map<String, Object> qryCarrierInfoDetailById(String carrierId, String namedSpace) {
        return carrierDAO.qryCarrierInfoDetailById(carrierId, namedSpace);
    }

    @Override
    public void insertCarrierH(Long carrierRrn, String comments, Long transRrn) {
        carrierDAO.insertCarrierH(carrierRrn, comments, StringUtils.EMPTY,
                                  new TransactionLog(transRrn, LocalContext.getUserRrn()));
    }

    @Override
    public void updateCarrierAvailableSlotCount(Long carrierRrn) {
        carrierDAO.updateCarrierAvailableSlotCount(carrierRrn);
    }

    @Override
    public void holdCarrier(Map transInfo) {
        String transPerformedBy = MapUtils.getString(transInfo, "transPerformedBy");
        long holdBy = MapUtils.getLong(transInfo, "holdBy");
        NamedObject pcd = (NamedObject) transInfo.get("pcd");
        long instanceRrn = pcd.getInstanceRrn();
        long facilityRrn = MapUtils.getLongValue(transInfo, "facilityRrn");
        TransReason transReason = (TransReason) transInfo.get("transReason");

        TransactionLog transactionLog = transactionLogManager.startTransactionLog(transPerformedBy,
                                                                                  TransactionNames.HOLD_CARRIER_KEY);
        long transRrn = transactionLog.getTransRrn();

        Carrier carrier = getCarrier(instanceRrn);
        if (!StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.HOLD_KEY)) {
            // step1: update pcd status and add history
            String cstEventId = eventManager.getEventIdBy2Status(facilityRrn, instanceRrn, carrier.getCarrierStatus(),
                                                                 PcdStatus.HOLD_KEY);
            statusManager.changePCDStatusByEvent(facilityRrn, transPerformedBy, instanceRrn, ObjectList.CARRIER_KEY,
                                                 cstEventId, "Hold");

            updateCarrierStatus(transactionLog, instanceRrn, carrier.getInstanceId(), PcdStatus.HOLD_KEY);

            // step2: update pod and door for assembled casset
            PcdAssembly pcdAssembly = pcdManager.getPcdAssembly(instanceRrn);
            if (pcdAssembly != null) {

                transInfo.put("pcdAssembly", pcdAssembly);
                if (pcdAssembly.getPodRrn() != null && pcdAssembly.getPodRrn() > 0) {
                    NamedObject pod = objectManager.getNamedObject(pcdAssembly.getPodRrn());
                    transInfo.put("pcd", pod);
                    podManager.holdPod(transInfo);
                }
                if (pcdAssembly.getDoorRrn() != null && pcdAssembly.getDoorRrn() > 0) {
                    NamedObject door = objectManager.getNamedObject(pcdAssembly.getDoorRrn());
                    transInfo.put("pcd", door);
                    doorManager.holdDoor(transInfo);
                }
            }
        }

        // step3: insert into multiple_Hold
        pcdManager.insertMultipleHold(instanceRrn, holdBy, transRrn);

        // step3: insert into trans_reason
        pcdManager.insertTransReason(transRrn, instanceRrn, transReason, 1);
        transactionLogManager.markTransactionLog(transactionLog);
    }

    @Override
    public void releaseCarrier(Map transInfo) {
        String transPerformedBy = MapUtils.getString(transInfo, "transPerformedBy");
        List releaseReasons = (List) transInfo.get("releaseReasons");
        NamedObject pcd = (NamedObject) transInfo.get("pcd");
        long instanceRrn = pcd.getInstanceRrn();
        String transNames = TransactionNames.RELEASE_CARRIER_KEY;
        TransReason transReason = (TransReason) transInfo.get("transReason");
        String targetStatus = MapUtils.getString(transInfo, "targetStatus");
        long facilityRrn = MapUtils.getLongValue(transInfo, "facilityRrn");
        TransactionLog transactionLog = transactionLogManager.startTransactionLog(transPerformedBy, transNames);

        // delete Multiple hold
        Object[] releaseReasonArray = releaseReasons.toArray();

        for (int i = 0; i < releaseReasonArray.length; i++) {
            HashMap releaseReason = (HashMap) releaseReasonArray[i];
            pcdManager.deleteMultipleHold(instanceRrn, Long.parseLong((String) releaseReason.get("sequenceNumber")));
        }

        if (!StringUtils.equalsIgnoreCase(targetStatus, PcdStatus.HOLD_KEY)) {
            Carrier carrier = getCarrier(instanceRrn);
            String cstEventId = eventManager.getEventIdBy2Status(facilityRrn, instanceRrn, carrier.getCarrierStatus(),
                                                                 targetStatus);
            statusManager.changePCDStatusByEvent(facilityRrn, transPerformedBy, instanceRrn, ObjectList.CARRIER_KEY,
                                                 cstEventId, "Hold");

            updateCarrierStatus(transactionLog, instanceRrn, carrier.getInstanceId(), targetStatus);
            PcdAssembly pcdAssembly = pcdManager.getPcdAssembly(instanceRrn);

            if (pcdAssembly != null) {
                transInfo.put("pcdAssembly", pcdAssembly);
                if (pcdAssembly.getPodRrn() != null && pcdAssembly.getPodRrn() > 0) {
                    NamedObject pod = objectManager.getNamedObject(pcdAssembly.getPodRrn());
                    transInfo.put("pcd", pod);
                    podManager.releasePod(transInfo);
                }
                if (pcdAssembly.getDoorRrn() != null && pcdAssembly.getDoorRrn() > 0) {
                    NamedObject door = objectManager.getNamedObject(pcdAssembly.getDoorRrn());
                    transInfo.put("pcd", door);
                    doorManager.releaseDoor(transInfo);
                }
            }
        }

        transReason.setReasonCategory(transNames);

        pcdManager.insertTransReason(transactionLog.getTransRrn(), instanceRrn, transReason, 1);

        transactionLogManager.markTransactionLog(transactionLog);
    }

    @Override
    public Page queryCarrierHistory(Page page, Map<String, Object> query) {
        // 处理时间
        String dueDateS = MapUtils.getString(query, "dueDateS");
        String dueDateE = MapUtils.getString(query, "dueDateE");
        if (StringUtils.isNotBlank(dueDateS)) {
            dueDateS += " 00:00:00";
            query.put("dueDateS", dueDateS);
        }
        if (StringUtils.isNotBlank(dueDateE)) {
            dueDateE += " 23:59:59";
            query.put("dueDateE", dueDateE);
        }

        return carrierDAO.queryCarrierHistory(page, query);

    }

    @Override
    public List<Map> getAllCarrier(Map<String, Object> getAllCarrierMap) {
        //        return carrierDAO.getCarrierListByPage(getAllCarrierMap, 0, Integer.MAX_VALUE);
        return (List<Map>) carrierDAO.getCarrierListByPage(new Page(1, Integer.MAX_VALUE), getAllCarrierMap)
                                     .getResults();
    }

    @Override
    public List<String> getInUseCarrierLotIdList(Long carrierRrn) {
        return carrierDAO.getInUseCarrierLotIdList(carrierRrn);
    }

    @Override
    public Long getNumberOfAvailableInCarrier(Long carrierRrn) {
        return carrierDAO.getNumberOfAvailableInCarrier(carrierRrn);
    }

    @Override
    public Long getAvailableCarrierRrnById(Long facilityRrn, String carrierId) {
        Assert.isFalse(StringUtils.isEmpty(carrierId),
                       Errors.create().content("Cassette Id can not be Empty!").build());
        String carrierIdUpper = carrierId.trim().toUpperCase();
        long carrierRrn = getCarrier(facilityRrn, carrierIdUpper).getInstanceRrn();
        checkIsInUseCarrier(carrierIdUpper, carrierRrn);
        return carrierRrn;
    }

    @Override
    public void exchangeCarrierSpecial(Lot lot, long toCarrierRrn, List<Map> carrierMappings, Carrier toCarrier,
                                       Long facilityRrn, String userId) {
        // 批次原晶舟
        pcdManager.changeStatusFromInUseToFreeOrAssembly(lot.getFacilityRrn(), lot.getTransPerformedby(),
                                                         lot.getCarrierRrn(), "Release for exchange cassette");
        // 批次新晶舟
        pcdManager.changeStatusFromFreeOrAssemblyToInUse(facilityRrn, userId, toCarrier.getInstanceRrn(),
                                                         "In use for exchange cassette");
        TransactionLog transactionLog = transactionLogManager.startTransactionLog(lot.getTransPerformedby(),
                                                                                  lot.getTransId());

        exchangeCarrier(transactionLog.getTransRrn(), 1L, lot, toCarrierRrn, carrierMappings);

        transactionLogManager.markTransactionLog(transactionLog);
    }

    /**
     * RunCard Lot的 Exchange Cassette
     * @param lot           必须为MainRC在Lot表中的Lot对象
     * @param toCarrierRrn
     * @param carrierMappings
     * @param toCarrier
     * @param facilityRrn
     * @param userId
     */
    @Override
    public void exchangeCarrierSpecial4Src(Lot lot, long toCarrierRrn, List<Map> carrierMappings, Carrier toCarrier,
                                           Long facilityRrn, String userId) {
        Long sourceCarrierRrn,sourceCarrierMapRrn;
        boolean needDealSourceCarrier = true;
        if (StringUtils.endsWith(lot.getLotId(), RunCardConstants.MAINLOT_ID_IN_MES)) {
            //MAINRC的Lot
            Map<String, Object> rcLotInfo = runCardQueryManager.getRCLotInfoMap(lot.getLotId());
            Lot lotForLotTable = lotQueryManager.getLot(lot.getLotRrn());
            long sourceCarrierRrnForLotTable = lotForLotTable.getCarrierRrn();
            sourceCarrierRrn = MapUtils.getLong(rcLotInfo, "carrierRrn", -1L);
            boolean isNotSame = sourceCarrierRrn != sourceCarrierRrnForLotTable;//不一致只有一种场景,就是MainRC和原LOT使用同一个Cassette时。
            Carrier sourceCarrier = carrierDAO.getCarrier(sourceCarrierRrn);
            sourceCarrierMapRrn = MapUtils.getLong(rcLotInfo, "carrierMapRrn", -1L);
            if (isNotSame || CarrierType.DUMMY.equalsIgnoreCase(sourceCarrier.getObjectSubtype())) {
                //MAINRC 的Cassette在和原LOT使用同一Cassette时,即真实Cassette是个虚拟Cassette时,不需要sourceCarrier切换状态,只需要把它剥离出去即可。
                // 不使用虚拟Cassette时,代表已经被交换过了,则交换后需要处理原Cassette。
                needDealSourceCarrier = false;
            }
        } else {
            sourceCarrierRrn = lot.getCarrierRrn();
            sourceCarrierMapRrn = lot.getCarrierMapRrn();
        }

        if (needDealSourceCarrier) {
            // 源晶舟
            pcdManager.changeStatusFromInUseToFreeOrAssembly(lot.getFacilityRrn(), lot.getTransPerformedby(), sourceCarrierRrn, "Release for exchange cassette");
        }
        // 批次新晶舟
        pcdManager.changeStatusFromFreeOrAssemblyToInUse(facilityRrn, userId, toCarrier.getInstanceRrn(), "In use for exchange cassette");

        TransactionLog transactionLog = transactionLogManager.startTransactionLog(lot.getTransPerformedby(), lot.getTransId());

        exchangeCarrier4Src(transactionLog.getTransRrn(), 1L, lot, sourceCarrierRrn, sourceCarrierMapRrn, toCarrierRrn, carrierMappings, needDealSourceCarrier);

        transactionLogManager.markTransactionLog(transactionLog);
    }

    @Override
    public boolean isPcdType(String carrierType) {
        String result = carrierDAO.getCarrierTypeIsPcdOrOpen(carrierType);
        if (StringUtils.isNotEmpty(result)) {
            if (StringUtils.equalsIgnoreCase(result, "1")) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Long createCarrierMapping(long transRrn, Carrier carrier, List<Map> units) {

        Long carrierMappingRrn = null;

        if (!units.isEmpty()) {
            carrierMappingRrn = NumberUtils.toLong(
                    IDGenerators.get(IDNames.SEQUENCE).generateId(SequenceNames.MODULE_SEQ_OBJECT_RRN));
            // 母批记录
            // insertCarrierMappingH(transRrn, carrier.getCarrierMapRrn());
            // 母批批操作与记录
            insertCarrierMapping(transRrn, carrier.getInstanceRrn(), units, carrierMappingRrn);
            updateCarrier(transRrn, carrier.getPollutionLevel(), carrier.getInstanceRrn(), carrierMappingRrn,
                          carrier.getLotId(), "", PcdStatus.IN_USE_KEY);

            PcdAssembly pcdAssembly = pcdManager.getPcdAssembly(carrier.getInstanceRrn());
            if (pcdAssembly != null) {
                this.updatePodAndDoorStatus(pcdAssembly, transRrn, PcdStatus.IN_USE_KEY);
            }
        }
        return carrierMappingRrn;
    }

    @Override
    public Long getCarrierRrnForLot(Long facilityRrn, Lot lot, String carrierId) {
        carrierId = StringUtils.trimToUpperCase(carrierId);
        Assert.isFalse(StringUtils.isEmpty(carrierId), Errors.create().content("Cassette is required!").build());

        Carrier carrier = getCarrier(facilityRrn, carrierId);

        Assert.isFalse(StringUtils.equalsIgnoreCase(carrier.getObjectSubtype(), Carrier.CarrierType.DUMMY.toString()),
                       Errors.create().content("The carrier: {} is a  dummy carrier!").args(carrier.getInstanceId())
                             .build());
        Assert.isFalse(carrier.getAvailableSlotCount() < lot.getQty1(),
                       Errors.create().content("The AvailableSlot is not " + "enough!").build());
        Assert.isFalse(!StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.FREE_KEY) &&
                               !StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.ASSEMBLY_KEY),
                       Errors.create().content("Invalid carrier status!").build());

        return getAvailableCarrierRrnById(facilityRrn, carrierId);
    }

    @Override
    public Long getCarrierRrnForLot(Long facilityRrn, Double lotQty, String carrierId) {
        carrierId = StringUtils.trimToUpperCase(carrierId);
        Assert.isFalse(StringUtils.isEmpty(carrierId), Errors.create().content("Cassette is required!").build());

        Carrier carrier = getCarrier(facilityRrn, carrierId);

        Assert.isFalse(StringUtils.equalsIgnoreCase(carrier.getObjectSubtype(), Carrier.CarrierType.DUMMY.toString()),
                       Errors.create().content("The carrier: {} is a  dummy carrier!").args(carrier.getInstanceId())
                             .build());
        Assert.isFalse(carrier.getAvailableSlotCount() < lotQty,
                       Errors.create().content("The AvailableSlot is not enough!").build());
        Assert.isFalse(!StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.FREE_KEY) &&
                               !StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.ASSEMBLY_KEY),
                       Errors.create().content("Invalid carrier status!").build());

        Assert.isFalse(isAssseblyNeed(carrier.getObjectSubtype()) &&
                               !StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.ASSEMBLY_KEY),
                       Errors.create().content("{} type carrier needed assembled first!")
                             .args(carrier.getObjectSubtype()).build());

        pcdManager.checkPcdIsValid(carrier.getInstanceRrn(), facilityRrn);

        return getAvailableCarrierRrnById(facilityRrn, carrierId);
    }

    @Override
    public Boolean isAssseblyNeed(String carrierType) {
        String assseblyNeed = StringUtils.EMPTY;
        assseblyNeed = referenceFileManager.getReferenceDetailExchange(ReferenceDetailNames.CARRIER_TYPE, carrierType,
                                                                       null, ReferenceFileConst.DATA_2_VALUE);
        return StringUtils.equalsIgnoreCase(assseblyNeed, "1");
    }

    @Override
    public String getTargetCarrierTypeByStatus(Lot lot) {
        if (StringUtils.equalsIgnoreCase(lot.getLotStatus(), LotStatus.RUNNING) ||
                StringUtils.equalsIgnoreCase(lot.getLotStatus(), LotStatus.PROCESSED) ||
                StringUtils.equalsIgnoreCase(lot.getLotStatus(), LotStatus.RUNNINGHOLD)) {
            return getToCarrierTypeLocation(lot);
        } else {
            return getFromCarriertypeLocation(lot);
        }
    }

    @Override
    public String getCarrierTypeByProcessLocation(String processLocation) {
        String carrierType = StringUtils.EMPTY;
        if (StringUtils.isNotBlank(processLocation)) {
            carrierType = referenceFileManager.getRefFileValue(ReferenceDetailNames.PROCESS_LOCARION_CARRIER_TYPE,
                                                               processLocation, ReferenceFileConst.DATA_1_VALUE);
        }
        return carrierType == null ? StringUtils.EMPTY : carrierType;
    }

    @Override
    public String getCarriertypeByProcessLocation(Lot lot) {
        String carrierType = StringUtils.EMPTY;

        // 以情境值为准
        String processLocation = processLocationContextManager.getCurrentProcessLocation(LocalContext.getFacilityRrn(),
                                                                                      lot);

        if (StringUtils.isNotBlank(processLocation)) {
            carrierType = referenceFileManager.getRefFileValue(ReferenceDetailNames.PROCESS_LOCARION_CARRIER_TYPE,
                                                               processLocation, ReferenceFileConst.DATA_1_VALUE);
        }
        return carrierType == null ? StringUtils.EMPTY : carrierType;
    }

    @Override
    public String getCarriertypeByProcessLocationContext(Lot lot) {
        String carrierType = StringUtils.EMPTY;

        String processLocation = processLocationContextManager.getCurrentProcessLocation(LocalContext.getFacilityRrn(),
                                                                                         lot);
        if (StringUtils.isBlank(processLocation)) {
            processLocation = processLocationContextManager.getCurrentProcessLocation(LocalContext.getFacilityRrn(),
                                                                                      lot);
        }

        if (StringUtils.isNotBlank(processLocation)) {
            carrierType = referenceFileManager.getRefFileValue(ReferenceDetailNames.PROCESS_LOCARION_CARRIER_TYPE,
                                                               processLocation, ReferenceFileConst.DATA_1_VALUE);
        }
        return carrierType == null ? StringUtils.EMPTY : carrierType;
    }

    @Override
    public void dispatchCarrier(long carrierRrn, int consumedSlotCount) {
        carrierDAO.dispatchCarrier(carrierRrn, consumedSlotCount);
    }

    @Override
    public Long getCarrierMapRrn(Long carrierRrn) {
        return carrierDAO.getCarrierMapRrn(carrierRrn);
    }

    @Override
    public void dispatch(long lotRrn, long jobRrn, long eqptRrn) {
        carrierDAO.updateCarrierForDispatch(lotRrn, jobRrn, eqptRrn);
    }

    @Override
    public String getTargetCarrierTypeByActionPoint(String actionPoint, Lot lot) {
        if (StringUtils.equalsIgnoreCase(actionPoint, ActionPointList.MOVEIN_KEY)) {
            return getFromCarriertypeLocation(lot);
        } else {
            return getToCarrierTypeLocation(lot);
        }
    }

    @Override
    public boolean isThisCarrierPmNotMoveIn(long carrierRrn) {
        List<Map<String, Object>> list = carrierDAO.getCarrierChecklistJob(carrierRrn);
        for (Map<String, Object> map : list) {
            String state = MapUtils.getString(map, "checklistJobState");
            String actionFlag1 = MapUtils.getString(map, "actionFlag1");
            if (StringUtils.equalsIgnoreCase("STARTED", state) && StringUtils.equalsIgnoreCase("1", actionFlag1)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void makeCarrierRun(long carrierRrn, long runRrn, Short loadPosition) {
        carrierDAO.updateRunRrn(runRrn, carrierRrn);
        carrierDAO.updateLoadPosition(loadPosition, carrierRrn);
    }

    @Override
    public void updateCarrierPollutionLevel(String carrierId, String pollutionLevel, String lotId, long transRrn) {
        Carrier carrier = getCarrier(LocalContext.getFacilityRrn(), carrierId);
        if (carrier != null && !StringUtils.equalsIgnoreCase(pollutionLevel, carrier.getPollutionLevel())) {
            carrier.setPollutionLevel(pollutionLevel);
            carrier.setTransPerformedby(LocalContext.getUserId());
            carrier.setTransId(TransactionNames.MOVEOUT_KEY);
            carrier.setLotId(lotId);

            carrierDAO.updateCarrierPollutionLevel(carrier, transRrn);
        }
    }

    @Override
    public void insertCarrierMapping(Long transRrn, Long carrierRrn, List<Map> carrierMappings,
                                     Long carrierMappingRrn) {
        carrierDAO.insertBatchCarrierMapping(carrierMappingRrn, carrierRrn, carrierMappings);
        insertCarrierMappingH(transRrn, carrierMappingRrn);
    }

    @Override
    public void exchangeCarrierByCarrier(Lot lot, Long toCarrierRrn, List<Map> carrierMappings) {
        TransactionLog transactionLog = transactionLogManager.startTransactionLog(lot.getTransPerformedby(),
                                                                                  lot.getTransId());
        exchangeCarrier(transactionLog.getTransRrn(), 1L, lot, toCarrierRrn, carrierMappings);
        transactionLogManager.markTransactionLog(transactionLog);
    }

    @Override
    public void checkPCDInfo(List<String> lotIdList, String actionPoint) {
        for (String lotId : lotIdList) {
            lotId = StringUtils.trimToUpperCase(lotId);
            Lot lot = lotQueryManager.getLot(lotId, LocalContext.getFacilityRrn());
            String targetCarrierType;
            Carrier carrier = getCarrier(new Carrier(lot.getCarrierRrn()));

            targetCarrierType = getTargetCarrierTypeByActionPoint(actionPoint, lot);

            Assert.isFalse(StringUtils.isEmpty(targetCarrierType),
                           Errors.create().key(MessageIdList.PROCESS_CARRIER_MUST_EMPTY)
                                 .content("Process location for {}  cannot be empty!").args(lot.getLotId()).build());
            Assert.isTrue(StringUtils.equalsIgnoreCase(carrier.getObjectSubtype(), targetCarrierType),
                          Errors.create().key(MessageIdList.PROCESS_NOT_MATCH_CASSETTE)
                                .content("Cassette type for {} not match, Please exchange Cassette!")
                                .args(lot.getLotId()).build());

            pcdManager.checkPcdIsValid(carrier.getInstanceRrn(), LocalContext.getFacilityRrn());
        }
    }

    @Override
    public void checkPcdInfo(List<Lot> lotList, String actionPoint) {
        for (Lot lot : lotList) {
            Carrier carrier = null;
            String targetCarrierType;

            if (RunCardUtils.checkLotIdIsRunCardLot(lot.getLotId())) {
                RunCardLotInfo runCardLotInfo = runCardQueryManager.getRunCardLotInfo(lot.getLotId());
                carrier = getCarrier(runCardLotInfo.getCarrierRrn());
                String carrierTypeByProcessLocation = getCarrierTypeByProcessLocation(
                        runCardLotInfo.getProcessLocation());
                targetCarrierType = getRunCardTargetCstType(actionPoint, carrierTypeByProcessLocation);
            } else {
                carrier = getCarrier(new Carrier(lot.getCarrierRrn()));
                targetCarrierType = getTargetCarrierTypeByActionPoint(actionPoint, lot);
            }

            Assert.isFalse(StringUtils.isEmpty(targetCarrierType),
                           Errors.create().key(MessageIdList.PROCESS_CARRIER_MUST_EMPTY)
                                 .content("Process location for {}  cannot be empty!").args(lot.getLotId()).build());
            Assert.isTrue(StringUtils.equalsIgnoreCase(carrier.getObjectSubtype(), targetCarrierType),
                          Errors.create().key(MessageIdList.PROCESS_NOT_MATCH_CASSETTE)
                                .content("Cassette type for {} not match, Please exchange Cassette!")
                                .args(lot.getLotId()).build());

            pcdManager.checkPcdIsValid(carrier.getInstanceRrn(), LocalContext.getFacilityRrn());
        }
    }

    /**
     * 兼容处理 AA-BB情况
     * @param actionPoint
     * @param carrierTypeByProcessLocation
     * @return
     */
    private String getRunCardTargetCstType(String actionPoint, String carrierTypeByProcessLocation) {
        String targetCarrierType = carrierTypeByProcessLocation;
        Matcher mathcher = PCDPATERN.matcher(carrierTypeByProcessLocation);
        Boolean match = mathcher.matches();
        if (match) {
            if (StringUtils.equalsIgnoreCase(actionPoint, ActionPointList.MOVEIN_KEY)) {
                targetCarrierType = carrierTypeByProcessLocation.substring(0, carrierTypeByProcessLocation
                        .indexOf(CSTTYPE_SEPERATER_KEY));
            } else if (StringUtils.equalsIgnoreCase(actionPoint, ActionPointList.MOVEOUT_KEY)){
                targetCarrierType = carrierTypeByProcessLocation
                        .substring(carrierTypeByProcessLocation.indexOf(CSTTYPE_SEPERATER_KEY) + 1);
            }
        }
        return targetCarrierType;
    }

    @Override
    public Long updateCarrierMapping(TransactionLog transactionLog, Long carrierRrn, List<Unit> unitList) {
        List<Map> unitMapList = unitList.stream().map(unit -> {
            Map unitMap = BeanUtils.copyBeanToMap(unit);
            unitMap.put("position", unit.getPositionInCarrier());
            unitMap.put("available", CheckRegexUtils.FLAG_ON);
            return unitMap;
        }).collect(Collectors.toList());

        Long carrierMappingRrn = updateCarrierMapping(transactionLog.getTransRrn(), carrierRrn, unitMapList);
        transactionLog.setTransSequence(transactionLog.getTransSequence() + 1);
        return carrierMappingRrn;
    }

    @Override
    public Long updateCarrierMapping(Long transRrn, Long carrierRrn, List<Map> carrierMappings) {
        Long carrierMappingRrn = null;
        if (!carrierMappings.isEmpty()) {
            carrierMappingRrn = new Long(
                    IDGenerators.get(IDNames.SEQUENCE).generateId(SequenceNames.MODULE_SEQ_OBJECT_RRN));
            // step 1. create new carrierMapping
            deleteCarrierMapping(transRrn, carrierRrn);
            this.insertCarrierMapping(transRrn, carrierRrn, carrierMappings, carrierMappingRrn);

            // step 2. update Lot And Units Carriers
            carrierDAO.updateUnitForCarrierMapping(carrierMappings);

            // step 3. update new carrier info
            carrierDAO.updateCarrierForCarrierMapRrn(carrierRrn, carrierMappings.size(), carrierMappingRrn);
            carrierDAO.insertCarrierH(carrierRrn, "", "", new TransactionLog(transRrn, LocalContext.getUserRrn()));
        } else {
            deleteCarrierMapping(transRrn, carrierRrn);
            carrierDAO.updateCarrierForCarrierMapRrn(carrierRrn, NumberUtils.INTEGER_ZERO, null);
        }
        return carrierMappingRrn;
    }

    @Override
    public Carrier setCarrierForVirtual(String userId, Long facilityRrn, String lotId, Double lotqty) {
        Carrier targetCarrier = new Carrier();
        targetCarrier.setInstanceId(Carrier.CarrierType.DUMMY.toString() + lotId);
        targetCarrier.setNamedSpace(objectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY));
        targetCarrier.setFacilityRrn(facilityRrn);
        targetCarrier.setObject(ObjectList.ENTITY_KEY);
        targetCarrier.setObjectType(ObjectList.CARRIER_KEY);
        targetCarrier.setObjectSubtype(Carrier.CarrierType.DUMMY.toString());
        targetCarrier.setAvailableSlotCount(SLOTMAXCOUNT - lotqty.longValue());
        targetCarrier.setTransId(TransactionNames.CREATE_KEY);
        targetCarrier.setTransPerformedby(userId);
        targetCarrier.setStatus(PcdStatus.FREE_KEY);

        return targetCarrier;
    }

    @Override
    public void exchangeCarrierByLot(Long transRrn, Long transSeq, Lot lot, Long newCarrierRrn) {
        exchangeCarrier(transRrn, transSeq, lot, newCarrierRrn, unitQueryManager.getUnitListByLot(lot.getLotRrn()));
    }

    @Override
    public Long getNumberOfUnitInCarrier(Long carrierRrn) {
        return carrierDAO.getNumberOfUnitInCarrier(carrierRrn);
    }

    @Override
    public List<Long> getInUseCarrierLotRrnList(Long carrierRrn) {
        return carrierDAO.getInUseCarrierLotRrnList(carrierRrn);
    }

    @Override
    public boolean checkCarrierDelete(long carrierRrn) {
        return carrierDAO.checkCarrierInCarrierMappingH(carrierRrn) || carrierDAO.checkCarrierInSortJob(carrierRrn);
    }

    @Override
    public Boolean getCarrierRefreshInfo(Long carrierRrn) {
        return carrierDAO.getCarrierRefreshInfo(carrierRrn);
    }

    @Override
    public void refreshCarrier(Long carrierRrn) {
        carrierDAO.refreshCarrier(carrierRrn);
    }

    @Override
    public String generateCstIdByType(String prefix, int width) {
        String newPodId = carrierDAO.generateCstIdByType(prefix, width);
        Assert.isFalse(StringUtils.contains(newPodId, "-1"),
                       Errors.create().key(MessageIdList.PCD_CASSETTE_OUT_OF_MAX).content("Error, Maximum upper limit")
                             .build());
        return newPodId;
    }

    @Override
    public void exchangeCarrierType(Carrier carrier, String comments, Long transRrn, Boolean moveOutFlag) {
        if (moveOutFlag) {
            Lot lot = lotQueryManager.getLotByCarrierRrn(carrier.getInstanceRrn());
            lot.setTransId(TransactionNames.EXCHANGE_TYPE_KEY);
            lot.setTransPerformedby(carrier.getTransId());
            lot.setLotComments(comments);
            createLotTransHistoryForExchanegCarrier(transRrn, 1L, lot);
        }
        carrierDAO.updateCarrierType(carrier, comments, transRrn);
    }

    @Override
    public Boolean checkSwitchCarrierType(String processLocation) {
        String carrierType = "";
        if (StringUtils.isNotBlank(processLocation)) {
            carrierType = referenceFileManager.getRefFileValue(
                    ReferenceDetailNames.PROCESS_LOCATION_AUTO_CHANGE_CASSETTE_TYPE, processLocation,
                    ReferenceFileConst.DATA_1_VALUE);
            if (StringUtils.isNotEmpty(carrierType) && WipUtils.PCDPATERN.matcher(carrierType).matches()) {
                return true;
            } else {
                return false;
            }
        }
        return false;
    }

    @Override
    public String generatePcdIdByPcdType(String pcdType, String prefix, String ignorePrefix, int length) {
        List<String> ignorePrefixSetting = new ArrayList<>();
        if (StringUtils.isNotEmptyTrim(ignorePrefix)) {
            //用户手动定义的列表
            ignorePrefixSetting.addAll(StringUtils.splitAsList(ignorePrefix, LotConstants.DEFAULY_SEPARATOR));
            //过滤与 固定前缀相同的数据,过滤小于前缀的脏数据
            ignorePrefixSetting= ignorePrefixSetting.stream().filter((e) -> !(StringUtils.equalsIgnoreCase(prefix, e)
                    || e.length() < prefix.length())).distinct().collect(Collectors.toList());
        }

        //拿到库中最大lot seq序列
        String currentDBMaxLotSeq = carrierDAO.generatePcdIdByPcdType(pcdType, prefix, ignorePrefixSetting, length);// TODO 要把前缀 和 忽略前缀的字符抛开后的真实 Seq

        String createdLotSeq = SystemConstant.Pcd.increasePcdPrefix(LotConstants.DEFAULT_IGNORE, currentDBMaxLotSeq, length, prefix, ignorePrefixSetting);
        Assert.isFalse(SystemConstant.Pcd.PCD_ID_OUT_OF_RANGE.equals(createdLotSeq),
                       Errors.create().key(MessageIdList.PCD_ID_IS_NOT_ENOUGH).content("PCD创建数量超过PCD规则定义,请联系管理员!").build());
        return createdLotSeq;
    }

    private void updatePodAndDoorStatus(PcdAssembly pcdAssembly, long transRrn, String status) {
        String user = LocalContext.getUserId();
        if (pcdAssembly.getPodRrn() != null && pcdAssembly.getPodRrn() > 0) {
            statusManager.updatePodStatus(pcdAssembly.getPodRrn(), status, user, "", transRrn, "");
        }
        if (pcdAssembly.getDoorRrn() != null && pcdAssembly.getDoorRrn() > 0) {
            statusManager.updateDoorStatus(pcdAssembly.getDoorRrn(), status, user, "", transRrn, "");
        }

    }

    private String getFromCarriertypeLocation(Lot lot) {
        String processLocation = getCarriertypeByProcessLocation(lot);

        Matcher mathcher = PCDPATERN.matcher(processLocation);

        Boolean match = mathcher.matches();
        if (match) {
            processLocation = processLocation.substring(0, processLocation.indexOf(CSTTYPE_SEPERATER_KEY));
        }

        return processLocation;
    }

    private String getToCarrierTypeLocation(Lot lot) {
        String processLocation = getCarriertypeByProcessLocation(lot);

        Matcher mathcher = PCDPATERN.matcher(processLocation);

        Boolean match = mathcher.matches();
        if (match) {
            processLocation = processLocation.substring(processLocation.indexOf(CSTTYPE_SEPERATER_KEY) + 1);
        }

        return processLocation;
    }

    private void createLotTransHistoryForExchanegCarrier(long transRrn, long sequence, Lot lot) {
        Lot lotExt = lotQueryManager.qryLotExtInfo(lot.getLotRrn());
        lot.setCustomerId(StringUtils.isNotBlank(lotExt.getCustomerId()) ? lotExt.getCustomerId() : "");
        lot.setShippingCode(StringUtils.isNotBlank(lotExt.getShippingCode()) ? lotExt.getShippingCode() : "");
        lot.setOuterOrderNO(StringUtils.isNotBlank(lotExt.getOuterOrderNO()) ? lotExt.getOuterOrderNO() : "");
        lot.setOutOrderType(StringUtils.isNotBlank(lotExt.getOutOrderType()) ? lotExt.getOutOrderType() : "");
        if (!lot.getSorterFlag()) {
            lot.setLocation(StringUtils.isNotBlank(lotExt.getLocation()) ? lotExt.getLocation() : "");
        }
        lot.setpLocation(StringUtils.isNotBlank(lotExt.getpLocation()) ? lotExt.getpLocation() : "");
        lotManager.insertLotTransH(transRrn, sequence, lot);
    }

    private void updateCarrier(Long transRrn, String pollutionLevel, Long newCarrierRrn, Long carrierMappingRrn,
                               String lotId, String carrierId, String status) {
        carrierDAO.updateCarrierMapRrnAndPollutionLevel(newCarrierRrn, carrierMappingRrn, pollutionLevel, status);
        carrierDAO.insertCarrierH(newCarrierRrn, StringUtils.EMPTY, carrierId,
                                  new TransactionLog(transRrn, LocalContext.getUserRrn()));
        PcdClean cleanInfo = pcdManager.getPcdCleanInfo(newCarrierRrn);
        if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
            pcdManager.insertPcdCleanH(transRrn, cleanInfo);
        }
    }

    private void deleteCarrierMapping(Long transRrn, Long carrierMappingRrn) {
        // dao.insertCarrierMappingHistory(transRrn, carrierRrn);
        insertCarrierMappingH(transRrn, carrierMappingRrn);
        // dao.deleteCarrierMapping(carrierRrn);
    }

    private void updateCarrierStatus(TransactionLog transactionLog, long instanceRrn, String targetCarrier,
                                     String targetStatus) {
        carrierDAO.updateCarrierForStatus(instanceRrn, targetStatus);
        carrierDAO.insertCarrierH(instanceRrn, StringUtils.EMPTY, targetCarrier, transactionLog);
        PcdClean cleanInfo = pcdManager.getPcdCleanInfo(instanceRrn);
        if (cleanInfo != null && StringUtils.isNotBlank(cleanInfo.getInstanceId())) {
            pcdManager.insertPcdCleanH(transactionLog.getTransRrn(), cleanInfo);
        }
    }

    /**
     * 检查Carrier是否被批次使用
     *
     * @param carrierId
     * @param carrierRrn
     */
    private void checkIsInUseCarrier(String carrierId, long carrierRrn) {
        List<String> lots = getInUseCarrierLotIdList(carrierRrn);
        if (!lots.isEmpty()) {
            StringBuilder errorMessage = new StringBuilder();
            errorMessage.append("Cassette Id: ").append(carrierId);
            errorMessage.append(" is already in use by Lot Id: ");
            for (String string : lots) {
                errorMessage.append(string);
                errorMessage.append(" ");
            }
            throw new SystemIllegalArgumentException(Errors.create().content(errorMessage.toString()).build());
        }
    }

}