# ReticleQueryManagerImpl.java

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

    import com.fa.sesa.threadlocal.LocalContext;
    import com.mycim.framework.utils.lang.StringUtils;
    import com.mycim.framework.utils.lang.collections.CollectionUtils;
    import com.mycim.server.ctx.exec.manager.ReticleFamilyContextManager;
    import com.mycim.server.ems.manager.EntityManager;
    import com.mycim.server.wip.dao.ReticleQueryDAO;
    import com.mycim.server.wip.manager.LotQueryManager;
    import com.mycim.server.wip.manager.ReticleQueryManager;
    import com.mycim.valueobject.bas.Relation;
    import com.mycim.valueobject.consts.EntityEnum;
    import com.mycim.valueobject.ems.Entity;
    import com.mycim.valueobject.wip.Lot;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.util.*;

    /**
     * @author Johnson.Wang
     * @version 6.0.0
     * @date 2019/9/23
     **/
    @Service
    @Transactional
    public class ReticleQueryManagerImpl implements ReticleQueryManager {

        @Autowired
        private ReticleFamilyContextManager reticleFamilyContextManager;

        @Autowired
        private ReticleQueryDAO reticleQueryDAO;

        @Autowired
        private EntityManager entityManager;

        @Autowired
        private LotQueryManager lotQueryManager;

        /**
         * Get reticles that has already entered equipment (equipment track in)
         *
         * @param lot
         * @param equipmentRrn
         * @return
         */
        @Override
        public List<Relation> getReticlesThatHasAlreadyEnteredEquipment(Lot lot, Long equipmentRrn) {
            return getReticleInfosForMovein(lot, equipmentRrn, false);
        }

        /**
         * Get reticles by relationship with the reticle family
         *
         * @param reticleFamilyRrn if it is not {@code null}, already entered the equipment
         * @param statusFLag       if it is {@code true}, the reticle's entity status must be IDLE or RELEASE or RUN
         * @return {@code Relation} object has value: ["instanceRrn", "instanceId", "instanceDesc", "fromRrn",
         * "toRrn"]
         */
        @Override
        public List<Relation> getReticlesForMovein(Long reticleFamilyRrn, Long entityRrn, Boolean statusFLag) {
            return reticleQueryDAO.getReticlesForMovein(reticleFamilyRrn, entityRrn, statusFLag);
        }

        /**
         * Get reticles that has already entered equipment (equipment track in), it is available entity status and
         * does not exceed the maximum usage times
         *
         * @param lot
         * @param equipmentRrn
         * @return
         */
        @Override
        public List<Relation> getAvailableReticlesForTrackIn(Lot lot, Long equipmentRrn) {
            List<Relation> reticleInfos = getReticleInfosForMovein(lot, equipmentRrn, true);

            for (Iterator<Relation> iterator = reticleInfos.iterator(); iterator.hasNext(); ) {
                Relation relation = iterator.next();

                Entity reticleEntity = entityManager.getEntity(relation.getInstanceRrn());

                if (isNeedCheckUseTimes(reticleEntity.getPDFlag())) {
                    Integer inuseWaferQty = lotQueryManager.getInuseWaferQtyForReticle(relation.getInstanceRrn());

                    if (isOutMaxTimesOfUse(reticleEntity.getMaxUseTimes(), reticleEntity.getCurrentUseTimes(),
                                           inuseWaferQty, lot.getQty1())) {
                        iterator.remove();
                    }
                }
            }
            return reticleInfos;
        }

        @Override
        public String getReticleIdsThatHasAlreadyEnteredEquipmentStr(Lot lot, Long equipmentRrn, Boolean isNeedFilter) {
            List<Relation> reticles = getReticlesThatHasAlreadyEnteredEquipment(lot, equipmentRrn);
            if (isNeedFilter) {
                reticles = filterSpecialStatus(reticles);
            }

            return buildReticleIdsStr(reticles);
        }

        @Override
        public String getAvailableReticleIdsStr(Lot lot, Long equipmentRrn) {
            return buildReticleIdsStr(getAvailableReticlesForTrackIn(lot, equipmentRrn));
        }

        @Override
        public String getAvailableReticleIdsStr(Long reticleFamilyRrn, Long equipmentRrn, Boolean statusFlag) {
            return buildReticleIdsStr(getReticlesForMovein(reticleFamilyRrn, equipmentRrn, statusFlag));
        }

        @Override
        public long getReticleFamilyRrnByLot(Lot lot) {
            return reticleFamilyContextManager
                    .getReticleFamilyRrn(lot.getProductRrn(), lot.getProductVersion(), lot.getProcessRrn(),
                                         lot.getProcessVersion(), lot.getRouteRrn(), lot.getOperationRrn());
        }

        public Boolean isOutMaxTimesOfUse(Integer maxUseTimes, Integer currentUseTimes, Integer inuseWaferQty,
                                          Double lotQty) {
            return maxUseTimes < currentUseTimes + inuseWaferQty + lotQty;
        }

        public Boolean isNeedCheckUseTimes(String pdFlag) {
            return EntityEnum.isPDFlagT(pdFlag);
        }

        private String buildReticleIdsStr(List<Relation> reticles) {
            if (CollectionUtils.isEmpty(reticles)) {
                return StringUtils.EMPTY;
            }
            StringBuilder reticleIds = new StringBuilder();
            for (Relation relation : reticles) {
                if (reticleIds.length() > 0) {
                    reticleIds.append(",").append(relation.getInstanceId());
                } else {
                    reticleIds.append(relation.getInstanceId());
                }
            }
            return reticleIds.toString();
        }

        private List<Relation> filterSpecialStatus(List<Relation> reticles) {
            Iterator<Relation> iterator = reticles.iterator();
            while (iterator.hasNext()) {
                Relation relation = iterator.next();
                String reticleStatus = entityManager.getEntityCurrentStatus(relation.getInstanceRrn());
                if (!EntityEnum.isRelease(reticleStatus)) {
                    iterator.remove();
                }
            }
            return reticles;
        }

        private List<Relation> getReticleInfosForMovein(Lot lot, Long equipmentRrn, Boolean statusFLag) {
            long reticleFamilyRrn = getReticleFamilyRrnByLot(lot);
            if (reticleFamilyRrn > 0) {
                return getReticlesForMovein(reticleFamilyRrn, equipmentRrn, statusFLag);
            }

            return Collections.emptyList();
        }

        private Map<String, Object> buildProcessInfoForContext(Lot lot) {
            Map<String, Object> processInfo = new HashMap<>();

            processInfo.put("facilityRrn", LocalContext.getFacilityRrn());
            processInfo.put("productRrn", lot.getProductRrn());
            processInfo.put("processRrn", lot.getProcessRrn());
            processInfo.put("routeRrn", lot.getRouteRrn());
            processInfo.put("operationRrn", lot.getOperationRrn());
            processInfo.put("routeSeq", lot.getRouteSeq());
            processInfo.put("operationSeq", lot.getOperationSeq());
            processInfo.put("processVersion", lot.getProcessVersion());

            return processInfo;
        }

    }