PcdManagerImpl.java

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


import com.fa.sesa.exception.Assert;
import com.fa.sesa.exception.Errors;
import com.fa.sesa.i18n.I18nUtils;
import com.fa.sesa.threadlocal.LocalContext;
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.server.base.manager.EventManager;
import com.mycim.server.base.manager.NamedObjectManager;
import com.mycim.server.base.manager.TransactionLogManager;
import com.mycim.server.carrier.dao.DoorDAO;
import com.mycim.server.carrier.dao.PcdDAO;
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.ems.manager.EntityManager;
import com.mycim.server.status.manager.StatusManager;
import com.mycim.server.system.manager.ReferenceFileManager;
import com.mycim.utils.WipUtils;
import com.mycim.valueobject.Constants;
import com.mycim.valueobject.MessageIdList;
import com.mycim.valueobject.ObjectList;
import com.mycim.valueobject.bas.NamedObject;
import com.mycim.valueobject.bas.TransactionLog;
import com.mycim.valueobject.consts.EventName;
import com.mycim.valueobject.consts.PcdStatus;
import com.mycim.valueobject.consts.ReferenceDetailNames;
import com.mycim.valueobject.consts.TransactionNames;
import com.mycim.valueobject.ems.*;
import com.mycim.valueobject.sys.ReferenceFileDetail;
import com.mycim.valueobject.wip.Lot;
import com.mycim.valueobject.wip.TransReason;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.stream.Collectors;

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

    private final static String PCD_CLEAN_EQPT_REFERENCE_ID = "$PCD_CLEAN_EQPT";

    @Autowired
    PcdDAO pcdDAO;

    @Autowired
    PodDAO podDAO;

    @Autowired
    DoorDAO doorDAO;

    @Autowired
    NamedObjectManager namedObjectManager;

    @Autowired
    ReferenceFileManager referenceFileManager;

    @Autowired
    TransactionLogManager transactionLogManager;

    @Autowired
    CarrierManager carrierManager;

    @Autowired
    PodManager podManager;

    @Autowired
    DoorManager doorManager;

    @Autowired
    EventManager eventManager;

    @Autowired
    EntityManager entityManager;

    @Autowired
    StatusManager statusManager;

    @Override
    public PcdAssembly getPcdAssembly(Long carrierRrn, Long podRrn, Long doorRrn) {
        return pcdDAO.getPcdAssembly(carrierRrn, podRrn, doorRrn);
    }

    @Override
    public boolean isPcdCleaningTimeIsLessThan3Days(Long pcdRrn) {
        if (pcdRrn == null || pcdRrn <= 0) {
            return false;
        }
        PcdClean pcdCleanInfo = pcdDAO.getPcdCleanInfo(pcdRrn);
        if (pcdCleanInfo != null && pcdCleanInfo.getInstanceRrn() > 0) {
            if (this.isCleanValidPcd(pcdCleanInfo.getCleanOverDate())) {
                // 过期时间三天前的时间(毫秒数)
                long time = DateUtils.addTimeByUnit(pcdCleanInfo.getCleanOverDate().getTime(), "D", -3);
                if (this.isCleanValidPcd(new Timestamp(time))) {
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public boolean isCleanValidPcd(Timestamp cleanOverDate) {
        if (cleanOverDate != null) {
            if (DateUtils.compareDateWithNow(cleanOverDate) >= 1) {
                return true;
            }
        }
        return false;
    }

    @Override
    public PcdClean getPcdCleanInfo(Long pcdRrn) {
        if (pcdRrn != null && pcdRrn > 0) {
            PcdClean pcdCleanInfo = pcdDAO.getPcdCleanInfo(pcdRrn);
            if (pcdCleanInfo != null && pcdCleanInfo.getInstanceRrn() > 0) {
                return pcdCleanInfo;
            }
        }
        return null;
    }

    @Override
    public Map<String, Object> checkPcdChangeStatusButton(Long facilityRrn, Long pcdRrn, String currentStatus) {
        Map<String, Object> btnMap = new HashMap();
        // WAIT_CLEAN状态下,如果前一个状态是ASSEMBLY状态,允许To Free按钮使用,以解绑PCD
        String preStatus = StringUtils.EMPTY;
        PcdAssembly assembly = null;
        preStatus = entityManager.getPreStatusByEntityRrn(pcdRrn);
        assembly = getPcdAssembly(pcdRrn, null, null);
        if (assembly == null || StringUtils.isBlank(assembly.getCarrierId())) {
            assembly = getPcdAssembly(null, pcdRrn, null);
            if (assembly == null || StringUtils.isBlank(assembly.getCarrierId())) {
                assembly = getPcdAssembly(null, null, pcdRrn);
            }
        }

        if (StringUtils.isNotBlank(preStatus)) {
            if (StringUtils.equals(currentStatus, PcdStatus.WAIT_CLEAN_KEY) &&
                    StringUtils.equals(preStatus, PcdStatus.ASSEMBLY_KEY)) {
                btnMap.put("toFreeBtn", true);
            } else {
                btnMap.put("toFreeBtn", isPcdChangeStatus(facilityRrn, pcdRrn, currentStatus, PcdStatus.FREE_KEY));
            }
        } else {
            if (StringUtils.equals(currentStatus, PcdStatus.WAIT_CLEAN_KEY) && assembly != null &&
                    StringUtils.isNotBlank(assembly.getCarrierId())) {
                btnMap.put("toFreeBtn", true);
            } else {
                btnMap.put("toFreeBtn", isPcdChangeStatus(facilityRrn, pcdRrn, currentStatus, PcdStatus.FREE_KEY));
            }
        }

        btnMap.put("toInDamageBtn", isPcdChangeStatus(facilityRrn, pcdRrn, currentStatus, PcdStatus.IN_DAMAGE_KEY));
        btnMap.put("toScrapBtn", isPcdChangeStatus(facilityRrn, pcdRrn, currentStatus, PcdStatus.SCRAP_KEY));
        btnMap.put("toWatiCleanBtn", isPcdChangeStatus(facilityRrn, pcdRrn, currentStatus, PcdStatus.WAIT_CLEAN_KEY));
        btnMap.put("toInCleanBtn", isPcdChangeStatus(facilityRrn, pcdRrn, currentStatus, PcdStatus.IN_CLEAN_KEY));
        btnMap.put("toWaitCheckBtn", isPcdChangeStatus(facilityRrn, pcdRrn, currentStatus, PcdStatus.WAIT_CHECK_KEY));
        btnMap.put("toHoldBtn", isPcdChangeStatus(facilityRrn, pcdRrn, currentStatus, PcdStatus.HOLD_KEY));
        btnMap.put("toStartCleanBtn", isPcdChangeStatus(facilityRrn, pcdRrn, currentStatus, PcdStatus.IN_CLEAN_KEY));
        btnMap.put("toEndCleanBtn", isPcdChangeStatus(facilityRrn, pcdRrn, currentStatus, PcdStatus.FREE_KEY));
        return btnMap;
    }

    @Override
    public boolean isPcdChangeStatus(Long facilityRrn, Long pcdRrn, String currentStatus, String tragetStatus) {
        String autoChangeStatusRefId = "$$AUTO_CHANGE_STATUS";
        String refNamedSpace = namedObjectManager.getNamedSpace(facilityRrn, ObjectList.REFERENCE_FILE_KEY);
        // 获取设置的只有系统能自动执行的事件模型
        List<ReferenceFileDetail> refColl = referenceFileManager.getRefFileValues(autoChangeStatusRefId, refNamedSpace);
        List<Map> eventInfoList = eventManager.getEventInfoList(facilityRrn, pcdRrn, currentStatus, tragetStatus);
        if (CollectionUtils.isNotEmpty(eventInfoList)) {
            Map<String, Object> eventMap = eventInfoList.get(0);
            if (CollectionUtils.isNotEmpty(refColl)) {
                for (ReferenceFileDetail de : refColl) {
                    if (StringUtils.isNotBlank(de.getData2Value())) {
                        // 如果有相同的,则代表是只有系统自动使用的,用户不可以手动修改
                        if (StringUtils.equals(de.getKey1Value(), MapUtils.getString(eventMap, "eventId"))) {
                            return false;
                        }
                    }
                }
            }
            return true;
        } else {
            return false;
        }
    }

    @Override
    public Map getPcdDefaultCleanCycleInfo(String category, String type) {
        return pcdDAO.getPcdDefaultCleanCycleInfo(category, type);
    }

    @Override
    public void deletePcdCleanInfo(Long pcdRrn, Long transRrn) {
        if (pcdRrn != null && pcdRrn > 0) {
            PcdClean cleanInfo = pcdDAO.getPcdCleanInfo(pcdRrn);
            pcdDAO.deletePcdCleanInfo(pcdRrn);
            pcdDAO.insertPcdCleanH(transRrn, cleanInfo);
        }
    }

    @Override
    public int insertPcdCleanH(Long transRrn, PcdClean cleanInfo) {
        return pcdDAO.insertPcdCleanH(transRrn, cleanInfo);
    }

    @Override
    public void insertPcdNamedObjectExt(Long pcdRrn, String flagType, String dmm_type) {
        pcdDAO.insertPcdNamedObjectExt(pcdRrn, flagType, dmm_type);
    }

    @Override
    public void updatePcdNamedObjectExt(Long pcdRrn, String flagType) {
        pcdDAO.updatePcdNamedObjectExt(pcdRrn, flagType);
    }

    @Override
    public void addPcdCleanInfo(Long pcdRrn, Long transRrn, Long facilityRrn, String pcdType, double cleanCycle) {
        if (pcdRrn != null && pcdRrn > 0) {
            PcdClean cleanInfo = new PcdClean();
            cleanInfo.setInstanceRrn(pcdRrn);
            cleanInfo.setCleanCycle(cleanCycle);
            cleanInfo.setPcdType(pcdType);
            long currentTime = System.currentTimeMillis();
            long cleanOverTime = (long) (currentTime + (cleanCycle * 24 * 60 * 60 * 1000));
            cleanInfo.setCleanOverDate(new Timestamp(cleanOverTime));
            cleanInfo.setCleanCount(0);
            pcdDAO.insertPcdCleanInfo(cleanInfo);
            pcdDAO.insertPcdCleanH(transRrn, cleanInfo);
        }
    }

    @Override
    public int countPcdCategoryByRrn(Long pcdRrn) {
        return pcdDAO.countPcdCategoryByRrn(pcdRrn);
    }

    @Override
    public void updatePcdCleanInfo(Long transRrn, PcdClean updateCleanInfo) {
        if (updateCleanInfo != null && updateCleanInfo.getInstanceRrn() > 0) {
            PcdClean existCleanInfo = getPcdCleanInfo(updateCleanInfo.getInstanceRrn());
            if (existCleanInfo != null && existCleanInfo.getInstanceRrn() > 0) {
                if (updateCleanInfo.getCleanCycle() > 0) {
                    existCleanInfo.setCleanCycle(updateCleanInfo.getCleanCycle());
                    // 如果需要更新清洗周期,则需要重新计算清洗有效截止时间
                    long cleanOverTime = 0;
                    if (existCleanInfo.getOutCleanDate() != null && existCleanInfo.getOutCleanDate().getTime() > 0) {
                        cleanOverTime = (long) (existCleanInfo.getOutCleanDate().getTime() +
                                (updateCleanInfo.getCleanCycle() * 24 * 60 * 60 * 1000));
                    } else {
                        // 没有清洗记录,使用创建时间计算
                        long currentTime = 0;
                        if (StringUtils.equalsIgnoreCase(existCleanInfo.getPcdType(), ObjectList.CARRIER_KEY)) {
                            Map<String, Object> carrierMap = carrierManager
                                    .qryCarrierInfoDetailById(updateCleanInfo.getInstanceId(),
                                                              updateCleanInfo.getNamedSpace());
                            currentTime = DateUtils.parse(MapUtils.getString(carrierMap, "carrierCreateTime"),
                                                          DateUtils.DATE_FORMAT4DATE).getTime();
                        } else if (StringUtils.equalsIgnoreCase(existCleanInfo.getPcdType(), ObjectList.POD_KEY)) {
                            Map<String, Object> podMap = podManager
                                    .qryPodInfoDetailById(updateCleanInfo.getInstanceId(),
                                                          updateCleanInfo.getNamedSpace());
                            currentTime = DateUtils
                                    .parse(MapUtils.getString(podMap, "podCreateTime"), DateUtils.DATE_FORMAT4DATE)
                                    .getTime();
                        } else if (StringUtils.equalsIgnoreCase(existCleanInfo.getPcdType(), ObjectList.DOOR_KEY)) {
                            Map<String, Object> doorMap = doorManager
                                    .qrysDoorInfoDetailById(updateCleanInfo.getInstanceId(),
                                                            updateCleanInfo.getNamedSpace());
                            currentTime = DateUtils
                                    .parse(MapUtils.getString(doorMap, "doorCreateTime"), DateUtils.DATE_FORMAT4DATE)
                                    .getTime();
                        }
                        cleanOverTime = (long) (currentTime + (updateCleanInfo.getCleanCycle() * 24 * 60 * 60 * 1000));
                    }
                    updateCleanInfo.setCleanOverDate(new Timestamp(cleanOverTime));
                    existCleanInfo.setCleanOverDate(new Timestamp(cleanOverTime));
                }
                if (updateCleanInfo.getInCleanDate() != null) {
                    existCleanInfo.setInCleanDate(updateCleanInfo.getInCleanDate());
                }
                if (updateCleanInfo.getOutCleanDate() != null) {
                    existCleanInfo.setOutCleanDate(updateCleanInfo.getOutCleanDate());
                } else {
                    updateCleanInfo.setOutCleanDate(existCleanInfo.getOutCleanDate());
                }
                if (StringUtils.isNotBlank(updateCleanInfo.getPcdType())) {
                    existCleanInfo.setPcdType(updateCleanInfo.getPcdType());
                }
                updateCleanInfo.setCleanCount(existCleanInfo.getCleanCount());

                pcdDAO.updatePcdCleanInfo(updateCleanInfo);
                pcdDAO.insertPcdCleanH(transRrn, existCleanInfo);
            }
        }
    }

    @Override
    public void inCleanPcd(String cleanEqptId, Long pcdRrn, String performedBy, Long facilityRrn) {
        cleanPcd(cleanEqptId, pcdRrn, performedBy, "in");
    }

    @Override
    public void outCleanPcd(Long pcdRrn, String performedBy, Long facilityRrn) {
        cleanPcd(StringUtils.EMPTY, pcdRrn, performedBy, "out");
    }

    @Override
    public PcdAssembly getPcdAssembly(Long carrierRrn) {
        return pcdDAO.getPcdAssembly(carrierRrn);
    }

    @Override
    public void deAssemblyPcd(Long carrierRrn, String user, Long transRrn) {
        TransactionLog transactionLog = null;
        if (transRrn == null || transRrn <= 0) {
            transactionLog = transactionLogManager.startTransactionLog(user, TransactionNames.CST_DEASSEMBLY);
            transRrn = transactionLog.getTransRrn();
        }
        pcdDAO.insertPcdAssemblyH(carrierRrn, user, transRrn);
        pcdDAO.deletePcdAssembly(carrierRrn);
        if (transactionLog != null) {
            transactionLogManager.markTransactionLog(transactionLog);
        }
    }

    @Override
    public void deAssemblyPcd(Long carrierRrn, Long podRrn, Long doorRrn, String user, Long facilityRrn) {
        TransactionLog transactionLog = transactionLogManager.startTransactionLog(user,
                                                                                  TransactionNames.CST_DEASSEMBLY);
        long transRrn = transactionLog.getTransRrn();
        Carrier carrier = carrierManager.getCarrier(carrierRrn);
        boolean isWaitClean = false;
        if (StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.WAIT_CLEAN_KEY)) {
            //wait clean 状态不变
            // statusManager.changePCDStatusByEvent(facilityRrn, user, carrierRrn, ObjectList.CARRIER_KEY,
            //                                      EventName.CST_WAIT_CLEAN_TO_FREE, "Deassembly");
            isWaitClean=true;
        } else {
            statusManager.changePCDStatusByEvent(facilityRrn, user, carrierRrn, ObjectList.CARRIER_KEY,
                                                 EventName.CST_ASSEMBLY_TO_FREE, "Deassembly");
        }
        if (podRrn != null && podRrn > 0) {
            POD pod = podManager.getPod(podRrn);
            if (StringUtils.equalsIgnoreCase(pod.getPodStatus(), PcdStatus.WAIT_CLEAN_KEY)) {
                //wait clean 状态不变
                // statusManager.changePCDStatusByEvent(facilityRrn, user, podRrn, ObjectList.POD_KEY,
                //                                      EventName.POD_WAIT_CLEAN_TO_FREE, "Deassembly");
                isWaitClean=true;
            } else {
                statusManager.changePCDStatusByEvent(facilityRrn, user, podRrn, ObjectList.POD_KEY,
                                                     EventName.POD_ASSEMBLY_TO_FREE, "Deassembly");
            }
        }


        /** by #40125 */
        if (doorRrn != null && doorRrn > 0) {
            Door door = doorManager.getDoor(doorRrn);
            if (StringUtils.equalsIgnoreCase(door.getDoorStatus(), PcdStatus.WAIT_CLEAN_KEY)) {
                //wait clean 状态不变
                // statusManager.changePCDStatusByEvent(facilityRrn, user, doorRrn, ObjectList.DOOR_KEY,
                //                                      EventName.DOOR_WAIT_CLEAN_TO_FREE, "Deassembly");
                isWaitClean=true;
            } else {
                statusManager.changePCDStatusByEvent(facilityRrn, user, doorRrn, ObjectList.DOOR_KEY,
                                                     EventName.DOOR_ASSEMBLY_TO_FREE, "Deassembly");
            }
        }

        deAssemblyPcd(carrierRrn, user, transRrn);
        // 在PCD解绑的时候一次性同步carrier信息到POD以及DOOR,平时信息查询通过PCD关联关系查询
        updatePcdByDeassembly(carrierRrn, podRrn, doorRrn, transRrn,isWaitClean);
        transactionLogManager.markTransactionLog(transactionLog);
    }

    private void updatePcdByDeassembly(Long carrierRrn, Long podRrn, Long doorRrn, Long transRrn,boolean isWaitClean) {
        // 在PCD解绑的时候一次性同步carrier信息到POD以及DOOR,平时信息查询通过PCD关联关系查询
        Carrier carrier = carrierManager.getCarrier(carrierRrn);
        POD pod = podRrn == null || podRrn == 0 ? null : podManager.getPod(podRrn);
        Door door = doorRrn == null || doorRrn == 0 ? null : doorManager.getDoor(doorRrn);
        //因为解除绑定是要判断当前状态是否为 wait_clean 状态
        //1.如果整体状态属于wait_clean 则意味着所有备件都要进行清理
        //2.简单粗暴的方式就是直接更改pcd_clean 表的时间,所有组件时间按照清理时间去更新.
        //3.首先状态不能转换成free 等其他状态.
        //4.获取清理时间 全部更换
        String groupState =isWaitClean?PcdStatus.WAIT_CLEAN_KEY: PcdStatus.FREE_KEY;
        if (isWaitClean) {//判断是否变更为 wait_clean
            //代码进到这 意味着 所有组件时间都要更新到清理时间
            List<Long> cleanOverTimes =new ArrayList<>();
            List<Consumer<Timestamp>> callback=new ArrayList<>();
            TransactionLog transactionLog=  transactionLogManager.startTransactionLog("System","Deassembly Wait Clean");
            final BiConsumer<Timestamp,Long> updateFunction=(time,rrn)->{
                pcdDAO.updateCleanOverTimeByRrn(rrn,time);
                PcdClean pcdClean = new PcdClean();
                pcdClean.setInstanceRrn(rrn);
                pcdClean.setCleanOverDate(time);
                pcdDAO.insertPcdCleanH(transactionLog.getTransRrn(),pcdClean);
            };
            callback.add((time)->{updateFunction.accept(time,carrierRrn);});
            cleanOverTimes.add(getPcdCleanInfo(carrierRrn).getCleanOverDate().getTime());
            if(pod!=null){
                cleanOverTimes.add(getPcdCleanInfo(podRrn).getCleanOverDate().getTime());
                callback.add((time)->{updateFunction.accept(time,podRrn);});
            }
            if(door!=null){
                cleanOverTimes.add(getPcdCleanInfo(doorRrn).getCleanOverDate().getTime());
                callback.add((time)->{updateFunction.accept(time,doorRrn);});
            }
            Long minTime = cleanOverTimes.stream().min(Long::compare).get();
            final Timestamp minTimeStamp=  new Timestamp(minTime);//最终全部改成这个时间
            callback.forEach((e)->e.accept(minTimeStamp));//更新
            transactionLogManager.markTransactionLog(transactionLog);
        }
        carrier.setCarrierStatus(groupState);
        final String commons = "Deassembly to "+groupState;
        //pod
        if (pod != null) {
            pod.setPodStatus(groupState);
            pod.setPollutionLevel(carrier.getPollutionLevel());
            pod.setMcsMoveStatus(carrier.getMcsMoveStatus());
            pod.setMcsLastMoveTime(carrier.getMcsLastMoveTime());
            pod.setMcsLastOperatorRrn(carrier.getMcsLastOperatorRrn());
            pod.setEqptRrn(carrier.getEqptRrn());
            pod.setJobRrn(carrier.getJobRrn());
            pod.setRunRrn(carrier.getRunRrn());
            pod.setCarrierMapRrn(carrier.getCarrierMapRrn());
            pod.setLoadPosition(carrier.getLoadPosition());
            podManager.updatePodByDeassembly(transRrn, pod);//此函数跟踪后 内部已经插入 POD_H流水数据
        }
        //door
        /** by #40125 */
        if (doorRrn != null && doorRrn > 0) {
            door.setDoorStatus(groupState);
            door.setPollutionLevel(carrier.getPollutionLevel());
            door.setMcsMoveStatus(carrier.getMcsMoveStatus());
            door.setMcsLastMoveTime(carrier.getMcsLastMoveTime());
            door.setMcsLastOperatorRrn(carrier.getMcsLastOperatorRrn());
            door.setEqptRrn(carrier.getEqptRrn());
            door.setJobRrn(carrier.getJobRrn());
            door.setRunRrn(carrier.getRunRrn());
            door.setCarrierMapRrn(carrier.getCarrierMapRrn());
            door.setLoadPosition(carrier.getLoadPosition());
            doorManager.updateDoorByDeassembly(transRrn, door);//此函数跟踪后 内部已经插入 DOOR_H流水数据
        }
        carrierManager.updateCarrierAvailableSlotCount(carrierRrn);
        carrierManager.insertCarrierH(carrierRrn,commons,transRrn);
        statusManager.updateCarrierStatus(carrierRrn, transRrn, groupState, "Deassembly");
    }

    @Override
    public void assemblyPcd(Long carrierRrn, Long podRrn, Long doorRrn, String user, Long facilityRrn) {
        TransactionLog transactionLog = transactionLogManager.startTransactionLog(user, TransactionNames.CST_ASSEMBLY);
        long transRrn = transactionLog.getTransRrn();

        statusManager.changePCDStatusByEvent(facilityRrn, user, carrierRrn, ObjectList.CARRIER_KEY,
                                             EventName.CST_FREE_TO_ASSEMBLY, "Assembly");
        carrierManager.insertCarrierH(carrierRrn,PcdStatus.ASSEMBLY_KEY,transRrn);//插入流水
        if(podRrn!=null&&podRrn>0){
            statusManager
                    .changePCDStatusByEvent(facilityRrn, user, podRrn, ObjectList.POD_KEY, EventName.POD_FREE_TO_ASSEMBLY,
                                            "Assembly");
            podManager.insertPodH(podRrn,PcdStatus.ASSEMBLY_KEY,transRrn);//插入流水
        }
        if (doorRrn != null && doorRrn > 0) {
            statusManager.changePCDStatusByEvent(facilityRrn, user, doorRrn, ObjectList.DOOR_KEY,
                                                 EventName.DOOR_FREE_TO_ASSEMBLY, "Assembly");
            doorManager.insertDoorH(doorRrn,PcdStatus.ASSEMBLY_KEY,transRrn);//插入流水
        }
        //插入pcd_Assembly 插入历史表
        pcdDAO.insertPcdAssembly(carrierRrn, podRrn, doorRrn, user);
        pcdDAO.insertPcdAssemblyH(carrierRrn, podRrn, doorRrn, user, transRrn);

        //修改pdc 组件状态
        statusManager.updatePcdStatus(carrierRrn, podRrn, doorRrn, PcdStatus.ASSEMBLY_KEY, transRrn, "Assembly");
        transactionLogManager.markTransactionLog(transactionLog);
    }

    @Override
    public Boolean isAssembledPCD(Long instanceRrn) {
        return pcdDAO.isAssembledPCD(instanceRrn);
    }

    @Override
    public List<Map> getHoldReasons(long instanceRrn) {
        return pcdDAO.getHoldReasons(instanceRrn);
    }

    @Override
    public void insertMultipleHold(long instanceRrn, long holdBy, long transRrn) {
        pcdDAO.insertMultipleHold(instanceRrn, holdBy, transRrn);
    }

    @Override
    public void insertTransReason(long transRrn, long instanceRrn, TransReason transReason, int reasonCodeSequence) {
        pcdDAO.insertTransReason(transRrn, instanceRrn, transReason, reasonCodeSequence);
    }

    @Override
    public void deleteMultipleHold(long instanceRrn, long sequence) {
        pcdDAO.deleteMultipleHold(instanceRrn, sequence);
    }

    @Override
    public List<Map> getHoldReasonCodes(String holdReasonGroupID, List<String> holdReasonRoles,
                                        String referenceFileId) {
        return pcdDAO.getHoldReasonCodes(holdReasonGroupID, holdReasonRoles, referenceFileId);
    }

    @Override
    public List<Map> getReleaseGroup(String holdCode, String classTableValue) {
        return pcdDAO.getReleaseGroup(holdCode, classTableValue);
    }

    @Override
    public List<Map> getReleaseReasonCodes(String releaseReasonGroupID, List releaseRoles, String referenceFileId) {
        return pcdDAO.getReleaseReasonCodes(releaseReasonGroupID, releaseRoles, referenceFileId);
    }

    @Override
    public void resetCleanCycle(List<Map<String, Object>> defaultCleanCycleList, Long facilityRrn, String user) {
        // update之前,获取设置的clean cycle
        List<Map<String, Object>> allCycleList = pcdDAO.getAllPcdDefaultCleanCycle();
        pcdDAO.batchInsertOrUpdatePcdDefaultCleanCycle(defaultCleanCycleList);
        // 重置默认周期后,同步修改所有P/C/D的周期信息 PS:source为旧配置,target为新的配置
        TransactionLog createTrans = transactionLogManager.startTransactionLog(user, TransactionNames.CREATE_KEY);

        TransactionLog modifyTrans = transactionLogManager.startTransactionLog(user, TransactionNames.MODIFY_KEY);

        for (Map<String, Object> targetCycleMap : defaultCleanCycleList) {
            String targetCategory = MapUtils.getString(targetCycleMap, "categoryCode");
            String targetCategoryDesc = MapUtils.getString(targetCycleMap, "categoryDesc");
            String targetType = MapUtils.getString(targetCycleMap, "typeCode");
            String targetTypeDesc = MapUtils.getString(targetCycleMap, "typeDesc");
            String targetDefaultCycle = MapUtils.getString(targetCycleMap, "defaultCleanCycle");
            double targetDefaultCycleD = NumberUtils.toDouble(targetDefaultCycle);

            boolean updateFlag = true;
            for (Map<String, Object> sourceCycleMap : allCycleList) {
                String sourceCategory = MapUtils.getString(sourceCycleMap, "category");
                String sourceType = MapUtils.getString(sourceCycleMap, "type");
                String sourceDefaultCycle = MapUtils.getString(sourceCycleMap, "defaultCleanCycle");

                if (StringUtils.equals(sourceCategory, targetCategory) && StringUtils.equals(sourceType, targetType)) {
                    if (!StringUtils.equals(sourceDefaultCycle, targetDefaultCycle)) {
                        // 如果相同的category和type下的defaultCleanCycle有改动,则修改所有相关的P/C/D
                        updateAllPCDCleanCycleByCategoryAndType(facilityRrn, user, targetCategory, targetType,
                                                                targetDefaultCycleD);
                        // modify history
                        pcdDAO.addPcdDefaultCleanCycleHistory(modifyTrans.getTransRrn(), targetCategoryDesc,
                                                              targetTypeDesc, targetDefaultCycleD, user);
                    }
                    updateFlag = false;
                    break;
                } else {
                    updateFlag = true;
                }
            }
            if (updateFlag) {
                // 走到此处,表示有新的配置是之前没有的,则直接根据category和type更新cycle信息
                updateAllPCDCleanCycleByCategoryAndType(facilityRrn, user, targetCategory, targetType,
                                                        targetDefaultCycleD);
                // create history
                pcdDAO.addPcdDefaultCleanCycleHistory(createTrans.getTransRrn(), targetCategoryDesc, targetTypeDesc,
                                                      targetDefaultCycleD, user);
            }
        }

        transactionLogManager.markTransactionLog(createTrans);
        transactionLogManager.markTransactionLog(modifyTrans);
    }

    @Override
    public List<ReferenceFileDetail> getCleanEqptRefDetailInfo(String eqptId, Long facilityRrn) {
        if (StringUtils.isBlank(eqptId)) {
            return new ArrayList<>();
        }

        List<ReferenceFileDetail> refDetailList = new ArrayList<ReferenceFileDetail>();
        List<ReferenceFileDetail> deatilColl = referenceFileManager
                .getRefFileValues(PCD_CLEAN_EQPT_REFERENCE_ID, facilityRrn);
        if (CollectionUtils.isNotEmpty(deatilColl)) {
            for (ReferenceFileDetail detail : deatilColl) {
                if (StringUtils.equals(detail.getData1Value(), eqptId)) {
                    refDetailList.add(detail);
                }
            }
        }
        return refDetailList;
    }

    @Override
    public String checkCleanEqptIsInUse(String eqptId, Long facilityRrn) {
        String entityNamedSpace = namedObjectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY);
        List<PcdClean> list = getPcdInfoByCleanEqpt(eqptId, entityNamedSpace);
        if (CollectionUtils.isNotEmpty(list)) {
            String msg = I18nUtils.getMessage(MessageIdList.PCD_EQPT_IS_CLEANING, "Equipment is cleaning.", eqptId);
            String cleaningP = "P:";
            String cleaningC = "C:";
            String cleaningD = "D:";
            for (PcdClean pc : list) {
                if (StringUtils.equals(pc.getPcdType(), ObjectList.POD_KEY)) {
                    cleaningP += pc.getInstanceId() + " ";
                }
                if (StringUtils.equals(pc.getPcdType(), ObjectList.CARRIER_KEY)) {
                    cleaningC += pc.getInstanceId() + " ";
                }
                if (StringUtils.equals(pc.getPcdType(), ObjectList.DOOR_KEY)) {
                    cleaningD += pc.getInstanceId() + " ";
                }
            }

            if (StringUtils.length(cleaningP) > 3) {
                msg += cleaningP + " ";
            }
            if (StringUtils.length(cleaningC) > 3) {
                msg += cleaningC + " ";
            }
            if (StringUtils.length(cleaningD) > 3) {
                msg += cleaningD + " ";
            }
            return msg;
        }
        return "";
    }

    @Override
    public List<PcdClean> getCleaningPcdByEqpt(String eqptId, Long facilityRrn) {
        List<PcdClean> list = new ArrayList<PcdClean>();
        String entityNamedSpace = namedObjectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY);
        list = getPcdInfoByCleanEqpt(eqptId, entityNamedSpace);
        return list;
    }

    @Override
    public void batchOutClean(Long facilityRrn, String eqptId, String cstIds, String podIds, String doorIds,
                              String user) {
        List<ReferenceFileDetail> eqptList = getCleanEqptRefDetailInfo(eqptId, facilityRrn);
        if (CollectionUtils.isNotEmpty(eqptList)) {
            String eqptCleaningMsg = checkCleanEqptIsInUse(eqptId, facilityRrn);
            Assert.isFalse(StringUtils.isBlank(eqptCleaningMsg),
                           Errors.create().key(MessageIdList.PCD_NO_CLEANING_PCD).content("当前设备没有正在清洗的PCD!").build());

            double cstMinMinutes = 0;
            double podMinMinutes = 0;
            double doorMinMinutes = 0;
            for (ReferenceFileDetail eqpt : eqptList) {
                double minMinutes = Double.parseDouble(eqpt.getData4Value());
                if (StringUtils.equals(eqpt.getData2Value(), "CASSETTE")) {
                    cstMinMinutes = minMinutes;
                } else if (StringUtils.equals(eqpt.getData2Value(), ObjectList.POD_KEY)) {
                    podMinMinutes = minMinutes;
                } else if (StringUtils.equals(eqpt.getData2Value(), ObjectList.DOOR_KEY)) {
                    doorMinMinutes = minMinutes;
                }
            }

            String[] cstArray = StringUtils.split(cstIds, ",");
            String[] podArray = StringUtils.split(podIds, ",");
            String[] doorArray = StringUtils.split(doorIds, ",");

            IsPcdCleaningDateAboveThanMinMinutes(podArray, podMinMinutes, facilityRrn);

            IsPcdCleaningDateAboveThanMinMinutes(cstArray, cstMinMinutes, facilityRrn);

            IsPcdCleaningDateAboveThanMinMinutes(doorArray, doorMinMinutes, facilityRrn);

            // batch out clean
            if (cstArray.length > 0) {
                batchOutCleanByPcd(eqptId, cstArray, ObjectList.CARRIER_KEY, EventName.CST_IN_CLEAN_TO_FREE,
                                   facilityRrn, user);
            }
            if (podArray.length > 0) {
                batchOutCleanByPcd(eqptId, podArray, ObjectList.POD_KEY, EventName.POD_IN_CLEAN_TO_FREE, facilityRrn,
                                   user);
            }
            if (doorArray.length > 0) {
                batchOutCleanByPcd(eqptId, doorArray, ObjectList.DOOR_KEY, EventName.DOOR_IN_CLEAN_TO_FREE, facilityRrn,
                                   user);
            }
        }
    }

    @Override
    public List<Map<String, Object>> getAllPcdCategory(Long facilityRrn) {
        List<ReferenceFileDetail> categoryColl = referenceFileManager.getRefFileValues("$PCD_CATEGORY",
                                                                                       namedObjectManager.getNamedSpace(
                                                                                               facilityRrn,
                                                                                               ObjectList.REFERENCE_FILE_KEY));

        List<Map<String, Object>> categoryList = new ArrayList<Map<String, Object>>();
        if (CollectionUtils.isNotEmpty(categoryColl)) {
            for (ReferenceFileDetail detail : categoryColl) {
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("categoryCode", detail.getKey1Value());
                map.put("categoryDesc", detail.getData1Value());

                categoryList.add(map);
            }
            return categoryList;
        }
        return new ArrayList<Map<String, Object>>();
    }

    @Override
    public List<Map<String, Object>> getAllPcdType(Long facilityRrn) {
        List<ReferenceFileDetail> categoryColl = referenceFileManager.getRefFileValues("$CARRIER_TYPE",
                                                                                       namedObjectManager.getNamedSpace(
                                                                                               facilityRrn,
                                                                                               ObjectList.REFERENCE_FILE_KEY));

        List<Map<String, Object>> categoryList = new ArrayList<Map<String, Object>>();
        if (CollectionUtils.isNotEmpty(categoryColl)) {
            for (ReferenceFileDetail detail : categoryColl) {
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("typeCode", detail.getKey1Value());
                map.put("typeDesc", detail.getData1Value());
                map.put("pcdFlag", detail.getData2Value());
                categoryList.add(map);
            }
            return categoryList;
        }
        return new ArrayList<Map<String, Object>>();
    }

    @Override
    public List<PcdClean> getPcdInfoByCleanEqpt(String eqptId, String entityNamedSpace) {
        return pcdDAO.getPcdInfoByCleanEqpt(eqptId, entityNamedSpace);
    }

    @Override
    public void batchInClean(Long facilityRrn, String eqptId, String cstIds, String podIds, String doorIds,
                             String user) {
        List<ReferenceFileDetail> eqptList = getCleanEqptRefDetailInfo(eqptId, facilityRrn);
        Assert.isTrue(CollectionUtils.isNotEmpty(eqptList),
                      Errors.create().key(MessageIdList.PCD_CLEAN_EQPT_MISSING).content("清洗作业设备不存在!").build());

        String eqptCleaningMsg = checkCleanEqptIsInUse(eqptId, facilityRrn);
        Assert.isFalse(StringUtils.isNotBlank(eqptCleaningMsg), Errors.create().content(eqptCleaningMsg).build());

        String[] cstArray = StringUtils.split(cstIds, ",");
        String[] podArray = StringUtils.split(podIds, ",");
        String[] doorArray = StringUtils.split(doorIds, ",");

        int cstMaxCount = 0;
        int podMaxCount = 0;
        int doorMaxCount = 0;
        for (ReferenceFileDetail eqpt : eqptList) {
            int maxCount = Integer.parseInt(eqpt.getData3Value());
            if (StringUtils.equals(eqpt.getData2Value(), "CASSETTE")) {
                cstMaxCount = maxCount;
            } else if (StringUtils.equals(eqpt.getData2Value(), ObjectList.POD_KEY)) {
                podMaxCount = maxCount;
            } else if (StringUtils.equals(eqpt.getData2Value(), ObjectList.DOOR_KEY)) {
                doorMaxCount = maxCount;
            }
        }

        Assert.isFalse(cstArray.length > cstMaxCount,
                       Errors.create().key(MessageIdList.PCD_CASSETTE_OUT_OF_MAX).content("Cassette超出最大计数!").build());
        Assert.isFalse(podArray.length > podMaxCount,
                       Errors.create().key(MessageIdList.PCD_POD_OUT_OF_MAX).content("Cassette超出最大计数!").build());
        Assert.isFalse(doorArray.length > doorMaxCount,
                       Errors.create().key(MessageIdList.PCD_DOOR_OUT_OF_MAX).content("Cassette超出最大计数!").build());

        // 检查PCD是否都存在并且是未绑定的空P/C/D
        if (cstArray.length > 0 && StringUtils.isNotBlank(cstArray[0])) {
            checkPcdIsExist(cstArray, facilityRrn);
        }

        if (podArray.length > 0 && StringUtils.isNotBlank(podArray[0])) {
            checkPcdIsExist(podArray, facilityRrn);

        }

        if (doorArray.length > 0 && StringUtils.isNotBlank(doorArray[0])) {
            checkPcdIsExist(doorArray, facilityRrn);
        }

        // batch in clean
        if (cstArray.length > 0) {
            batchInCleanByPcd(eqptId, cstArray, ObjectList.CARRIER_KEY, EventName.CST_WAIT_CLEAN_TO_IN_CLEAN,
                              facilityRrn, user);
        }
        if (podArray.length > 0) {
            batchInCleanByPcd(eqptId, podArray, ObjectList.POD_KEY, EventName.POD_WAIT_CLEAN_TO_IN_CLEAN, facilityRrn,
                              user);
        }
        if (doorArray.length > 0) {
            batchInCleanByPcd(eqptId, doorArray, ObjectList.DOOR_KEY, EventName.DOOR_WAIT_CLEAN_TO_IN_CLEAN,
                              facilityRrn, user);
        }
    }

    @Override
    public void checkPcdIsValid(Long carrierRrn, Long facilityRrn) {
        String cleanInvalidMsg = checkPcdIsValidReturnString(carrierRrn, facilityRrn);

        Assert.isFalse(StringUtils.isNotBlank(cleanInvalidMsg),
                       Errors.create().content(cleanInvalidMsg).build());

        Map<String, Object> resultMap = checkNotAllowUsePcdStatusByCarrier(carrierRrn, facilityRrn);
        Assert.isTrue(MapUtils.getBooleanValue(resultMap, "result", true),
                      Errors.create().content(MapUtils.getString(resultMap, "msg", "Invalid Entity")).build());
    }

    /**
     * 通过carrierRrn检查OPEN-CST或PCD当前状态是否允许使用
     *
     * @return 返回一个map,包含两个key(1.result,2.msg) result为boolean类型,true 表示允许,false 表示不允许 msg为String类型,包含不允许的原因
     */
    @Override
    public Map<String, Object> checkNotAllowUsePcdStatusByCarrier(Long carrierRrn, Long facilityRrn) {
        Map<String, Object> resultMap = new HashMap<String, Object>();
        resultMap.put("result", true);
        String refId = "$$NOT_ALLOW_USE_CASSETTE_STATUS";
        String refNamedSpace = namedObjectManager.getNamedSpace(facilityRrn, ObjectList.REFERENCE_FILE_KEY);
        List<ReferenceFileDetail> notAllowStatusColl = referenceFileManager.getRefFileValues(refId, refNamedSpace);
        if (CollectionUtils.isEmpty(notAllowStatusColl)) {
            return resultMap;
        }

        PcdAssembly pcd = this.getPcdAssembly(carrierRrn, null, null);
        // 如果是PCD组装的,则逐个检查每个组件
        if (pcd != null && StringUtils.isNotBlank(pcd.getPodId())) {
            Long podRrn = pcd.getPodRrn();
            Long doorRrn = pcd.getDoorRrn();
            Carrier c = carrierManager.getCarrier(carrierRrn);
            POD p = podRrn==null||podRrn==0?null:podManager.getPod(podRrn);
            Door d = doorRrn==null||doorRrn==0?null:doorManager.getDoor(doorRrn);

            for (ReferenceFileDetail refDetail : notAllowStatusColl) {
                if (StringUtils.equalsIgnoreCase(c.getCarrierStatus(), refDetail.getKey1Value())) {
                    resultMap.put("result", false);
                    resultMap.put("msg", "Invalid " + c.getInstanceId() + " status: " + refDetail.getKey1Value());
                    break;
                }
                if (p!=null&&StringUtils.equalsIgnoreCase(p.getPodStatus(), refDetail.getKey1Value())) {
                    resultMap.put("result", false);
                    resultMap.put("msg", "Invalid " + p.getInstanceId() + " status: " + refDetail.getKey1Value());
                    break;
                }
                if (d!=null&&StringUtils.equalsIgnoreCase(d.getDoorStatus(), refDetail.getKey1Value())) {
                    resultMap.put("result", false);
                    resultMap.put("msg", "Invalid " + d.getInstanceId() + " status: " + refDetail.getKey1Value());
                    break;
                }
            }
        } else {
            for (ReferenceFileDetail refDetail : notAllowStatusColl) {
                Carrier c = carrierManager.getCarrier(carrierRrn);
                if (StringUtils.equalsIgnoreCase(c.getCarrierStatus(), refDetail.getKey1Value())) {
                    resultMap.put("result", false);
                    resultMap.put("msg", "Invalid " + c.getInstanceId() + " status: " + refDetail.getKey1Value());
                    break;
                }
            }
        }
        return resultMap;
    }

    @Override
    public double getDefaultCleanCycle(String category, String type, Long facilityRrn) {
        if (StringUtils.isNotBlank(category) && StringUtils.isNotBlank(type)) {
            Map<String, Object> map = getPcdDefaultCleanCycleInfo(category, type);
            return MapUtils.getDoubleValue(map, "defaultCleanCycle");
        }
        return 0;
    }

    @Override
    public String generateCarrierId(String prefix) {
        String subfix = pcdDAO.getCarrierIdSerial(prefix);
        Integer serialNumber = Integer.parseInt(subfix);
        Assert.isFalse(serialNumber >= 9999,
                       Errors.create().key(MessageIdList.CARRIER_ID_OVERFLOW).content("晶舟号已超过系统定义,请联系管理员!").build());
        serialNumber++;
        DecimalFormat dfSuffix = new DecimalFormat("0000");
        subfix = dfSuffix.format(serialNumber);
        return prefix + subfix;
    }

    @Override
    public String generateCarrierId(String prefix, int serialLength) {
        String zerosPattern = "";
        String maxSerial = "";
        for (int i = 0; i < serialLength; i++) {
            maxSerial += "9";
            zerosPattern += "0";
        }
        String subfix = pcdDAO.getCarrierIdSerial(prefix, serialLength);
        Long serialNumber = Long.parseLong(subfix);
        Assert.isFalse(serialNumber >= NumberUtils.toLong(maxSerial),
                       Errors.create().key(MessageIdList.CARRIER_ID_OVERFLOW).content("晶舟号已超过系统定义,请联系管理员!").build());
        serialNumber++;
        DecimalFormat dfSuffix = new DecimalFormat(zerosPattern);
        subfix = dfSuffix.format(serialNumber);
        return prefix + subfix;
    }

    @Override
    public boolean checkPcdOverClean(long pcdRrn) {
        PcdClean pcdCleanInfo = getPcdCleanInfo(pcdRrn);
        if (pcdCleanInfo != null && pcdCleanInfo.getInstanceRrn() > 0) {
            if (!isCleanValidPcd(pcdCleanInfo.getCleanOverDate())) {
                return false;
            }
        }
        return true;
    }

    @Override
    public void exchangePCD(List<Map> transInfo) {
        for (Map transMap : transInfo) {
            Lot lot = (Lot) transMap.get("lot");
            Long targetCarrierRrn = MapUtils.getLongValue(transMap, "targetCarrierRrn", 0L);
            List<Map> unitsList = (List<Map>) transMap.get("unitsList");
            this.changeStatusFromInUseToFreeOrAssembly(MapUtils.getLong(transMap, "facilityRrn"),
                                                       MapUtils.getString(transMap, "user"), lot.getCarrierRrn(),
                                                       "Release for move out exchange pcd");
            this.changeStatusFromFreeOrAssemblyToInUse(MapUtils.getLong(transMap, "facilityRrn"),
                                                       MapUtils.getString(transMap, "user"), targetCarrierRrn,
                                                       "In use for move out exchange pcd");
            carrierManager.exchangeCarrierByCarrier(lot, targetCarrierRrn, unitsList);
        }
    }

    @Override
    public void changeStatusFromInUseToFreeOrAssembly(Long facilityRrn, String user, Long carrierRrn, String comments) {
        Carrier carrier = new Carrier();
        carrier = carrierManager.getCarrier(carrierRrn);
        if (StringUtils.isNotBlank(carrier.getInstanceId())) {
            if (carrier.getSlotCount() > carrier.getAvailableSlotCount()) {
                // 将PCD的状态从IN USE切换到ASSEMBLY
                PcdAssembly pcd = getPcdAssembly(carrierRrn, null, null);
                if (pcd != null && StringUtils.isNotBlank(pcd.getPodId())) {
                    // pcd-cst
                    POD p =pcd.getPodRrn()==null||pcd.getPodRrn()<=0?null: podManager.getPod(pcd.getPodRrn());
                    Door d =pcd.getDoorRrn()==null||pcd.getDoorRrn()<=0?null: doorManager.getDoor(pcd.getDoorRrn());

                    /**
                     * 释放PCD,如果释放时,批次上PCD是IN_USE状态,直接释放为ASSEMBLY状态
                     * 如果批次上PCD某个组件是WAIT_CLEAN状态,则需要先释放为ASSEMBLY状态
                     * 再从ASSEMBLY状态自动切换为WAIT_CLEAN状态
                     */
                    if (StringUtils.equalsIgnoreCase(PcdStatus.IN_USE_KEY, carrier.getCarrierStatus())) {
                        statusManager.changePCDStatusByEvent(facilityRrn, user, pcd.getCarrierRrn(),
                                                             ObjectList.CARRIER_KEY, EventName.CST_IN_USE_TO_ASSEMBLY,
                                                             comments);
                    } else if (StringUtils.equalsIgnoreCase(PcdStatus.WAIT_CLEAN_KEY, carrier.getCarrierStatus())) {
                        statusManager.changePCDStatusByEvent(facilityRrn, user, pcd.getCarrierRrn(),
                                                             ObjectList.CARRIER_KEY,
                                                             EventName.CST_WAIT_CLEAN_TO_ASSEMBLY, comments);
                        statusManager.changePCDStatusByEvent(facilityRrn, user, pcd.getCarrierRrn(),
                                                             ObjectList.CARRIER_KEY,
                                                             EventName.CST_ASSEMBLY_TO_WAIT_CLEAN, comments);
                    }

                    if (p!=null&&StringUtils.equalsIgnoreCase(PcdStatus.IN_USE_KEY, p.getPodStatus())) {
                        statusManager.changePCDStatusByEvent(facilityRrn, user, pcd.getPodRrn(), ObjectList.POD_KEY,
                                                             EventName.POD_IN_USE_TO_ASSEMBLY, comments);
                    } else if (p!=null&&StringUtils.equalsIgnoreCase(PcdStatus.WAIT_CLEAN_KEY, p.getPodStatus())) {
                        statusManager.changePCDStatusByEvent(facilityRrn, user, pcd.getPodRrn(), ObjectList.POD_KEY,
                                                             EventName.POD_WAIT_CLEAN_TO_ASSEMBLY, comments);
                        statusManager.changePCDStatusByEvent(facilityRrn, user, pcd.getPodRrn(), ObjectList.POD_KEY,
                                                             EventName.POD_ASSEMBLY_TO_WAIT_CLEAN, comments);
                    }

                    if (d!=null&&StringUtils.equalsIgnoreCase(PcdStatus.IN_USE_KEY, d.getDoorStatus())) {
                        statusManager.changePCDStatusByEvent(facilityRrn, user, pcd.getDoorRrn(), ObjectList.DOOR_KEY,
                                                             EventName.DOOR_IN_USE_TO_ASSEMBLY, comments);
                    } else if (d!=null&&StringUtils.equalsIgnoreCase(PcdStatus.WAIT_CLEAN_KEY, d.getDoorStatus())) {
                        statusManager.changePCDStatusByEvent(facilityRrn, user, pcd.getDoorRrn(), ObjectList.DOOR_KEY,
                                                             EventName.DOOR_WAIT_CLEAN_TO_ASSEMBLY, comments);
                        statusManager.changePCDStatusByEvent(facilityRrn, user, pcd.getDoorRrn(), ObjectList.DOOR_KEY,
                                                             EventName.DOOR_ASSEMBLY_TO_WAIT_CLEAN, comments);
                    }
                } else {
                    /**
                     * 释放晶舟,如果批次上晶舟是IN_USE,则直接释放为FREE状态 如果批次上晶舟是WAIT_CLEAN状态,则先释放为FREE状态,
                     * 再从FREE状态自动切换为WAIT_CLEAN状态
                     */
                    if (StringUtils.equalsIgnoreCase(PcdStatus.IN_USE_KEY, carrier.getCarrierStatus())) {
                        statusManager.changePCDStatusByEvent(facilityRrn, user, carrierRrn, ObjectList.CARRIER_KEY,
                                                             EventName.CST_IN_USE_TO_FREE, comments);
                    } else if (StringUtils.equalsIgnoreCase(PcdStatus.WAIT_CLEAN_KEY, carrier.getCarrierStatus())) {
                        statusManager.changePCDStatusByEvent(facilityRrn, user, carrierRrn, ObjectList.CARRIER_KEY,
                                                             EventName.CST_WAIT_CLEAN_TO_FREE, comments);
                        statusManager.changePCDStatusByEvent(facilityRrn, user, carrierRrn, ObjectList.CARRIER_KEY,
                                                             EventName.CST_FREE_TO_WAIT_CLEAN, comments);
                    }
                }
            }
        }
    }

    @Override
    public void changeStatusFromFreeOrAssemblyToInUse(Long facilityRrn, String user, Long carrierRrn, String comments) {
        Carrier carrier = carrierManager.getCarrier(carrierRrn);
        if (StringUtils.isNotBlank(carrier.getInstanceId())) {
            if (carrierManager.isPcdType(carrier.getObjectSubtype())) {
                // 将PCD的状态从ASSEMBLY切换为IN USE
                if (StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.ASSEMBLY_KEY)) {
                    PcdAssembly pcd = getPcdAssembly(carrierRrn, null, null);
                    if (pcd != null && StringUtils.isNotBlank(pcd.getPodId())) {

                        statusManager.changePCDStatusByEvent(facilityRrn, user, pcd.getCarrierRrn(),
                                                             ObjectList.CARRIER_KEY, EventName.CST_ASSEMBLY_TO_IN_USE,
                                                             comments);
                        if(pcd.getPodRrn()!=null&&pcd.getPodRrn()>0){
                            statusManager.changePCDStatusByEvent(facilityRrn, user, pcd.getPodRrn(), ObjectList.POD_KEY,
                                                                 EventName.POD_ASSEMBLY_TO_IN_USE, comments);
                        }
                        if(pcd.getDoorRrn()!=null&&pcd.getDoorRrn()>0){
                            statusManager.changePCDStatusByEvent(facilityRrn, user, pcd.getDoorRrn(), ObjectList.DOOR_KEY,
                                                                 EventName.DOOR_ASSEMBLY_TO_IN_USE, comments);
                        }
                    }
                }
            } else {
                // 将OPEN-CST的状态从FREE切换为IN USE
                if (StringUtils.equalsIgnoreCase(carrier.getCarrierStatus(), PcdStatus.FREE_KEY)) {
                    statusManager.changePCDStatusByEvent(facilityRrn, user, carrierRrn, ObjectList.CARRIER_KEY,
                                                         EventName.CST_FREE_TO_IN_USE, comments);
                }
            }
        }
    }

    @Override
    public void checkPCDCleanTimeAndLogEvent(String waitCleanKey) {
        // 在允许事件集里面,在自动的类表值里面,超过清洗时间
        List<PcdClean> pcdCleanInfos = this.qryPCDCleanInfo(waitCleanKey);

        Set<Long> pcdRrnSet = new HashSet<>(); // set做防止重复更改状态的作用
        for (PcdClean cleanInfo : pcdCleanInfos) {
            if(pcdRrnSet.contains(cleanInfo.getInstanceRrn())){
                continue;
            }
            // 如果到了清洗时间,执行log event,将Carrier, Pod,Door变成WAIT_CLEAN状态
            statusManager.changePCDStatusAndEvent("SYSTEM", cleanInfo.getInstanceRrn(), cleanInfo.getPcdType(),
                                                  cleanInfo.getEventRrn(), "Wait clean by system auto",
                                                  PcdStatus.WAIT_CLEAN_KEY);
            pcdRrnSet.add(cleanInfo.getInstanceRrn());

            // 如果是组合的PCD,只要其中有一个组件过期,其它组件即使没过期也做过期处理(状态变成WAIT_CLEAN)
            // 如果是组合的PCD,把三个组件都查询出来
            PcdAssembly pcd = new PcdAssembly();
            if (StringUtils.equalsIgnoreCase(cleanInfo.getPcdType(), ObjectList.CARRIER_KEY)) {
                pcd = this.getPcdAssembly(cleanInfo.getInstanceRrn(), null, null);
            } else if (StringUtils.equalsIgnoreCase(cleanInfo.getPcdType(), ObjectList.POD_KEY)) {
                pcd = this.getPcdAssembly(null, cleanInfo.getInstanceRrn(), null);
            } else if (StringUtils.equalsIgnoreCase(cleanInfo.getPcdType(), ObjectList.DOOR_KEY)) {
                pcd = this.getPcdAssembly(null, null, cleanInfo.getInstanceRrn());
            }
            if (pcd != null) {
                if (StringUtils.isNotBlank(pcd.getCarrierId())) {
                    // 是组合的PCD,组装clean信息
                    //修改 pod
                    if(pcd.getPodRrn()!=null&&pcd.getPodRrn()>0){
                        PcdClean podCleanInfo = this.getPcdCleanInfo(pcd.getPodRrn());
                        podCleanInfo.setInstanceId(pcd.getPodId());
                        pcdRrnSet = checkOtherPCDCleanTimeAndLogEvent(pcdRrnSet, podCleanInfo, cleanInfo);
                    }
                    //修改door
                    if (pcd.getDoorRrn() != null && pcd.getDoorRrn() > 0) {
                        PcdClean doorCleanInfo = this.getPcdCleanInfo(pcd.getDoorRrn());
                        doorCleanInfo.setInstanceId(pcd.getDoorId());
                        pcdRrnSet = checkOtherPCDCleanTimeAndLogEvent(pcdRrnSet, doorCleanInfo, cleanInfo);
                    }
                    //修改cst
                    PcdClean carrierCleanInfo = this.getPcdCleanInfo(pcd.getCarrierRrn());
                    carrierCleanInfo.setInstanceId(pcd.getCarrierId());
                    pcdRrnSet = checkOtherPCDCleanTimeAndLogEvent(pcdRrnSet, carrierCleanInfo, cleanInfo);
                }
            }
        }
    }

    @Override
    public List<PcdClean> qryPCDCleanInfo(String targetStatus) {
        return pcdDAO.qryPCDCleanInfo(targetStatus);
    }

    @Override
    public Set<Long> checkOtherPCDCleanTimeAndLogEvent(Set<Long> pcdRrnSet, PcdClean targetPcdClean,
                                                       PcdClean sourcePcdClean) {
        if (targetPcdClean != null && targetPcdClean.getInstanceRrn() > 0) {
            long facilityRrn = LocalContext.getFacilityRrn();
            // 查询事件信息
            Map<String, Object> eventMap = getEventInfoByPcdId(facilityRrn, targetPcdClean.getInstanceId(),
                                                               targetPcdClean.getPcdType(), PcdStatus.WAIT_CLEAN_KEY);
            if (MapUtils.isNotEmpty(eventMap)) {

                targetPcdClean.setEventId(MapUtils.getString(eventMap, "eventId", StringUtils.EMPTY));
                targetPcdClean.setEventRrn(MapUtils.getLongValue(eventMap, "eventRrn"));

                int setSize = pcdRrnSet.size();
                pcdRrnSet.add(targetPcdClean.getInstanceRrn());
                if (setSize == pcdRrnSet.size()) {
                    // 重复更改
                } else {
                    statusManager.changePCDStatusAndEvent("SYSTEM", targetPcdClean.getInstanceRrn(),
                                                          targetPcdClean.getPcdType(), targetPcdClean.getEventRrn(),
                                                          "Wait clean by system auto", PcdStatus.WAIT_CLEAN_KEY);
                }
            }
        }

        return pcdRrnSet;
    }

    @Override
    public Map<String, Object> getEventInfoByPcdId(Long facilityRrn, String pcdId, String pcdType,
                                                   String targetStatus) {
        String currentStatus = StringUtils.EMPTY;
        String entityNamedSpace = namedObjectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY);
        long entityRrn = 0;
        if (StringUtils.equalsIgnoreCase(pcdType, ObjectList.CARRIER_KEY)) {
            Carrier c = carrierManager.getCarrier(facilityRrn, pcdId);
            if (c != null && c.getInstanceRrn() > 0) {
                entityRrn = c.getInstanceRrn();
                currentStatus = c.getCarrierStatus();
            }
        } else if (StringUtils.equalsIgnoreCase(pcdType, ObjectList.POD_KEY)) {
            long podRrn = namedObjectManager.getNamedObjectRrn(pcdId, entityNamedSpace, ObjectList.ENTITY_KEY,
                                                               ObjectList.POD_KEY);
            POD p = podManager.getPod(podRrn);
            if (p != null && p.getInstanceRrn() > 0) {
                entityRrn = p.getInstanceRrn();
                currentStatus = p.getPodStatus();
            }
        } else if (StringUtils.equalsIgnoreCase(pcdType, ObjectList.DOOR_KEY)) {
            long doorRrn = namedObjectManager.getNamedObjectRrn(pcdId, entityNamedSpace, ObjectList.ENTITY_KEY,
                                                                ObjectList.DOOR_KEY);
            Door d = doorManager.getDoor(doorRrn);
            if (d != null && d.getInstanceRrn() > 0) {
                entityRrn = d.getInstanceRrn();
                currentStatus = d.getDoorStatus();
            }
        }

        if (StringUtils.isNotBlank(currentStatus)) {
            List<Map> eventInfoList = eventManager.getEventInfoList(facilityRrn, entityRrn, currentStatus,
                                                                    targetStatus);
            if (CollectionUtils.isNotEmpty(eventInfoList)) {
                // 获取第一个
                Map<String, Object> eventMap = eventInfoList.get(0);
                return eventMap;
            }
        }
        return null;
    }

    @Override
    public String checkPcdIsValidReturnString(Long carrierRrn, long facilityRrn) {
        StringBuilder cleanInvalidMsg = new StringBuilder();
        if (carrierRrn != null && carrierRrn > 0) {
            PcdAssembly pcd = this.getPcdAssembly(carrierRrn);
            if (pcd != null && pcd.getCarrierRrn() > 0) {
                PcdClean carrierCleanInfo = this.getPcdCleanInfo(pcd.getCarrierRrn());
                PcdClean podCleanInfo = this.getPcdCleanInfo(pcd.getPodRrn());
                PcdClean doorCleanInfo = this.getPcdCleanInfo(pcd.getDoorRrn());

                if (carrierCleanInfo != null) {
                    if (!this.isCleanValidPcd(carrierCleanInfo.getCleanOverDate())) {
                        cleanInvalidMsg.append(pcd.getCarrierId());
                    }
                }

                if (podCleanInfo != null) {
                    if (!this.isCleanValidPcd(podCleanInfo.getCleanOverDate())) {
                        if (StringUtils.isNotBlank(cleanInvalidMsg.toString())) {
                            cleanInvalidMsg.append(", ");
                        }
                        cleanInvalidMsg.append(pcd.getPodId());
                    }
                }

                if (doorCleanInfo != null) {
                    if (!this.isCleanValidPcd(doorCleanInfo.getCleanOverDate())) {
                        if (StringUtils.isNotBlank(cleanInvalidMsg.toString())) {
                            cleanInvalidMsg.append(", ");
                        }
                        cleanInvalidMsg.append(pcd.getDoorId());
                    }
                }

                if (cleanInvalidMsg.length() > 0) {
                    cleanInvalidMsg.append(" is out of clean date! ");
                }
            } else {
                // 如果不是pcd组装晶舟,则查询open-cst
                Carrier carrier = new Carrier(carrierRrn);
                carrier = carrierManager.getCarrier(carrier);
                if (StringUtils.isNotBlank(carrier.getInstanceId())) {
                    PcdClean carrierCleanInfo = this.getPcdCleanInfo(carrier.getInstanceRrn());
                    if (carrierCleanInfo != null && !this.isCleanValidPcd(carrierCleanInfo.getCleanOverDate())) {
                        cleanInvalidMsg.append(carrier.getInstanceId() + " is out of clean date! ");
                    }
                } else {
                    cleanInvalidMsg.append("Cassette is invalid!");
                }
            }
        }
        return cleanInvalidMsg.toString();
    }

    public void updateAllPCDCleanCycleByCategoryAndType(Long facilityRrn, String user, String cateogory, String type,
                                                        double tragetCleanCycle) {
        String namedSpcace = namedObjectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY);
        updateAllCstCleanCycleByCategoryAndType(facilityRrn, user, cateogory, type, tragetCleanCycle, namedSpcace);
        updateAllPodCleanCycleByCategoryAndType(facilityRrn, user, cateogory, type, tragetCleanCycle, namedSpcace);
        updateAllDoorCleanCycleByCategoryAndType(facilityRrn, user, cateogory, type, tragetCleanCycle, namedSpcace);
    }

    public void updateAllCstCleanCycleByCategoryAndType(Long facilityRrn, String user, String cateogory, String type,
                                                        double tragetCleanCycle, String carrierNamedSpcace) {
        Map<String, Object> getAllCarrierMap = new HashMap<String, Object>();
        getAllCarrierMap.put("category", cateogory);
        getAllCarrierMap.put("type", type);
        getAllCarrierMap.put("namedSpace", carrierNamedSpcace);
        List<Map> allCarrier = carrierManager.getAllCarrier(getAllCarrierMap);
        if (CollectionUtils.isNotEmpty(allCarrier)) {
            for (Map carrierMap : allCarrier) {
                Map<String, Object> carrierAttrMap = new HashMap<String, Object>();
                carrierAttrMap.put("carrierRrn", MapUtils.getLong(carrierMap, "carrierRrn"));
                carrierAttrMap.put("carrierId", MapUtils.getString(carrierMap, "carrierId"));
                carrierAttrMap.put("carrierDesc", MapUtils.getString(carrierMap, "carrierDesc"));
                carrierAttrMap.put("carrierStatus", MapUtils.getString(carrierMap, "carrierStatus"));
                carrierAttrMap.put("carrierType", MapUtils.getString(carrierMap, "carrierType"));

                carrierAttrMap.put("carrierColor", MapUtils.getString(carrierMap, "carrierColorCode"));
                carrierAttrMap.put("carrierCategory", MapUtils.getString(carrierMap, "carrierCategoryCode"));
                // carrierAttrMap.put("carrierPosition", MapUtils.getString(carrierMap, ""));
                // carrierAttrMap.put("carrierlocation", MapUtils.getString(carrierMap, ""));

                carrierAttrMap.put("carrierSlotCount", MapUtils.getString(carrierMap, "slotCount"));
                carrierAttrMap.put("carrierAvailableSlotCount", MapUtils.getString(carrierMap, "availableSlotCount"));
                carrierAttrMap.put("carrierPollutionLevel", MapUtils.getString(carrierMap, "carrierPollution"));
                carrierAttrMap.put("carrierAllowableEvent", MapUtils.getString(carrierMap, "carrierAllowable"));
                carrierAttrMap.put("carrierEngineerGroupID", MapUtils.getString(carrierMap, "carrierEngineer", ""));
                carrierAttrMap.put("carrierCleanCycle", tragetCleanCycle);

                carrierManager.updateCarrierEntity(carrierAttrMap, facilityRrn, user);
            }
        }
    }

    public void updateAllPodCleanCycleByCategoryAndType(Long facilityRrn, String user, String cateogory, String type,
                                                        double tragetCleanCycle, String carrierNamedSpcace) {
        Map<String, Object> getAllPodMap = new HashMap<String, Object>();
        getAllPodMap.put("category", cateogory);
        getAllPodMap.put("type", type);
        getAllPodMap.put("namedSpace", carrierNamedSpcace);
        List<Map> allPod = podManager.getAllPod(getAllPodMap);
        if (CollectionUtils.isNotEmpty(allPod)) {
            for (Map podMap : allPod) {
                Map<String, Object> updatePodMap = new HashMap<String, Object>();
                updatePodMap.put("podRrn", MapUtils.getLong(podMap, "podRrn"));
                updatePodMap.put("podId", MapUtils.getString(podMap, "podId", ""));
                updatePodMap.put("podDesc", MapUtils.getString(podMap, "podDesc", ""));
                updatePodMap.put("podStatus", MapUtils.getString(podMap, "podStatus", ""));
                updatePodMap.put("podType", MapUtils.getString(podMap, "podType", ""));
                updatePodMap.put("podPollutionLevel", MapUtils.getString(podMap, "podPollution", ""));
                updatePodMap.put("podAllowableEvent", MapUtils.getString(podMap, "podAllowable", ""));
                updatePodMap.put("podEngineerGroupID", MapUtils.getString(podMap, "podEngineer", ""));
                updatePodMap.put("podSlotCount", MapUtils.getString(podMap, "podSlotCount", ""));
                updatePodMap.put("podPosition", MapUtils.getString(podMap, "podLoadPosition", ""));
                updatePodMap.put("podColor", MapUtils.getString(podMap, "podColor", ""));
                updatePodMap.put("podCategory", MapUtils.getString(podMap, "podCategoryCode", ""));
                updatePodMap.put("podCleanCycle", tragetCleanCycle);

                podManager.updatePodEntity(updatePodMap, facilityRrn, user);
            }
        }
    }

    public void updateAllDoorCleanCycleByCategoryAndType(Long facilityRrn, String user, String cateogory, String type,
                                                         double tragetCleanCycle, String doorNamedSpcace) {
        Map<String, Object> getAllDoorMap = new HashMap<String, Object>();
        getAllDoorMap.put("category", cateogory);
        getAllDoorMap.put("type", type);
        getAllDoorMap.put("namedSpace", doorNamedSpcace);
        List<Map> allDoor = doorManager.getAllDoor(getAllDoorMap);
        if (CollectionUtils.isNotEmpty(allDoor)) {
            for (Map doorMap : allDoor) {
                Map<String, Object> updateDoorMap = new HashMap<String, Object>();
                updateDoorMap.put("doorRrn", MapUtils.getLong(doorMap, "doorRrn"));
                updateDoorMap.put("doorId", MapUtils.getString(doorMap, "doorId", ""));
                updateDoorMap.put("doorDesc", MapUtils.getString(doorMap, "doorDesc", ""));
                updateDoorMap.put("doorStatus", MapUtils.getString(doorMap, "doorStatus", ""));
                updateDoorMap.put("doorType", MapUtils.getString(doorMap, "doorType", ""));
                updateDoorMap.put("doorColor", MapUtils.getString(doorMap, "doorColor", ""));
                updateDoorMap.put("doorPosition", MapUtils.getString(doorMap, "doorLoadPosition", ""));
                updateDoorMap.put("doorSlotCount", MapUtils.getString(doorMap, "doorSlotCount", ""));
                updateDoorMap.put("doorPollutionLevel", MapUtils.getString(doorMap, "doorPollution", ""));
                updateDoorMap.put("doorAllowableEvent", MapUtils.getString(doorMap, "doorAllowable", ""));
                updateDoorMap.put("doorEngineerGroupID", MapUtils.getString(doorMap, "doorEngineer", ""));
                updateDoorMap.put("doorCategory", MapUtils.getString(doorMap, "doorCategoryCode", ""));
                updateDoorMap.put("doorCleanCycle", tragetCleanCycle);

                doorManager.updateDoorEntity(updateDoorMap, facilityRrn, user);
            }
        }
    }

    /**
     * 检查P/C/D是否是目标状态下的未绑定的空P/C/D
     */
    public void checkPcdIsExistAndNotAssemblyOrInUseAndCurrentStatus(String pcdId, Long facilityRrn,
                                                                     String targetStatus) {
        NamedObject no = new NamedObject(pcdId, namedObjectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY),
                                         ObjectList.ENTITY_KEY);
        no = namedObjectManager.getNamedObject(no);
        if (null == no) {
            no = new NamedObject();
        }
        Assert.isTrue(no.getInstanceRrn() > 0,
                      Errors.create().key(MessageIdList.PCD_NO_CLEANING_EQPT_ID).content("{} is not exist!").args(pcdId)
                            .build());

        if (StringUtils.equals(no.getObjectType(), ObjectList.CARRIER_KEY)) {
            PcdAssembly pcdAll = getPcdAssembly(no.getInstanceRrn(), null, null); // C

            Assert.isTrue(pcdAll == null || StringUtils.isBlank(pcdAll.getCarrierId()),
                          Errors.create().key(MessageIdList.PCD_DEASSEMBLY_PCD_FIRST).content("请先解绑PCD").build());
            // 没有绑定关系,检查是否是空晶舟
            Carrier c = carrierManager.getCarrier(no.getInstanceRrn());

            Assert.isTrue(c.getAvailableSlotCount().equals(c.getSlotCount()),
                          Errors.create().key(MessageIdList.PCD_IS_IN_USE).content("{} is in use!").args(pcdId)
                                .build());
            Assert.isTrue(StringUtils.equals(c.getCarrierStatus(), targetStatus),
                          Errors.create().key(MessageIdList.PCD_STATUS_ERROR)
                                .content("{} current " + "status is " + "not {}!").args(pcdId, targetStatus).build());

        } else if (StringUtils.equals(no.getObjectType(), ObjectList.POD_KEY)) {
            PcdAssembly pcdAll = getPcdAssembly(null, no.getInstanceRrn(), null); // P

            Assert.isFalse(pcdAll != null && StringUtils.isNotBlank(pcdAll.getCarrierId()),
                           Errors.create().key(MessageIdList.PCD_DEASSEMBLY_PCD_FIRST).content("请先解绑PCD").build());
            POD p = podManager.getPod(no.getInstanceRrn());

            Assert.isTrue(StringUtils.equals(p.getPodStatus(), targetStatus),
                          Errors.create().key(MessageIdList.PCD_STATUS_ERROR)
                                .content("{} current status " + "is not {}!").args(pcdId, targetStatus).build());
        } else if (StringUtils.equals(no.getObjectType(), ObjectList.DOOR_KEY)) {
            PcdAssembly pcdAll = getPcdAssembly(null, null, no.getInstanceRrn()); // D
            Assert.isFalse(pcdAll != null && StringUtils.isNotBlank(pcdAll.getCarrierId()),
                           Errors.create().key(MessageIdList.PCD_DEASSEMBLY_PCD_FIRST).content("请先解绑PCD").build());
            Door d = doorManager.getDoor(no.getInstanceRrn());
            Assert.isTrue(StringUtils.equals(d.getDoorStatus(), targetStatus),
                          Errors.create().key(MessageIdList.PCD_STATUS_ERROR)
                                .content("{} current status" + " " + "is not {}!").args(pcdId, targetStatus).build());
        }
    }

    private void batchOutCleanByPcd(String eqptId, String[] pcdIds, String pcdType, String eventId, Long facilityRrn,
                                    String user) {
        String comments = "Out clean from " + eqptId;
        String targetStatus = PcdStatus.FREE_KEY;
        String currentStatus = PcdStatus.IN_CLEAN_KEY;
        String returnMsg = "";
        for (int i = 0; i < pcdIds.length; i++) {
            statusManager.changePcdStatusByLogEvent(facilityRrn, user, pcdIds[i], pcdType, eventId, comments,
                                                    targetStatus, currentStatus, eqptId);
        }
    }

    /**
     * 判断pcd的清洗时间是否大于设置的最小清洗时间
     */
    private void IsPcdCleaningDateAboveThanMinMinutes(String[] pcdIds, double minMinutes, Long facilityRrn) {
        if (pcdIds.length > 0) {
            for (int i = 0; i < pcdIds.length; i++) {
                String pcdId = StringUtils.EMPTY;
                pcdId = pcdIds[i];
                NamedObject no = new NamedObject(pcdId,
                                                 namedObjectManager.getNamedSpace(facilityRrn, ObjectList.ENTITY_KEY),
                                                 ObjectList.ENTITY_KEY);
                no = namedObjectManager.getNamedObject(no);
                Assert.isTrue(no.getInstanceRrn() > 0,
                              Errors.create().key(MessageIdList.PCD_PCD_MISSING).content("PCD 不存在!").build());

                PcdClean pc = getPcdCleanInfo(no.getInstanceRrn());
                Assert.isTrue(pc != null && pc.getInCleanDate() != null,
                              Errors.create().key(MessageIdList.PCD_NOT_CLEAN_RECORD).content("{} has no clean record!")
                                    .args(pcdId).build());
                Assert.isFalse(pc.getCleaningMinutes() < minMinutes,
                               Errors.create().key(MessageIdList.PCD_CLEAN_LESS_MIN_TIME)
                                     .content("{} &#039s clean time is less than" + " min time!").args(pcdId).build());
            }
        }
    }

    private void cleanPcd(String cleanEqptId, Long pcdRrn, String performedBy, String inOrOutClean) {
        if (pcdRrn != null && pcdRrn > 0) {
            NamedObject pcd = new NamedObject(pcdRrn);
            pcd = namedObjectManager.getNamedObject(pcd);
            if (pcd != null & StringUtils.isNotBlank(pcd.getInstanceId())) {
                if (StringUtils.equalsIgnoreCase(pcd.getObjectType(), ObjectList.CARRIER_KEY) ||
                        StringUtils.equalsIgnoreCase(pcd.getObjectType(), ObjectList.POD_KEY) ||
                        StringUtils.equalsIgnoreCase(pcd.getObjectType(), ObjectList.DOOR_KEY)) {
                    if (StringUtils.equalsIgnoreCase("in", inOrOutClean)) {
                        inCleanPcd(cleanEqptId, pcd, performedBy);
                    } else if (StringUtils.equalsIgnoreCase("out", inOrOutClean)) {
                        outCleanPcd(pcd, performedBy);
                    }
                }
            }
        }
    }

    /**
     * 开始清洗工作
     *
     * @param pcd
     * @param performedBy
     */
    private void inCleanPcd(String cleanEqptId, NamedObject pcd, String performedBy) {
        PcdClean cleanInfo = getPcdCleanInfo(pcd.getInstanceRrn());
        if (cleanInfo != null && cleanInfo.getInstanceRrn() > 0) {
            TransactionLog transactionLog = transactionLogManager.startTransactionLog(performedBy,
                                                                                      TransactionNames.IN_CLEAN_KEY);
            long currentTime = System.currentTimeMillis();
            cleanInfo.setInCleanDate(new Timestamp(currentTime));
            cleanInfo.setOutCleanDate(null);
            cleanInfo.setCleanEqptId(cleanEqptId);

            pcdDAO.updatePcdCleanInfo(cleanInfo);
            pcdDAO.insertPcdCleanH(transactionLog.getTransRrn(), cleanInfo);

            transactionLogManager.markTransactionLog(transactionLog);
        }
    }

    /**
     * pcd 结束清洗工作
     *
     * @param pcd
     * @param performedBy
     */
    private void outCleanPcd(NamedObject pcd, String performedBy) {
        PcdClean cleanInfo = pcdDAO.getPcdCleanInfo(pcd.getInstanceRrn());
        if (cleanInfo != null && cleanInfo.getInstanceRrn() > 0) {
            TransactionLog transactionLog = transactionLogManager.startTransactionLog(performedBy,
                                                                                      TransactionNames.OUT_CLEAN_KEY);

            // 清洗工作结束后,
            cleanInfo.setInstanceRrn(pcd.getInstanceRrn());
            long currentTime = System.currentTimeMillis();
            cleanInfo.setOutCleanDate(new Timestamp(currentTime)); // 记录清洗结束时间
            long cleanOverTime = (long) (currentTime + (cleanInfo.getCleanCycle() * 24 * 60 * 60 * 1000));
            cleanInfo.setCleanOverDate(new Timestamp(cleanOverTime)); // 更新清洗有效期限
            cleanInfo.setCleanCount(cleanInfo.getCleanCount() + 1); // 追加清洗次数

            String cleanEqptId = cleanInfo.getCleanEqptId();
            cleanInfo.setCleanEqptId(StringUtils.EMPTY); // 结束清洗时清除清洗机台
            pcdDAO.updatePcdCleanInfo(cleanInfo);
            cleanInfo.setCleanEqptId(cleanEqptId); // 历史需要记录结束清洗时的清洗机台
            pcdDAO.insertPcdCleanH(transactionLog.getTransRrn(), cleanInfo);
            // change cassette type 后清洗完成自动切换type
            cleanExchangePCDType(cleanInfo);
            transactionLogManager.markTransactionLog(transactionLog);
        }
    }

    private void batchInCleanByPcd(String eqptId, String[] pcdIds, String pcdType, String eventId, Long facilityRrn,
                                   String user) {
        String comments = "In clean from " + eqptId;
        String targetStatus = PcdStatus.IN_CLEAN_KEY;
        String currentStatus = PcdStatus.WAIT_CLEAN_KEY;
        for (int i = 0; i < pcdIds.length; i++) {
            statusManager.changePcdStatusByLogEvent(facilityRrn, user, pcdIds[i], pcdType, eventId, comments,
                                                    targetStatus, currentStatus, eqptId);
        }
    }

    /**
     * 检查PCD是否是未绑定的空的P/C/D,并且当前状态是WAIT_CLEAN
     *
     * @return 返回空字符串表示是,返回不为空的字符串表示不是
     */
    private void checkPcdIsExist(String[] pcdIds, Long facilityRrn) {
        for (int i = 0; i < pcdIds.length; i++) {
            // 检查PCD是否都存在并且是未绑定的空P/C/D
            checkPcdIsExistAndNotAssemblyOrInUseAndCurrentStatus(pcdIds[i], facilityRrn, PcdStatus.WAIT_CLEAN_KEY);
        }
    }

    @Override
    public void manualExchangePCDType(Carrier carrier) {
        Map<String,Object> map = getAutoChangeCassetteTypes();
        String sourceCstType = MapUtils.getString(map, "sourceCstType");
        String targetCstType = MapUtils.getString(map, "targetCstType");
        Assert.state(StringUtils.equalsIgnoreCase(carrier.getObjectSubtype(), sourceCstType),
                     Errors.create().key(MessageIdList.CARRIER_MANUAL_CHANGE_TYPE_ERROR)
                           .content("Manual change cassette type can only is {}!").args(sourceCstType).build());
        TransactionLog transactionLog = transactionLogManager
                .startTransactionLog(LocalContext.getUserId(),TransactionNames.EXCHANGE_TYPE_KEY);

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("Manual exchange carrier type! Change Type:")
                     .append(sourceCstType)
                     .append("-->")
                     .append(targetCstType);

        map.put("comments", stringBuilder.toString());
        map.put("carrierRrn", carrier.getInstanceRrn());
        map.put("transRrn", transactionLog.getTransRrn());
        exchangePodAndDoorType(map);

        //手动切换carrier type
        carrier.setObjectSubtype(targetCstType);
        carrier.setInstanceId(StringUtils.EMPTY);
        carrierManager.exchangeCarrierType(carrier, stringBuilder.toString(), transactionLog.getTransRrn(),false);
        transactionLogManager.markTransactionLog(transactionLog);
    }

    @Override
    public void autoExchangePCDType(Long carrierRrn) {
        Map<String,Object> map = getAutoChangeCassetteTypes();
        String sourceCstType = MapUtils.getString(map, "sourceCstType");
        String targetCstType = MapUtils.getString(map, "targetCstType");
        TransactionLog transactionLog = transactionLogManager
                .startTransactionLog("SYSTEM", TransactionNames.EXCHANGE_TYPE_KEY);
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("Move out auto exchange carrier type! Change Type:")
                     .append(sourceCstType)
                     .append("-->")
                     .append(targetCstType);
        map.put("comments", stringBuilder.toString());
        map.put("carrierRrn", carrierRrn);
        map.put("transRrn", transactionLog.getTransRrn());
        exchangePodAndDoorType(map);

        //moveOut 自动切换carrier type
        Carrier carrier = carrierManager.getCarrier(carrierRrn);
        carrier.setObjectSubtype(targetCstType);
        carrier.setTransId(TransactionNames.EXCHANGE_TYPE_KEY);
        carrier.setTransPerformedby("SYSTEM");
        carrier.setInstanceId(StringUtils.EMPTY);
        carrierManager.exchangeCarrierType(carrier, stringBuilder.toString(), transactionLog.getTransRrn(),true);
        transactionLogManager.markTransactionLog(transactionLog);
    }

    @Override
    public void cleanExchangePCDType(PcdClean pcdClean) {
        Map<String,Object> map = getAutoChangeCassetteTypes();
        String sourceCstType = MapUtils.getString(map, "sourceCstType");
        String targetCstType = MapUtils.getString(map, "targetCstType");
        StringBuilder stringBuilder = new StringBuilder();
        TransactionLog transactionLog = transactionLogManager
                .startTransactionLog("SYSTEM", TransactionNames.EXCHANGE_TYPE_KEY);
        if (StringUtils.equalsIgnoreCase(pcdClean.getPcdType(), Constants.PCD_TYPE.CARRIER_KEY)) {
            stringBuilder.append("Clean after exchange carrier type! Change Type:")
                         .append(targetCstType)
                         .append("-->")
                         .append(sourceCstType);
            Carrier carrier = carrierManager.getCarrier(pcdClean.getInstanceRrn());
            if (StringUtils.equalsIgnoreCase(targetCstType, carrier.getObjectSubtype())) {
                carrier.setObjectSubtype(sourceCstType);
                carrier.setInstanceId(StringUtils.EMPTY);
                carrier.setTransId(TransactionNames.EXCHANGE_TYPE_KEY);
                carrier.setTransPerformedby("SYSTEM");
                carrierManager.exchangeCarrierType(carrier, stringBuilder.toString(), transactionLog.getTransRrn(),false);
            }
        } else if(StringUtils.equalsIgnoreCase(pcdClean.getPcdType(), Constants.PCD_TYPE.POD_KEY)) {
            stringBuilder.append("Clean after exchange pod type! Change Type:")
                         .append(targetCstType)
                         .append("-->")
                         .append(sourceCstType);
            POD pod = podManager.getPod(pcdClean.getInstanceRrn());
            if (StringUtils.equalsIgnoreCase(targetCstType, pod.getObjectSubtype())) {
                pod.setObjectSubtype(sourceCstType);
                podDAO.updatePodType(pod, stringBuilder.toString(), transactionLog.getTransRrn());
            }
        } else if(StringUtils.equalsIgnoreCase(pcdClean.getPcdType(), Constants.PCD_TYPE.DOOR_KEY)) {
            stringBuilder.append("Clean after exchange door type! Change Type:")
                         .append(targetCstType)
                         .append("-->")
                         .append(sourceCstType);
            Door door = doorManager.getDoor(pcdClean.getInstanceRrn());
            if (StringUtils.equalsIgnoreCase(targetCstType, door.getObjectSubtype())) {
                door.setObjectSubtype(sourceCstType);
                doorDAO.updateDoorType(door, stringBuilder.toString(), transactionLog.getTransRrn());
            }
        } else {

        }
        transactionLogManager.markTransactionLog(transactionLog);
    }

    /**
     * 获取类表值对应关系  change Cassette type
     * A-B
     * @return
     */
    private Map<String,Object> getAutoChangeCassetteTypes() {
        Map<String,Object> map = new HashMap<>();
        boolean flag = true;
        String nameSpace =  namedObjectManager.getNamedSpace(LocalContext.getFacilityRrn(),
                                                             ObjectList.REFERENCE_FILE_KEY);
        ReferenceFileDetail referenceFileDetail = new ReferenceFileDetail(ReferenceDetailNames.PROCESS_LOCATION_AUTO_CHANGE_CASSETTE_TYPE,
                                                                          nameSpace,
                                                                          ObjectList.REFERENCE_FILE_KEY);
        List<ReferenceFileDetail> referenceFileDetails = referenceFileManager
                .getReferenceFileDetails(referenceFileDetail, LocalContext.getFacilityRrn());

        if (CollectionUtils.isNotEmpty(referenceFileDetails)) {
            for (ReferenceFileDetail ref : referenceFileDetails) {
                Matcher mathcher = WipUtils.PCDPATERN.matcher(ref.getData1Value());
                if (mathcher.matches()){
                    map.put("targetCstType", StringUtils.split(ref.getData1Value(), "-")[1]);
                    map.put("sourceCstType", StringUtils.split(ref.getData1Value(), "-")[0]);
                }
            }
        }else {
            flag = false;
        }

        if (MapUtils.isEmpty(map)) {
            flag = false;
        }

        Assert.state( flag , Errors.create().key(MessageIdList.REFERENCE_$$AUTO_CHANGE_CASSETTE_TYPE).build());
        return map;
    }

    /**
     * 判断是否绑定pod、door  如果绑定一起改变type
     * @param map
     */
    private void exchangePodAndDoorType(Map<String,Object> map) {
        Long carrierRrn = MapUtils.getLong(map, "carrierRrn");
        Long transRrn = MapUtils.getLong(map, "transRrn");
        String targetCstType = MapUtils.getString(map, "targetCstType");
        String comments = MapUtils.getString(map, "comments");
        PcdAssembly pcdAssembly = pcdDAO.getPcdAssembly(carrierRrn);
        if (ObjectUtils.isNotEmpty(pcdAssembly)){
            // 修改pod type
            if (pcdAssembly.getPodRrn().longValue() > 0) {
                POD pod = podManager.getPod(pcdAssembly.getPodRrn().longValue());
                pod.setObjectSubtype(targetCstType);
                podDAO.updatePodType(pod, comments, transRrn);
            }
            // 修改door type
            if (pcdAssembly.getDoorRrn().longValue() > 0) {
                Door door = doorManager.getDoor(pcdAssembly.getDoorRrn().longValue());
                door.setObjectSubtype(targetCstType);
                doorDAO.updateDoorType(door, comments, transRrn);
            }
        }
    }


}