PiLotCheckManagerImpl.java
package com.mycim.server.pilot.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.BooleanUtils;
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.time.DateUtils;
import com.mycim.server.base.manager.NamedObjectManager;
import com.mycim.server.ems.manager.EntityManager;
import com.mycim.server.ems.manager.EquipmentManager;
import com.mycim.server.pilot.manager.PiLotCheckManager;
import com.mycim.server.pilot.manager.PiLotReqManager;
import com.mycim.server.wip.manager.EquipmentQueryManager;
import com.mycim.server.wip.manager.PiLotInqManager;
import com.mycim.valueobject.Constants;
import com.mycim.valueobject.MessageIdList;
import com.mycim.valueobject.ObjectList;
import com.mycim.valueobject.consts.EntityEnum;
import com.mycim.valueobject.consts.PiLotStatusEnum;
import com.mycim.valueobject.consts.PiLotTypeEnum;
import com.mycim.valueobject.ems.Entity;
import com.mycim.valueobject.ems.Equipment;
import com.mycim.valueobject.ems.Season;
import com.mycim.valueobject.ems.pilot.IdleType;
import com.mycim.valueobject.ems.pilot.PiLotSetup;
import com.mycim.valueobject.ems.pilot.PiLotView;
import com.mycim.valueobject.ems.pilot.dto.PiLotSetupQueryDTO;
import com.mycim.valueobject.sys.Message;
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.Date;
import java.util.List;
import java.util.Objects;
/**
* @author songpy
* @version 1.0.0
* @date 2021/9/12
**/
@Service
@Transactional
public class PiLotCheckManagerImpl implements PiLotCheckManager {
@Autowired
private PiLotInqManager piLotInqManager;
@Autowired
private NamedObjectManager namedObjectManager;
@Autowired
private PiLotReqManager piLotReqManager;
@Autowired
private EquipmentQueryManager equipmentQueryManager;
@Autowired
private EquipmentManager equipmentManager;
@Autowired
private EntityManager entityManager;
@Override
public String checkEquipmentPiLot(Lot lot, Equipment equipment) {
StringBuilder msg = new StringBuilder(StringUtils.EMPTY);
PiLotView view = piLotInqManager.getPiLotViewByLotRrn(lot.getLotRrn());
if (Objects.isNull(view)) {
view = piLotInqManager.getPiLotViewByChildLotRrn(lot.getLotRrn());
}
// pilot绑定lot
if (Objects.nonNull(view)) {
// 绑定lot 不允许进入pilot设置机台外的其他机台
if (StringUtils.equalsIgnoreCase(lot.getRouteId(), view.getStartRoute()) &&
StringUtils.equalsIgnoreCase(lot.getOperationId(), view.getStartStep())) {
Equipment viewEquipment = equipmentManager.getEquipment(view.getEqptRrn());
if (!StringUtils.equalsIgnoreCase(getParentEquipment(viewEquipment), getParentEquipment(equipment))) {
msg.append(I18nUtils.getMessage(MessageIdList.PILOT_CHECK_EQPT_IN_USE, new Object[]{equipment.getInstanceId(),view.getEqptId()}));
}
}
if (StringUtils.isNotEmpty(msg.toString())) {
return msg.toString();
}
if (!StringUtils.equalsIgnoreCase(equipment.getObjectSubtype(), "P")) {
return msg.toString();
}
//子母批模式
if (StringUtils.equalsIgnoreCase(view.getSingleLot(), PiLotStatusEnum.NO.toString())) {
msg.append(checkSplitBefore(equipment, view));
if (StringUtils.isNotEmpty(msg.toString())) {
return msg.toString();
}
msg.append(checkSplitAfter(lot, equipment, view));
if (StringUtils.isNotEmpty(msg.toString())) {
return msg.toString();
}
}
// 单批模式
if (StringUtils.equalsIgnoreCase(view.getSingleLot(), PiLotStatusEnum.YES.toString())) {
msg.append(checkSingleLot(lot,equipment,view));
if (StringUtils.isNotEmpty(msg.toString())) {
return msg.toString();
}
}
} else {
// 触发pilot
msg.append(checkIdleTimePiLot(equipment));
if (StringUtils.isNotEmpty(msg.toString())) {
return msg.toString();
}
// 校验设置pilot 其他lot卡控进入设备
msg.append(checkEquipment(equipment));
}
return msg.toString();
}
@Override
public void checkEquipmentPiLotByPM(String eqpId) {
PiLotSetupQueryDTO queryDTO = new PiLotSetupQueryDTO();
queryDTO.addStatus(PiLotStatusEnum.ON.toString());
queryDTO.setType(PiLotTypeEnum.PM.toString());
queryDTO.setEqptRrn(
namedObjectManager.getNamedObjectRrn(eqpId, LocalContext.getFacilityRrn(), ObjectList.ENTITY_KEY));
PiLotSetup setup = piLotInqManager.getPiLotSetup(queryDTO);
if (Objects.nonNull(setup)) {
piLotReqManager.createPiLotView(setup);
}
}
@Override
public void checkPiLotBoundLot(Long lotRrn) {
PiLotView view = piLotInqManager.getPiLotViewByLotRrn(lotRrn);
Assert.state(Objects.nonNull(view), Errors.create().key(MessageIdList.PILOT_NOT_USED_SPLIT)
.content("This lot cannot be used Pilot Split").build());
Assert.state(view.getCanSplit(),
Errors.create().key(MessageIdList.PILOT_NOT_SPLIT).content("This Lot Not Split").build());
Assert.state(StringUtils.equalsIgnoreCase(view.getSingleLot(), PiLotStatusEnum.NO.toString()),
Errors.create().key(MessageIdList.PILOT_SINGLE_LOT_CANNOT_SPLIT)
.content("Pilot single lot cannot split!").build());
}
@Override
public void checkPiLotNormalFunction(long lotRrn,long baseLotRrn) {
PiLotView piLotView = getPiLotView(lotRrn, baseLotRrn);
Assert.state(ObjectUtils.isEmpty(piLotView), Errors.create().key(MessageIdList.PILOT_NOT_USED_FUNCTION)
.content("Pilot This Function cannot be used").build());
}
@Override
public String checkEquipmentPiLot(Lot lot, Long eqptRrn) {
Equipment equipment = equipmentManager.getEquipment(eqptRrn);
return checkEquipmentPiLot(lot, equipment);
}
@Override
public PiLotView getPiLotView(Long lotRrn, Long baseLotRrn) {
PiLotView piLotView = piLotInqManager.getPiLotViewByLotRrn(lotRrn);
if (ObjectUtils.isNotEmpty(piLotView)) {
return piLotView;
}
if (lotRrn.longValue() == baseLotRrn.longValue()){
return piLotInqManager.getPiLotViewByLotRrn(baseLotRrn);
} else {
return piLotInqManager.getPiLotViewByChildLotRrn(lotRrn);
}
}
@Override
public void checkPilotRepositionStep(Lot lot, Boolean pilotFlag) {
PiLotView view = getPiLotView(lot.getLotRrn(),lot.getBasedLotRrn());
if(pilotFlag) {
if (ObjectUtils.isEmpty(view)){
Assert.state(false, Errors.create().key(MessageIdList.PILOT_NOT_USED_SPLIT)
.content("This lot cannot be used Pilot Split").build());
}else {
// 单批模式没有分批可以使用跳步 子母批模式未分批前不能使用跳步
if (StringUtils.equalsIgnoreCase(view.getSingleLot(), PiLotStatusEnum.NO.toString())) {
Assert.state(StringUtils.isNotEmpty(view.getChildLotId()), Errors.create().key(MessageIdList.PILOT_NOT_SPLIT)
.content("This Lot Not Split").build());
}
}
} else {
Assert.state(ObjectUtils.isEmpty(view), Errors.create().key(MessageIdList.PILOT_NOT_USED_FUNCTION)
.content("Pilot This Function cannot be used").build());
}
}
private String checkSplitAfter(Lot lot, Equipment equipment, PiLotView view) {
StringBuilder msg = new StringBuilder();
PiLotView piLotView = piLotInqManager.getSplitAfterPiLotViewByEqpt(equipment.getInstanceId(),
equipment.getInstanceRrn());
// 分批后母批不能进入任何设备 子批只能走pilot chamber #42103
// 没有设置pilot其他的chamber #42283
if(ObjectUtils.isEmpty(piLotView)
&& StringUtils.equalsIgnoreCase(view.getStatus(), PiLotStatusEnum.WAITMERGE.toString())
&& StringUtils.isNotEmpty(equipment.getParentEntityId())) {
Equipment parentEquipment = equipmentManager.getEquipment(equipment.getParentEntityRrn());
//子批flow未完成
if (!view.getComplete()) {
//母批 需等待
if (StringUtils.equalsIgnoreCase(view.getBoundLotId(),lot.getLotId())) {
msg.append(I18nUtils.getMessage(MessageIdList.PILOT_CHILDLOT_FLOW_NOCOMPLETE,"{}:Pilot child lot flow not complete!",
new Object[]{equipment.getInstanceId()}));
}
//子批
if (StringUtils.equalsIgnoreCase(view.getChildLotId(),lot.getLotId())) {
checkPilotChamber(equipment, parentEquipment, view.getParallelRunType(), msg);
}
//子批flow完成 子批hold 母批走flow
} else {
checkPilotChamber(equipment, parentEquipment, view.getParallelRunType(), msg);
}
}
if (Objects.isNull(piLotView)) {
return msg.toString();
}
// 母批
if (StringUtils.equalsIgnoreCase(lot.getLotId(), piLotView.getBoundLotId())) {
if (!piLotView.getComplete()) {
msg.append(I18nUtils.getMessage(MessageIdList.PILOT_CHILDLOT_FLOW_NOCOMPLETE,"{}:Pilot child lot flow not complete!",
new Object[]{equipment.getInstanceId()}));
}
}
return msg.toString();
}
private String checkSplitBefore(Equipment equipment,PiLotView view) {
// 先检查是否存在已经触发的
StringBuilder msg = new StringBuilder();
List<PiLotView> piLotViewList = piLotInqManager.getSplitBeforePiLotViewByEqpt(equipment.getInstanceId(),
equipment.getInstanceRrn());
// 分批前母批 不能进入任何设备 chamber
if(CollectionUtils.isEmpty(piLotViewList)) {
if((StringUtils.equalsIgnoreCase(view.getStatus(),PiLotStatusEnum.WAITSPLIT.toString()) ||
(StringUtils.equalsIgnoreCase(view.getStatus(),PiLotStatusEnum.WAITMERGE.toString()) &&
StringUtils.isEmpty(view.getChildLotId()))
) && StringUtils.isNotEmpty(equipment.getParentEntityId())) {
msg.append(I18nUtils.getMessage(MessageIdList.PILOT_PARENT_LOT_NOT_SPLIT,"{}:Pilot parent lot not spilt!",
new Object[]{equipment.getInstanceId()}));
}
}
for (PiLotView piLotView : piLotViewList) {
if (StringUtils.equalsIgnoreCase(piLotView.getStatus(), PiLotStatusEnum.WAITSPLIT.toString()) ||
(StringUtils.equalsIgnoreCase(piLotView.getStatus(),PiLotStatusEnum.WAITMERGE.toString()) &&
StringUtils.isEmpty(piLotView.getChildLotId()))) {
msg.append(I18nUtils.getMessage(MessageIdList.PILOT_PARENT_LOT_NOT_SPLIT,"{}:Pilot parent lot not spilt!",
new Object[]{equipment.getInstanceId()}));
}
}
return msg.toString();
}
private String checkIdleTimePiLot(Equipment equipment) {
StringBuilder msg = new StringBuilder();
Boolean needPiLot = false;
PiLotSetup piLotSetup = piLotInqManager.getPiLotSetupIdleByEquipment(equipment.getInstanceId(),
equipment.getInstanceRrn());
if (Objects.nonNull(piLotSetup)) {
needPiLot = checkNeedPilotByTime(piLotSetup, equipment, needPiLot);
if (Boolean.TRUE.equals(needPiLot)) {
piLotReqManager.createPiLotView(piLotSetup);
piLotSetup = piLotInqManager.getPiLotSetupByRrn(piLotSetup.getPiLotRrn());
PiLotView view = piLotInqManager.getPiLotViewByRrn(piLotSetup.getBoundViewRrn());
// msg.append("Pilot:" + view.getViewId() + ",Please handle(bound Lot)!</br>");
msg.append(I18nUtils.getMessage(MessageIdList.PILOT_SETUP_AND_TRIGGER, "{}:setup and trigger pilot!Please assign lot!Pilot numberId:{}!",
new Object[]{equipment.getInstanceId(), view.getViewId()}));
}
}
return msg.toString();
}
private Long getEquipmentMaxRunTime(PiLotSetup piLotSetup, IdleType idleType, Equipment equipment) {
Long maxRunTime;
String lastMoveInTimeStr;
Date sysDate = new Date(System.currentTimeMillis());
Date lastUpdateTime = Objects.nonNull(
piLotSetup.getCloseTime()) ? piLotSetup.getCloseTime() : piLotSetup.getCreateTime();
Entity entity = entityManager.getEntity(equipment.getInstanceRrn());
if (StringUtils.endsWithIgnoreCase(Season.SUBTYPE_LOTLEVEL, idleType.getSubType())) {
lastMoveInTimeStr = equipmentQueryManager.getLastMoveInTimeByEqp(equipment.getInstanceId(),
equipment.getInstanceRrn());
} else {
Entity parentEntity = entityManager.getEntity(entity.getParentEntityRrn());
lastMoveInTimeStr = equipmentQueryManager.getLastProcessStartTime(parentEntity.getInstanceRrn(),
parentEntity.getInstanceId(),
entity.getChamberType());
}
Date maxRunTimeStart = getEffectiveTime4PiLot(lastMoveInTimeStr, lastUpdateTime);
maxRunTime = sysDate.getTime() - maxRunTimeStart.getTime();
return maxRunTime;
}
private Long getEquipmentIdleByWaferLevel(Equipment equipment, PiLotSetup piLotSetup) {
Long idleTimeSum = 0L;
Date sysDate = new Date(System.currentTimeMillis());
Date lastUpdateTime = Objects.nonNull(
piLotSetup.getCloseTime()) ? piLotSetup.getCloseTime() : piLotSetup.getCreateTime();
String equipmentStatus = entityManager.getEntityCurrentStatus(equipment.getInstanceRrn());
Entity entity = entityManager.getEntity(equipment.getInstanceRrn());
String mainEquipmentId = namedObjectManager.getNamedObjectId(entity.getParentEntityRrn());
if (StringUtils.equalsIgnoreCase(EntityEnum.IDLE.getValue(), equipmentStatus)) {
String lastProcessEndTimeStr = equipmentQueryManager.getLastProcessEndTime(mainEquipmentId,
entity.getChamberType());
Date lastProcessEndTime = getEffectiveTime4PiLot(lastProcessEndTimeStr, lastUpdateTime);
idleTimeSum = sysDate.getTime() - lastProcessEndTime.getTime();
} else if (StringUtils.equalsIgnoreCase(EntityEnum.RUN.getValue(), equipmentStatus)) {
if (equipmentQueryManager.checkEquipmentHasRunningLot(entity.getParentEntityRrn())) {
idleTimeSum = 0L;
} else {
Date idleStartTime;
String lastHoldTimeStr = equipmentQueryManager.getLastHoldTimeOnEqp(entity.getParentEntityRrn());
String equipmentLastMoveOutTimeStr = equipmentQueryManager.getLastProcessEndTime(mainEquipmentId,
entity.getChamberType());
idleStartTime = getEffectiveTime4PiLot(lastHoldTimeStr, lastUpdateTime);
idleStartTime = getEffectiveTime4PiLot(equipmentLastMoveOutTimeStr, idleStartTime);
idleTimeSum = sysDate.getTime() - idleStartTime.getTime();
}
}
return idleTimeSum;
}
private Long getEquipmentIdleByLotLevel(Equipment equipment, PiLotSetup piLotSetup) {
Long idleTimeSum = 0L;
Date sysDate = new Date(System.currentTimeMillis());
Date lastUpdateTime = Objects.nonNull(
piLotSetup.getCloseTime()) ? piLotSetup.getCloseTime() : piLotSetup.getCreateTime();
String equipmentStatus = entityManager.getEntityCurrentStatus(equipment.getInstanceRrn());
if (StringUtils.equalsIgnoreCase(EntityEnum.IDLE.getValue(), equipmentStatus)) {
String equipmentLastMoveOutTimeStr = equipmentQueryManager.getEquipmentLastMoveOutTime(
equipment.getInstanceId());
Date equipmentLastMoveOutTime = getEffectiveTime4PiLot(equipmentLastMoveOutTimeStr, lastUpdateTime);
idleTimeSum = sysDate.getTime() - equipmentLastMoveOutTime.getTime();
} else if (StringUtils.equalsIgnoreCase(EntityEnum.RUN.getValue(), equipmentStatus)) {
if (equipmentQueryManager.checkEquipmentHasRunningLot(equipment.getInstanceRrn())) {
idleTimeSum = 0L;
} else {
String lastHoldTimeStr = equipmentQueryManager.getLastHoldTimeOnEqp(equipment.getInstanceRrn());
String equipmentLastMoveOutTimeStr = equipmentQueryManager.getEquipmentLastMoveOutTime(
equipment.getInstanceId());
Date idleStartTime = getEffectiveTime4PiLot(lastHoldTimeStr, lastUpdateTime);
idleStartTime = getEffectiveTime4PiLot(equipmentLastMoveOutTimeStr, idleStartTime);
idleTimeSum = sysDate.getTime() - idleStartTime.getTime();
}
}
return idleTimeSum;
}
private Date getEffectiveTime4PiLot(String timeStr, Date lastUpdateTime) {
Date effectiveTime;
if (StringUtils.isNotBlank(timeStr)) {
effectiveTime = DateUtils.parse(timeStr, DateUtils.DATE_FORMAT);
} else {
effectiveTime = lastUpdateTime;
}
effectiveTime = effectiveTime.after(lastUpdateTime) ? effectiveTime : lastUpdateTime;
return effectiveTime;
}
private String getParentEquipment(Equipment equipment){
if(StringUtils.isNotEmpty(equipment.getParentEntityId())){
return equipment.getParentEntityId();
}else {
return equipment.getInstanceId();
}
}
private String checkSingleLot(Lot lot, Equipment equipment,PiLotView view) {
StringBuilder msg = new StringBuilder();
PiLotView piLotView = piLotInqManager.getSingleLotPiLotViewByEqpt(equipment.getInstanceId(),
equipment.getInstanceRrn());
//chamber机台 pilot逻辑
if (Objects.isNull(piLotView) && StringUtils.isNotEmpty(equipment.getParentEntityId())) {
Equipment parentEquipment = equipmentManager.getEquipment(equipment.getParentEntityRrn());
checkPilotChamber(equipment, parentEquipment, view.getParallelRunType(), msg);
}
if (Objects.isNull(piLotView)) {
return msg.toString();
}
return msg.toString();
}
// 没有绑定pilot的lot
private String checkEquipment(Equipment equipment) {
StringBuilder msg = new StringBuilder();
PiLotView piLotView = piLotInqManager.getPiLotViewByEqpt(equipment.getInstanceId(),
equipment.getInstanceRrn());
if (Objects.isNull(piLotView)) {
return msg.toString();
}
if (StringUtils.equalsIgnoreCase(piLotView.getStatus(), PiLotStatusEnum.ONGOING.toString())
&& StringUtils.isEmpty(piLotView.getBoundLotId())) {
msg.append(I18nUtils.getMessage(MessageIdList.PILOT_NOT_ASSIGN_LOT, "{}:setup pilot!Please assign lot!Pilot NumberId:{}!",
new Object[]{equipment.getInstanceId(), piLotView.getViewId()}));
} else if ( StringUtils.equalsIgnoreCase(piLotView.getStatus(), PiLotStatusEnum.WAITCOMPLETE.toString()) ||
StringUtils.equalsIgnoreCase(piLotView.getStatus(), PiLotStatusEnum.WAITMERGE.toString()) ||
StringUtils.equalsIgnoreCase(piLotView.getStatus(), PiLotStatusEnum.WAITSPLIT.toString())){
msg.append(I18nUtils.getMessage(MessageIdList.PILOT_EQPT_SETUP_PILOT, "{}:setup pilot!Pilot NumberId:{}!",
new Object[]{equipment.getInstanceId(),piLotView.getViewId()}));
}
return msg.toString();
}
/**
* pilot chamber机台卡控逻辑
* @param equipment
* @param parentEquipment
* @param runType
* @param msg
*/
private void checkPilotChamber(Equipment equipment, Equipment parentEquipment, String runType, StringBuilder msg) {
// 并行chamber single模式卡控
if (StringUtils.equalsIgnoreCase(parentEquipment.getChamberMode(), PiLotStatusEnum.PARALLEL.toString()) &&
StringUtils.equalsIgnoreCase(runType, PiLotStatusEnum.SINGLE.toString())) {
msg.append(I18nUtils.getMessage(MessageIdList.PILOT_PARALLEL_EQPT_SIGNLE,"{}:Pilot setup parallel run type is single!",
new Object[]{equipment.getInstanceId()}));
}
// 并行chamber mulit模式 其他腔设置pilot
if (StringUtils.equalsIgnoreCase(parentEquipment.getChamberMode(), PiLotStatusEnum.PARALLEL.toString()) &&
StringUtils.equalsIgnoreCase(runType, PiLotStatusEnum.MULTI.toString())) {
PiLotView otherPiLotView = piLotInqManager.getPiLotViewByEqpt(equipment.getInstanceId(),
equipment.getInstanceRrn());
if (ObjectUtils.isNotEmpty(otherPiLotView)) {
msg.append(I18nUtils.getMessage(MessageIdList.PILOT_EQPT_SETUP_PILOT, "{}:setup pilot!Pilot NumberId:{}!",
new Object[]{equipment.getInstanceId(),otherPiLotView.getViewId()}));
}
}
}
/**
* 判断是否满足by idletime的pilot 触发条件
* @param piLotSetup
* @param equipment
* @param needPiLot
* @return
*/
@Override
public Boolean checkNeedPilotByTime(PiLotSetup piLotSetup, Equipment equipment, Boolean needPiLot) {
IdleType idleType = piLotSetup.getIdle();
double idleTime = idleType.getIdleTime() * 60 * 60 * 1000;
double maxRunTime = idleType.getMaxRunTime() * 60 * 60 * 1000;
Long idleTimeSum = 0L;
Long realmaxRunTime = 0L;
if (StringUtils.equalsIgnoreCase(idleType.getSubType(), IdleType.SUBTYPE_LOTLEVEL.toString())) {
idleTimeSum = getEquipmentIdleByLotLevel(equipment, piLotSetup);
} else if (StringUtils.equalsIgnoreCase(idleType.getSubType(), IdleType.SUBTYPE_WAFERLEVEL.toString())) {
idleTimeSum = getEquipmentIdleByWaferLevel(equipment, piLotSetup);
}
if (idleTimeSum > 0 && idleTimeSum > idleTime) {
needPiLot = true;
} else if (idleTimeSum == 0 && maxRunTime != 0) {
realmaxRunTime = getEquipmentMaxRunTime(piLotSetup, idleType, equipment);
if (realmaxRunTime > maxRunTime) {
needPiLot = true;
}
}
return needPiLot;
}
}