using MECF.Framework.Common.RecipeCenter; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.Schedulers; using Aitex.Core.Common; using MECF.Framework.Common.CommonData; using System.Windows; using Aitex.Core.RT.IOCore; using Aitex.Core.RT.Log; using Aitex.Core.Util; using MECF.Framework.Common.Routine; using PunkHPX8_RT.Modules; using Aitex.Core.RT.Fsm; using MECF.Framework.Common.SubstrateTrackings; using MECF.Framework.Common.ToolLayout; using MECF.Framework.RT.Core.Equipments; using MECF.Framework.Common.ProcessCell; using SecsGem.Core.ItemModel; using PunkHPX8_RT.Modules.Transporter; using PunkHPX8_Core; using Aitex.Core.RT.SCCore; using PunkHPX8_RT.Modules.SRD; using PunkHPX8_RT.Devices.EFEM; using Aitex.Sorter.Common; using PunkHPX8_RT.Modules.Reservoir; using PunkHPX8_RT.Modules.PlatingCell; using PunkHPX8_RT.Modules.VpwMain; using PunkHPX8_RT.Modules.VpwCell; namespace PunkHPX8_RT.Schedulers { public class SchedulerSequenceManager : Singleton { #region 常量 private const string ENGINEERING = "Engineering"; private const string PRODUCTION = "Production"; private const string TRNPA = "TRNPA"; private const string TRNPB = "TRNPB"; #endregion /// /// 解析Wafer所有调度工序 /// /// /// public List AnalyWaferAllSchedulerSequence(WaferInfo waferInfo,ModuleName vpw, SequenceRecipe sequenceRecipe) { List schedulerSequences = new List(); int index = 0; MoveItem moveItem = new MoveItem((ModuleName)waferInfo.OriginStation, waferInfo.OriginSlot, ModuleName.Aligner1, 0, Hand.Blade1,Flip.Upper,Flip.Upper); SchedulerSequence efemRobotSequence = CreateEfemRobotSequence(moveItem, null, sequenceRecipe.SubstrateSize, ref index); schedulerSequences.Add(efemRobotSequence); SchedulerSequence alignerSequence = CreateAlignerSequence(sequenceRecipe, ref index); schedulerSequences.Add(alignerSequence); MoveItem alignerToMoveItem = new MoveItem(ModuleName.Aligner1, 0, vpw, 0,Hand.Blade1, Flip.Upper, Flip.Upper); SchedulerSequence alignerRobotSequence = CreateEfemRobotSequence(alignerToMoveItem, null, sequenceRecipe.SubstrateSize, ref index); schedulerSequences.Add(alignerRobotSequence); //解析sequence recipe后续的工序 var sequences = AnalyseSequenceRecipeScheduler(sequenceRecipe,false); if (sequences.Count == 0) { return schedulerSequences; } SchedulerSequence firstSequence = sequences[0]; if (firstSequence.ModuleType != ModuleType.VPW) { return schedulerSequences; } firstSequence.SchedulerModule = SchedulerManager.Instance.GetScheduler(vpw); firstSequence.ModuleName = vpw; //调整工序后面的索引 foreach (SchedulerSequence item in sequences) { item.SequenceIndex = index; index++; } schedulerSequences.AddRange(sequences); //若经过srd if (SequenceRecipeManager.Instance.IsContainedSrd(sequenceRecipe)) { SrdRecipe srdRecipe = SequenceRecipeManager.Instance.GetSrdRecipeBySequenceRecipe(sequenceRecipe); if (srdRecipe != null) { //从VPW取至SRD MoveItem lastToSrdItem = new MoveItem(); lastToSrdItem.SourceModule = ModuleName.Unknown; //A面 lastToSrdItem.SourceSlot = 0; lastToSrdItem.SourceType = ModuleType.VPW; lastToSrdItem.DestinationType = ModuleType.SRD; lastToSrdItem.SourceSlot = 0; lastToSrdItem.DestinationModule = ModuleName.Unknown; lastToSrdItem.RobotHand = Hand.Blade1; lastToSrdItem.PickRobotFlip = Flip.Upper; lastToSrdItem.PlaceRobotFlip = Flip.Upper; SchedulerSequence lastToSrdSequence = CreateEfemRobotSequence(lastToSrdItem, null,sequenceRecipe.SubstrateSize, ref index); schedulerSequences.Add(lastToSrdSequence); SchedulerSequence srdSequence = CreateSRDSequence(srdRecipe, ref index); schedulerSequences.Add(srdSequence); //SRD完成后回至LoadPort MoveItem srdToLoadPortItem = new MoveItem(); srdToLoadPortItem.SourceModule = ModuleName.Unknown; srdToLoadPortItem.SourceType = ModuleType.SRD; srdToLoadPortItem.SourceSlot = 0; srdToLoadPortItem.DestinationType = ModuleType.LoadPort; srdToLoadPortItem.DestinationModule = (ModuleName)waferInfo.OriginStation; srdToLoadPortItem.DestinationSlot = waferInfo.OriginSlot; srdToLoadPortItem.RobotHand = Hand.Blade1; lastToSrdItem.PickRobotFlip = Flip.Upper; lastToSrdItem.PlaceRobotFlip = Flip.Upper; SchedulerSequence srdToLoadPortSequence = CreateEfemRobotSequence(srdToLoadPortItem, null,sequenceRecipe.SubstrateSize, ref index); schedulerSequences.Add(srdToLoadPortSequence); } else { LOG.WriteBackgroundLog(eEvent.ERR_SEQUENCE, "System", $"{sequenceRecipe.Ppid} srd recipe is invalid"); return new List(); } } else { //若无SRD,直接从VPW转LoadPort MoveItem srdToLoadPortItem = new MoveItem(); srdToLoadPortItem.SourceModule = ModuleName.Unknown; srdToLoadPortItem.SourceType = ModuleType.VPW; srdToLoadPortItem.SourceSlot = 0; srdToLoadPortItem.DestinationType = ModuleType.LoadPort; srdToLoadPortItem.DestinationModule = (ModuleName)waferInfo.OriginStation; srdToLoadPortItem.DestinationSlot = waferInfo.OriginSlot; srdToLoadPortItem.RobotHand = Hand.Blade1; srdToLoadPortItem.PickRobotFlip = Flip.Upper; srdToLoadPortItem.PlaceRobotFlip = Flip.Upper; SchedulerSequence pufToLoaderSequence = CreateEfemRobotSequence(srdToLoadPortItem, null,sequenceRecipe.SubstrateSize, ref index); schedulerSequences.Add(pufToLoaderSequence); } return schedulerSequences; } /// /// 解析Dummy Wafer所有调度工序 /// /// /// public List AnalyDummyWaferAllSchedulerSequence(SequenceRecipe sequenceRecipe, ModuleName vpw, WaferInfo waferInfo) { List schedulerSequences = new List(); int index = 0; MoveItem moveItem = new MoveItem((ModuleName)waferInfo.OriginStation, waferInfo.OriginSlot, ModuleName.Aligner1, 0, Hand.Blade1, Flip.Upper, Flip.Upper); SchedulerSequence efemRobotSequence = CreateEfemRobotSequence(moveItem, null, sequenceRecipe.SubstrateSize, ref index); schedulerSequences.Add(efemRobotSequence); SchedulerSequence alignerSequence = CreateAlignerSequence(sequenceRecipe, ref index); schedulerSequences.Add(alignerSequence); MoveItem alignerToMoveItem = new MoveItem(ModuleName.Aligner1, 0, vpw, 0, Hand.Blade1, Flip.Upper, Flip.Upper); SchedulerSequence alignerRobotSequence = CreateEfemRobotSequence(alignerToMoveItem, null, sequenceRecipe.SubstrateSize, ref index); schedulerSequences.Add(alignerRobotSequence); //解析sequence recipe后续的工序 var sequences = AnalyseSequenceRecipeScheduler(sequenceRecipe, false); if (sequences.Count == 0) { return schedulerSequences; } SchedulerSequence firstSequence = sequences[0]; if (firstSequence.ModuleType != ModuleType.VPW) { return schedulerSequences; } firstSequence.SchedulerModule = SchedulerManager.Instance.GetScheduler(vpw); firstSequence.ModuleName = vpw; //调整工序后面的索引 foreach (SchedulerSequence item in sequences) { item.SequenceIndex = index; index++; } schedulerSequences.AddRange(sequences); SchedulerSequence lastSequence=sequences[sequences.Count - 1]; //若无SRD,直接从VPW转LoadPort MoveItem srdToLoadPortItem = new MoveItem(); srdToLoadPortItem.SourceModule = ModuleName.Unknown; srdToLoadPortItem.SourceType = lastSequence.ModuleType; srdToLoadPortItem.SourceSlot = 0; srdToLoadPortItem.DestinationType = ModuleType.Dummy; srdToLoadPortItem.DestinationModule = (ModuleName)waferInfo.OriginStation; srdToLoadPortItem.DestinationSlot = waferInfo.OriginSlot; srdToLoadPortItem.RobotHand = Hand.Blade1; srdToLoadPortItem.PickRobotFlip = Flip.Upper; srdToLoadPortItem.PlaceRobotFlip = Flip.Upper; SchedulerSequence toDummySequence = CreateEfemRobotSequence(srdToLoadPortItem, null, sequenceRecipe.SubstrateSize, ref index); schedulerSequences.Add(toDummySequence); return schedulerSequences; } /// /// 分析Sequence recipe对应的调度步骤 /// /// /// private List AnalyseSequenceRecipeScheduler(SequenceRecipe sequenceRecipe,bool isDummy) { List schedulerSequences= new List(); List tmpLst = new List(); var processResult = AnalyseLastProcessRecipeIndex(sequenceRecipe.Recipes); bool isExistSrd = sequenceRecipe.Recipes.FindIndex(O=>O.ToLower().EndsWith("srd.rcp"))!=-1; int lastIndex = isExistSrd ? sequenceRecipe.Recipes.Count - 2 : sequenceRecipe.Recipes.Count - 1; for(int i=0;i /// 分析最后加工recipe索引 /// /// /// private (int lastIndex,List processIndexList) AnalyseLastProcessRecipeIndex(List recipes) { int index = 0; List lst = new List(); for(int i=0;i /// 创建EFEM Robot步骤 /// /// /// private SchedulerSequence CreateEfemRobotSequence(MoveItem moveItem,List synModules,int waferSize,ref int index) { SchedulerSequence sequence = new SchedulerSequence(); sequence.SchedulerModule = SchedulerManager.Instance.GetScheduler(ModuleName.EfemRobot); sequence.SequenceIndex= index; sequence.ModuleName=ModuleName.EfemRobot; sequence.State = RState.Init; sequence.Recipe = null; sequence.ModuleType=ModuleType.EfemRobot; sequence.Parameters =moveItem; sequence.MaterialType = MaterialType.Wafer; sequence.SynchronousModuleMessages = synModules; sequence.WaferSize = waferSize; index++; return sequence; } /// /// 创建Aligner步骤 /// /// /// private SchedulerSequence CreateAlignerSequence(SequenceRecipe recipe,ref int index) { SchedulerSequence sequence = new SchedulerSequence(); sequence.SchedulerModule = SchedulerManager.Instance.GetScheduler(ModuleName.Aligner1); sequence.SequenceIndex = index; sequence.ModuleName = ModuleName.Aligner1; sequence.State = RState.Init; sequence.Recipe = recipe; sequence.ModuleType = ModuleType.Aligner; sequence.Parameters = null; sequence.MaterialType = MaterialType.Wafer; sequence.WaferSize = sequence.WaferSize; index++; return sequence; } /// /// 创建Loader步骤 /// /// /// private SchedulerSequence CreateSRDSequence(SrdRecipe recipe,ref int index) { SchedulerSequence sequence = new SchedulerSequence(); sequence.SequenceIndex = index; sequence.State = RState.Init; sequence.Recipe = recipe; sequence.ModuleType = ModuleType.SRD; sequence.MaterialType = MaterialType.Wafer; index++; return sequence; } /// /// 根据模块类型获取可用模块 /// /// /// public ModuleName GetAvaibleModuleCell(string sequenceType,ModuleType moduleType) { List entities = Singleton.Instance.GetModulesByModuleType(moduleType); List avaibles = new List(); foreach (var item in entities) { bool result = CheckAvaibleModule(item,moduleType,sequenceType); if (result) { avaibles.Add(item); } } return GetMinTimeToReadyModule(avaibles,moduleType); } /// /// 根据化学液计算时间获取可用PlatingCellCell(参考其他PlatingCell剩余时间) /// /// /// public (ModuleName platingCellModule, ModuleName rinseModule) CalculateAvaiblePlatingCellCellByChemistry(string chemistry, string startRinse, string sequenceType, string waferHolderId, int waferSize,DateTime startTime, int depRecipeTimeLength, int qdrRecipeTimeLength, bool checkConflict, SchedulerModulePartTime platingCellPartTime, SchedulerModulePartTime qdrParTime, ref bool isExistEnablePlatingCell) { bool isShowLog = SC.GetValue("Scheduler.IsShowLog"); if (!Enum.TryParse(startRinse, out ModuleName startRinseModule) ) { startRinse = ""; } isExistEnablePlatingCell = false; return (ModuleName.Unknown, ModuleName.Unknown); } /// /// 检验模块是否可用 /// /// /// public bool CheckAvaibleModule(IModuleEntity item,ModuleType moduleType,string sequenceType) { if (item.IsDisable) { return false; } if (!item.IsAuto) { return false; } if (item.IsError) { return false; } if (item.IsInit) { return false; } if (moduleType==ModuleType.PlatingCell) { if(item.IsProduction && sequenceType == ENGINEERING) return false; if (item.IsEngineering && sequenceType == PRODUCTION) return false; } return true; } /// /// 获取剩余时间最小的模块 /// /// /// private ModuleName GetMinTimeToReadyModule(List avaibles,ModuleType moduleType) { if (avaibles.Count == 1) { return avaibles[0].Module; } int timeToReady = int.MaxValue; IModuleEntity selectedModule = null; List idleModuleEtities = new List(); foreach (var item in avaibles) { if (item.IsIdle) { if(moduleType!=ModuleType.PlatingCell) { return item.Module; } idleModuleEtities.Add(item); } if (item.TimeToReady < timeToReady&&idleModuleEtities.Count==0) { timeToReady = item.TimeToReady; selectedModule = item; } } if (selectedModule != null) { return selectedModule.Module; } return ModuleName.Unknown; } /// /// 根据化学液获取可用的PlatingCell集合 /// /// /// public List GetAvaiblePlatingCellList(string chemistry, string sequenceType, int waferSize, bool isEmpty) { List reservoirEntities = Singleton.Instance.GetModulesByModuleType(ModuleType.Reservoir); List avaibles = new List(); foreach (IModuleEntity item in reservoirEntities) { ReservoirEntity entity = item as ReservoirEntity; if (entity.Chemistry == chemistry && entity.IsAuto && entity.IsInitialized) { avaibles.Add(entity.Module.ToString()); } } List metals = new List(); foreach (string item in avaibles) { ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(item); if (reservoirItem == null) { continue; } foreach (PlatingCellItem subItem in reservoirItem.PlatingCells) { PlatingCellEntity entity = Singleton.Instance.GetModule($"PlatingCell{subItem.PlatingCellID}"); if (!CheckAvaibleModule(entity, ModuleType.PlatingCell, sequenceType)) { continue; } if (entity.MetalWaferSize != waferSize) { continue; } if (!isEmpty || (isEmpty && entity.WaferInfo == null)) { metals.Add(entity); } } } return metals; } /// /// 获取可用的VPW集合 /// /// /// public List GetAvaibleVpwList(int waferSize, bool isEmpty) { List vpwEtities = Singleton.Instance.GetModulesByModuleType(ModuleType.VPW); List avaibles = new List(); List vpws = new List(); foreach (IModuleEntity item in vpwEtities) { VpwCellEntity entity = (VpwCellEntity)item; if (!CheckAvaibleModule(entity, ModuleType.VPW, "")) { continue; } if (entity.VpwWaferSize != waferSize) { continue; } if (!isEmpty || (isEmpty && entity.WaferInfo == null)) { vpws.Add(entity); } } return vpws; } } }