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 MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Robot; namespace Venus_RT.Modules { class VCETask : ModuleTask { public VCETask(ModuleName mod) : base(mod) { } } class VenusPMTask : ModuleTask { public enum RecipeJobType { PreClean, Process, PostClean, WtwClean, } public override int TimeToReady { get { switch (Status) { case ModuleStatus.Idle: case ModuleStatus.IdleClean: case ModuleStatus.PreJobClean: case ModuleStatus.PostJobClean: case ModuleStatus.WTWClean: case ModuleStatus.Processing: { return Scheduler.IsAvailable ? 0 : (Scheduler.TimeToReady + 500) / 1000; } } return int.MaxValue; } } private WaferTask _wafer; private SchedulerPM _pmScheduler => Scheduler as SchedulerPM; Queue>>> _pendingWaferTasks = new Queue>>> (); KeyValuePair>> _runingWaferTask = new KeyValuePair>> (Guid.Empty, new Queue>()); public VenusPMTask(ModuleName mod) : base(mod) { } public override RState Run() { if(_runingWaferTask.Value.Count == 0) { if(_pendingWaferTasks.Count > 0) { _runingWaferTask = _pendingWaferTasks.Dequeue(); } else { if(Scheduler.IsIdle && Status == ModuleStatus.Idle) { if (WaferManager.Instance.CheckNoWafer(Module, 0) && Scheduler.IsOnline) { if (_pmScheduler.RunIdleCleanTask()) // Check Idle Clean { Status = ModuleStatus.StartIdleClean; } } } } } else { if(Scheduler.IsIdle && Status == ModuleStatus.Idle) { switch(_runingWaferTask.Value.First().Key) { case RecipeJobType.PreClean: { if(WaferManager.Instance.CheckNoWafer(Module,0)) { if (_pmScheduler.RunJobCleanTask(_runingWaferTask.Value.First().Value)) { Status = ModuleStatus.StartPreJobClean; } else { LOG.Write(eEvent.WARN_ROUTER, Module, $"Run Prejob clean recipe {_runingWaferTask.Value.First().Value} failed"); TryDequeueRuningTask(Status); Status = ModuleStatus.Idle; } } } break; case RecipeJobType.Process: { if (WaferManager.Instance.CheckHasWafer(Module, 0)) { if(_wafer.waferId == _runingWaferTask.Key) { Scheduler.EventWaferArrived?.Invoke(this, new WaferMoveArgs(ModuleName.TMRobot, 0, Module, 0)); Status = ModuleStatus.StartProcess; } else { LOG.Write(eEvent.WARN_ROUTER, Module, $"wafer id dismatch while launch {_wafer.sourceMod}.{_wafer.sourceSlot + 1} process recipe"); } } } break; case RecipeJobType.PostClean: { if (WaferManager.Instance.CheckNoWafer(Module, 0)) { if (_pmScheduler.RunJobCleanTask(_runingWaferTask.Value.First().Value)) { Status = ModuleStatus.StartPostJobClean; } else { LOG.Write(eEvent.WARN_ROUTER, Module, $"Run Postjob clean recipe {_runingWaferTask.Value.First().Value} failed"); TryDequeueRuningTask(Status); Status = ModuleStatus.Idle; } } } break; case RecipeJobType.WtwClean: { if (WaferManager.Instance.CheckNoWafer(Module, 0)) { if (_pmScheduler.RunJobCleanTask(_runingWaferTask.Value.First().Value)) { Status = ModuleStatus.StartWTWClean; } else { LOG.Write(eEvent.WARN_ROUTER, Module, $"Run WTW clean recipe {_runingWaferTask.Value.First().Value} failed"); TryDequeueRuningTask(Status); Status = ModuleStatus.Idle; } } } break; } } } if(_runingWaferTask.Value.Count > 0 || Status != ModuleStatus.Idle) { if (Scheduler.IsIdle) { switch (Status) { case ModuleStatus.Processing: { var wafer = WaferManager.Instance.GetWafer(Module, 0); if (Scheduler.IsOnline) { if (!wafer.IsEmpty && wafer.ProcessState == EnumWaferProcessStatus.Completed) { _wafer.Return(); TryDequeueRuningTask(Status); Status = ModuleStatus.Idle; } } else // handle offline exception situation { TryDequeueRuningTask(Status); Status = ModuleStatus.Idle; } } break; case ModuleStatus.PreJobClean: case ModuleStatus.PostJobClean: case ModuleStatus.WTWClean: { TryDequeueRuningTask(Status); Status = ModuleStatus.Idle; } break; case ModuleStatus.IdleClean: { Status = ModuleStatus.Idle; } break; case ModuleStatus.StartProcess: { if (RouteManager.IsATMMode) { Status = ModuleStatus.Processing; } } break; } } else { switch (Status) { case ModuleStatus.StartProcess: Status = ModuleStatus.Processing; break; case ModuleStatus.StartIdleClean: Status = ModuleStatus.IdleClean; break; case ModuleStatus.StartPreJobClean: Status = ModuleStatus.PreJobClean; break; case ModuleStatus.StartPostJobClean: Status = ModuleStatus.PostJobClean; break; case ModuleStatus.StartWTWClean: Status = ModuleStatus.WTWClean; break; } } } return RState.Running; } void TryDequeueRuningTask( ModuleStatus status) { bool bMatch = false; if (_runingWaferTask.Value.Count > 0) { switch(status) { case ModuleStatus.PreJobClean: if(_runingWaferTask.Value.First().Key == RecipeJobType.PreClean) { _runingWaferTask.Value.Dequeue(); bMatch = true; } break; case ModuleStatus.Processing: if(_runingWaferTask.Value.First().Key == RecipeJobType.Process) { _runingWaferTask.Value.Dequeue(); bMatch = true; } break; case ModuleStatus.PostJobClean: if(_runingWaferTask.Value.First().Key == RecipeJobType.PostClean) { _runingWaferTask.Value.Dequeue(); bMatch = true; } break; case ModuleStatus.WTWClean: if(_runingWaferTask.Value.First().Key == RecipeJobType.WtwClean) { _runingWaferTask.Value.Dequeue(); bMatch = true; } break; case ModuleStatus.WaitPreJobClean: case ModuleStatus.WaitProcess: case ModuleStatus.WaitPostJobClean: case ModuleStatus.WaitWTWClean: case ModuleStatus.Idle: _runingWaferTask.Value.Dequeue(); break; } } if(!bMatch) { string runingTask = _runingWaferTask.Value.Count > 0 ? _runingWaferTask.Value.First().ToString() : "Empty"; LOG.Write(eEvent.WARN_ROUTER, Module, $"TryDequeueRuningTask() failed, PM Status: {status}, runing task : {runingTask}"); } if(_runingWaferTask.Value.Count == 0 && _runingWaferTask.Key != Guid.Empty) { _runingWaferTask = new KeyValuePair>>(Guid.Empty, new Queue>()); } } public override void WaferArrived(WaferTask wafer, int slot) { _wafer = wafer; } public override void WaferLeaved(WaferTask wafer, int slot) { _wafer = null; } public void SubscribeWaferTask(WaferTask waferTask) { var waferRecipes = new Queue>(); if (!string.IsNullOrWhiteSpace(waferTask.processRecipe)) { waferRecipes.Enqueue(new KeyValuePair(RecipeJobType.Process, waferTask.processRecipe)); } if (!string.IsNullOrWhiteSpace(waferTask.wtwCleanRecipe)) { waferRecipes.Enqueue(new KeyValuePair(RecipeJobType.WtwClean, waferTask.wtwCleanRecipe)); } if(_runingWaferTask.Key == waferTask.waferId || _pendingWaferTasks.ToList().Exists(kv => kv.Key == waferTask.waferId)) { LOG.Write(eEvent.WARN_ROUTER, Module, $"wafer {waferTask.sourceMod}.{waferTask.sourceSlot + 1} already subscribe job task."); } else if (_runingWaferTask.Key == Guid.Empty || _runingWaferTask.Value.Count == 0) { _runingWaferTask = new KeyValuePair>>(waferTask.waferId, waferRecipes); } else { _pendingWaferTasks.Enqueue(new KeyValuePair>>(waferTask.waferId, waferRecipes)); } } public void SubscribeLotCleanTask(ControlJobInfo cj, RecipeJobType cleanType) { var lotCleanRecipes = new Queue>(); if(cleanType == RecipeJobType.PreClean && !string.IsNullOrWhiteSpace(cj.PreJobClean)) { lotCleanRecipes.Enqueue(new KeyValuePair(cleanType, cj.PreJobClean)); } else if(cleanType == RecipeJobType.PostClean && !string.IsNullOrWhiteSpace (cj.PostJobClean)) { lotCleanRecipes.Enqueue(new KeyValuePair(cleanType, cj.PostJobClean)); } else { return; } if ((_runingWaferTask.Key == cj.InnerId && _runingWaferTask.Value.ToList().Exists(pv => pv.Key == cleanType)) || _pendingWaferTasks.ToList().Exists(task => task.Key == cj.InnerId && task.Value.ToList().Exists(pv => pv.Key == cleanType))) { LOG.Write(eEvent.WARN_ROUTER, Module, $"Control job {cj.Name} already subscribe {cleanType} task."); } else if (_runingWaferTask.Key == Guid.Empty || _runingWaferTask.Value.Count == 0) { _runingWaferTask = new KeyValuePair>>(cj.InnerId, lotCleanRecipes); } else { _pendingWaferTasks.Enqueue(new KeyValuePair>>(cj.InnerId, lotCleanRecipes)); } } public bool ReadyMoveIn(WaferTask wafer) { return _runingWaferTask.Key == wafer.waferId && _runingWaferTask.Value.Count > 0 && _runingWaferTask.Value.First().Key == RecipeJobType.Process; } public bool IsAllJobCompleted() { return Status == ModuleStatus.Idle && (_runingWaferTask.Key == Guid.Empty || _runingWaferTask.Value.Count == 0) && _pendingWaferTasks.Count == 0; } public override void ResetTask() { base.ResetTask(); _wafer = null; _pendingWaferTasks.Clear(); _runingWaferTask = new KeyValuePair>>(Guid.Empty, new Queue>()); } } class PreAlignTask : ModuleTask { private WaferTask _wafer; public PreAlignTask(ModuleName aligner) : base(aligner) { } public override RState Run() { var tmRobot = Singleton.Instance.GetScheduler(ModuleName.TMRobot) as SchedulerSETMRobot; if (tmRobot == null || !tmRobot.IsAvailable) return RState.Running; switch (Status) { case ModuleStatus.WaitAlign: { tmRobot.Align(0); Status = ModuleStatus.Aligning; } break; case ModuleStatus.Aligning: { _wafer.IsAligned = true; Status = ModuleStatus.Idle; } break; } return RState.Running; } public override void WaferArrived(WaferTask wafer, int slot) { _wafer = wafer; Status = ModuleStatus.WaitAlign; } public override void WaferLeaved(WaferTask wafer, int slot) { _wafer = null; } } class VenusTMRobotTask : ModuleTask { public VenusTMRobotTask(ModuleName mod) : base(mod) { } public override RState Run() { return base.Run(); } } class VenusDispatcher : ICycle { private RState _tmRobotStatus { get { return (_dictModuleTask[ModuleName.TMRobot].Scheduler as SchedulerSETMRobot).RobotStatus; } } private List _lstControlJobs = new List(); private List _lstProcessJobs = new List(); private List _lstWaferTasks = new List(); private Dictionary _dictModuleTask = new Dictionary(); private Queue _qeReturnWafers = new Queue(); private Queue> _tmSchdActions = new Queue>(); private List _curTmAction = new List(); private bool _isCycleMode; private int _cycleSetPoint = 0; private int _cycledCount = 0; private int _cycledWafer = 0; private float _throughput = 0.0f; private int _tmRobotSingleArmOption = 0; private Dictionary _lpCycleWafer = new Dictionary(); private Dictionary _lpCycleCount = new Dictionary(); private Dictionary _lpCycleSP = new Dictionary(); private Dictionary _lpThroughput = new Dictionary(); private Stopwatch _cycleWatch = new Stopwatch(); private SchedulerFACallback _faCallback; private SchedulerDBCallback _dbCallback; public SequenceLLInOutPath LLInOutPath => SequenceLLInOutPath.DInDOut; public bool HasJobRunning => _lstControlJobs.Count > 0; private RState _cycleState = RState.Init; public RState CycleState => _cycleState; private Dictionary _loadportControlJobDic = new Dictionary(); public List InUseRecipes = new List(); public VenusDispatcher() { _faCallback = new SchedulerFACallback(); _dbCallback = new SchedulerDBCallback(); InitModules(); 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()); DATA.Subscribe("Scheduler.InUsingRecipe", () => InUseRecipes); foreach (var lp in new List { ModuleName.LP1, ModuleName.LP2, ModuleName.LP3 }) { _loadportControlJobDic[lp.ToString()] = null; DATA.Subscribe($"{lp}.CurrentControlJob", () => _loadportControlJobDic[lp.ToString()], SubscriptionAttribute.FLAG.IgnoreSaveDB); DATA.Subscribe($"{lp}.CycledCount", () => _lpCycleCount[lp], SubscriptionAttribute.FLAG.IgnoreSaveDB); DATA.Subscribe($"{lp}.CycledWafer", () => _lpCycleWafer[lp], SubscriptionAttribute.FLAG.IgnoreSaveDB); DATA.Subscribe($"{lp}.CycleSetPoint", () => _lpCycleSP[lp], SubscriptionAttribute.FLAG.IgnoreSaveDB); DATA.Subscribe($"{lp}.Throughput", () => _lpThroughput[lp], SubscriptionAttribute.FLAG.IgnoreSaveDB); } } #region public interface /// /// 获取lp当前的ControlJob /// /// /// public ControlJobInfo GetLoadPortCurrentControlJob(ModuleName lp) { if (ModuleHelper.IsLoadPort(lp)) { return _loadportControlJobDic.ContainsKey(lp.ToString()) ? _loadportControlJobDic[lp.ToString()] : null; } else { return null; } } public RState Start(params object[] objs) { if (WaferManager.Instance.HasDuplicatedWafer) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, "System has dummy wafers, please verify all the wafer position, and delete the invalid wafer"); return RState.Failed; } _tmRobotSingleArmOption = SC.GetValue("TM.SingleArmOption"); _isCycleMode = SC.GetValue("System.IsCycleMode"); _cycleSetPoint = _isCycleMode ? SC.GetValue("System.CycleCount") : 0; _cycledWafer = 0; _cycledCount = 0; _throughput = 0; Clear(); _cycleWatch.Stop(); _lpCycleWafer.Clear(); _lpCycleCount.Clear(); return RState.Running; } 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"]; if (string.IsNullOrEmpty(jobId)) { jobId = "CJ_Local_" + module; } //bool autoStart = (bool)param["AutoStart"]; 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; } 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.Quequed; cj.PreJobClean = preCleanRecipe; cj.PostJobClean = postCleanRecipe; cj.SequenceNameList = slotSequence; cj.CycleNumber = SC.GetValue("System.CycleCount"); // only for temperary debug 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 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; } //--- 2024-03-21 增加了waferinfo 相应的lotId信息 start--- WaferInfo waferInfo = WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i); cj.LotWafers.Add(waferInfo); waferInfo.SequenceName = slotSequence[i]; waferInfo.LotId = lotId; //--- 2024-03-21 增加了waferinfo 相应的lotId信息 end--- 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, 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.Completed) return false; } InUseRecipes.Clear(); 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); if (_loadportControlJobDic.ContainsKey(cj.Module)) { _loadportControlJobDic[cj.Module] = null; } 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.Queued); //PreJobClean(cj); 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 RState Monitor() { prelude(); RunWaferTask(); RunModuleTasks(); RoutingWafers(); epilogue(); return _cycleState; } public void Abort() { } public void Clear() { foreach (var module in _dictModuleTask) { module.Value.Status = ModuleStatus.Idle; module.Value.Scheduler.ResetTask(); module.Value.ResetTask(); } List keys = _loadportControlJobDic.Keys.ToList(); foreach (var key in keys) { _loadportControlJobDic[key] = null; } _lstWaferTasks.Clear(); _tmSchdActions.Clear(); _curTmAction.Clear(); _lstControlJobs.Clear(); _lstProcessJobs.Clear(); InUseRecipes.Clear(); _cycleState = RState.End; } #endregion #region manual return wafer // manual return one wafer while system is auto running public bool ManualReturnWafer(object[] objs) { ModuleName SourceModule = (ModuleName)objs[0]; int SourceSlot = (int)objs[1]; if (!_dictModuleTask.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 (!_dictModuleTask[SourceModule].Scheduler.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"); _qeReturnWafers.Enqueue(wafer); return true; } public RState CheckManualReturnWafer() { var pmWaferCount = _dictModuleTask.Where(mod => ModuleHelper.IsPm(mod.Key) && WaferManager.Instance.CheckHasWafer(mod.Key, 0)).Count(); var tmRobotWaferCount = (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) ? 1 : 0) + (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1) ? 1 : 0); if (!_dictModuleTask[ModuleName.TMRobot].Scheduler.IsIdle && (pmWaferCount > 0 || tmRobotWaferCount > 0)) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.TMRobot, $"The TM Robot is not ready for return wafer."); return RState.Failed; } foreach (var mod in _dictModuleTask) { if (ModuleHelper.IsLoadPort(mod.Key)) continue; if (!ModuleHelper.IsAligner(mod.Key) && !mod.Value.Scheduler.IsIdle) { LOG.Write(eEvent.ERR_ROUTER, mod.Key, $"{mod.Key} is not ready for return wafer."); return RState.Failed; } int nSlotNumber = (ModuleHelper.IsTMRobot(mod.Key) ? 2 : 1); for (int slot = 0; slot < nSlotNumber; slot++) { var wafer = WaferManager.Instance.GetWafer(mod.Key, slot); if (wafer.IsEmpty) continue; var destLP = (ModuleName)wafer.OriginStation; if (!_dictModuleTask[destLP].Scheduler.IsAvailable) { LOG.Write(eEvent.ERR_ROUTER, destLP, $"The destination Loadport {destLP} is not ready for return wafer."); return RState.Failed; } if (!ModuleHelper.IsLoadPort(destLP)) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"The wafer {wafer.WaferOrigin} cannot be return, please manually drag it."); return RState.Failed; } } } Clear(); return RState.Running; } public RState ReturnAllWafers() { int systemWaferCount = 0; foreach (var mod in _dictModuleTask) { if (ModuleHelper.IsLoadPort(mod.Key)) continue; int nSlotNumber = ModuleHelper.IsTMRobot(mod.Key) ? 2 : 1; for (int slot = 0; slot < nSlotNumber; slot++) { var wafer = WaferManager.Instance.GetWafer(mod.Key, slot); if (!wafer.IsEmpty) systemWaferCount++; } } if (systemWaferCount == 0) return RState.End; ReturnInternalWafers(); return RState.Running; } #endregion #region internal implementation private void InitModules() { foreach (var module in ModuleHelper.InstalledModules) { if (ModuleHelper.IsInstalled(module)) { if (ModuleHelper.IsPm(module)) { _dictModuleTask[module] = new VenusPMTask(module); } else if (ModuleHelper.IsTMRobot(module)) { _dictModuleTask[module] = new VenusTMRobotTask(module); } else if (ModuleHelper.IsAligner(module)) { _dictModuleTask[module] = new PreAlignTask(module); } else if (ModuleHelper.IsLoadPort(module)) { _dictModuleTask[module] = new LoadPortTask(module); } } } } private void prelude() { bool available = true; foreach (var mod in _dictModuleTask) { available = mod.Value.Scheduler.IsAvailable; // force scheduler update } } private void epilogue() { UpdateProcessJobStatus(); UpdateControlJobStatus(); CreateNewWaferTask(); _lstWaferTasks.RemoveAll(item => ModuleHelper.IsLoadPort(item.destMod) && ModuleHelper.IsLoadPort(item.currentMod) && item.pressureStatus == RState.End); } private void RunWaferTask() { foreach (var task in _lstWaferTasks) { task.Run(); } } private void RunModuleTasks() { foreach (var task in _dictModuleTask) { task.Value.Run(); } } private void RoutingWafers() { if (_tmSchdActions.Count > 0 || _curTmAction.Count > 0) { RunSchdTMActions(); return; } if (_tmRobotStatus != RState.End) return; if (_tmRobotSingleArmOption != 0) { RoutingWaferWithSingleArm(); return; } var waitInWafers = _lstWaferTasks.Where(wt => ModuleHelper.IsLoadPort(wt.currentMod) && ModuleHelper.IsPm(wt.destMod)).OrderBy(wt => wt.currentSlot).ToList(); var readyReturnWafers = _lstWaferTasks.Where(wt => ModuleHelper.IsPm(wt.currentMod) && ModuleHelper.IsLoadPort(wt.destMod)).ToList(); var emptyAndReadyIn20sPMs = _dictModuleTask.Where(pm => ModuleHelper.IsPm(pm.Key) && !_lstWaferTasks.Exists(wt => wt.currentMod == pm.Key) && pm.Value.TimeToReady < 20).OrderBy(pm => pm.Value.TimeToReady).Select(pm => pm.Key).ToList(); var robotWafers = _lstWaferTasks.Where(wt => ModuleHelper.IsTMRobot(wt.currentMod)).ToList(); var aligner = _dictModuleTask.Where(mod => ModuleHelper.IsAligner(mod.Key)).ToList(); if(robotWafers.Count == 0) { if(readyReturnWafers.Count > 0) { if(readyReturnWafers.Count == 1) { // return one wafer _tmSchdActions.Enqueue(new List { new MoveItem(readyReturnWafers.First().currentMod, 0, ModuleName.TMRobot, (int)Hand.Blade1, Hand.Blade1) }); _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, (int)Hand.Blade1, readyReturnWafers.First().destMod, readyReturnWafers.First().destSlot, Hand.Blade1) }); } else { // return two wafer _tmSchdActions.Enqueue(new List { new MoveItem(readyReturnWafers[0].currentMod, 0, ModuleName.TMRobot, (int)Hand.Blade1, Hand.Blade1) }); _tmSchdActions.Enqueue(new List { new MoveItem(readyReturnWafers[1].currentMod, 0, ModuleName.TMRobot, (int)Hand.Blade2, Hand.Blade2) }); var doublePlaceActions = new List { new MoveItem(ModuleName.TMRobot, (int)Hand.Blade1, readyReturnWafers[0].destMod, readyReturnWafers[0].destSlot, Hand.Blade1), new MoveItem(ModuleName.TMRobot, (int)Hand.Blade2, readyReturnWafers[1].destMod, readyReturnWafers[1].destSlot, Hand.Blade2) }; _tmSchdActions.Enqueue(doublePlaceActions); } } else if (_lstWaferTasks.Exists(wt => ModuleHelper.IsAligner(wt.currentMod))) { var alignerWafer = _lstWaferTasks.Where(wt => ModuleHelper.IsAligner(wt.currentMod)).First(); _tmSchdActions.Enqueue(new List { new MoveItem(alignerWafer.currentMod, alignerWafer.currentSlot, ModuleName.TMRobot, (int)Hand.Blade1, Hand.Blade1) }); if (emptyAndReadyIn20sPMs.Contains(alignerWafer.destMod)) { var pmTask = _dictModuleTask[alignerWafer.destMod] as VenusPMTask; if(pmTask.ReadyMoveIn(alignerWafer)) { // move aligner wafer to PM _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, (int)Hand.Blade1, alignerWafer.destMod, alignerWafer.destSlot, Hand.Blade1) }); } } } else { if(emptyAndReadyIn20sPMs.Count >= 2 && waitInWafers.Count >= 2 && waitInWafers[0].currentMod == waitInWafers[1].currentMod) { var doublePickActions = new List() { new MoveItem(waitInWafers[0].currentMod, waitInWafers[0].currentSlot, ModuleName.TMRobot, (int)Hand.Blade1, Hand.Blade1), new MoveItem(waitInWafers[1].currentMod, waitInWafers[1].currentSlot, ModuleName.TMRobot, (int)Hand.Blade2, Hand.Blade2) }; _tmSchdActions.Enqueue(doublePickActions); if(aligner.Count > 0) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, (int)Hand.Blade1, aligner.First().Key, 0, Hand.Blade1) }); } } else if(emptyAndReadyIn20sPMs.Count >= 1 && waitInWafers.Count >= 1) { _tmSchdActions.Enqueue(new List { new MoveItem(waitInWafers[0].currentMod, waitInWafers[0].currentSlot, ModuleName.TMRobot, (int)Hand.Blade1, Hand.Blade1) }); if (aligner.Count > 0) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, (int)Hand.Blade1, aligner.First().Key, 0, Hand.Blade1) }); } } } } else if(robotWafers.Count == 1) { var freeHand = robotWafers.First().currentSlot == 0 ? Hand.Blade2 : Hand.Blade1; if (readyReturnWafers.Count >= 1) { // return one wafer _tmSchdActions.Enqueue(new List { new MoveItem(readyReturnWafers.First().currentMod, 0, ModuleName.TMRobot, (int)freeHand, freeHand) }); _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, (int)freeHand, readyReturnWafers.First().destMod, readyReturnWafers.First().destSlot, freeHand) }); } else { if (_lstWaferTasks.Exists(wt => ModuleHelper.IsAligner(wt.currentMod))) { var alignerWafer = _lstWaferTasks.Where(wt => ModuleHelper.IsAligner(wt.currentMod)).First(); _tmSchdActions.Enqueue(new List { new MoveItem(alignerWafer.currentMod, alignerWafer.currentSlot, ModuleName.TMRobot, (int)freeHand, freeHand) }); if(!robotWafers.First().IsAligned) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, robotWafers.First().currentSlot, aligner.First().Key, 0, (Hand)robotWafers.First().currentSlot) }); } if (emptyAndReadyIn20sPMs.Contains(alignerWafer.destMod)) { var pmTask = _dictModuleTask[alignerWafer.destMod] as VenusPMTask; if (pmTask.ReadyMoveIn(alignerWafer)) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, (int)freeHand, alignerWafer.destMod, alignerWafer.destSlot, freeHand) }); } } } else { if (ModuleHelper.IsPm(robotWafers.First().destMod)) { if (!robotWafers.First().IsAligned) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, robotWafers.First().currentSlot, aligner.First().Key, 0, (Hand)robotWafers.First().currentSlot) }); } else if (emptyAndReadyIn20sPMs.Contains(robotWafers.First().destMod)) { var pmTask = _dictModuleTask[robotWafers.First().destMod] as VenusPMTask; if (pmTask.ReadyMoveIn(robotWafers.First())) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, robotWafers.First().currentSlot, robotWafers.First().destMod, robotWafers.First().destSlot, (Hand)(robotWafers.First().currentSlot)) }); } } } else if (ModuleHelper.IsLoadPort(robotWafers.First().destMod) && _dictModuleTask[robotWafers.First().destMod].Scheduler.IsAvailable) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, robotWafers.First().currentSlot, robotWafers.First().destMod, robotWafers.First().destSlot, (Hand)(robotWafers.First().currentSlot)) }); } } } } else // had better not go here { foreach(var wafer in robotWafers) { if (ModuleHelper.IsPm(wafer.destMod)) { if (!wafer.IsAligned) { if (!_lstWaferTasks.Exists(wt => ModuleHelper.IsAligner(wt.currentMod))) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, wafer.currentSlot, aligner.First().Key, 0, (Hand)wafer.currentSlot) }); } } else if (emptyAndReadyIn20sPMs.Contains(wafer.destMod)) { var pmTask = _dictModuleTask[wafer.destMod] as VenusPMTask; if (pmTask.ReadyMoveIn(wafer)) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, wafer.currentSlot, wafer.destMod, wafer.destSlot, (Hand)(wafer.currentSlot)) }); } } } else if (ModuleHelper.IsLoadPort(wafer.destMod) && _dictModuleTask[wafer.destMod].Scheduler.IsAvailable) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, wafer.currentSlot, wafer.destMod, wafer.destSlot, (Hand)(wafer.currentSlot)) }); } } } } private void RoutingWaferWithSingleArm() { var waitInWafers = _lstWaferTasks.Where(wt => ModuleHelper.IsLoadPort(wt.currentMod) && ModuleHelper.IsPm(wt.destMod)).OrderBy(wt => wt.currentSlot).ToList(); var readyReturnWafers = _lstWaferTasks.Where(wt => ModuleHelper.IsPm(wt.currentMod) && ModuleHelper.IsLoadPort(wt.destMod)).ToList(); var emptyAndReadyIn20sPMs = _dictModuleTask.Where(pm => ModuleHelper.IsPm(pm.Key) && !_lstWaferTasks.Exists(wt => wt.currentMod == pm.Key) && pm.Value.TimeToReady < 20).OrderBy(pm => pm.Value.TimeToReady).Select(pm => pm.Key).ToList(); var aligner = _dictModuleTask.Where(mod => ModuleHelper.IsAligner(mod.Key)).ToList(); var validHand = _tmRobotSingleArmOption == 1 ? Hand.Blade1 : Hand.Blade2; var robotWafers = _lstWaferTasks.Where(wt => wt.currentMod == ModuleName.TMRobot && wt.currentSlot == (int)validHand).ToList(); if (robotWafers.Count == 0) { if (readyReturnWafers.Count > 0) { // return one wafer _tmSchdActions.Enqueue(new List { new MoveItem(readyReturnWafers.First().currentMod, 0, ModuleName.TMRobot, (int)validHand, validHand) }); _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, (int)validHand, readyReturnWafers.First().destMod, readyReturnWafers.First().destSlot, validHand) }); } else { if(_lstWaferTasks.Exists(wt => ModuleHelper.IsAligner(wt.currentMod))) { var alignerWafer = _lstWaferTasks.Find(wt => ModuleHelper.IsAligner(wt.currentMod)); if(emptyAndReadyIn20sPMs.Contains(alignerWafer.destMod)) { _tmSchdActions.Enqueue(new List { new MoveItem(alignerWafer.currentMod, 0, ModuleName.TMRobot, (int)validHand, validHand) }); // move aligner wafer to PM var pmTask = _dictModuleTask[alignerWafer.destMod] as VenusPMTask; if (pmTask.ReadyMoveIn(alignerWafer)) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, (int)validHand, alignerWafer.destMod, alignerWafer.destSlot, validHand) }); } } } else { foreach(var wafer in waitInWafers) { if(emptyAndReadyIn20sPMs.Contains(wafer.destMod)) { // move one wafer from LP to aligner _tmSchdActions.Enqueue(new List { new MoveItem(wafer.currentMod, wafer.currentSlot, ModuleName.TMRobot, (int)validHand, validHand) }); _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, (int)validHand, aligner.First().Key, 0, validHand) }); } } } } } else { var wafer = robotWafers[0]; if (ModuleHelper.IsPm(wafer.destMod)) { if (!wafer.IsAligned) { if (!_lstWaferTasks.Exists(wt => ModuleHelper.IsAligner(wt.currentMod))) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, wafer.currentSlot, aligner.First().Key, 0, (Hand)wafer.currentSlot) }); } } else if (emptyAndReadyIn20sPMs.Contains(wafer.destMod)) { var pmTask = _dictModuleTask[wafer.destMod] as VenusPMTask; if (pmTask.ReadyMoveIn(wafer)) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, wafer.currentSlot, wafer.destMod, wafer.destSlot, (Hand)(wafer.currentSlot)) }); } } } else if (ModuleHelper.IsLoadPort(wafer.destMod) && _dictModuleTask[wafer.destMod].Scheduler.IsAvailable) { _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, wafer.currentSlot, wafer.destMod, wafer.destSlot, (Hand)(wafer.currentSlot)) }); } } } private void RunSchdTMActions() { if (_dictModuleTask[ModuleName.TMRobot].IsIdle) { if (_tmSchdActions.Count > 0) { if (_curTmAction.Count == 0 || IsMovingActionsDone(_curTmAction)) { var nextActions = _tmSchdActions.First(); if (nextActions.Exists(action => !_lstWaferTasks.Exists(wafer => (wafer.movingStatus == RState.End || wafer.movingStatus == RState.Init) && wafer.currentMod == action.SourceModule && wafer.currentSlot == action.SourceSlot))) return; if (ModuleHelper.IsPm(nextActions.First().Module) && !_dictModuleTask[nextActions.First().Module].IsIdle) /// wait PMTask status update to idle return; _curTmAction = _tmSchdActions.Dequeue(); foreach (var action in _curTmAction) { var waferTask = _lstWaferTasks.Find(wafer => (wafer.movingStatus == RState.End || wafer.movingStatus == RState.Init) && wafer.currentMod == action.SourceModule && wafer.currentSlot == action.SourceSlot); waferTask.MoveTo(action.DestinationModule, action.DestinationSlot); } (_dictModuleTask[ModuleName.TMRobot].Scheduler as SchedulerSETMRobot).SendMoveItems(_curTmAction.ToArray()); } } else if (_curTmAction.Count >= 0 && IsMovingActionsDone(_curTmAction)) // all scheduled actions done { _curTmAction.Clear(); } } } private bool isReturnActionsDone(List items) { foreach (var item in items) { if (WaferManager.Instance.CheckHasWafer(item.SourceModule, item.SourceSlot) || WaferManager.Instance.CheckNoWafer(item.DestinationModule, item.DestinationSlot)) return false; } return true; } private bool IsMovingActionsDone(List actions) { bool CheckWaferExistence(ModuleName mod, int slot) { return ModuleHelper.IsLoadPort(mod) ? WaferManager.Instance.CheckHasWafer(mod, slot) : _lstWaferTasks.Exists(wt => wt.currentMod == mod && wt.currentSlot == slot); } if (actions.Count == 1) { if (CheckWaferExistence(actions.First().SourceModule, actions.First().SourceSlot) || !CheckWaferExistence(actions.First().DestinationModule, actions.First().DestinationSlot)) return false; } else { // initialize all the wafer existance before move var slotWafers = new Dictionary, bool>(); foreach (var ac in actions) { var scrSlot = new KeyValuePair(ac.SourceModule, ac.SourceSlot); var destSlot = new KeyValuePair(ac.DestinationModule, ac.DestinationSlot); if (!slotWafers.ContainsKey(scrSlot)) { slotWafers[scrSlot] = true; } if (!slotWafers.ContainsKey(destSlot)) { slotWafers[destSlot] = false; } } // simulate moved result foreach (var ac in actions) { var scrSlot = new KeyValuePair(ac.SourceModule, ac.SourceSlot); var destSlot = new KeyValuePair(ac.DestinationModule, ac.DestinationSlot); if (!slotWafers[scrSlot]) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"{slotWafers[scrSlot]} do not has a wafer"); } else { slotWafers[scrSlot] = false; } if (slotWafers[destSlot]) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"{slotWafers[destSlot]} has a wafer"); } else { slotWafers[destSlot] = true; } } foreach (var slot in slotWafers) { if (slot.Value != CheckWaferExistence(slot.Key.Key, slot.Key.Value)) return false; } } return true; } private void WaferArrived(WaferTask wafer, MoveItem item) { _dictModuleTask[item.DestinationModule].WaferArrived(wafer, item.DestinationSlot); //--2024-03-21 增加了PortJobWaferEnd 上报事件 start-- if (ModuleHelper.IsLoadPort(item.DestinationModule)) { ControlJobInfo currentControlJob = GetLoadPortCurrentControlJob(item.DestinationModule); if (currentControlJob != null) { _faCallback.JobWaferEnd(currentControlJob, item.SourceSlot); } } //--2024-03-21 增加了PortJobWaferEnd 上报事件 end-- } private void WaferLeaved(WaferTask wafer, MoveItem item) { _dictModuleTask[item.SourceModule].WaferLeaved(wafer, item.DestinationSlot); //--2024-03-21 增加了PortJobWaferStart 上报事件 start-- if (ModuleHelper.IsLoadPort(item.SourceModule)) { (_dictModuleTask[wafer.destMod] as VenusPMTask).SubscribeWaferTask(wafer); ControlJobInfo currentControlJob = GetLoadPortCurrentControlJob(item.SourceModule); if (currentControlJob != null) { _faCallback.JobWaferStart(currentControlJob, item.SourceSlot); } } //--2024-03-21 增加了PortJobWaferStart 上报事件 end-- } private bool CheckAllWaferReturned(ProcessJobInfo pj, bool checkAllProcessed) { bool allWaferReturn = true; foreach (var slot in pj.SlotWafers) { var wafer = WaferManager.Instance.GetWafer(slot.Item1, slot.Item2); if (wafer.IsEmpty || (wafer.ProcessState != EnumWaferProcessStatus.Completed && checkAllProcessed)) { allWaferReturn = false; break; } } return allWaferReturn; } private void ReturnInternalWafers() { if (_tmSchdActions.Count > 0 || _curTmAction.Count > 0) { RunSchdTMReturnActions(); return; } if (_tmRobotStatus != RState.End) return; for (int i = 0; i < 2; i++) { if(WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, i)) { var wafer = WaferManager.Instance.GetWafer(ModuleName.TMRobot, i); _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, i, (ModuleName)wafer.OriginStation, wafer.OriginSlot, (Hand)i) }); return; } } foreach(var mod in _dictModuleTask) { if (ModuleHelper.IsTMRobot(mod.Key) || ModuleHelper.IsLoadPort(mod.Key)) continue; if(WaferManager.Instance.CheckHasWafer(mod.Key, 0)) { var wafer = WaferManager.Instance.GetWafer(mod.Key, 0); var freeHands = GetTMFreeHand(); if(freeHands.Count > 0) { var hand = freeHands[0]; _tmSchdActions.Enqueue(new List { new MoveItem(mod.Key, 0, ModuleName.TMRobot, (int)hand, hand) }); _tmSchdActions.Enqueue(new List { new MoveItem(ModuleName.TMRobot, (int)hand, (ModuleName)wafer.OriginStation, wafer.OriginSlot, hand) }); return; } } } } private List GetTMFreeHand() { var lstHands = new List(); if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) && _tmRobotSingleArmOption != 2) lstHands.Add(Hand.Blade1); if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1) && _tmRobotSingleArmOption != 1) lstHands.Add(Hand.Blade2); return lstHands; } private void UpdateProcessJobStatus() { if (_tmRobotStatus == RState.Running) return; foreach (var pj in _lstProcessJobs) { if (CheckAllWaferReturned(pj, pj.State != EnumProcessJobState.Stopping)) { pj.SetState(EnumProcessJobState.ProcessingComplete); JobDataRecorder.EndPJ(pj.InnerId.ToString(), 0, 0); } } } private void UpdateControlJobStatus() { if (_lstControlJobs.Count == 0 || _tmRobotStatus == RState.Running) return; var runningJobs = _lstControlJobs.FindAll(cj => cj.State == EnumControlJobState.Executing); var queueJobs = _lstControlJobs.FindAll(cj => cj.State == EnumControlJobState.Queued).OrderBy(cj => cj.StartTime); if (runningJobs.Count == 0) { if (queueJobs.Count() > 0) { // start the first waiting job RunControlJob(queueJobs.First()); } } else { foreach (var runningjob in runningJobs) { // update the running job RunControlJob(runningjob); } foreach (var queueJob in queueJobs) { if (!runningJobs.Exists(cj => IsControlJobIntersect(cj, queueJob))) { // parallelly start the not intersect job RunControlJob(queueJob); } } } // update system cycle status if (_lstControlJobs.All(cj => cj.State == EnumControlJobState.Completed)) _cycleState = RState.End; List cjRemoveList = new List(); foreach (var cj in _lstControlJobs) { LoadportCassetteState state = (LoadportCassetteState)DATA.Poll($"{cj.Module}.CassetteState"); if (cj.State == EnumControlJobState.Completed && state != LoadportCassetteState.Normal) { cjRemoveList.Add(cj); } } 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 bool RunControlJob(ControlJobInfo cj) { switch (cj.JetState) { case EnumJetCtrlJobState.Quequed: { if (IsCtrlJobNeedPreClean(cj)) { cj.JetState = EnumJetCtrlJobState.PreJobClean; var PMs = GetWaitPreCleanPMsByCtrlJob(cj); foreach (var pm in PMs) { var pmTask = _dictModuleTask[pm] as VenusPMTask; if (pmTask != null && pmTask.Scheduler.IsOnline) { pmTask.SubscribeLotCleanTask(cj, VenusPMTask.RecipeJobType.PreClean); } } } else { ActiveControlJob(cj); } cj.SetState(EnumControlJobState.Executing); } break; case EnumJetCtrlJobState.PreJobClean: { if (IsPreJobCleanDone(cj)) { ActiveControlJob(cj); } } break; case EnumJetCtrlJobState.Processing: { if (IsCtrlJobNeedPostClean(cj)) { if (IsAllJobWaferProcessedOrProcessing(cj)) { cj.JetState = EnumJetCtrlJobState.PostJobClean; var PMs = GetWaitPreCleanPMsByCtrlJob(cj); foreach (var pm in PMs) { var pmTask = _dictModuleTask[pm] as VenusPMTask; if (pmTask != null && pmTask.Scheduler.IsOnline) { pmTask.SubscribeLotCleanTask(cj, VenusPMTask.RecipeJobType.PostClean); } } } } else { if (IsAllProcessJobComplete(cj)) { cj.JetState = EnumJetCtrlJobState.Completed; } } } break; case EnumJetCtrlJobState.PostJobClean: { if (IsPostJobCleanDone(cj)) { cj.JetState = EnumJetCtrlJobState.Completed; } } break; } // caculate processed wafer by control job var lp = 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 (_lpCycleCount[lp] < cj.CycleNumber) { 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 = 0; if (cj.JetState == EnumJetCtrlJobState.Completed) { cj.EndTime = DateTime.Now; _faCallback.JobFinished(cj, GetFirstProcessJob(cj)); _dbCallback.LotFinished(cj); if (_lpCycleCount[lp] >= cj.CycleNumber) (Singleton.Instance.GetScheduler(lp) as SchedulerLoadPort).NoteJobComplete(); _lpCycleCount[lp]++; lpCycleWafer = _lpCycleCount[lp] * cj.LotWafers.Count; _lpThroughput[lp] = (float)(lpCycleWafer / (DateTime.Now - cj.StartTime).TotalHours); LOG.Write(eEvent.EV_ROUTER, lp, $"Cycle Count: {_lpCycleCount[lp]}, Total Processed Wafer: {lpCycleWafer}, Throughput: {_lpThroughput[lp]}"); if (_lpCycleCount[lp] < cj.CycleNumber) { cj.SetState(EnumControlJobState.Executing); cj.JetState = EnumJetCtrlJobState.Quequed; cj.LotInnerId = Guid.NewGuid(); _dbCallback.LotCreated(cj); foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) { 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; } } } _lstWaferTasks.RemoveAll(wt => wt.currentMod == lp); } else { cj.SetState(EnumControlJobState.Completed); } } else { lpCycleWafer = _lpCycleCount[lp] * cj.LotWafers.Count + countProcessed; } if (lpCycleWafer != _lpCycleWafer[lp]) { _lpCycleWafer[lp] = lpCycleWafer; if (_lpCycleCount[lp] > 0) { _lpThroughput[lp] = (float)(lpCycleWafer / (DateTime.Now - cj.StartTime).TotalHours); } } return cj.State == EnumControlJobState.Completed; } private bool IsControlJobIntersect(ControlJobInfo cj1, ControlJobInfo cj2) { var cj1Pms = GetWaitPreCleanPMsByCtrlJob(cj1); var cj2Pms = GetWaitPreCleanPMsByCtrlJob(cj2); return cj1Pms.Intersect(cj2Pms).Count() > 0; } private bool IsCtrlJobNeedPreClean(ControlJobInfo cj) { return !string.IsNullOrWhiteSpace(cj.PreJobClean); } private bool IsCtrlJobNeedPostClean(ControlJobInfo cj) { return !string.IsNullOrWhiteSpace(cj.PostJobClean.Trim()); } private bool IsPreJobCleanDone(ControlJobInfo cj) { var pms = GetWaitPreCleanPMsByCtrlJob(cj); foreach (var pm in pms) { var pmTask = _dictModuleTask[pm] as PMTask; if (pmTask.Scheduler.IsOnline && !pmTask.IsPreJobCleanDone()) return false; } return true; } private bool IsPostJobCleanDone(ControlJobInfo cj) { var pms = GetWaitPreCleanPMsByCtrlJob(cj); foreach (var pm in pms) { var pmTask = _dictModuleTask[pm] as PMTask; if (pmTask.Scheduler.IsOnline && !pmTask.IsPostJobCleanDone()) return false; } return true; } 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 bool ActiveControlJob(ControlJobInfo cj) { 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); } } 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 IsAllJobWaferProcessedOrProcessing(ControlJobInfo cj) { List allModules = _dictModuleTask.Keys.ToList(); 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 ModuleName GetComingAvailablePM(ControlJobInfo cj) { var lstInProcessPMs = new List(); foreach (var wafer in cj.LotWafers) { if (!wafer.IsEmpty && wafer.NextSequenceStep == 0 && wafer.ProcessJob != null) { foreach (var pm in wafer.ProcessJob.Sequence.PMs) { if (!lstInProcessPMs.Contains(pm) && _dictModuleTask[pm].Scheduler.IsOnline) lstInProcessPMs.Add(pm); } } } Dictionary pmAssociatedWaferCount = new Dictionary(); foreach (var mod in _dictModuleTask) { if (ModuleHelper.IsPm(mod.Key) && mod.Value.Scheduler.IsOnline && lstInProcessPMs.Contains(mod.Key)) { pmAssociatedWaferCount[mod.Key] = 0; } } var unprocessedWafer = _lstWaferTasks.Where(task => ModuleHelper.IsPm(task.destMod)); foreach (var wafer in unprocessedWafer) { if (ModuleHelper.IsPm(wafer.destMod) && _dictModuleTask[wafer.destMod].Scheduler.IsOnline && lstInProcessPMs.Contains(wafer.destMod)) { pmAssociatedWaferCount[wafer.destMod]++; } } var oderedPMCount = pmAssociatedWaferCount.OrderBy(p => p.Value).ToDictionary(p => p.Key, o => o.Value); if (oderedPMCount.Count > 0 && oderedPMCount.First().Value < 2) return oderedPMCount.First().Key; return ModuleName.System; } private void CreateNewWaferTask() { var runningJobs = _lstControlJobs.FindAll(cj => cj.JetState == EnumJetCtrlJobState.PreJobClean || cj.JetState == EnumJetCtrlJobState.Processing); var onDutyPms = new Dictionary(); foreach (var job in runningJobs) { var pms = GetWaitPreCleanPMsByCtrlJob(job); foreach (var pm in pms) { if (_dictModuleTask[pm].Scheduler.IsOnline && !onDutyPms.ContainsKey(pm)) { onDutyPms[pm] = _lstWaferTasks.Where(wt => wt.destMod == pm).Count(); } } } if (runningJobs.Count == 0 || onDutyPms.Count == 0) return; var oderedPMs = onDutyPms.OrderBy(kv => kv.Value).OrderBy(kv => _dictModuleTask[kv.Key].TimeToReady); if (oderedPMs.First().Value < 1) { foreach (var runningJob in runningJobs) { foreach (var wafer in runningJob.LotWafers) { if (wafer.IsEmpty || wafer.ProcessJob == null) continue; if (wafer.ProcessJob.Sequence.PMs.Contains(oderedPMs.First().Key) && wafer.NextSequenceStep == 0 && !_lstWaferTasks.Exists(task => task.sourceMod == (ModuleName)wafer.OriginStation && task.sourceSlot == wafer.OriginSlot)) { CreateWaferTasks(wafer, oderedPMs.First().Key); return; } } } } } private void CreateWaferTasks(WaferInfo wafer, ModuleName pm) { var recipeName = wafer.ProcessJob.Sequence.GetRecipe(pm); var recipeContent = RecipeFileManager.Instance.LoadRecipe(pm.ToString(), recipeName, false, RecipeType.Process.ToString()); Recipe recipe = Recipe.Load(recipeContent); int temperature = 0; if (int.TryParse(recipe.Header.Temperature, out int temp)) { temperature = temp; } var waferTask = new WaferTask((ModuleName)wafer.OriginStation, wafer.OriginSlot, pm, 0, temperature, wafer.InnerId, recipeName, wafer.ProcessJob.Sequence.WTWCleanRecipe, wafer.ProcessJob.Sequence.LLInOutPath, wafer.ProcessJob.Sequence.LLDelayTime, IsSequenceNeedAlign(wafer.ProcessJob.Sequence)); // post clean waferTask.OnWaferArrived += WaferArrived; waferTask.OnWaferLeaved += WaferLeaved; _lstWaferTasks.Add(waferTask); } private bool IsSequenceNeedAlign(SequenceInfo sequence) { // check wether need align foreach (var step in sequence.Steps) { foreach (var mod in step.StepModules) { if (ModuleHelper.IsAligner(mod)) return true; } } return false; } private void RunSchdTMReturnActions() { var tmRobot = Singleton.Instance.GetScheduler(ModuleName.TMRobot) as SchedulerSETMRobot; if (tmRobot == null || !tmRobot.IsAvailable) return; if (_tmSchdActions.Count > 0) { if (_curTmAction.Count == 0 || isReturnActionsDone(_curTmAction)) { var nextActions = _tmSchdActions.First(); var nextModule = nextActions.First().Module; if (!_dictModuleTask[nextModule].IsIdle) return; _curTmAction = _tmSchdActions.Dequeue(); tmRobot.SendMoveItems(_curTmAction.ToArray()); } } else if (_curTmAction.Count >= 0 && isReturnActionsDone(_curTmAction)) // all scheduled actions done { _curTmAction.Clear(); } } #endregion internal implementation #region sequence/recipe validation private bool CheckSequencePmReady(SequenceInfo seq, out string reason) { reason = ""; foreach (var pm in seq.PMs) { if (ModuleHelper.IsInstalled(pm) && (_dictModuleTask[pm].Scheduler.IsOnline || !Singleton.Instance.IsAutoMode)) return true; } reason = $"Sequence {seq.Name} no valid PM, " + string.Join("|", seq.PMs); return false; } 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.IsNullOrWhiteSpace((string)stepInfo.StepParameter[attr])) { var recipeName = (string)stepInfo.StepParameter[attr]; if (!string.IsNullOrWhiteSpace(recipeName)) { var recipeContent = RecipeFileManager.Instance.LoadRecipe($"{module}", recipeName, false, "Process"); if (string.IsNullOrWhiteSpace(recipeContent)) { reason = $"Can not find recipe file{recipeName}"; return false; } } } } } } reason = ""; 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; } #endregion } }