using System; using System.Collections.Generic; using System.Linq; using Aitex.Core.Common; using Aitex.Core.RT.Routine; using Aitex.Core.RT.SCCore; using Aitex.Sorter.Common; using Aitex.Core.RT.Log; using Aitex.Core.Util; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.RecipeCenter; using Aitex.Core.RT.Fsm; using MECF.Framework.Common.Jobs; using MECF.Framework.Common.Routine; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.SubstrateTrackings; using MECF.Framework.Common.Schedulers; using MECF.Framework.Common.DBCore; using Venus_Core; using Venus_RT.Modules.Schedulers; using Venus_RT.Scheduler; namespace Venus_RT.Modules { enum ModulePriority { High, Middle, Low, Stop, } enum MovingStatus { Staying, Waiting, Moving, } class ModuleFlag { public ModulePriority Priority { get; set; } public MovingStatus MovingStatus { get; set; } public ModuleFlag(ModulePriority _priority) { Priority = _priority; MovingStatus = MovingStatus.Staying; } } class AutoCycle : IRoutine { private List _lstControlJobs = new List(); private List _lstProcessJobs = new List(); private RState _cycleState = RState.Init; Dictionary _atmSchedulers = new Dictionary(); Dictionary _atmModules = new Dictionary(); Dictionary _vacSchedulers = new Dictionary(); Dictionary _vacModules = new Dictionary(); SchedulerTMRobot _tmRobot = new SchedulerTMRobot(); SchedulerEfemRobot _efemRobot = new SchedulerEfemRobot(); List _vacReadyOutSlots = new List(); List _vacReadyInSlots = new List(); List _atmReadyOutSlots = new List(); List _atmReadyInSlots = new List(); List _LLAInSlot = new List { 0, 1, 2, 3 }; List _LLAOutSlot = new List { 0, 1, 2, 3 }; List _LLBInSlot = new List { 0, 1, 2, 3 }; List _LLBOutSlot = new List { 0, 1, 2, 3 }; List _movingItems = new List(); List _efemMovingItems = new List(); Dictionary _vacWaferTargets = new Dictionary(); Dictionary _atmWaferTargets = new Dictionary(); private SchedulerFACallback _faCallback; private SchedulerDBCallback _dbCallback; private bool _isCycleMode; private int _cycleSetPoint = 0; private int _cycledCount = 0; private int _cycledWafer = 0; #region public interface public AutoCycle() { _faCallback = new SchedulerFACallback(); _dbCallback = new SchedulerDBCallback(); InitModules(); DATA.Subscribe("Scheduler.CycledCount", () => _cycledCount); DATA.Subscribe("Scheduler.CycledWafer", () => _cycledWafer); DATA.Subscribe("Scheduler.CycleSetPoint", () => _cycleSetPoint); DATA.Subscribe("Scheduler.PjIdList", () => Array.ConvertAll(_lstProcessJobs.ToArray(), x => x.InnerId.ToString()).ToList()); } public RState Start(params object[] objs) { _isCycleMode = SC.GetValue("System.IsCycleMode"); _cycleSetPoint = _isCycleMode ? SC.GetValue("System.CycleCount") : 0; _cycledWafer = 0; _cycledCount = 0; return RState.Running; } public RState Monitor() { prelude(); driveAtmSystem(); driveVacSystem(); epilogue(); return _cycleState; } public void Abort() { } public void CreateJob(Dictionary param) { _isCycleMode = SC.GetValue("System.IsCycleMode"); _cycleSetPoint = _isCycleMode ? SC.GetValue("System.CycleCount") : 0; string[] slotSequence = (string[])param["SlotSequence"]; string jobId = (string)param["JobId"]; string module = (string)param["Module"]; //bool autoStart = (bool)param["AutoStart"]; string lotId = jobId; if (param.ContainsKey("LotId")) lotId = (string)param["LotId"]; if (slotSequence.Length != SC.GetValue("EFEM.LoadPort.SlotNumber")) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"slot sequence parameter not valid, length is {slotSequence.Length}, should be {SC.GetValue("EFEM.LoadPort.SlotNumber")}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } if (!ModuleHelper.IsLoadPort(ModuleHelper.Converter(module))) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"{module} should be LoadPort"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } if (string.IsNullOrEmpty(jobId)) { jobId = "CJ_Local_" + module; } if (_lstControlJobs.Exists(x => x.Name == jobId)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"{jobId} already created"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } SchedulerLoadPort lp = _atmSchedulers[ModuleHelper.Converter(module)] as SchedulerLoadPort; if (!lp.CheckReadyRunJob()) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"{module} not ready"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } ControlJobInfo cj = new ControlJobInfo(); cj.Name = jobId; cj.Module = module; cj.LotName = lotId; cj.LotInnerId = Guid.NewGuid(); cj.LotWafers = new List(); cj.SetState(EnumControlJobState.WaitingForStart); Dictionary seqSlot = new Dictionary(); Dictionary>> seqSlotWafers = new Dictionary>>(); Dictionary indexSequence = new Dictionary(); bool enableGroupBySequence = SC.GetValue("Scheduler.GroupWaferBySequence"); for (int i = 0; i < SC.GetValue("EFEM.LoadPort.SlotNumber"); i++) { if (string.IsNullOrEmpty(slotSequence[i]) || string.IsNullOrEmpty(slotSequence[i].Trim())) continue; string groupName = enableGroupBySequence ? slotSequence[i].Trim() : i.ToString(); indexSequence[groupName] = slotSequence[i]; if (!seqSlot.ContainsKey(groupName)) { seqSlot[groupName] = new bool[SC.GetValue("EFEM.LoadPort.SlotNumber")]; } if (!seqSlotWafers.ContainsKey(groupName)) { seqSlotWafers[groupName] = new List>(); } seqSlot[groupName][i] = true; if (!WaferManager.Instance.CheckHasWafer(module, i)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job wafer: {module} slot {i + 1} not in the carrier"); return; } if (!WaferManager.Instance.CheckWafer(ModuleHelper.Converter(module), i, WaferStatus.Normal)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job wafer: {module} slot {i + 1} status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Status}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } if (WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState != EnumWaferProcessStatus.Idle) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job wafer: {module} slot {i + 1} process status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } cj.LotWafers.Add(WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i)); WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).SequenceName = slotSequence[i]; seqSlotWafers[groupName].Add(Tuple.Create(ModuleHelper.Converter(module), i)); cj.JobWaferSize = WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Size; LOG.Write(eEvent.EV_ROUTER, ModuleName.System, $"Assigned wafer job, wafer {module}.{i + 1}, sequence: {slotSequence[i]}"); } if (seqSlotWafers.Count == 0) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job has not assign wafer"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } List pjs = new List(); string[] seqs = seqSlot.Keys.ToArray(); for (int i = 0; i < seqs.Length; i++) { ProcessJobInfo pj = new ProcessJobInfo(); pj.Name = jobId + "_" + (i + 1); pj.Sequence = SequenceInfoHelper.GetInfo(indexSequence[seqs[i]]); pj.ControlJobName = cj.Name; pj.LotName = lotId; pj.SlotWafers = seqSlotWafers[seqs[i]]; pj.SetState(EnumProcessJobState.Queued); if (!CheckSequencePmReady(pj.Sequence, null, out _, out string reason)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"no valid chamber for the {reason}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } if (!SC.GetValue("System.IsATMMode") && !CheckSequenceRecipeFileValid(pj.Sequence, out reason)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"recipe file not valid in the sequence, {reason}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } pjs.Add(pj); } _dbCallback.LotUpdate(cj); foreach (var pj in pjs) { cj.ProcessJobNameList.Add(pj.Name); _lstProcessJobs.Add(pj); } _lstControlJobs.Add(cj); //AssociatedPMWithLP(cj); _faCallback.JobCreated(cj, GetFirstProcessJob(cj)); } public bool CheckAllJobDone() { foreach (var cj in _lstControlJobs) { if (cj.State == EnumControlJobState.Executing || cj.State == EnumControlJobState.Paused) return false; } return true; } public bool CheckJobJustDone(out string sJobName) { foreach (var cj in _lstControlJobs) { if (cj.State == EnumControlJobState.Completed && !cj.BeenPosted) { //LP;WaferSize;Lot;Number;Start;End; sJobName = $"{cj.Module};{cj.JobWaferSize};{cj.LotName};{cj.LotWafers.Count};{cj.StartTime:T};{cj.EndTime:T}"; cj.BeenPosted = true; return true; } } sJobName = "NULL"; return false; } public ProcessJobInfo GetFirstProcessJob(ControlJobInfo cj) { foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) return pj; } return null; } public void AbortJob(string jobName) { ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"abort job rejected, not found job with id {jobName}"); return; } List pjAbortList = new List(); foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) { pj.SetState(EnumProcessJobState.Aborting); pjAbortList.Add(pj); int unprocessed = 0; int aborted = 0; WaferInfo[] wafers = WaferManager.Instance.GetWaferByProcessJob(pj.Name); foreach (var waferInfo in wafers) { waferInfo.ProcessJob = null; waferInfo.NextSequenceStep = 0; if (waferInfo.ProcessState != EnumWaferProcessStatus.Completed) unprocessed++; } JobDataRecorder.EndPJ(pj.InnerId.ToString(), aborted, unprocessed); } } _faCallback.JobAborted(cj, GetFirstProcessJob(cj)); _dbCallback.LotFinished(cj); foreach (var pj in pjAbortList) { _lstProcessJobs.Remove(pj); } _lstControlJobs.Remove(cj); } public void StopJob(string jobName) { ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"stop job rejected, not found job with id {jobName}"); return; } foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) { pj.SetState(EnumProcessJobState.Stopping); } } _faCallback.JobStopped(cj, GetFirstProcessJob(cj)); _dbCallback.LotFinished(cj); } public void ResumeJob(string jobName) { ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"resume job rejected, not found job with id {jobName}"); return; } if (cj.State == EnumControlJobState.Paused) { cj.SetState(EnumControlJobState.Executing); } _faCallback.JobResumed(cj, GetFirstProcessJob(cj)); } public void PauseJob(string jobName) { ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"pause job rejected, not found job with id {jobName}"); return; } if (cj.State == EnumControlJobState.Executing) { cj.SetState(EnumControlJobState.Paused); } _faCallback.JobPaused(cj, GetFirstProcessJob(cj)); } public void StartJob(string jobName) { ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"start job rejected, not found job with id {jobName}"); return; } if (cj.State == EnumControlJobState.WaitingForStart) { cj.SetState(EnumControlJobState.Executing); //PreJobClean(cj); (_atmSchedulers[ModuleHelper.Converter(cj.Module)] as SchedulerLoadPort).NoteJobStart(); cj.StartTime = DateTime.Now; _dbCallback.LotCreated(cj); _faCallback.JobStarted(cj, GetFirstProcessJob(cj)); //if (!_blockingWatcher.IsRunning) // _blockingWatcher.Restart(); //ResetTraceFlag(); } } #endregion private void InitModules() { foreach(var module in new ModuleName[]{ ModuleName.LP1, ModuleName.LP2, ModuleName.LP3, ModuleName.Aligner1, ModuleName.Aligner2, ModuleName.Cooling1, ModuleName.Cooling2}) { if(ModuleHelper.IsInstalled(module)) { if(ModuleHelper.IsLoadPort(module)) { _atmSchedulers[module] = new SchedulerLoadPort(module); _atmModules[module] = new ModuleFlag(ModulePriority.Middle); } else if(ModuleHelper.IsAligner(module) || ModuleHelper.IsCooling(module)) { _atmSchedulers[module] = new SchedulerAligner(module); _atmModules[module] = new ModuleFlag(ModulePriority.Middle); } } } foreach (var module in new ModuleName[] { ModuleName.PMA, ModuleName.PMB, ModuleName.PMC, ModuleName.PMD, ModuleName.PME, ModuleName.PMF}) { if (ModuleHelper.IsInstalled(module)) { _vacSchedulers[module] = new SchedulerPM(module); _vacModules[module] = new ModuleFlag(ModulePriority.Middle); } } foreach (var module in new ModuleName[] { ModuleName.LLA, ModuleName.LLB }) { if (ModuleHelper.IsInstalled(module)) { var llScheduler = new SchedulerLoadLock(module); _vacSchedulers[module] = llScheduler; _vacModules[module] = new ModuleFlag(ModulePriority.Middle); _atmSchedulers[module] = llScheduler; _atmModules[module] = new ModuleFlag(ModulePriority.Middle); } } } private void prelude() { } private void epilogue() { foreach(var mod in _vacSchedulers) { if(mod.Value.IsAvailable) { var tars = _vacWaferTargets.Where(item => item.Key.Module == mod.Key).ToArray(); foreach(var tar in tars) { var wafer = WaferManager.Instance.GetWafer(tar.Key.Module, tar.Key.Slot); if (wafer.IsEmpty) continue; if(wafer.InnerId == tar.Value) { // wafer arrive _vacWaferTargets.Remove(tar.Key); wafer.NextSequenceStep++; } } } } foreach(var mod in _atmSchedulers) { if (mod.Value.IsAvailable) { var tars = _atmWaferTargets.Where(item => item.Key.Module == mod.Key).ToArray(); foreach (var tar in tars) { var wafer = WaferManager.Instance.GetWafer(tar.Key.Module, tar.Key.Slot); if (wafer.IsEmpty) continue; if (wafer.InnerId == tar.Value) { // wafer arrive _atmWaferTargets.Remove(tar.Key); if (!ModuleHelper.IsLoadPort(tar.Key.Module)) wafer.NextSequenceStep++; } } } } } private void driveVacSystem() { PumpingTMRobotTask(); ProcessTMRobotTask(); RuningTMRobotTask(); } #region Vacuum System private void PumpingTMRobotTask() { foreach(var mod in _vacSchedulers) { if (mod.Value.IsAvailable && _vacModules[mod.Key].MovingStatus == MovingStatus.Staying) { if(ModuleHelper.IsLoadLock(mod.Key)) { var inSlots = mod.Key == ModuleName.LLA ? _LLAInSlot : _LLBInSlot; foreach(var slot in inSlots) { if(WaferManager.Instance.CheckHasWafer(mod.Key, slot) && IsVacWaferReadyOut(mod.Key, slot)) { _vacReadyOutSlots.Add(new SlotItem(mod.Key, slot)); _vacModules[mod.Key].MovingStatus = MovingStatus.Waiting; } } var outSlots = mod.Key == ModuleName.LLA ? _LLAOutSlot : _LLBOutSlot; foreach(var slot in outSlots) { if(WaferManager.Instance.CheckNoWafer(mod.Key, slot)) { _vacReadyInSlots.Add(new SlotItem(mod.Key, slot)); _vacModules[mod.Key].MovingStatus = MovingStatus.Waiting; } } } else { if(WaferManager.Instance.CheckHasWafer(mod.Key, 0) && IsVacWaferReadyOut(mod.Key, 0)) // processed? { _vacReadyOutSlots.Add(new SlotItem(mod.Key, 0)); _vacModules[mod.Key].MovingStatus = MovingStatus.Waiting; } else { _vacReadyInSlots.Add(new SlotItem(mod.Key, 0)); _vacModules[mod.Key].MovingStatus = MovingStatus.Waiting; } } } } } bool SearchWaferDestination(SlotItem outSlot, out SlotItem destSlot) { destSlot = new SlotItem(ModuleName.System, -1); var wafer = WaferManager.Instance.GetWafer(outSlot.Module, outSlot.Slot); foreach (var next_module in wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules) { var validSlots = GetModuleValidSlots(next_module); foreach (var nSlot in validSlots) { if (_movingItems.Exists(mItem => mItem.DestinationModule == next_module && mItem.DestinationSlot == nSlot)) continue; var ready_in = _vacReadyInSlots.Find(item => item.Module == next_module && item.Slot == nSlot); if (ready_in != null) { destSlot.Module = ready_in.Module; destSlot.Slot = nSlot; return true; } } } return false; } List GetModuleValidSlots(ModuleName mod) { var validSlot = new List(); if(ModuleHelper.IsLoadLock(mod)) { var inSlots = mod == ModuleName.LLA ? _LLAInSlot : _LLBInSlot; foreach (var slot in inSlots) { if (WaferManager.Instance.CheckNoWafer(mod, slot) && !_movingItems.Exists(item => item.DestinationModule == mod && item.DestinationSlot == slot)) validSlot.Add(slot); if (WaferManager.Instance.CheckHasWafer(mod, slot) && _movingItems.Exists(item => item.SourceModule == mod && item.SourceSlot == slot)) validSlot.Add(slot); } } else if(ModuleHelper.IsPm(mod)) { if (WaferManager.Instance.CheckNoWafer(mod, 0) && !_movingItems.Exists(item => item.DestinationModule == mod)) validSlot.Add(0); if (WaferManager.Instance.CheckHasWafer(mod, 0) && _movingItems.Exists(item => item.SourceModule == mod)) validSlot.Add(0); } return validSlot.Distinct().ToList(); } List SearchWaitInSlots(ModuleName inModule) { List inSlots = new List(); var validSlots = GetModuleValidSlots(inModule); foreach(var slot in _vacReadyOutSlots) { if (slot.Module == inModule) continue; var wafer = WaferManager.Instance.GetWafer(slot.Module, slot.Slot); foreach (var next_module in wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules) { if(next_module == inModule && validSlots.Count > 0) { inSlots.Add(new MoveItem(slot.Module, slot.Slot, inModule, validSlots.First(), Hand.None) ); validSlots.RemoveAt(0); } } } return inSlots; } private void ProcessTMRobotTask() { if(_tmRobot.IsAvailable) { if(WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) || WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1)) { return; } foreach(var slot in _vacReadyOutSlots) { if(ModuleHelper.IsLoadLock(slot.Module)) { if(SearchWaferDestination(slot, out SlotItem destSlot)) { _movingItems.Add(new MoveItem(slot.Module, slot.Slot, destSlot.Module, destSlot.Slot, Hand.None)); // check whether match double pick pattern var out_slot_2 = _vacReadyOutSlots.Find(item => item.Module == slot.Module && item.Slot != slot.Slot); if (out_slot_2 != null) { if(SearchWaferDestination(out_slot_2, out SlotItem destSlot_2)) { _movingItems.Add(new MoveItem(out_slot_2.Module, out_slot_2.Slot, destSlot_2.Module, destSlot_2.Slot, Hand.None)); } } // check whether match swap pattern var in_slots = SearchWaitInSlots(slot.Module); foreach(var in_slot in in_slots) { _movingItems.Add(in_slot); } return; } } else // PM { if(SearchWaferDestination(slot, out SlotItem destSlot)) { _movingItems.Add(new MoveItem(slot.Module, slot.Slot, destSlot.Module, destSlot.Slot, Hand.None)); // check whether match swap pattern var in_slots = SearchWaitInSlots(slot.Module); if(in_slots.Count >= 1) { _movingItems.Add(in_slots.First()); } else { // check whether match double move pattern var same_dest = SearchWaitInSlots(destSlot.Module); foreach (var item in same_dest) { _movingItems.Add(item); } } return; } } } } } private void RuningTMRobotTask() { if(_tmRobot.IsAvailable) { if(_tmRobot.PostMoveItems(_movingItems.ToArray())) { foreach(var item in _movingItems) { var wafer = WaferManager.Instance.GetWafer(item.SourceModule, item.SourceSlot); if (wafer.IsEmpty) { // post alarm LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Cannot run TM moving task as Get {item.SourceModule}{item.SourceModule} Wafer Info failed"); return; } var slot = new SlotItem(item.DestinationModule, item.DestinationSlot); _vacWaferTargets[slot] = wafer.InnerId; } _movingItems.Clear(); } } } private Hand GetTMRobotFreeHand() { var blade1HasWafer = WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0); var blade2HasWafer = WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1); if (blade1HasWafer && blade2HasWafer) return Hand.None; else if (!blade1HasWafer && !blade2HasWafer) return Hand.Both; else if (blade1HasWafer) return Hand.Blade2; else return Hand.Blade1; } private bool IsVacWaferReadyOut(ModuleName mod, int slot) { var wafer = WaferManager.Instance.GetWafer(mod, slot); if (wafer.IsEmpty) return false; if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) return false; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) { // should not go here LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Wafer:{wafer.WaferOrigin} NextSequenceStep {wafer.NextSequenceStep} exceeds {wafer.ProcessJob.Sequence.Name} max steps number"); return false; } if (wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(mod)) return false; foreach(var module in wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules) { if (ModuleHelper.IsLoadLock(module) || ModuleHelper.IsPm(module)) return true; } return false; } #endregion Vacuum System #region Atm System private void driveAtmSystem() { PumpingEFEMRobotTask(); RuningEFEMRobotTask(); } private void PumpingEFEMRobotTask() { if (!_efemRobot.IsAvailable) return; foreach(var scheduler in _atmSchedulers) { if (!scheduler.Value.IsAvailable) continue; if(ModuleHelper.IsLoadLock(scheduler.Key)) { if (ProcessLLEFEMRobotTask(scheduler.Key)) return; } else if(ModuleHelper.IsLoadPort(scheduler.Key)) { if (ProcessLPEFEMRobotTask(scheduler.Key)) return; } else if(ModuleHelper.IsAligner(scheduler.Key)) { if (ProcessAlignerEFEMRobotTask(scheduler.Key)) return; } else if(ModuleHelper.IsCooling(scheduler.Key)) { if (ProcessCoolingEFEMRobotTask(scheduler.Key)) return; } else { // should not go here LOG.Write(eEvent.ERR_ROUTER, $"Wrong Module: {scheduler.Key} in ATM System"); } } } List GetEfemRobotWaferReadyInHands(ModuleName mod) { var slots = new List(); for(int i = 0; i < 2; i++) { var wafer = WaferManager.Instance.GetWafer(ModuleName.EfemRobot, i); if (wafer.IsEmpty) continue; if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) continue; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) { // should not go here LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Wafer:{wafer.WaferOrigin} NextSequenceStep {wafer.NextSequenceStep} exceeds {wafer.ProcessJob.Sequence.Name} max steps number"); continue; } if (wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(mod)) { slots.Add(i); } } return slots; } bool IsAtmWaferReadyOut(ModuleName mod, int slot) { var wafer = WaferManager.Instance.GetWafer(mod, slot); if (wafer.IsEmpty) return false; if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) return false; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) { // should not go here LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Wafer:{wafer.WaferOrigin} NextSequenceStep {wafer.NextSequenceStep} exceeds {wafer.ProcessJob.Sequence.Name} max steps number"); return false; } if (wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(mod)) return false; if(ModuleHelper.IsLoadLock(mod)) { foreach (var module in wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules) { if (ModuleHelper.IsPm(module)) return false; } } return true; } private bool ProcessLLEFEMRobotTask(ModuleName ll) { var robotSlots = GetEfemRobotWaferReadyInHands(ll); var inSlots = ll == ModuleName.LLA ? _LLAInSlot : _LLBInSlot; foreach(var slot in inSlots) { if(robotSlots.Count >= 1) { if(WaferManager.Instance.CheckNoWafer(ll, slot)) { _efemMovingItems.Add(new MoveItem(ModuleName.EfemRobot, robotSlots.First(), ll, slot, (Hand)robotSlots.First())); robotSlots.RemoveAt(0); } } } var outSlots = ll == ModuleName.LLA ? _LLAOutSlot : _LLBOutSlot; foreach(var slot in outSlots) { if(IsAtmWaferReadyOut(ll, slot)) { var robotHand = GetEFEMRobotFreeHand(); if(robotHand != Hand.None) { _efemMovingItems.Add(new MoveItem(ll, slot, ModuleName.EfemRobot, (int)robotHand, robotHand)); } } } return _efemMovingItems.Count > 0; } private bool ProcessLPEFEMRobotTask(ModuleName lp) { // check return for(int i = 0; i < 2; i++) { if(WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, i)) { var returnWafer = WaferManager.Instance.GetWafer(ModuleName.EfemRobot, i); if (returnWafer.IsEmpty) continue; if (returnWafer.ProcessJob == null || returnWafer.ProcessJob.Sequence == null) continue; if (returnWafer.NextSequenceStep >= returnWafer.ProcessJob.Sequence.Steps.Count && lp == (ModuleName)returnWafer.OriginStation) // need return { _efemMovingItems.Add(new MoveItem(ModuleName.EfemRobot, i, lp, returnWafer.OriginSlot, (Hand)i)); } } } if (_efemMovingItems.Count > 0) return true; var outSlots = GetNextWaferInJobQueue(lp); foreach(var slot in outSlots) { var hand = GetEFEMRobotFreeHand(); if(hand != Hand.None) { _efemMovingItems.Add(new MoveItem(lp, slot, ModuleName.EfemRobot, (int)hand, hand)); } } return _efemMovingItems.Count > 0; } private List GetNextWaferInJobQueue(ModuleName lp) { var inSlots = new List(); //if (GetSystemInnerWaferCount() >= SystemInternalWaferCount || GetBufferWaferCount() > _maxBufferWaferCount) //return new SlotItem(ModuleName.System, -1); foreach (var cj in _lstControlJobs) { if (lp != ModuleName.System && (cj.Module != lp.ToString())) continue; if (cj.State == EnumControlJobState.Executing) { foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name && pj.State == EnumProcessJobState.Processing) { foreach (var pjSlotWafer in pj.SlotWafers) { if (CheckWaferNeedProcessAndPMAvailable(pjSlotWafer.Item1, pjSlotWafer.Item2)) { //return new SlotItem(pjSlotWafer.Item1, pjSlotWafer.Item2); inSlots.Add(pjSlotWafer.Item2); } } } } } } return inSlots; } private bool ProcessAlignerEFEMRobotTask(ModuleName aligner) { if(WaferManager.Instance.CheckHasWafer(aligner, 0) && IsAtmWaferReadyOut(aligner, 0)) { var hand = GetEFEMRobotFreeHand(); if(hand != Hand.None) { _efemMovingItems.Add(new MoveItem(aligner, 0, ModuleName.EfemRobot, (int)hand, hand)); } } else { var robotSlots = GetEfemRobotWaferReadyInHands(aligner); if(robotSlots.Count > 0) { _efemMovingItems.Add(new MoveItem(aligner, 0, ModuleName.EfemRobot, robotSlots.First(), (Hand)robotSlots.First())); } } return _efemMovingItems.Count > 0; } private bool ProcessCoolingEFEMRobotTask(ModuleName cooling) { if (WaferManager.Instance.CheckHasWafer(cooling, 0) && IsAtmWaferReadyOut(cooling, 0)) { var hand = GetEFEMRobotFreeHand(); if (hand != Hand.None) { _efemMovingItems.Add(new MoveItem(cooling, 0, ModuleName.EfemRobot, (int)hand, hand)); } } else { var robotSlots = GetEfemRobotWaferReadyInHands(cooling); if (robotSlots.Count > 0) { _efemMovingItems.Add(new MoveItem(cooling, 0, ModuleName.EfemRobot, robotSlots.First(), (Hand)robotSlots.First())); } } return _efemMovingItems.Count > 0; } private void RuningEFEMRobotTask() { if (_efemRobot.IsAvailable) { if (_efemRobot.PostMoveItems(_efemMovingItems.ToArray())) { foreach (var item in _efemMovingItems) { var wafer = WaferManager.Instance.GetWafer(item.SourceModule, item.SourceSlot); if (wafer.IsEmpty) { // post alarm LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Cannot run EFEM moving task as Get {item.SourceModule}{item.SourceModule} Wafer Info failed"); return; } var slot = new SlotItem(item.DestinationModule, item.DestinationSlot); _atmWaferTargets[slot] = wafer.InnerId; } _efemMovingItems.Clear(); } } } private Hand GetEFEMRobotFreeHand() { for(int i = 0; i < 2; i++) { if ((WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, i) && !_efemMovingItems.Exists(item => item.DestinationModule == ModuleName.EfemRobot && item.DestinationSlot == i)) || (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, i) && _efemMovingItems.Exists(item => item.SourceModule == ModuleName.EfemRobot && item.SourceSlot == i))) return (Hand)i; } return Hand.None; } #endregion Atm System #region Sequence validation private bool CheckSequencePmReady(SequenceInfo seq, List pmOccupied, out List pmUsed, out string reason) { pmUsed = new List(); reason = ""; bool pmIsReady = true; for (int i = 0; i < seq.Steps.Count; i++) { SequenceStepInfo stepInfo = seq.Steps[i]; bool hasPm = false; foreach (var module in stepInfo.StepModules) { if (!ModuleHelper.IsPm(module)) { hasPm = true; break; } if (ModuleHelper.IsInstalled(module) && (pmOccupied == null || !pmOccupied.Contains(module))) { hasPm = true; } if (!pmUsed.Contains(module)) pmUsed.Add(module); } if (pmIsReady && !hasPm) { reason = $"Step {i + 1} no valid PM, " + string.Join("|", stepInfo.StepModules); pmIsReady = false; } } return pmIsReady; } private bool CheckSequenceRecipeFileValid(SequenceInfo seq, out string reason) { for (int i = 0; i < seq.Steps.Count; i++) { SequenceStepInfo stepInfo = seq.Steps[i]; foreach (var module in stepInfo.StepModules) { if (ModuleHelper.IsPm(module)) { string attr = $"{module}Recipe"; if (stepInfo.StepParameter.ContainsKey(attr) && !string.IsNullOrEmpty((string)stepInfo.StepParameter[attr])) { var recipeName = (string)stepInfo.StepParameter[attr]; if (!string.IsNullOrEmpty(recipeName)) { var recipeContent = RecipeFileManager.Instance.LoadRecipe($"", recipeName, false); if (string.IsNullOrEmpty(recipeContent)) { reason = $"Can not find recipe file{recipeName}"; return false; } } } } } } reason = ""; return true; } private bool CheckWaferNeedProcessAndPMAvailable(ModuleName waferModule, int waferSlot, ModuleName processIn = ModuleName.System) { WaferInfo wafer = WaferManager.Instance.GetWafer(waferModule, waferSlot); if (wafer.IsEmpty) return false; if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) return false; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) return false; for (int i = wafer.NextSequenceStep; i < wafer.ProcessJob.Sequence.Steps.Count; i++) { var hasPmAvailable = false; foreach (var stepModule in wafer.ProcessJob.Sequence.Steps[i].StepModules) { if (!ModuleHelper.IsPm(stepModule)) break; var pm = _vacSchedulers[stepModule] as SchedulerPM; if (pm != null) { if (pm.IsAvailable || (pm.IsOnline && pm.Task != SchedulerModule.TaskType.PreJobProcess)) { hasPmAvailable = true; break; } } } if (!hasPmAvailable) continue; if (processIn == ModuleName.System) return true; if (wafer.ProcessJob.Sequence.Steps[i].StepModules .Contains(processIn)) return true; } return false; } #endregion } }