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, // 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 : 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 = (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; SequenceLLInOutPath _LLInOutPath = SequenceLLInOutPath.DInDOut; 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; public bool HasJobRunning => _lstControlJobs.Count > 0; #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.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() { if(_cycleState == RState.Running) { 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 (!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; } 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(); } _cycleState = RState.Running; } public void Clear() { _vacWaferTargets.Clear(); _atmWaferTargets.Clear(); _movingItems.Clear(); _efemMovingItems.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(); _lstProcessJobs.Clear(); //_mapTarget.Clear(); //queMoveAction.Clear(); //_curAction.state = ActionState.Done; } #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(); StartNewJob(); UpdateLLInOutPathProperty(); } private void driveVacSystem() { PumpingTMRobotTask(); ProcessTMRobotTask(); RuningTMRobotTask(); } #region Vacuum System private void PumpingTMRobotTask() { if (!_tmRobot.IsAvailable) 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 = GetCloneWafer(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.Moving) { destSlot.Module = next_module; destSlot.Slot = nSlot; return true; } } } } return false; } List GetModuleValidSlots(ModuleName mod) { var validSlot = new List(); 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.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(); } 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.Moving) continue; if (IsLoadLockReservedByEFEM(slot.Key.Module)) continue; var wafer = GetCloneWafer(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.First(), Hand.None)); } validSlots.RemoveAt(0); } } } 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) { if(WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) || WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1)) { 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; 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 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(item.Key.Module); UpdateModuleMovingStatus(destSlot_2.Module); } } } // check whether match swap pattern var in_slots = SearchWaitInSlots(slot.Key.Module); foreach(var in_slot in in_slots) { if(_vacModules[in_slot.Module].MovingStatus == MovingStatus.Idle) { _movingItems.Add(in_slot); UpdateItemMovingStatus(in_slot); } } 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 swap pattern var in_slots = SearchWaitInSlots(slot.Key.Module); if(in_slots.Count >= 1) { _movingItems.Add(in_slots.First()); UpdateItemMovingStatus(in_slots.First()); } else { // check whether match double move pattern var same_dest = SearchWaitInSlots(destSlot.Module); if(same_dest.Count > 0 && _vacModules[same_dest[0].SourceModule].MovingStatus == MovingStatus.Idle) { _movingItems.Add(same_dest[0]); UpdateItemMovingStatus(same_dest[0]); } } return; } } } _vacReadyOutSlots.Clear(); _vacReadyInSlots.Clear(); } } 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 _cycleState = RState.Failed; 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; } } } } 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: mod.Value.MovingStatus = MovingStatus.Idle; break; } } else { switch(mod.Value.MovingStatus) { case MovingStatus.StartProcess: mod.Value.MovingStatus = MovingStatus.Processing; 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 = GetCloneWafer(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.IsAligner(scheduler.Key)) { if (ProcessAlignerEFEMRobotTask(scheduler.Key)) return; } else if(ModuleHelper.IsCooling(scheduler.Key)) { if (ProcessCoolingEFEMRobotTask(scheduler.Key)) return; } else if (ModuleHelper.IsLoadPort(scheduler.Key)) { if (ProcessLPEFEMRobotTask(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++) { if (_efemRobotSingleArmOption != 0 && _efemRobotSingleArmOption != i + 1) continue; var wafer = GetCloneWafer(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 = GetCloneWafer(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) { // need return return true; } else 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) { if (_vacModules[ll].MovingStatus != MovingStatus.Idle || IsLoadLockReservedByTM(ll)) 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 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 Tuple GetPMWaferExistence() { int exist = 0; int empty = 0; foreach (var module in _vacModules) { if (ModuleHelper.IsPm(module.Key) && _vacSchedulers[module.Key].IsOnline && module.Value.InUsed) { if (WaferManager.Instance.CheckHasWafer(module.Key, 0)) exist++; else empty++; } } return new Tuple(exist, 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) { if (_efemMovingItems.Exists(item => item.DestinationModule == ll || item.SourceModule == ll)) return true; return false; } private bool IsLoadLockReservedByTM(ModuleName ll) { if (_movingItems.Exists(item => item.DestinationModule == ll || item.SourceModule == ll)) return true; return false; } private Tuple 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 new Tuple(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) { return _LLASlotNumber + pmWaferStatus.Item2 - llaWaferStatus.Item1 - llaWaferStatus.Item2 - GetTMRobotWaferCount() - GetAtmInerWaferCount() - GetEfemRoborWaferCount(); } else if(_LLInOutPath == SequenceLLInOutPath.BInAOut) { return _LLBSlotNumber + pmWaferStatus.Item2 - llbWaferStatus.Item1 - llbWaferStatus.Item2 - GetTMRobotWaferCount() - GetAtmInerWaferCount() - GetEfemRoborWaferCount(); } else if(_LLInOutPath == SequenceLLInOutPath.AInAOut || (_LLInOutPath == SequenceLLInOutPath.DInDOut && _vacSchedulers[ModuleName.LLA].IsOnline && !_vacSchedulers[ModuleName.LLB].IsOnline)) { return llaWaferStatus.Item1 > 0 ? 0 : (_LLASlotNumber) / 2 + pmWaferStatus.Item2 - llaWaferStatus.Item2 - GetTMRobotWaferCount() - GetEfemRoborWaferCount() - GetAtmInerWaferCount(); } else if (_LLInOutPath == SequenceLLInOutPath.BInBOut || (_LLInOutPath == SequenceLLInOutPath.DInDOut && !_vacSchedulers[ModuleName.LLA].IsOnline && _vacSchedulers[ModuleName.LLB].IsOnline)) { return llbWaferStatus.Item1 > 0 ? 0 : (_LLBSlotNumber) / 2 + pmWaferStatus.Item2 - llbWaferStatus.Item2 -GetTMRobotWaferCount() - GetEfemRoborWaferCount() - GetAtmInerWaferCount(); } else if(_LLInOutPath == SequenceLLInOutPath.DInDOut && _vacSchedulers[ModuleName.LLA].IsOnline && _vacSchedulers[ModuleName.LLB].IsOnline) { return llaWaferStatus.Item1 + llbWaferStatus.Item1 > 1 ? 0 : (_LLASlotNumber + _LLBSlotNumber) / 2 + pmWaferStatus.Item2 - llaWaferStatus.Item1 - llaWaferStatus.Item2 - llbWaferStatus.Item1 - llbWaferStatus.Item2 - 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 = GetCloneWafer(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); if (inSlots.Count >= 2) return inSlots; } } } } } } } return inSlots; } 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 (_efemRobot.IsAvailable && _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) { string attr = "AlignAngle"; string angle = string.Empty; int step = wafer.NextSequenceStep; if (!wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(align.Key)) { step = wafer.NextSequenceStep - 1; } if (wafer.ProcessJob.Sequence.Steps[step].StepParameter.ContainsKey(attr)) { angle = (string)wafer.ProcessJob.Sequence.Steps[step].StepParameter[attr]; if (_efemRobot.Align(float.Parse(angle))) { 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; } } } } #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); 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 = GetCloneWafer(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; if (!_vacSchedulers.Keys.Contains(stepModule)) continue; var pm = _vacSchedulers[stepModule] as SchedulerPM; if (pm != null) { if (_vacModules[stepModule].InUsed) { 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; } private List GetPmUsedInRunningPj() { var pmUsed = new List(); foreach (var cj in _lstControlJobs) { if (cj.State != EnumControlJobState.Executing && cj.State != EnumControlJobState.Paused) 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 List GetPmNeeded(ControlJobInfo cj) { List result = new List(); foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName != cj.Name) continue; var seq = pj.Sequence; for (int i = 0; i < seq.Steps.Count; i++) { SequenceStepInfo stepInfo = seq.Steps[i]; foreach (var module in stepInfo.StepModules) { if (ModuleHelper.IsPm(module) && _vacSchedulers.ContainsKey(module)) { var pm = _vacSchedulers[module] as SchedulerPM; if (pm != null && !result.Contains(pm)) { result.Add(pm); } } } } } return result; } 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++; } if(wafer.IsEmpty || wafer.ProcessJob == null) { // 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))) { //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); 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; } } } } } _vacMoveFinishTrig.CLK = _vacWaferTargets.Count == 0; if (_vacMoveFinishTrig.Q) { foreach(var item in _movingItems) { if(_vacModules[item.DestinationModule].MovingStatus == MovingStatus.Moving) { _vacModules[item.DestinationModule].MovingStatus = MovingStatus.Idle; } if(_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))) { //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) { 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; } } } } } _atmMoveFinishTrig.CLK = _atmWaferTargets.Count == 0; if(_atmMoveFinishTrig.Q) { foreach(var item in _efemMovingItems) { if(ModuleHelper.IsLoadLock(item.Module)) { _vacModules[item.Module].MovingStatus = MovingStatus.Idle; } } UpateSequenceStep(_efemMovingItems); _efemMovingItems.Clear(); } } private void PreJobClean(ControlJobInfo cj) { if (cj.IsPreJobCleanDone) return; cj.IsPreJobCleanDone = true; List pms = GetPmNeeded(cj); foreach (var pm in pms) { pm.PreJobClean(); } } 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); PreJobClean(cj); return true; } private void StartNewJob() { ControlJobInfo cjActived = null; List pmOccupied = GetPmUsedInRunningPj(); foreach (var cj in _lstControlJobs) { if (cj.State != EnumControlJobState.Executing) continue; cjActived = cj; foreach (var pjName in cjActived.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 {cjActived.Name}"); continue; } if (pj.State == EnumProcessJobState.Queued) { if (CheckSequencePmReady(pj.Sequence, pmOccupied, out var pmUsed, out string reason)) { ActiveProcessJob(pj); foreach (var moduleName in pmUsed) { if (!pmOccupied.Contains(moduleName)) pmOccupied.Add(moduleName); } } break; } } } } 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 CheckAllWaferReturned(ProcessJobInfo pj, bool checkAllProcessed) { bool allWaferReturn = true; for (int i = 0; i < pj.SlotWafers.Count; ++i) { WaferInfo wafer = GetCloneWafer(pj.SlotWafers[i].Item1, pj.SlotWafers[i].Item2); if (wafer.IsEmpty) continue; foreach(var mod in _vacModules) { if(ModuleHelper.IsPm(mod.Key)) { if (checkAllProcessed && IsWaferNeedGotoModule(wafer, mod.Key)) { mod.Value.InUsed = true; allWaferReturn = false; } } } } return allWaferReturn; } private void UpdateProcessJobStatus() { foreach(var mod in _vacModules) { if (ModuleHelper.IsPm(mod.Key)) { mod.Value.InUsed = false; } } 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 CompleteJobClean(ControlJobInfo cj) { List pms = GetPmNeeded(cj); foreach (var pm in pms) { pm.CompleteJobClean(); } } private void UpdateControlJobStatus() { if (_lstControlJobs.Count == 0) return; bool allControlJobComplete = true; List cjRemoveList = new List(); foreach (var cj in _lstControlJobs) { if (!Singleton.Instance.GetScheduler(ModuleHelper.Converter(cj.Module)).IsAvailable) { allControlJobComplete = false; continue; } if (cj.State == EnumControlJobState.Executing) { bool allPjCompleted = true; 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.Complete && pj.State != EnumProcessJobState.ProcessingComplete) { allPjCompleted = false; break; } if (allPjCompleted) { pj.SlotWafers.ForEach(p => { if (ModuleHelper.IsLoadPort(p.Item1)) { allPjCompleted = allPjCompleted && WaferManager.Instance.CheckHasWafer(ModuleHelper.Converter(p.Item1.ToString()), p.Item2); } }); } } if (allPjCompleted) { cj.SetState(EnumControlJobState.Completed); cj.EndTime = DateTime.Now; if (!(_isCycleMode && _cycledCount + 1 < _cycleSetPoint)) { CompleteJobClean(cj); } _faCallback.JobFinished(cj, GetFirstProcessJob(cj)); _dbCallback.LotFinished(cj); if (!(_isCycleMode && _cycledCount < _cycleSetPoint)) (Singleton.Instance.GetScheduler(ModuleHelper.Converter(cj.Module)) as SchedulerLoadPort).NoteJobComplete(); } } LoadportCassetteState state = (LoadportCassetteState)DATA.Poll($"{cj.Module}.CassetteState"); if (cj.State == EnumControlJobState.Completed && state != LoadportCassetteState.Normal) { cjRemoveList.Add(cj); } allControlJobComplete = allControlJobComplete && cj.State == EnumControlJobState.Completed; } if (_isCycleMode && _cycledCount < (_isCycleMode ? _cycleSetPoint : 0)) { int countPerCycle = 0; int countProcessed = 0; foreach (var pj in _lstProcessJobs) { foreach (var pjSlotWafer in pj.SlotWafers) { countPerCycle++; WaferInfo wafer = GetCloneWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); if (!wafer.IsEmpty && !IsWaferNeedGotoModule(wafer, ModuleName.PMA) && !IsWaferNeedGotoModule(wafer, ModuleName.PMB) && !IsWaferNeedGotoModule(wafer, ModuleName.PMC) && !IsWaferNeedGotoModule(wafer, ModuleName.PMD)) countProcessed++; } } _cycledWafer = _cycledCount * countPerCycle + countProcessed; if (allControlJobComplete) { _cycledCount++; if (_cycledCount < _cycleSetPoint) { foreach (var cj in _lstControlJobs) { cj.SetState(EnumControlJobState.Executing); 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) { bool allPjCompleted = true; 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 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 } }