using System; using System.Collections.Generic; using System.Linq; using System.Diagnostics; 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; using Venus_Unity; namespace Venus_RT.Modules { enum ModulePriority { High, Middle, Low, Stop, } public enum MovingStatus { Idle, Waiting, Moving, // PM Status WaitProcess, StartProcess, Processing, StartIdleClean, IdleClean, WaitPreJobClean, StartPreJobClean, PreJobClean, WaitPostJobClean, StartPostJobClean, PostJobClean, WaitWTWClean, StartWTWClean, WTWClean, // Align Status WaitAlign, StartAlign, Aligning, } class ModuleFlag { public ModulePriority Priority { get; set; } public MovingStatus MovingStatus { get; set; } public bool InUsed { get; set; } public ModuleFlag(ModulePriority _priority) { Priority = _priority; MovingStatus = MovingStatus.Idle; InUsed = true; } } class AutoCycle : ICycle { 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 = (SchedulerTMRobot)Singleton.Instance.GetScheduler(ModuleName.TMRobot); SchedulerEfemRobot _efemRobot = (SchedulerEfemRobot)Singleton.Instance.GetScheduler(ModuleName.EfemRobot); Dictionary _vacReadyOutSlots = new Dictionary(); Dictionary _vacReadyInSlots = new Dictionary(); Dictionary _atmReadyOutSlots = new Dictionary(); Dictionary _atmReadyInSlots = new Dictionary(); 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 }; int _LLASlotNumber = 4; int _LLBSlotNumber = 4; int _efemRobotSingleArmOption = 0; List _movingItems = new List(); List _efemMovingItems = new List(); Dictionary _vacWaferTargets = new Dictionary(); Dictionary _atmWaferTargets = new Dictionary(); R_TRIG _vacMoveFinishTrig = new R_TRIG(); R_TRIG _atmMoveFinishTrig = new R_TRIG(); private SchedulerFACallback _faCallback; private SchedulerDBCallback _dbCallback; private bool _isCycleMode; private int _cycleSetPoint = 0; private int _cycledCount = 0; private int _cycledWafer = 0; private float _throughput = 0.0f; private Dictionary _lpCycleWafer = new Dictionary(); private Dictionary _lpCycleCount = new Dictionary(); private Stopwatch _cycleWatch = new Stopwatch(); private List _waitPreCleanPMs = new List(); private List _waitPostCleanPMs = new List(); private Dictionary _waitWTWCleanPMs = new Dictionary(); private List _lstReturnWafers = new List(); SequenceLLInOutPath _LLInOutPath = SequenceLLInOutPath.DInDOut; public SequenceLLInOutPath LLInOutPath { get { return _LLInOutPath; } } public bool HasJobRunning => _lstControlJobs.Count > 0; public RState CycleState => _cycleState; private Dictionary _loadportControlJobDic = new Dictionary(); #region public interface public AutoCycle() { _faCallback = new SchedulerFACallback(); _dbCallback = new SchedulerDBCallback(); InitModules(); _LLASlotNumber = SC.GetValue("LLA.SlotNumber"); _LLBSlotNumber = SC.GetValue("LLB.SlotNumber"); _LLAInSlot.RemoveAll(item => item >= _LLASlotNumber); _LLAOutSlot.RemoveAll(item => item >= _LLASlotNumber); _LLBInSlot.RemoveAll(item => item >= _LLBSlotNumber); _LLBOutSlot.RemoveAll(item => item >= _LLBSlotNumber); _efemRobotSingleArmOption = SC.GetValue("EFEM.SingleArmOption"); DATA.Subscribe("Scheduler.CycledCount", () => _cycledCount, SubscriptionAttribute.FLAG.IgnoreSaveDB); DATA.Subscribe("Scheduler.CycledWafer", () => _cycledWafer, SubscriptionAttribute.FLAG.IgnoreSaveDB); DATA.Subscribe("Scheduler.CycleSetPoint", () => _cycleSetPoint, SubscriptionAttribute.FLAG.IgnoreSaveDB); DATA.Subscribe("Scheduler.Throughput", () => _throughput, SubscriptionAttribute.FLAG.IgnoreSaveDB); DATA.Subscribe("Scheduler.PjIdList", () => Array.ConvertAll(_lstProcessJobs.ToArray(), x => x.InnerId.ToString()).ToList()); DATA.Subscribe("Scheduler.PjNameList", () => Array.ConvertAll(_lstProcessJobs.ToArray(), x => x.LotName.ToString()).ToList()); for(int i=1;i<=3;i++) { _loadportControlJobDic[$"LP{i}"] = null; string lp = $"LP{i}"; DATA.Subscribe($"{lp}.CurrentControlJob", () => _loadportControlJobDic[lp],SubscriptionAttribute.FLAG.IgnoreSaveDB); } } public RState Start(params object[] objs) { _isCycleMode = SC.GetValue("System.IsCycleMode"); _cycleSetPoint = _isCycleMode ? SC.GetValue("System.CycleCount") : 0; _cycledWafer = 0; _cycledCount = 0; _throughput = 0; _cycleWatch.Stop(); _lpCycleWafer.Clear(); _lpCycleCount.Clear(); return RState.Running; } public RState Monitor() { if (_cycleState == RState.Running) { prelude(); driveAtmSystem(); driveVacSystem(); epilogue(); } return _cycleState; } public void Abort() { } public bool CreateJob(Dictionary param,out string reason) { reason = ""; _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"]; if (string.IsNullOrEmpty(jobId)) { jobId = "CJ_Local_" + module; } string lotId = jobId; if (param.ContainsKey("LotId")) lotId = (string)param["LotId"]; string preCleanRecipe = param.ContainsKey("PreCleanRecipeName") ? (string)param["PreCleanRecipeName"] : string.Empty; string postCleanRecipe = param.ContainsKey("PostCleanRecipeName") ? (string)param["PostCleanRecipeName"] : string.Empty; if (slotSequence.Length != SC.GetValue("EFEM.LoadPort.SlotNumber")) { reason = $"slot sequence parameter not valid, length is {slotSequence.Length}, should be {SC.GetValue("EFEM.LoadPort.SlotNumber")}"; LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (!ModuleHelper.IsLoadPort(ModuleHelper.Converter(module))) { reason = $"{module} should be LoadPort"; LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (_lstControlJobs.Exists(x => x.Name == jobId)) { reason = $"{jobId} already created"; LOG.Write(eEvent.WARN_ROUTER, ModuleName.System,reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } SchedulerLoadPort lp = _atmSchedulers[ModuleHelper.Converter(module)] as SchedulerLoadPort; if (!lp.CheckReadyRunJob()) { reason = $"{module} not ready"; LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } 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); cj.JetState = EnumJetCtrlJobState.Created; cj.PreJobClean = preCleanRecipe; cj.PostJobClean = postCleanRecipe; cj.SequenceNameList = slotSequence; cj.CarrierID = ""; 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)) { reason = $"job wafer: {module} slot {i + 1} not in the carrier"; LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, reason); return false; } if (!WaferManager.Instance.CheckWafer(ModuleHelper.Converter(module), i, WaferStatus.Normal)) { reason = $"job wafer: {module} slot {i + 1} status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Status}"; LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState != EnumWaferProcessStatus.Idle) { reason = $"job wafer: {module} slot {i + 1} process status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState}"; LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } 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) { reason = $"job has not assign wafer"; LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } 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]]); if(pj.Sequence==null) { reason = $"invalid sequence[{indexSequence[seqs[i]]}]"; return false; } pj.ControlJobName = cj.Name; pj.LotName = lotId; pj.SlotWafers = seqSlotWafers[seqs[i]]; pj.SetState(EnumProcessJobState.Queued); if (!CheckSequencePmReady(pj.Sequence, null, out _, out reason)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"no valid chamber for the {reason}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (!RouteManager.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 false; } pjs.Add(pj); } _dbCallback.LotUpdate(cj); foreach (var pj in pjs) { cj.ProcessJobNameList.Add(pj.Name); _lstProcessJobs.Add(pj); } _lstControlJobs.Add(cj); //AssociatedPMWithLP(cj); _loadportControlJobDic[cj.Module] = cj; _faCallback.JobCreated(cj, GetFirstProcessJob(cj)); return true; } 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 bool AbortJob(string jobName,out string reason) { reason = ""; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { reason=$"abort job rejected, not found job with id {jobName}"; LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, reason); return false; } 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); if (_loadportControlJobDic.ContainsKey(cj.Module)) { _loadportControlJobDic[cj.Module] = null; } return true; } public bool StopJob(string jobName,out string reason) { reason = ""; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { reason = $"stop job rejected, not found job with id {jobName}"; LOG.Write(eEvent.WARN_ROUTER, ModuleName.System,reason); return false; } foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) { pj.SetState(EnumProcessJobState.Stopping); } } _faCallback.JobStopped(cj, GetFirstProcessJob(cj)); _dbCallback.LotFinished(cj); return true; } public bool ResumeJob(string jobName,out string reason) { reason = ""; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { reason = $"resume job rejected, not found job with id {jobName}"; //LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"resume job rejected, not found job with id {jobName}"); return false; } if (cj.State == EnumControlJobState.Paused) { cj.SetState(EnumControlJobState.Executing); } _faCallback.JobResumed(cj, GetFirstProcessJob(cj)); _cycleState = RState.Running; return true; } public bool PauseJob(string jobName,out string reason) { reason = ""; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { reason = $"pause job rejected, not found job with id {jobName}"; //LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"pause job rejected, not found job with id {jobName}"); return false; } if (cj.State == EnumControlJobState.Executing) { cj.SetState(EnumControlJobState.Paused); } _faCallback.JobPaused(cj, GetFirstProcessJob(cj)); if (!_lstControlJobs.Exists(job => job.State == EnumControlJobState.Executing)) _cycleState = RState.Paused; return true; } public bool StartJob(string jobName,out string reason) { reason = ""; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { reason = $"start job rejected, not found job with id {jobName}"; LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, reason); return false; } if (cj.State == EnumControlJobState.WaitingForStart) { cj.SetState(EnumControlJobState.Executing); //PreJobClean(cj); cj.JetState = EnumJetCtrlJobState.Quequed; (_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(); } if (!_cycleWatch.IsRunning) { _cycleWatch.Restart(); } if (!_lpCycleWafer.Keys.Contains(ModuleHelper.Converter(cj.Module))) { _lpCycleCount.Add(ModuleHelper.Converter(cj.Module), 0); _lpCycleWafer.Add(ModuleHelper.Converter(cj.Module), 0); } _cycleState = RState.Running; return true; } public void Clear() { _vacWaferTargets.Clear(); _atmWaferTargets.Clear(); _movingItems.Clear(); _efemMovingItems.Clear(); _waitPreCleanPMs.Clear(); _waitPostCleanPMs.Clear(); _waitWTWCleanPMs.Clear(); _vacMoveFinishTrig.RST = true; _atmMoveFinishTrig.RST = true; _efemRobot.ResetTask(); _tmRobot.ResetTask(); foreach (var module in _vacSchedulers) { module.Value.ResetTask(); _vacModules[module.Key].MovingStatus = MovingStatus.Idle; } foreach (var module in _atmSchedulers) { module.Value.ResetTask(); _atmModules[module.Key].MovingStatus = MovingStatus.Idle; } _lstControlJobs.Clear(); List keys = _loadportControlJobDic.Keys.ToList(); foreach(var key in keys) { _loadportControlJobDic[key] = null; } _lstProcessJobs.Clear(); _cycleState = RState.End; } public bool ManualReturnWafer(object[] objs) { ModuleName SourceModule = (ModuleName)objs[0]; int SourceSlot = (int)objs[1]; if (!_vacSchedulers.Keys.Contains(SourceModule) && !_atmSchedulers.Keys.Contains(SourceModule)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"Invalid source module {SourceModule} for manual return wafer"); return false; } if (WaferManager.Instance.CheckNoWafer(SourceModule, SourceSlot)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"Can not return wafer as {SourceModule} {SourceSlot} has no wafer"); return false; } if (!_vacSchedulers.Keys.Contains(SourceModule) || !_vacSchedulers[SourceModule].IsIdle) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"The module: {SourceModule} is not ready for return wafer"); return false; } var wafer = WaferManager.Instance.GetWafer(SourceModule, SourceSlot); LOG.Write(eEvent.EV_ROUTER, ModuleName.System, $"Manual return wafer: {wafer.WaferOrigin} at {SourceModule} {SourceSlot} while system is auto running"); _lstReturnWafers.Add(wafer.InnerId); return true; } #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] = Singleton.Instance.GetScheduler(module); _atmModules[module] = new ModuleFlag(ModulePriority.Middle); } else if (ModuleHelper.IsAligner(module) || ModuleHelper.IsCooling(module)) { _atmSchedulers[module] = Singleton.Instance.GetScheduler(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] = Singleton.Instance.GetScheduler(module); _vacModules[module] = new ModuleFlag(ModulePriority.Middle); } } foreach (var module in new ModuleName[] { ModuleName.LLA, ModuleName.LLB }) { if (ModuleHelper.IsInstalled(module)) { var llScheduler = Singleton.Instance.GetScheduler(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() { CheckWaferArrived(); ProcessPMTask(); ProcessAlignerTask(); UpdateProcessJobStatus(); UpdateControlJobStatus(); //DispatchCtrlJobs(); UpdateLLInOutPathProperty(); } private void driveVacSystem() { if (_tmRobot.IsAvailable && _movingItems.Count == 0 && _tmRobot.RobotStatus != RState.Running) { RoutingInnerPart(); RuningTMRobotTask(); } } #region Vacuum System private void PumpingTMRobotTask() { if (!_tmRobot.IsAvailable || _tmRobot.RobotStatus == RState.Running) return; foreach (var mod in _vacSchedulers) { if (mod.Value.IsAvailable && _vacModules[mod.Key].MovingStatus == MovingStatus.Idle) { if (ModuleHelper.IsLoadLock(mod.Key)) { var inSlots = mod.Key == ModuleName.LLA ? _LLAInSlot : _LLBInSlot; foreach (var slot in inSlots) { if (IsLoadLockReservedByEFEM(mod.Key)) continue; if (WaferManager.Instance.CheckHasWafer(mod.Key, slot) && IsVacWaferReadyOut(mod.Key, slot)) { _vacReadyOutSlots.Add(new SlotItem(mod.Key, slot), mod.Value.WaferArrivedTicks(slot)); } } var outSlots = mod.Key == ModuleName.LLA ? _LLAOutSlot : _LLBOutSlot; foreach (var slot in outSlots) { if (IsLoadLockReservedByEFEM(mod.Key)) continue; if (WaferManager.Instance.CheckNoWafer(mod.Key, slot)) { _vacReadyInSlots.Add(new SlotItem(mod.Key, slot), mod.Value.WaferArrivedTicks(slot)); } } } else { if (WaferManager.Instance.CheckHasWafer(mod.Key, 0) && IsVacWaferReadyOut(mod.Key, 0)) // processed? { _vacReadyOutSlots.Add(new SlotItem(mod.Key, 0), mod.Value.WaferArrivedTicks(0)); } else if (WaferManager.Instance.CheckNoWafer(mod.Key, 0)) { _vacReadyInSlots.Add(new SlotItem(mod.Key, 0), mod.Value.WaferArrivedTicks(0)); } } } } _vacReadyOutSlots = _vacReadyOutSlots.OrderByDescending(item => item.Value).ToDictionary(k => k.Key, v => v.Value); } bool SearchWaferDestination(SlotItem outSlot, out SlotItem destSlot) { destSlot = new SlotItem(ModuleName.System, -1); var wafer = WaferManager.Instance.GetWafer(outSlot.Module, outSlot.Slot); if (wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) return false; 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; foreach (var inItem in _vacReadyInSlots) { if (inItem.Key.Module == next_module && inItem.Key.Slot == nSlot && _vacModules[next_module].MovingStatus == MovingStatus.Idle && _vacSchedulers[next_module].IsAvailable) { destSlot.Module = next_module; destSlot.Slot = nSlot; return true; } } } } return false; } bool SearchCanReplaceSlot(SlotItem llSlot, out SlotItem replaceSlot, out SlotItem destSlot) { replaceSlot = new SlotItem(ModuleName.System, -1); destSlot = new SlotItem(ModuleName.System, -1); var wafer = WaferManager.Instance.GetWafer(llSlot.Module, llSlot.Slot); if (wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) return false; foreach (var next_module in wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules) { if (!ModuleHelper.IsPm(next_module) || WaferManager.Instance.CheckNoWafer(next_module, 0)) continue; if (SearchWaferDestination(new SlotItem(next_module, 0), out destSlot)) { replaceSlot.Module = next_module; replaceSlot.Slot = 0; return true; } } return false; } Queue GetModuleValidSlots(ModuleName mod) { var validSlot = new Queue(); if (ModuleHelper.IsLoadLock(mod)) { if (!IsLoadLockReservedByEFEM(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.Enqueue(slot); if (WaferManager.Instance.CheckHasWafer(mod, slot) && _movingItems.Exists(item => item.SourceModule == mod && item.SourceSlot == slot)) validSlot.Enqueue(slot); } } } else if (ModuleHelper.IsPm(mod)) { if (WaferManager.Instance.CheckNoWafer(mod, 0) && !_movingItems.Exists(item => item.DestinationModule == mod)) validSlot.Enqueue(0); if (WaferManager.Instance.CheckHasWafer(mod, 0) && _movingItems.Exists(item => item.SourceModule == mod)) validSlot.Enqueue(0); } return validSlot; } private int GetModuleSlotCount(ModuleName mod) { int nSlotCount = 1; if (ModuleHelper.IsLoadLock(mod)) { nSlotCount = mod == ModuleName.LLA ? _LLASlotNumber : _LLBSlotNumber; } else if (ModuleHelper.IsLoadPort(mod)) { nSlotCount = SC.GetValue("EFEM.LoadPort.SlotNumber"); } else if (ModuleHelper.IsTMRobot(mod) || ModuleHelper.IsEFEMRobot(mod)) { nSlotCount = 2; } return nSlotCount; } List SearchWaitInSlots(ModuleName inModule) { List inSlots = new List(); var validSlots = GetModuleValidSlots(inModule); foreach (var slot in _vacReadyOutSlots) { if (slot.Key.Module == inModule || _vacModules[slot.Key.Module].MovingStatus != MovingStatus.Idle) continue; if (IsLoadLockReservedByEFEM(slot.Key.Module)) continue; var wafer = WaferManager.Instance.GetWafer(slot.Key.Module, slot.Key.Slot); if (wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) continue; foreach (var next_module in wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules) { if (next_module == inModule && validSlots.Count > 0 && !_movingItems.Exists(item => item.SourceModule == slot.Key.Module && item.SourceSlot == slot.Key.Slot)) { if (!inSlots.Exists(item => item.SourceModule == slot.Key.Module && item.SourceSlot == slot.Key.Slot)) { inSlots.Add(new MoveItem(slot.Key.Module, slot.Key.Slot, inModule, validSlots.Dequeue(), Hand.None)); } } } } return inSlots; } private void UpdateItemMovingStatus(MoveItem moveItem, MovingStatus status = MovingStatus.Moving) { if (!ModuleHelper.IsTMRobot(moveItem.DestinationModule)) { _vacModules[moveItem.DestinationModule].MovingStatus = status; } if (!ModuleHelper.IsTMRobot(moveItem.SourceModule)) { _vacModules[moveItem.SourceModule].MovingStatus = status; } } private void UpdateModuleMovingStatus(ModuleName module, MovingStatus status = MovingStatus.Moving) { if (!ModuleHelper.IsTMRobot(module)) { _vacModules[module].MovingStatus = status; } } private void ProcessTMRobotTask() { if (_tmRobot.IsAvailable && _tmRobot.RobotStatus != RState.Running) { if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) || WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1)) { for (int i = 0; i < 2; i++) { if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, i)) { if (SearchWaferDestination(new SlotItem(ModuleName.TMRobot, i), out SlotItem destSlot)) { _movingItems.Add(new MoveItem(ModuleName.TMRobot, i, destSlot.Module, destSlot.Slot, (Hand)i)); UpdateModuleMovingStatus(destSlot.Module); } } } return; } foreach (var slot in _vacReadyOutSlots) { if (_vacModules[slot.Key.Module].MovingStatus != MovingStatus.Idle) continue; if (ModuleHelper.IsLoadLock(slot.Key.Module)) { if (IsLoadLockReservedByEFEM(slot.Key.Module)) continue; var llStatus = GetLLProcessStatusCount(slot.Key.Module); if (SearchWaferDestination(slot.Key, out SlotItem destSlot)) { _movingItems.Add(new MoveItem(slot.Key.Module, slot.Key.Slot, destSlot.Module, destSlot.Slot, Hand.None)); UpdateModuleMovingStatus(slot.Key.Module); UpdateModuleMovingStatus(destSlot.Module); if (llStatus.Item2 > 1) { // check whether match double pick pattern foreach (var item in _vacReadyOutSlots) { if (item.Key.Module == slot.Key.Module && item.Key.Slot != slot.Key.Slot) { if (SearchWaferDestination(item.Key, out SlotItem destSlot_2)) { _movingItems.Add(new MoveItem(item.Key.Module, item.Key.Slot, destSlot_2.Module, destSlot_2.Slot, Hand.None)); UpdateModuleMovingStatus(destSlot_2.Module); break; } } } if (_movingItems.Count == 1) { foreach (var item in _vacReadyOutSlots) { if (item.Key.Module == slot.Key.Module && item.Key.Slot != slot.Key.Slot) { if (SearchCanReplaceSlot(item.Key, out SlotItem replaceSlot, out SlotItem destSlot_2)) { _movingItems.Add(new MoveItem(item.Key.Module, item.Key.Slot, replaceSlot.Module, replaceSlot.Slot, Hand.None)); _movingItems.Add(new MoveItem(replaceSlot.Module, replaceSlot.Slot, destSlot_2.Module, destSlot_2.Slot, Hand.None)); UpdateModuleMovingStatus(replaceSlot.Module); UpdateModuleMovingStatus(destSlot.Module); return; } } } } } // check whether match swap pattern int inCount = 0; var in_slots = SearchWaitInSlots(slot.Key.Module); foreach (var in_slot in in_slots) { if (_vacModules[in_slot.DestinationModule].MovingStatus == MovingStatus.Idle && inCount < 2) { _movingItems.Add(in_slot); UpdateItemMovingStatus(in_slot); inCount++; } } return; } } else // PM { if (SearchWaferDestination(slot.Key, out SlotItem destSlot)) { _movingItems.Add(new MoveItem(slot.Key.Module, slot.Key.Slot, destSlot.Module, destSlot.Slot, Hand.None)); UpdateModuleMovingStatus(slot.Key.Module); UpdateModuleMovingStatus(destSlot.Module); // check whether match double move pattern int inCount = 0; var same_dest = SearchWaitInSlots(destSlot.Module); foreach (var in_slot in same_dest) { if (_vacModules[in_slot.SourceModule].MovingStatus == MovingStatus.Idle && inCount < 1) { _movingItems.Add(in_slot); UpdateItemMovingStatus(in_slot); inCount++; } } // check whether match swap pattern if (inCount == 0 && !IsPMWaferNeedWTWClean(slot.Key.Module, out string wtwClean)) { int swapCount = 0; var in_slots = SearchWaitInSlots(slot.Key.Module); foreach (var swap_slot in in_slots) { if (_vacModules[swap_slot.SourceModule].MovingStatus == MovingStatus.Idle && swapCount < 1) { _movingItems.Add(swap_slot); UpdateItemMovingStatus(swap_slot); swapCount++; } } } return; } } } _vacReadyOutSlots.Clear(); _vacReadyInSlots.Clear(); } } bool PushTMRobotWafers() { if (_tmRobot.IsAvailable && _tmRobot.RobotStatus != RState.Running) { for (int i = 0; i < 2; i++) { if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, i) && IsWaferCtrlJobRuning(ModuleName.TMRobot, i)) { if (SearchWaferDestination(new SlotItem(ModuleName.TMRobot, i), out SlotItem destSlot)) { _movingItems.Add(new MoveItem(ModuleName.TMRobot, i, destSlot.Module, destSlot.Slot, (Hand)i)); UpdateModuleMovingStatus(destSlot.Module); } } } } return _movingItems.Count > 0; } bool PushPmWafers() { if (_tmRobot.IsAvailable && _tmRobot.RobotStatus != RState.Running) { foreach (var slot in _vacReadyOutSlots) { if (_vacModules[slot.Key.Module].MovingStatus != MovingStatus.Idle) continue; if (ModuleHelper.IsPm(slot.Key.Module)) { if (SearchWaferDestination(slot.Key, out SlotItem destSlot)) { _movingItems.Add(new MoveItem(slot.Key.Module, slot.Key.Slot, destSlot.Module, destSlot.Slot, Hand.None)); UpdateModuleMovingStatus(slot.Key.Module); UpdateModuleMovingStatus(destSlot.Module); // check whether match double move pattern int inCount = 0; var same_dest = SearchWaitInSlots(destSlot.Module); foreach (var in_slot in same_dest) { if (_vacModules[in_slot.SourceModule].MovingStatus == MovingStatus.Idle && inCount < 1) { _movingItems.Add(in_slot); UpdateItemMovingStatus(in_slot); inCount++; } } // check whether match swap pattern if (inCount == 0 && !IsPMWaferNeedWTWClean(slot.Key.Module, out string wtwClean)) { int swapCount = 0; var in_slots = SearchWaitInSlots(slot.Key.Module); foreach (var swap_slot in in_slots) { if (_vacModules[swap_slot.SourceModule].MovingStatus == MovingStatus.Idle && swapCount < 1) { _movingItems.Add(swap_slot); UpdateItemMovingStatus(swap_slot); swapCount++; } } } } } } } return _movingItems.Count > 0; } private bool PushLoadlockWafers() { if (_tmRobot.IsAvailable && _tmRobot.RobotStatus != RState.Running) { foreach (var slot in _vacReadyOutSlots) { if (_vacModules[slot.Key.Module].MovingStatus != MovingStatus.Idle) continue; if (ModuleHelper.IsLoadLock(slot.Key.Module)) { if (IsLoadLockReservedByEFEM(slot.Key.Module)) continue; var llStatus = GetLLProcessStatusCount(slot.Key.Module); if (SearchWaferDestination(slot.Key, out SlotItem destSlot)) { _movingItems.Add(new MoveItem(slot.Key.Module, slot.Key.Slot, destSlot.Module, destSlot.Slot, Hand.None)); UpdateModuleMovingStatus(slot.Key.Module); UpdateModuleMovingStatus(destSlot.Module); if (llStatus.Item2 > 1) { // check whether match double pick pattern foreach (var item in _vacReadyOutSlots) { if (item.Key.Module == slot.Key.Module && item.Key.Slot != slot.Key.Slot) { if (SearchWaferDestination(item.Key, out SlotItem destSlot_2)) { _movingItems.Add(new MoveItem(item.Key.Module, item.Key.Slot, destSlot_2.Module, destSlot_2.Slot, Hand.None)); UpdateModuleMovingStatus(destSlot_2.Module); break; } } } if (_movingItems.Count == 1) { foreach (var item in _vacReadyOutSlots) { if (item.Key.Module == slot.Key.Module && item.Key.Slot != slot.Key.Slot) { if (SearchCanReplaceSlot(item.Key, out SlotItem replaceSlot, out SlotItem destSlot_2)) { _movingItems.Add(new MoveItem(item.Key.Module, item.Key.Slot, replaceSlot.Module, replaceSlot.Slot, Hand.None)); _movingItems.Add(new MoveItem(replaceSlot.Module, replaceSlot.Slot, destSlot_2.Module, destSlot_2.Slot, Hand.None)); UpdateModuleMovingStatus(replaceSlot.Module); UpdateModuleMovingStatus(destSlot.Module); return true; } } } } } // check whether match swap pattern int inCount = 0; var in_slots = SearchWaitInSlots(slot.Key.Module); foreach (var in_slot in in_slots) { if (_vacModules[in_slot.DestinationModule].MovingStatus == MovingStatus.Idle && inCount < 2) { _movingItems.Add(in_slot); UpdateItemMovingStatus(in_slot); inCount++; } } return true; } } } } return _movingItems.Count > 0; } private bool SearchOutLLSlot(ModuleName pm, ModuleName ll, out SlotItem outSlot) { outSlot = new SlotItem(ModuleName.System, -1); var wafer = WaferManager.Instance.GetWafer(pm, 0); if (wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) return false; if (wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(ll)) { var slotNumber = ll == ModuleName.LLA ? _LLASlotNumber : _LLBSlotNumber; for (int slot = 0; slot < slotNumber; slot++) { if ((WaferManager.Instance.CheckNoWafer(ll, slot) && !_movingItems.Exists(item => item.DestinationModule == ll && item.DestinationSlot == slot)) || (WaferManager.Instance.CheckHasWafer(ll, slot) && _movingItems.Exists(item => item.SourceModule == ll && item.SourceSlot == slot))) { outSlot.Module = ll; outSlot.Slot = slot; return true; } } } return false; } private bool SearchValidInPM(ModuleName ll, int slot, out SlotItem inSlot) { inSlot = new SlotItem(ModuleName.System, -1); var wafer = WaferManager.Instance.GetWafer(ll, slot); if (wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) return false; foreach (var mod in _vacModules) { if (ModuleHelper.IsPm(mod.Key) && _vacSchedulers[mod.Key].IsAvailable && _vacModules[mod.Key].MovingStatus == MovingStatus.Idle) { if (wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(mod.Key)) { if ((WaferManager.Instance.CheckNoWafer(mod.Key, 0) && !_movingItems.Exists(item => item.DestinationModule == mod.Key)) || (WaferManager.Instance.CheckHasWafer(mod.Key, 0) && _movingItems.Exists(item => item.SourceModule == mod.Key) && !_movingItems.Exists(item => item.DestinationModule == mod.Key) && !IsPMWaferNeedWTWClean(mod.Key, out string wtwClean))) { inSlot.Module = mod.Key; inSlot.Slot = 0; return true; } } } } return false; } private bool ForwardPMsWaferToLL(ModuleName ll) { int outWaferCount = 0; foreach (var module in _vacModules) { if (outWaferCount >= 2) break; if (ModuleHelper.IsPm(module.Key) && _vacSchedulers[module.Key].IsAvailable && _vacModules[module.Key].MovingStatus == MovingStatus.Idle) { if (IsWaferCtrlJobRuning(module.Key, 0)) { if (SearchOutLLSlot(module.Key, ll, out SlotItem outSlot)) { outWaferCount++; _movingItems.Add(new MoveItem(module.Key, 0, outSlot.Module, outSlot.Slot, Hand.None)); } } } } return _movingItems.Count > 0; } private bool ForwardLLWaferToPMs(ModuleName ll) { int inWaferCount = 0; var slots = (_vacSchedulers[ll] as SchedulerLoadLock).GetOrderedOutSlot(); foreach (var slot in slots) { if (slot >= (ll == ModuleName.LLA ? _LLASlotNumber : _LLBSlotNumber)) continue; if (inWaferCount >= 2) break; if (IsWaferCtrlJobRuning(ll, slot) && SearchValidInPM(ll, slot, out SlotItem inSlot)) { inWaferCount++; _movingItems.Add(new MoveItem(ll, slot, inSlot.Module, inSlot.Slot, Hand.None)); } } return _movingItems.Count > 0; } private bool ExchangeLLWafersWithPMs(ModuleName ll) { ForwardPMsWaferToLL(ll); ForwardLLWaferToPMs(ll); return _movingItems.Count > 0; } private bool ProcessVacManaulReturnWafers() { if (_lstReturnWafers.Count == 0) return false; Queue returnSlots = new Queue(); for (int i = 0; i < 2; i++) { if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, i)) { var wafer_id = WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0).InnerId; if (_lstReturnWafers.FindIndex(item => item == wafer_id) != -1) { returnSlots.Enqueue(new SlotItem(ModuleName.TMRobot, i)); } } } foreach (var mod in _vacModules) { if (ModuleHelper.IsPm(mod.Key) && WaferManager.Instance.CheckHasWafer(mod.Key, 0) && _vacSchedulers[mod.Key].IsIdle) { var wafer_id = WaferManager.Instance.GetWafer(mod.Key, 0).InnerId; if (_lstReturnWafers.FindIndex(item => item == wafer_id) != -1) { returnSlots.Enqueue(new SlotItem(mod.Key, 0)); } } } if (returnSlots.Count == 0) return false; var llaSlotStatus = GetLLProcessStatusCount(ModuleName.LLA); var llbSlotStatus = GetLLProcessStatusCount(ModuleName.LLB); ModuleName destLL = ModuleName.System; if (IsLoadReadyForTM(ModuleName.LLA) && llaSlotStatus.empty > 0 && (!IsLoadReadyForTM(ModuleName.LLB) || (!_vacSchedulers[ModuleName.LLB].IsVac && llaSlotStatus.empty == llbSlotStatus.empty) || llaSlotStatus.empty > llbSlotStatus.empty)) { destLL = ModuleName.LLA; } else if (IsLoadReadyForTM(ModuleName.LLB) && llbSlotStatus.empty > 0) { destLL = ModuleName.LLB; } if (destLL != ModuleName.System) { int slotNumber = destLL == ModuleName.LLA ? _LLASlotNumber : _LLBSlotNumber; for (int i = 0; i < slotNumber; i++) { if (WaferManager.Instance.CheckNoWafer(destLL, i)) { if (_movingItems.Count >= 2 || returnSlots.Count == 0) break; var source = returnSlots.Dequeue(); _movingItems.Add(new MoveItem(source.Module, source.Slot, destLL, i, Hand.None)); } } } return _movingItems.Count > 0; } private void RoutingInnerPart() { if (ProcessVacManaulReturnWafers()) return; if (PushTMRobotWafers()) return; var pmSlotStaus = GetPMValidSlotsStatus(); if (pmSlotStaus.Processed + pmSlotStaus.Empty == 0) return; var llaSlotStatus = GetLLProcessStatusCount(ModuleName.LLA); var llbSlotStatus = GetLLProcessStatusCount(ModuleName.LLB); if (LLInOutPath == SequenceLLInOutPath.AInBOut || LLInOutPath == SequenceLLInOutPath.BInAOut) { ModuleName InModule = LLInOutPath == SequenceLLInOutPath.AInBOut ? ModuleName.LLA : ModuleName.LLB; ModuleName OutModule = LLInOutPath == SequenceLLInOutPath.AInBOut ? ModuleName.LLB : ModuleName.LLA; int InNumber = Math.Min(InModule == ModuleName.LLA ? llaSlotStatus.unprocessed : llbSlotStatus.unprocessed, pmSlotStaus.Empty); int OutNumber = Math.Min(OutModule == ModuleName.LLA ? llaSlotStatus.empty : llbSlotStatus.empty, pmSlotStaus.Processed); if (InNumber + OutNumber == 0) return; if (IsLoadReadyForTM(InModule) && (!IsLoadReadyForTM(OutModule) || (!_vacSchedulers[OutModule].IsVac && InNumber == OutNumber) || InNumber > OutNumber)) { // move in wafer ForwardLLWaferToPMs(InModule); } else if (IsLoadReadyForTM(OutModule) && OutNumber > 0) { // move out wafer ForwardPMsWaferToLL(OutModule); } } else if (LLInOutPath == SequenceLLInOutPath.AInAOut || LLInOutPath == SequenceLLInOutPath.BInBOut) { ModuleName loadlock = LLInOutPath == SequenceLLInOutPath.AInAOut ? ModuleName.LLA : ModuleName.LLB; if (IsLoadReadyForTM(loadlock)) { // switch wafer ExchangeLLWafersWithPMs(loadlock); } } else // DInDOut or mix pattern { int llaOutNumber = Math.Min(Math.Min(pmSlotStaus.Processed, 2), llaSlotStatus.empty); int llaInNumber = Math.Min(Math.Min(llaSlotStatus.unprocessed, 2), pmSlotStaus.Empty + llaOutNumber); int llaExchangeNumber = llaOutNumber + llaInNumber; int llbOutNumber = Math.Min(Math.Min(pmSlotStaus.Processed, 2), llbSlotStatus.empty); int llbInNumber = Math.Min(Math.Min(llbSlotStatus.unprocessed, 2), pmSlotStaus.Empty + llbOutNumber); int llbExchangeNumber = llbOutNumber + llbInNumber; if (IsLoadReadyForTM(ModuleName.LLA) && ((!_vacSchedulers[ModuleName.LLB].IsVac && llaExchangeNumber == llbExchangeNumber) || (llaExchangeNumber > llbExchangeNumber) || (!IsLoadReadyForTM(ModuleName.LLB) && llaExchangeNumber > 0))) { ExchangeLLWafersWithPMs(ModuleName.LLA); } else if (IsLoadReadyForTM(ModuleName.LLB) && llbExchangeNumber > 0) { ExchangeLLWafersWithPMs(ModuleName.LLB); } } } private void RuningTMRobotTask() { if (_movingItems.Count > 0) { if (_tmRobot.PostMoveItems(_movingItems.ToArray())) { foreach (var item in _movingItems) { var wafer = WaferManager.Instance.GetWafer(item.SourceModule, item.SourceSlot); if (wafer.IsEmpty) { // post alarm _cycleState = RState.Failed; LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Cannot run TM moving task as Get {item.SourceModule}{item.SourceSlot} Wafer Info failed"); return; } var slot = new SlotItem(item.DestinationModule, item.DestinationSlot); _vacWaferTargets[slot] = wafer.InnerId; } } } } private void ProcessPMTask() { foreach (var mod in _vacModules) { if (ModuleHelper.IsPm(mod.Key)) { if (_vacSchedulers[mod.Key].IsAvailable) { switch (mod.Value.MovingStatus) { case MovingStatus.WaitProcess: { _vacSchedulers[mod.Key].EventWaferArrived?.Invoke(this, new WaferMoveArgs(ModuleName.TMRobot, 0, mod.Key, 0)); mod.Value.MovingStatus = MovingStatus.StartProcess; } break; case MovingStatus.Processing: { if (IsPMWaferNeedWTWClean(mod.Key, out string WTWCleanRecipe)) { _waitWTWCleanPMs.Add(mod.Key, WTWCleanRecipe); } mod.Value.MovingStatus = MovingStatus.Idle; } break; case MovingStatus.IdleClean: case MovingStatus.PreJobClean: case MovingStatus.PostJobClean: case MovingStatus.WTWClean: mod.Value.MovingStatus = MovingStatus.Idle; break; case MovingStatus.Idle: { var pmScheduler = _vacSchedulers[mod.Key] as SchedulerPM; if (IsPMKeepEmpty(mod.Key)) { if (_waitWTWCleanPMs.ContainsKey(mod.Key)) { mod.Value.MovingStatus = MovingStatus.WaitWTWClean; } else if (_waitPreCleanPMs.Contains(mod.Key)) { mod.Value.MovingStatus = MovingStatus.WaitPreJobClean; } else if (_waitPostCleanPMs.Contains(mod.Key)) { mod.Value.MovingStatus = MovingStatus.WaitPostJobClean; } else if (pmScheduler.RunIdleCleanTask()) // Check Idle Clean { mod.Value.MovingStatus = MovingStatus.StartIdleClean; } } } break; case MovingStatus.WaitPreJobClean: { mod.Value.MovingStatus = MovingStatus.Idle; var pmScheduler = _vacSchedulers[mod.Key] as SchedulerPM; if (IsPMKeepEmpty(mod.Key)) { if (IsPreJobCleanPending(mod.Key, out string preCleanRecipe)) { if (pmScheduler.RunJobCleanTask(preCleanRecipe)) { mod.Value.MovingStatus = MovingStatus.StartPreJobClean; } _waitPreCleanPMs.Remove(mod.Key); } } } break; case MovingStatus.WaitPostJobClean: { mod.Value.MovingStatus = MovingStatus.Idle; var pmScheduler = _vacSchedulers[mod.Key] as SchedulerPM; if (IsPMKeepEmpty(mod.Key)) { if (IsPostJobCleanPending(mod.Key, out string postCleanRecipe)) { if (pmScheduler.RunJobCleanTask(postCleanRecipe)) { mod.Value.MovingStatus = MovingStatus.StartPostJobClean; } _waitPostCleanPMs.Remove(mod.Key); } } } break; case MovingStatus.WaitWTWClean: { mod.Value.MovingStatus = MovingStatus.Idle; var pmScheduler = _vacSchedulers[mod.Key] as SchedulerPM; if (IsPMKeepEmpty(mod.Key) && _waitWTWCleanPMs.ContainsKey(mod.Key)) { string WTWCleanRecipe = _waitWTWCleanPMs[mod.Key].Trim(); if (WTWCleanRecipe.Length > 0) { if (pmScheduler.RunJobCleanTask(WTWCleanRecipe)) { mod.Value.MovingStatus = MovingStatus.StartWTWClean; } _waitWTWCleanPMs.Remove(mod.Key); } } } break; } } else { switch (mod.Value.MovingStatus) { case MovingStatus.StartProcess: mod.Value.MovingStatus = MovingStatus.Processing; break; case MovingStatus.StartIdleClean: mod.Value.MovingStatus = MovingStatus.IdleClean; break; case MovingStatus.StartPreJobClean: mod.Value.MovingStatus = MovingStatus.PreJobClean; break; case MovingStatus.StartPostJobClean: mod.Value.MovingStatus = MovingStatus.PostJobClean; break; case MovingStatus.StartWTWClean: mod.Value.MovingStatus = MovingStatus.WTWClean; break; } } } } } 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() { if (_efemMovingItems.Count == 0 && _efemRobot.IsAvailable && _efemRobot.RobotStatus != RState.Running) { PumpingEFEMRobotTask(); RuningEFEMRobotTask(); } } private void PumpingEFEMRobotTask() { if (ProcessAtmManaulReturnWafers()) return; var aligners = _atmSchedulers.Where(x => ModuleHelper.IsAligner(x.Key) && x.Value.IsAvailable).ToDictionary(k => k.Key, v => v.Value); foreach (var aligner in aligners) { if (_atmModules[aligner.Key].MovingStatus != MovingStatus.Idle) { ProcessAlignerTask(); if (_atmModules[aligner.Key].MovingStatus != MovingStatus.Idle) return; } if (ProcessAlignerEFEMRobotTask(aligner.Key)) return; } var lls = _atmSchedulers.Where(x => ModuleHelper.IsLoadLock(x.Key) && x.Value.IsAvailable && !IsLoadLockReservedByTM(x.Key)).ToDictionary(k => k.Key, v => v.Value); foreach (var ll in lls) { if (ProcessLLEFEMRobotTask(ll.Key)) return; } var lps = _atmSchedulers.Where(x => ModuleHelper.IsLoadPort(x.Key) && x.Value.IsAvailable).ToDictionary(k => k.Key, v => v.Value); foreach (var lp in lps) { if (ProcessLPEFEMRobotTask(lp.Key)) return; } } List GetEfemRobotWaferReadyInHands(ModuleName mod) { var slots = new List(); for (int i = 0; i < 2; i++) { if (_efemRobotSingleArmOption != 0 && _efemRobotSingleArmOption != i + 1) continue; var wafer = WaferManager.Instance.GetWafer(ModuleName.EfemRobot, i); if (wafer.IsEmpty) continue; if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.NextSequenceStep == wafer.ProcessJob.Sequence.Steps.Count) 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) { if (!IsWaferCtrlJobRuning(mod, slot)) return false; var wafer = WaferManager.Instance.GetWafer(mod, slot); if (_lstReturnWafers.Contains(wafer.InnerId)) 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 in IsAtmWaferReadyOut()"); return false; } else if (wafer.NextSequenceStep < wafer.ProcessJob.Sequence.Steps.Count) { 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.IsPm(module)) return false; } } // Wafer Delay if (ModuleHelper.IsLoadLock(mod) && (wafer.ProcessState == EnumWaferProcessStatus.Completed)) { if (wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep - 1].StepParameter.ContainsKey("LLDelayTime")) { var delayTime = (string)wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep - 1].StepParameter["LLDelayTime"]; int delay = int.Parse(delayTime); return _atmSchedulers[mod].WaferArrivedTicks(slot) >= delay * 1000; } // need return return true; } return true; } private bool ProcessLLEFEMRobotTask(ModuleName ll) { if (_vacModules[ll].MovingStatus != MovingStatus.Idle) return false; 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 slotNumber = ll == ModuleName.LLA ? _LLASlotNumber : _LLBSlotNumber; Dictionary slot_delays = new Dictionary(); for (int i = 0; i < slotNumber; i++) { if (IsAtmWaferReadyOut(ll, i)) { slot_delays[i] = _atmSchedulers[ll].WaferArrivedTicks(i); } } slot_delays = slot_delays.OrderByDescending(item => item.Value).ToDictionary(k => k.Key, v => v.Value); if (slot_delays.Count > 0) { var robotHand = GetEFEMRobotFreeHand(); if (robotHand != Hand.None) { _efemMovingItems.Add(new MoveItem(ll, slot_delays.ElementAt(0).Key, ModuleName.EfemRobot, (int)robotHand, robotHand)); } if (slot_delays.Count > 1) { var secondHand = GetEFEMRobotFreeHand(); if (secondHand != Hand.None) { _efemMovingItems.Add(new MoveItem(ll, slot_delays.ElementAt(1).Key, ModuleName.EfemRobot, (int)secondHand, secondHand)); } } } return _efemMovingItems.Count > 0; } private (int exist, int empty) GetPMWaferExistence() { int exist = 0; int empty = 0; List usedPMs = GetPmUsedInRunningPj(); foreach (var module in _vacModules) { if (ModuleHelper.IsPm(module.Key) && ((_vacSchedulers[module.Key].IsOnline && usedPMs.Contains(module.Key)) || _cycleState == RState.End)) { if (WaferManager.Instance.CheckHasWafer(module.Key, 0)) exist++; else empty++; } } return (exist, empty); } private (int Processed, int Empty) GetPMValidSlotsStatus() { int Processed = 0; int Empty = 0; List usedPMs = GetPmUsedInRunningPj(); foreach (var module in _vacModules) { if (ModuleHelper.IsPm(module.Key) && usedPMs.Contains(module.Key) && _vacSchedulers[module.Key].IsAvailable && _vacModules[module.Key].MovingStatus == MovingStatus.Idle) { var wafer = WaferManager.Instance.GetWafer(module.Key, 0); if (wafer.IsEmpty) { Empty++; } else if (wafer.ProcessState == EnumWaferProcessStatus.Completed) { Processed++; } } } return (Processed, Empty); } private int GetTMRobotWaferCount() { return (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) ? 1 : 0) + (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1) ? 1 : 0); } private int GetEfemRoborWaferCount() { return (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 0) ? 1 : 0) + (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 1) ? 1 : 0); } private bool IsLoadLockReservedByEFEM(ModuleName ll) { List unfinished = new List(); foreach (var item in _atmWaferTargets) { var moving = _efemMovingItems.Find(m_item => m_item.DestinationModule == item.Key.Module && m_item.DestinationSlot == item.Key.Slot); if (!unfinished.Contains(moving.SourceModule)) unfinished.Add(moving.SourceModule); if (!unfinished.Contains(moving.DestinationModule)) unfinished.Add(moving.DestinationModule); } return unfinished.Contains(ll); } private bool IsLoadLockReservedByTM(ModuleName ll) { List unfinished = new List(); foreach (var item in _vacWaferTargets) { var moving = _movingItems.Find(m_item => m_item.DestinationModule == item.Key.Module && m_item.DestinationSlot == item.Key.Slot); if (!unfinished.Contains(moving.SourceModule)) unfinished.Add(moving.SourceModule); if (!unfinished.Contains(moving.DestinationModule)) unfinished.Add(moving.DestinationModule); } return unfinished.Contains(ll); } private bool IsLoadReadyForTM(ModuleName ll) { return _vacSchedulers[ll].IsAvailable && _vacModules[ll].MovingStatus == MovingStatus.Idle && !IsLoadLockReservedByEFEM(ll); } private bool IsLoadLockReadyForEFEMManualMove(ModuleName ll) { return ((_vacSchedulers[ll].IsIdle && !_vacSchedulers[ll].IsOnline) || _vacSchedulers[ll].IsAvailable) && _vacModules[ll].MovingStatus == MovingStatus.Idle && !IsLoadLockReservedByTM(ll); } private bool IsPMKeepEmpty(ModuleName pm) { return WaferManager.Instance.CheckNoWafer(pm, 0) && _movingItems.FindIndex(item => item.DestinationModule == pm) == -1; } private bool IsPreJobCleanPending(ModuleName pm, out string preCleanRecipe) { preCleanRecipe = string.Empty; foreach (var cj in _lstControlJobs) { if (cj.JetState == EnumJetCtrlJobState.PreJobClean && _waitPreCleanPMs.Contains(pm)) { preCleanRecipe = cj.PreJobClean; return true; } } return false; } private bool IsPostJobCleanPending(ModuleName pm, out string postCleanRecipe) { postCleanRecipe = string.Empty; foreach (var cj in _lstControlJobs) { if (cj.JetState == EnumJetCtrlJobState.PostJobClean && _waitPostCleanPMs.Contains(pm)) { postCleanRecipe = cj.PostJobClean; return true; } } return false; } private (int processed, int unprocessed, int empty) GetLLProcessStatusCount(ModuleName ll) { int processedCount = 0; int unprocessCount = 0; int slotCount = ll == ModuleName.LLA ? _LLASlotNumber : _LLBSlotNumber; if (ModuleHelper.IsLoadLock(ll)) { for (int i = 0; i < slotCount; i++) { if (WaferManager.Instance.CheckHasWafer(ll, i)) { if (CheckWaferNeedProcessAndPMAvailable(ll, i)) unprocessCount++; else processedCount++; } } } return (processedCount, unprocessCount, slotCount - processedCount - unprocessCount); } private int GetAtmInerWaferCount() { int nCount = 0; foreach (var atmModule in _atmModules) { if (!ModuleHelper.IsEFEMRobot(atmModule.Key) && !ModuleHelper.IsLoadPort(atmModule.Key) && WaferManager.Instance.CheckHasWafer(atmModule.Key, 0)) nCount++; } return nCount; } private int CanPushInWaferNumber() { if (_tmRobot.RobotStatus == RState.Running) return 0; var llaWaferStatus = GetLLProcessStatusCount(ModuleName.LLA); var llbWaferStatus = GetLLProcessStatusCount(ModuleName.LLB); var pmWaferStatus = GetPMWaferExistence(); if (_LLInOutPath == SequenceLLInOutPath.AInBOut) { if (_atmSchedulers[ModuleName.LLB].IsAtm && llbWaferStatus.processed >= 2) return 0; int canPushIn = llbWaferStatus.processed == _LLBSlotNumber ? 0 : pmWaferStatus.empty + llaWaferStatus.empty - GetTMRobotWaferCount() - GetAtmInerWaferCount() - GetEfemRoborWaferCount(); // 修正 单手臂占用影响Throughput的问题 if (canPushIn == 1/* && llbWaferStatus.processed >= 2*/) return 0; return canPushIn; } else if (_LLInOutPath == SequenceLLInOutPath.BInAOut) { if (_atmSchedulers[ModuleName.LLA].IsAtm && llaWaferStatus.processed >= 2) return 0; int canPushIn = llaWaferStatus.processed == _LLASlotNumber ? 0 : pmWaferStatus.empty + llbWaferStatus.empty - GetTMRobotWaferCount() - GetAtmInerWaferCount() - GetEfemRoborWaferCount(); if (canPushIn == 1 && llaWaferStatus.processed >= 2) return 0; return canPushIn; } else if (_LLInOutPath == SequenceLLInOutPath.AInAOut || (_LLInOutPath == SequenceLLInOutPath.DInDOut && _vacSchedulers[ModuleName.LLA].IsOnline && !_vacSchedulers[ModuleName.LLB].IsOnline)) { return llaWaferStatus.processed > 0 ? 0 : _LLASlotNumber / 2 + pmWaferStatus.empty - llaWaferStatus.unprocessed - GetTMRobotWaferCount() - GetEfemRoborWaferCount() - GetAtmInerWaferCount(); } else if (_LLInOutPath == SequenceLLInOutPath.BInBOut || (_LLInOutPath == SequenceLLInOutPath.DInDOut && !_vacSchedulers[ModuleName.LLA].IsOnline && _vacSchedulers[ModuleName.LLB].IsOnline)) { return llbWaferStatus.processed > 0 ? 0 : _LLBSlotNumber / 2 + pmWaferStatus.empty - llbWaferStatus.unprocessed - GetTMRobotWaferCount() - GetEfemRoborWaferCount() - GetAtmInerWaferCount(); } else if (_LLInOutPath == SequenceLLInOutPath.DInDOut && _vacSchedulers[ModuleName.LLA].IsOnline && _vacSchedulers[ModuleName.LLB].IsOnline) { return llaWaferStatus.processed + llbWaferStatus.processed > 1 ? 0 : (llaWaferStatus.empty + llbWaferStatus.empty) / 2 + pmWaferStatus.empty - GetTMRobotWaferCount() - GetEfemRoborWaferCount() - GetAtmInerWaferCount(); } else { _cycleState = RState.Failed; LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, "Both LLA/LLB offline, stop runing."); return 0; } } private bool IsForwardPathBlocking(ModuleName mod, int slot) { var wafer = WaferManager.Instance.GetWafer(mod, slot); if (wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) return false; foreach (var next_module in wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules) { var SlotNumber = GetModuleSlotCount(next_module); for (int i = 0; i < SlotNumber; i++) { if (WaferManager.Instance.CheckNoWafer(next_module, i)) return false; } } return true; } private bool ProcessLPEFEMRobotTask(ModuleName lp) { // check return for (int i = 0; i < 2; i++) { if (_efemRobotSingleArmOption != 0 && _efemRobotSingleArmOption != i + 1) continue; 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)); } } } var canPushInWafers = CanPushInWaferNumber(); if (_efemMovingItems.Count == 0 && canPushInWafers > 0) { var outSlots = GetNextWaferInJobQueue(lp); var hand = GetEFEMRobotFreeHand(); if (hand != Hand.None && outSlots.Count > 0 && !IsForwardPathBlocking(lp, outSlots[0])) { _efemMovingItems.Add(new MoveItem(lp, outSlots[0], ModuleName.EfemRobot, (int)hand, hand)); } if (canPushInWafers > 1) { hand = GetEFEMRobotFreeHand(); if (hand != Hand.None && outSlots.Count > 1 && !IsForwardPathBlocking(lp, outSlots[1])) { _efemMovingItems.Add(new MoveItem(lp, outSlots[1], ModuleName.EfemRobot, (int)hand, hand)); } } } return _efemMovingItems.Count > 0; } private List GetNextWaferInJobQueue(ModuleName lp) { var inSlots = new List(); 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)) { var wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); if (!wafer.IsEmpty && wafer.NextSequenceStep == 0) { inSlots.Add(pjSlotWafer.Item2); } } } } } } } return inSlots.OrderBy(item => item).Take(2).ToList(); } private bool ProcessAlignerEFEMRobotTask(ModuleName aligner) { if (WaferManager.Instance.CheckHasWafer(aligner, 0) && IsAtmWaferReadyOut(aligner, 0) && _atmModules[aligner].MovingStatus == MovingStatus.Idle) { var hand = GetEFEMRobotFreeHand(); if (hand != Hand.None) { _efemMovingItems.Add(new MoveItem(aligner, 0, ModuleName.EfemRobot, (int)hand, hand)); } //// check with whether another robot arm wafer wait go to aligner //var robotSlots = GetEfemRobotWaferReadyInHands(aligner); //if (robotSlots.Count > 0) //{ // _efemMovingItems.Add(new MoveItem(ModuleName.EfemRobot, robotSlots.First(), aligner, 0, (Hand)robotSlots.First())); //} } else if (WaferManager.Instance.CheckNoWafer(aligner, 0)) { var robotSlots = GetEfemRobotWaferReadyInHands(aligner); if (robotSlots.Count > 0) { _efemMovingItems.Add(new MoveItem(ModuleName.EfemRobot, robotSlots.First(), aligner, 0, (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 (_efemMovingItems.Count > 0) { if (_efemRobot.PostMoveItems(_efemMovingItems.ToArray())) { foreach (var item in _efemMovingItems) { var wafer = WaferManager.Instance.GetWafer(item.SourceModule, item.SourceSlot); if (wafer.IsEmpty) { // post alarm _cycleState = RState.Failed; 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; if (ModuleHelper.IsLoadLock(item.Module)) { _vacModules[item.Module].MovingStatus = MovingStatus.Moving; } } } } } private Hand GetEFEMRobotFreeHand() { for (int i = 0; i < 2; i++) { if (_efemRobotSingleArmOption != 0 && _efemRobotSingleArmOption != i + 1) continue; 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) && !_efemMovingItems.Exists(item => item.DestinationModule == ModuleName.EfemRobot && item.DestinationSlot == i))) return (Hand)i; } return Hand.None; } private void ProcessAlignerTask() { foreach (var align in _atmModules) { if (ModuleHelper.IsAligner(align.Key)) { switch (align.Value.MovingStatus) { case MovingStatus.WaitAlign: if (_efemRobot.IsAvailable) { WaferInfo wafer = WaferManager.Instance.GetWafer(align.Key, 0); if (!wafer.IsEmpty) { if (_efemRobot.Align(0F)) { align.Value.MovingStatus = MovingStatus.StartAlign; return; } } align.Value.MovingStatus = MovingStatus.Idle; } break; case MovingStatus.StartAlign: if (!_efemRobot.IsAvailable) { align.Value.MovingStatus = MovingStatus.Aligning; } break; case MovingStatus.Aligning: { if (_efemRobot.IsAvailable) { align.Value.MovingStatus = MovingStatus.Idle; } } break; } } } } private bool ProcessAtmManaulReturnWafers() { if (_lstReturnWafers.Count == 0) return false; for (int i = 0; i < 2; i++) { if (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, i)) { var wafer = WaferManager.Instance.GetWafer(ModuleName.EfemRobot, i); if (_lstReturnWafers.FindIndex(id => id == wafer.InnerId) != -1) { _efemMovingItems.Add(new MoveItem(ModuleName.EfemRobot, i, (ModuleName)wafer.OriginStation, wafer.OriginSlot, (Hand)i)); _lstReturnWafers.Remove(wafer.InnerId); } } } if (_efemMovingItems.Count > 0) return true; var lls = _atmSchedulers.Where(x => ModuleHelper.IsLoadLock(x.Key) && IsLoadLockReadyForEFEMManualMove(x.Key)).ToDictionary(k => k.Key, v => v.Value); foreach (var ll in lls) { int slotNumber = ll.Key == ModuleName.LLA ? _LLASlotNumber : _LLBSlotNumber; for (int i = 0; i < slotNumber; i++) { if (_efemMovingItems.Count >= 2) return true; if (WaferManager.Instance.CheckHasWafer(ll.Key, i)) { var wafer = WaferManager.Instance.GetWafer(ll.Key, i); var robotHand = GetEFEMRobotFreeHand(); if (robotHand != Hand.None && _lstReturnWafers.FindIndex(id => id == wafer.InnerId) != -1) { _efemMovingItems.Add(new MoveItem(ll.Key, i, ModuleName.EfemRobot, (int)robotHand, robotHand)); } } } } return _efemMovingItems.Count > 0; } #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($"{module}", recipeName, false, "Process"); 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++) { foreach (var stepModule in wafer.ProcessJob.Sequence.Steps[i].StepModules) { if (!_vacSchedulers.Keys.Contains(stepModule)) continue; if (!_vacSchedulers[stepModule].IsOnline) continue; if (ModuleHelper.IsPm(stepModule)) { if (stepModule == processIn || processIn == ModuleName.System) return true; } else break; } } return false; } private List GetPmUsedInRunningPj() { var pmUsed = new List(); foreach (var cj in _lstControlJobs) { if (cj.JetState != EnumJetCtrlJobState.PreJobClean && cj.JetState != EnumJetCtrlJobState.PostJobClean && cj.JetState != EnumJetCtrlJobState.Processing) continue; foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name && (pj.State == EnumProcessJobState.Processing || pj.State == EnumProcessJobState.Paused)) { for (int i = 0; i < pj.Sequence.Steps.Count; i++) { SequenceStepInfo stepInfo = pj.Sequence.Steps[i]; foreach (var module in stepInfo.StepModules) { if (ModuleHelper.IsPm(module) && !pmUsed.Contains(module)) pmUsed.Add(module); } } } } } return pmUsed; } private void UpateSequenceStep(List items) { foreach (var item in items) { if (!ModuleHelper.IsTMRobot(item.DestinationModule) && !ModuleHelper.IsEFEMRobot(item.DestinationModule)) { var wafer = WaferManager.Instance.GetWafer(item.DestinationModule, item.DestinationSlot); if (!wafer.IsEmpty && wafer.ProcessJob != null) { if (wafer.NextSequenceStep < wafer.ProcessJob.Sequence.Steps.Count && wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(item.DestinationModule)) wafer.NextSequenceStep++; } else { // should not go here LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"UpateSequenceStep() failed, location: {item.DestinationModule}:{item.DestinationSlot + 1}, NextSequenceStep: {wafer.NextSequenceStep}"); } } } } private void CheckWaferArrived() { if (_cycleState != RState.Running) return; foreach (var mod in _vacSchedulers.Append(new KeyValuePair(ModuleName.TMRobot, _tmRobot))) { 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 || wafer.InnerId.ToString().Length < 10) continue; if (wafer.InnerId == tar.Value && _tmRobot.RobotStatus != RState.Running) { // wafer arrive _vacWaferTargets.Remove(tar.Key); if (!ModuleHelper.IsTMRobot(tar.Key.Module)) _vacSchedulers[tar.Key.Module].WaferArrived(tar.Key.Slot); //LOG.Write(eEvent.EV_ROUTER, ModuleName.System, $"wafer {wafer.WaferOrigin}: {wafer.InnerId} arrived {tar.Key.Module}{tar.Key.Slot + 1}"); if (ModuleHelper.IsPm(tar.Key.Module)) { _vacModules[tar.Key.Module].MovingStatus = MovingStatus.WaitProcess; } else { var curItem = _movingItems.Find(item => item.DestinationModule == tar.Key.Module && item.DestinationSlot == tar.Key.Slot); if (ModuleHelper.IsPm(curItem.SourceModule) && _movingItems.FindIndex(item => item.DestinationModule == curItem.SourceModule) == -1) { if (_waitWTWCleanPMs.ContainsKey(curItem.SourceModule)) { _vacModules[curItem.SourceModule].MovingStatus = MovingStatus.WaitWTWClean; } else if (_waitPostCleanPMs.Contains(curItem.SourceModule)) { _vacModules[curItem.SourceModule].MovingStatus = MovingStatus.WaitPostJobClean; } } } if (!ModuleHelper.IsTMRobot(tar.Key.Module)) wafer.NextSequenceStep++; } } if (ModuleHelper.IsLoadLock(mod.Key)) { if (!IsLoadLockReservedByTM(mod.Key) && mod.Value.IsAvailable) { _vacModules[mod.Key].MovingStatus = MovingStatus.Idle; } } } _vacMoveFinishTrig.CLK = _vacWaferTargets.Count == 0 && _tmRobot.RobotStatus != RState.Running; if (_vacMoveFinishTrig.Q) { foreach (var item in _movingItems) { if (!ModuleHelper.IsTMRobot(item.DestinationModule) && _vacModules[item.DestinationModule].MovingStatus == MovingStatus.Moving) { _vacModules[item.DestinationModule].MovingStatus = MovingStatus.Idle; } if (!ModuleHelper.IsTMRobot(item.SourceModule) && _vacModules[item.SourceModule].MovingStatus == MovingStatus.Moving) { _vacModules[item.SourceModule].MovingStatus = MovingStatus.Idle; } } //UpateSequenceStep(_movingItems); _movingItems.Clear(); } foreach (var mod in _atmSchedulers.Append(new KeyValuePair(ModuleName.EfemRobot, _efemRobot))) { 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 || wafer.InnerId.ToString().Length < 10) continue; if (wafer.InnerId == tar.Value && _efemRobot.RobotStatus != RState.Running) { //LOG.Write(eEvent.EV_ROUTER, ModuleName.System, $"wafer {wafer.WaferOrigin}: {wafer.InnerId} arrived {tar.Key.Module}{tar.Key.Slot + 1}"); // wafer arrive if (!ModuleHelper.IsLoadPort(tar.Key.Module) && !ModuleHelper.IsEFEMRobot(tar.Key.Module)) _atmSchedulers[tar.Key.Module].WaferArrived(tar.Key.Slot); _atmWaferTargets.Remove(tar.Key); if (ModuleHelper.IsAligner(tar.Key.Module)) { _atmModules[tar.Key.Module].MovingStatus = MovingStatus.WaitAlign; } if (!ModuleHelper.IsEFEMRobot(tar.Key.Module)) wafer.NextSequenceStep++; } } if (ModuleHelper.IsLoadLock(mod.Key)) { if (!IsLoadLockReservedByEFEM(mod.Key) && mod.Value.IsAvailable) { _vacModules[mod.Key].MovingStatus = MovingStatus.Idle; } } } //_atmMoveFinishTrig.CLK = _atmWaferTargets.Count == 0 && _efemRobot.RobotStatus != RState.Running; if (_atmWaferTargets.Count == 0 && _efemRobot.RobotStatus != RState.Running && _efemMovingItems.Count > 0) { foreach (var item in _efemMovingItems) { if (ModuleHelper.IsLoadLock(item.Module)) { _vacModules[item.Module].MovingStatus = MovingStatus.Idle; } } //UpateSequenceStep(_efemMovingItems); _efemMovingItems.Clear(); //LOG.Write(eEvent.EV_ROUTER, ModuleName.System, $"All EFEM commands complete!"); } } private bool ActiveProcessJob(ProcessJobInfo pj) { foreach (var pjSlotWafer in pj.SlotWafers) { WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); wafer.ProcessJob = pj; wafer.NextSequenceStep = 0; WaferDataRecorder.SetPjInfo(wafer.InnerId.ToString(), pj.InnerId.ToString()); } ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == pj.ControlJobName); CarrierInfo carrier = CarrierManager.Instance.GetCarrier(cj.Module); JobDataRecorder.StartPJ(pj.InnerId.ToString(), carrier.InnerId.ToString(), cj.InnerId.ToString(), pj.Name, cj.Module, cj.Module, pj.SlotWafers.Count); pj.SetState(EnumProcessJobState.Processing); return true; } private void DispatchCtrlJobs() { var quequedJobs = _lstControlJobs.FindAll(job => job.JetState == EnumJetCtrlJobState.Quequed); if (quequedJobs.Count > 0) { var runingJobs = _lstControlJobs.FindAll(job => IsCtrlJobRuning(job)); if (runingJobs.Count >= 2) return; if (runingJobs.Count == 0 || IsCtrlJobEndingState(runingJobs.First())) { var startJob = quequedJobs.OrderBy(job => job.StartTime).First(); if (IsCtrlJobNeedPreClean(startJob)) { startJob.JetState = EnumJetCtrlJobState.PreJobClean; _waitPreCleanPMs.Clear(); var PMs = GetWaitPreCleanPMsByCtrlJob(startJob); foreach (var pm in PMs) { _waitPreCleanPMs.Add(pm); } } else { startJob.JetState = EnumJetCtrlJobState.Processing; foreach (var pjName in startJob.ProcessJobNameList) { var pj = _lstProcessJobs.Find(x => x.Name == pjName); if (pj == null) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Not find pj named {pjName} in {startJob.Name}"); continue; } if (pj.State == EnumProcessJobState.Queued) { ActiveProcessJob(pj); //break; 先将所有的 Process Job全部激活, 不做PreJobClean, PreJobClean 逻辑 引入 ProcessPMTask() } } } } } var preJobClean = _lstControlJobs.Find(job => job.JetState == EnumJetCtrlJobState.PreJobClean); if (preJobClean != null && _waitPreCleanPMs.Count == 0) { preJobClean.JetState = EnumJetCtrlJobState.Processing; foreach (var pjName in preJobClean.ProcessJobNameList) { var pj = _lstProcessJobs.Find(x => x.Name == pjName); if (pj == null) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Not find pj named {pjName} in {preJobClean.Name}"); continue; } if (pj.State == EnumProcessJobState.Queued) { ActiveProcessJob(pj); //break; 先将所有的 Process Job全部激活, 不做PreJobClean, PreJobClean 逻辑 引入 ProcessPMTask() } } } } private static bool IsWaferNeedGotoModule(WaferInfo wafer, ModuleName module) { 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++) { if (wafer.ProcessJob.Sequence.Steps[i].StepModules .Contains(module)) return true; } return false; } private bool IsWaferCtrlJobRuning(ModuleName mod, int slot) { var wafer = WaferManager.Instance.GetWafer(mod, slot); if (wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) return false; var waferCtrlJob = _lstControlJobs.Find(item => (int)ModuleHelper.Converter(item.Module) == wafer.OriginStation); if(waferCtrlJob != null) { return waferCtrlJob.State == EnumControlJobState.Executing; } return false; } private bool CheckAllWaferReturned(ProcessJobInfo pj, bool checkAllProcessed) { bool allWaferReturn = true; for (int i = 0; i < pj.SlotWafers.Count; ++i) { WaferInfo wafer = WaferManager.Instance.GetWafer(pj.SlotWafers[i].Item1, pj.SlotWafers[i].Item2); if (wafer.IsEmpty) { allWaferReturn = false; continue; } foreach (var mod in _vacModules) { if (ModuleHelper.IsPm(mod.Key)) { if (checkAllProcessed && IsWaferNeedGotoModule(wafer, mod.Key)) { allWaferReturn = false; } } } } return allWaferReturn; } private void UpdateProcessJobStatus() { if (_efemRobot.RobotStatus == RState.Running ||_tmRobot.RobotStatus == RState.Running) return; foreach (var pj in _lstProcessJobs) { if (pj.State == EnumProcessJobState.Processing) { if (CheckAllWaferReturned(pj, true)) { pj.SetState(EnumProcessJobState.ProcessingComplete); JobDataRecorder.EndPJ(pj.InnerId.ToString(), 0, 0); } } else if (pj.State == EnumProcessJobState.Stopping) { if (CheckAllWaferReturned(pj, false)) { pj.SetState(EnumProcessJobState.ProcessingComplete); JobDataRecorder.EndPJ(pj.InnerId.ToString(), 0, 0); } } } } private void UpdateControlJobStatus() { if (_lstControlJobs.Count == 0) return; if (_efemRobot.RobotStatus == RState.Running || _tmRobot.RobotStatus == RState.Running) return; bool allControlJobComplete = true; List cjRemoveList = new List(); foreach (var cj in _lstControlJobs) { if(cj.JetState == EnumJetCtrlJobState.Quequed) { var runingJobs = _lstControlJobs.FindAll(job => IsCtrlJobRuning(job)); if(runingJobs.Count == 0 || (runingJobs.Count == 1 && IsCtrlJobEndingState(runingJobs.First()))) { var quequedJobs = _lstControlJobs.FindAll(job => job.JetState == EnumJetCtrlJobState.Quequed); var firstQuequeJob = quequedJobs.OrderBy(job => job.StartTime).First(); if(firstQuequeJob.InnerId == cj.InnerId) { if (IsCtrlJobNeedPreClean(cj)) { cj.JetState = EnumJetCtrlJobState.PreJobClean; _waitPreCleanPMs.Clear(); var PMs = GetWaitPreCleanPMsByCtrlJob(cj); foreach (var pm in PMs) { _waitPreCleanPMs.Add(pm); } } else { cj.JetState = EnumJetCtrlJobState.Processing; foreach (var pjName in cj.ProcessJobNameList) { var pj = _lstProcessJobs.Find(x => x.Name == pjName); if (pj == null) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Not find pj named {pjName} in {cj.Name}"); continue; } if (pj.State == EnumProcessJobState.Queued) { ActiveProcessJob(pj); } } } } } } else if(IsCtrlJobRuning(cj)) { if (cj.JetState == EnumJetCtrlJobState.PreJobClean) { if (_waitPreCleanPMs.Count == 0) { cj.JetState = EnumJetCtrlJobState.Processing; foreach (var pjName in cj.ProcessJobNameList) { var pj = _lstProcessJobs.Find(x => x.Name == pjName); if (pj == null) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Not find pj named {pjName} in {cj.Name}"); continue; } if (pj.State == EnumProcessJobState.Queued) { ActiveProcessJob(pj); } } } } else { if (cj.JetState == EnumJetCtrlJobState.Processing && IsCtrlJobNeedPostClean(cj)) { if (IsAllJobWaferProcessedOrProcessing(cj)) { cj.JetState = EnumJetCtrlJobState.PostJobClean; _waitPostCleanPMs.Clear(); var PMs = GetWaitPreCleanPMsByCtrlJob(cj); foreach (var pm in PMs) { _waitPostCleanPMs.Add(pm); } } } if (IsAllProcessJobComplete(cj) && (!IsCtrlJobNeedPostClean(cj) || cj.JetState == EnumJetCtrlJobState.PostJobClean && _waitPostCleanPMs.Count == 0)) { cj.JetState = EnumJetCtrlJobState.Completed; cj.SetState(EnumControlJobState.Completed); cj.EndTime = DateTime.Now; _faCallback.JobFinished(cj, GetFirstProcessJob(cj)); _dbCallback.LotFinished(cj); if (!(_isCycleMode && _cycledCount < _cycleSetPoint)) (Singleton.Instance.GetScheduler(ModuleHelper.Converter(cj.Module)) as SchedulerLoadPort).NoteJobComplete(); _lpCycleCount[ModuleHelper.Converter(cj.Module)]++; } } int countProcessed = 0; foreach (var pjName in cj.ProcessJobNameList) { var pj = _lstProcessJobs.Find(x => x.Name == pjName); if (pj == null) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Not find pj named {pjName} in {cj.Name}"); continue; } // caculate process wafer by process if (_isCycleMode && _cycledCount < (_isCycleMode ? _cycleSetPoint : 0)) { foreach (var pjSlotWafer in pj.SlotWafers) { WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); if (!wafer.IsEmpty && wafer.ProcessState == EnumWaferProcessStatus.Completed) countProcessed++; } } } int lpCycleWafer = _lpCycleCount[ModuleHelper.Converter(cj.Module)] * cj.LotWafers.Count + (cj.JetState == EnumJetCtrlJobState.Completed && cj.LotWafers.Count == countProcessed ? 0 : countProcessed); if (_lpCycleCount[ModuleHelper.Converter(cj.Module)] != _cycledCount || lpCycleWafer != _lpCycleWafer[ModuleHelper.Converter(cj.Module)]) { _lpCycleWafer[ModuleHelper.Converter(cj.Module)] = lpCycleWafer; } } LoadportCassetteState state = (LoadportCassetteState)DATA.Poll($"{cj.Module}.CassetteState"); if (cj.State == EnumControlJobState.Completed && state != LoadportCassetteState.Normal && cj.JetState == EnumJetCtrlJobState.Completed) { cjRemoveList.Add(cj); } allControlJobComplete = allControlJobComplete && cj.State == EnumControlJobState.Completed; } if (_isCycleMode && _cycledCount < (_isCycleMode ? _cycleSetPoint : 0)) { int totolCycleWafer = _lpCycleWafer.Sum(item => item.Value); if (totolCycleWafer != _cycledWafer || _lpCycleCount.Sum(item => item.Value) > 0 && _throughput < 0.01) // refresh _throughput in time { _cycledWafer = totolCycleWafer; if (_cycledCount > 0) { _throughput = (float)(_cycledWafer / _cycleWatch.Elapsed.TotalHours); } else { _throughput = 0; } } if (allControlJobComplete) { _cycledCount++; if (_cycledCount < _cycleSetPoint) { foreach (var cj in _lstControlJobs) { cj.SetState(EnumControlJobState.Executing); cj.JetState = EnumJetCtrlJobState.Quequed; cj.LotInnerId = Guid.NewGuid(); _dbCallback.LotCreated(cj); } foreach (var pj in _lstProcessJobs) { pj.SetState(EnumProcessJobState.Queued); pj.InnerId = Guid.NewGuid(); foreach (var pjSlotWafer in pj.SlotWafers) { WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); wafer.ProcessJob = null; wafer.NextSequenceStep = 0; wafer.ProcessState = EnumWaferProcessStatus.Idle; } } } else _cycleState = RState.End; } } foreach (var cj in cjRemoveList) { List pjRemoveList = new List(); foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) pjRemoveList.Add(pj); } foreach (var pj in pjRemoveList) { _lstProcessJobs.Remove(pj); } _lstControlJobs.Remove(cj); } } private void UpdateLLInOutPathProperty() { if (_lstControlJobs.Count == 0) return; List inOutPaths = new List(); foreach (var cj in _lstControlJobs) { if (cj.State == EnumControlJobState.Executing) { foreach (var pjName in cj.ProcessJobNameList) { var pj = _lstProcessJobs.Find(x => x.Name == pjName); if (pj == null || pj.State != EnumProcessJobState.Processing) continue; if (!inOutPaths.Exists(item => item == pj.Sequence.LLInOutPath)) inOutPaths.Add(pj.Sequence.LLInOutPath); } } } if (inOutPaths.Count == 1) { _LLInOutPath = inOutPaths[0]; } } private bool IsAllJobWaferProcessedOrProcessing(ControlJobInfo cj) { List allModules = _vacModules.Keys.ToList().Union(_atmModules.Keys.ToList()).ToList(); allModules.Add(ModuleName.EfemRobot); allModules.Add(ModuleName.TMRobot); int original = (int)ModuleHelper.Converter(cj.Module); foreach (var mod in allModules) { if (ModuleHelper.IsLoadPort(mod) && (int)mod != original) continue; var wafers = WaferManager.Instance.GetWafers(mod); foreach (var wafer in wafers) { if (wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.ProcessJob.ControlJobName != cj.Name) continue; if (wafer.ProcessState != EnumWaferProcessStatus.Completed && wafer.ProcessState != EnumWaferProcessStatus.InProcess) return false; ; } } return true; } private bool IsAllProcessJobComplete(ControlJobInfo cj) { foreach(var pj in _lstProcessJobs) { if(pj.ControlJobName == cj.Name && pj.State != EnumProcessJobState.ProcessingComplete) { return false; } } return true; } private bool IsCtrlJobRuning(ControlJobInfo cj) { return cj.JetState == EnumJetCtrlJobState.PreJobClean || cj.JetState == EnumJetCtrlJobState.Processing || cj.JetState == EnumJetCtrlJobState.PostJobClean; } private bool IsCtrlJobEndingState(ControlJobInfo cj) { return cj.JetState == EnumJetCtrlJobState.PostJobClean || (cj.JetState == EnumJetCtrlJobState.Processing && IsAllJobWaferProcessedOrProcessing(cj) && !IsCtrlJobNeedPostClean(cj)); } private bool IsCtrlJobNeedPreClean(ControlJobInfo cj) { return cj.PreJobClean.Trim().Length > 0; } private bool IsCtrlJobNeedPostClean(ControlJobInfo cj) { return cj.PostJobClean.Trim().Length > 0; } private bool IsPMWaferNeedWTWClean(ModuleName pm, out string WTWCleanRecipe) { WTWCleanRecipe = string.Empty; var wafer = WaferManager.Instance.GetWafer(pm, 0); if (wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.ProcessJob.Sequence.WTWCleanRecipe.Trim().Length == 0) return false; WTWCleanRecipe = wafer.ProcessJob.Sequence.WTWCleanRecipe; return true; } private List GetWaitPreCleanPMsByCtrlJob(ControlJobInfo cj) { var pmlist = new List(); foreach(var pj in _lstProcessJobs) { if(pj.ControlJobName == cj.Name) { foreach(var pm in pj.Sequence.PMs) { if (!pmlist.Contains(pm)) pmlist.Add(pm); } } } return pmlist; } private static WaferInfo GetCloneWafer(ModuleName mod, int slot) { var Wafer = WaferManager.Instance.GetWafer(mod, slot); if (Wafer.IsEmpty || Wafer.ProcessJob == null || Wafer.ProcessJob.Sequence == null) return Wafer; var cloneWafer = SerializeHelper.DeepCopyJson(Wafer); if (Wafer.IsEmpty || Wafer.ProcessJob == null || Wafer.ProcessJob.Sequence == null) return Wafer; return cloneWafer; } #endregion #region Manual Return All Wafer public RState CheckManualReturnWafer() { if(!_tmRobot.IsIdle && (GetPMWaferExistence().exist + GetTMRobotWaferCount() > 0)) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.TMRobot, $"The TM Robot is not ready for return wafer."); return RState.Failed; } if (!_efemRobot.IsIdle) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.EfemRobot, $"The EFEM Robot is not ready for return wafer."); return RState.Failed; } var schedulers = _vacSchedulers.Concat(_atmSchedulers); foreach(var mod in schedulers) { if (ModuleHelper.IsLoadPort(mod.Key)) continue; int nSlotNumber = ModuleHelper.IsLoadLock(mod.Key) ? (mod.Key == ModuleName.LLA ? _LLASlotNumber : _LLBSlotNumber) : (ModuleHelper.IsTMRobot(mod.Key) || ModuleHelper.IsEFEMRobot(mod.Key) ? 2 : 1); for(int slot = 0; slot < nSlotNumber; slot++) { var wafer = WaferManager.Instance.GetWafer(mod.Key, slot); if (wafer.IsEmpty) continue; if(!mod.Value.IsIdle) { LOG.Write(eEvent.ERR_ROUTER, mod.Key, $"{mod.Key} is not ready for return wafer."); return RState.Failed; } var destLP = (ModuleName)wafer.OriginStation; if(!_atmSchedulers[destLP].IsAvailable) { LOG.Write(eEvent.ERR_ROUTER, destLP, $"The destination Loadport {destLP} is not ready for return wafer."); return RState.Failed; } } } Clear(); return RState.Running; } public RState ReturnAllWafers() { var llaWaferStatus = GetLLProcessStatusCount(ModuleName.LLA); var llbWaferStatus = GetLLProcessStatusCount(ModuleName.LLB); var pmWaferStatus = GetPMWaferExistence(); if (pmWaferStatus.exist + (_LLASlotNumber - llaWaferStatus.empty) + (_LLBSlotNumber - llbWaferStatus.empty) + GetTMRobotWaferCount() + GetAtmInerWaferCount() + GetEfemRoborWaferCount() == 0) { Clear(); return RState.End; } ReturnAtmWafers(); ReturnVacWafers(); CheckReturnWafersArrived(); return RState.Running; } private void ReturnVacWafers() { if(_tmRobot.IsAvailable && _tmRobot.RobotStatus != RState.Running && _movingItems.Count == 0) { var tmRobotWaferCount = GetTMRobotWaferCount(); if(tmRobotWaferCount > 0 || GetPMWaferExistence().exist > 0) { var llaWaferStatus = GetLLProcessStatusCount(ModuleName.LLA); var llbWaferStatus = GetLLProcessStatusCount(ModuleName.LLB); if (IsLoadReadyForTM(ModuleName.LLA) && llaWaferStatus.empty > 0 && (!IsLoadReadyForTM(ModuleName.LLB) || (!_vacSchedulers[ModuleName.LLB].IsVac && llaWaferStatus.empty == llbWaferStatus.empty) || llaWaferStatus.empty > llbWaferStatus.empty)) { if (tmRobotWaferCount > 0) ReturnTMRobotWafersToLL(ModuleName.LLA); else ReturnPMWafersToLL(ModuleName.LLA); } else if (IsLoadReadyForTM(ModuleName.LLB) && llbWaferStatus.empty > 0) { if (tmRobotWaferCount > 0) ReturnTMRobotWafersToLL(ModuleName.LLB); else ReturnPMWafersToLL(ModuleName.LLB); } } RuningTMRobotTask(); } } private void ReturnAtmWafers() { if(_efemRobot.IsAvailable && _efemRobot.RobotStatus != RState.Running && _efemMovingItems.Count == 0) { var aligners = _atmSchedulers.Where(x => ModuleHelper.IsAligner(x.Key) && x.Value.IsAvailable && WaferManager.Instance.CheckHasWafer(x.Key, 0)).ToDictionary(k => k.Key, v => v.Value); var lls = _vacSchedulers.Where(x => ModuleHelper.IsLoadLock(x.Key) && IsLoadLockReadyForEFEMManualMove(x.Key) && GetLLProcessStatusCount(x.Key).processed + GetLLProcessStatusCount(x.Key).unprocessed > 0).ToDictionary(k => k.Key, v => v.Value); if (GetEfemRoborWaferCount() > 0) { for(int slot = 0; slot < 2; slot++) { var wafer = WaferManager.Instance.GetWafer(ModuleName.EfemRobot, slot); if(!wafer.IsEmpty) { _efemMovingItems.Add(new MoveItem(ModuleName.EfemRobot, slot, (ModuleName)wafer.OriginStation, wafer.OriginSlot, (Hand)slot)); } } } else if(aligners.Count > 0) { foreach(var align in aligners) { var wafer = WaferManager.Instance.GetWafer(align.Key, 0); var hand = GetEFEMRobotFreeHand(); if (!wafer.IsEmpty && hand != Hand.None) { _efemMovingItems.Add(new MoveItem(align.Key, 0, ModuleName.EfemRobot, (int)hand, hand)); } } } else if(lls.Count > 0) { ModuleName source = lls.First().Key; if(lls.Count > 1) { if (GetLLProcessStatusCount(ModuleName.LLB).empty < GetLLProcessStatusCount(ModuleName.LLA).empty) source = ModuleName.LLB; else source = ModuleName.LLA; } int slotNumber = source == ModuleName.LLA ? _LLASlotNumber : _LLBSlotNumber; for(int i = 0; i < slotNumber; i++) { var wafer = WaferManager.Instance.GetWafer(source, i); var hand = GetEFEMRobotFreeHand(); if (!wafer.IsEmpty && hand != Hand.None) { _efemMovingItems.Add(new MoveItem(source, i, ModuleName.EfemRobot, (int)hand, hand)); } } } RuningEFEMRobotTask(); } } private void ReturnPMWafersToLL(ModuleName ll) { var validSlots = GetModuleValidSlots(ll); foreach(var pm in _vacSchedulers) { if (!ModuleHelper.IsPm(pm.Key)) continue; if(pm.Value.IsIdle && WaferManager.Instance.CheckHasWafer(pm.Key, 0) && validSlots.Count > 0) { _movingItems.Add(new MoveItem( pm.Key, 0, ll, validSlots.Dequeue(), Hand.None)); if (_movingItems.Count >= 2) return; } } } private void ReturnTMRobotWafersToLL(ModuleName ll) { var validSlots = GetModuleValidSlots(ll); for(int slot = 0; slot < 2; slot++) { if(WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, slot)) { _movingItems.Add(new MoveItem(ModuleName.TMRobot, slot, ll, validSlots.Dequeue(), (Hand)slot)); } } } private void CheckReturnWafersArrived() { foreach (var mod in _vacSchedulers.Append(new KeyValuePair(ModuleName.TMRobot, _tmRobot))) { 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 || wafer.InnerId.ToString().Length < 10) continue; if (wafer.InnerId == tar.Value && _tmRobot.RobotStatus != RState.Running) { // wafer arrive _vacWaferTargets.Remove(tar.Key); } } if (ModuleHelper.IsLoadLock(mod.Key)) { if (!IsLoadLockReservedByTM(mod.Key) && mod.Value.IsAvailable) { _vacModules[mod.Key].MovingStatus = MovingStatus.Idle; } } } _vacMoveFinishTrig.CLK = _vacWaferTargets.Count == 0 && _tmRobot.RobotStatus != RState.Running; if (_vacMoveFinishTrig.Q) { foreach (var item in _movingItems) { if (!ModuleHelper.IsTMRobot(item.DestinationModule) && _vacModules[item.DestinationModule].MovingStatus == MovingStatus.Moving) { _vacModules[item.DestinationModule].MovingStatus = MovingStatus.Idle; } if (!ModuleHelper.IsTMRobot(item.SourceModule) && _vacModules[item.SourceModule].MovingStatus == MovingStatus.Moving) { _vacModules[item.SourceModule].MovingStatus = MovingStatus.Idle; } } _movingItems.Clear(); } foreach (var mod in _atmSchedulers.Append(new KeyValuePair(ModuleName.EfemRobot, _efemRobot))) { 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 || wafer.InnerId.ToString().Length < 10) continue; if (wafer.InnerId == tar.Value && _efemRobot.RobotStatus != RState.Running) { if (!ModuleHelper.IsLoadPort(tar.Key.Module) && !ModuleHelper.IsEFEMRobot(tar.Key.Module)) _atmSchedulers[tar.Key.Module].WaferArrived(tar.Key.Slot); _atmWaferTargets.Remove(tar.Key); if (ModuleHelper.IsAligner(tar.Key.Module)) { _atmModules[tar.Key.Module].MovingStatus = MovingStatus.WaitAlign; } } } if (ModuleHelper.IsLoadLock(mod.Key)) { if (!IsLoadLockReservedByEFEM(mod.Key) && mod.Value.IsAvailable) { _vacModules[mod.Key].MovingStatus = MovingStatus.Idle; } } } if (_atmWaferTargets.Count == 0 && _efemRobot.RobotStatus != RState.Running && _efemMovingItems.Count > 0) { foreach (var item in _efemMovingItems) { if (ModuleHelper.IsLoadLock(item.Module)) { _vacModules[item.Module].MovingStatus = MovingStatus.Idle; } } _efemMovingItems.Clear(); } } #endregion } }