Browse Source

Parallel routing algorithm for kepler/venus.

sangwq 9 months ago
parent
commit
ec6ba70d8e
2 changed files with 250 additions and 180 deletions
  1. 21 53
      Venus/Venus_RT/Modules/SystemDispatcher.cs
  2. 229 127
      Venus/Venus_RT/Modules/VenusDispatcher.cs

+ 21 - 53
Venus/Venus_RT/Modules/SystemDispatcher.cs

@@ -774,12 +774,6 @@ namespace Venus_RT.Modules
         private SchedulerFACallback _faCallback;
         private SchedulerDBCallback _dbCallback;
 
-        private bool _isCycleMode;
-        private int _cycleSetPoint = 0;
-        private int _cycledCount = 0;
-        private int _cycledWafer = 0;
-        private float _throughput = 0.0f;
-
         private readonly int _LLASlotNumber = 4;
         private readonly int _LLBSlotNumber = 4;
 
@@ -789,7 +783,7 @@ namespace Venus_RT.Modules
         private Dictionary<ModuleName, int> _lpCycleWafer = new Dictionary<ModuleName, int>();
         private Dictionary<ModuleName, int> _lpCycleCount = new Dictionary<ModuleName, int>();
         private Dictionary<ModuleName, int> _lpCycleSP = new Dictionary<ModuleName, int>();
-        private Dictionary<ModuleName, int> _lpThroughput = new Dictionary<ModuleName, int>();
+        private Dictionary<ModuleName, float> _lpThroughput = new Dictionary<ModuleName, float>();
         private Stopwatch _cycleWatch = new Stopwatch();
 
         private SequenceLLInOutPath _LLInOutPath = SequenceLLInOutPath.DInDOut;
@@ -820,7 +814,8 @@ namespace Venus_RT.Modules
             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> { ModuleName.LP1, ModuleName.LP2, ModuleName.LP3})
+
+            foreach (var lp in new List<ModuleName> { ModuleName.LP1, ModuleName.LP2, ModuleName.LP3})
             {
                 _loadportControlJobDic[lp.ToString()] = null;
                 DATA.Subscribe($"{lp}.CurrentControlJob", () => _loadportControlJobDic[lp.ToString()], SubscriptionAttribute.FLAG.IgnoreSaveDB);
@@ -859,12 +854,7 @@ namespace Venus_RT.Modules
 
             _efemRobotSingleArmOption   = SC.GetValue<int>("EFEM.SingleArmOption");
             _tmRobotSingleArmOption     = SC.GetValue<int>("TM.SingleArmOption");
-            _isCycleMode                = SC.GetValue<bool>("System.IsCycleMode");
-            _cycleSetPoint              = _isCycleMode ? SC.GetValue<int>("System.CycleCount") : 0;
             _LLSlotInOutOption          = (LLSlotInOutOpt)SC.GetValue<int>("System.LoadlockSlotInOutOption");
-            _cycledWafer                = 0;
-            _cycledCount                = 0;
-            _throughput                 = 0;
 
             // rounding TM robot single arm option
             if(_tmRobotSingleArmOption > 2 && _LLSlotInOutOption == LLSlotInOutOpt.AllInAllOut)
@@ -901,9 +891,6 @@ namespace Venus_RT.Modules
         public bool CreateJob(Dictionary<string, object> param, out string reason)
         {
             reason = "";
-            _isCycleMode = SC.GetValue<bool>("System.IsCycleMode");
-            _cycleSetPoint = _isCycleMode ? SC.GetValue<int>("System.CycleCount") : 0;
-
 
             string[] slotSequence = (string[])param["SlotSequence"];
             string jobId = (string)param["JobId"];
@@ -955,7 +942,7 @@ namespace Venus_RT.Modules
             cj.PreJobClean = preCleanRecipe;
             cj.PostJobClean = postCleanRecipe;
             cj.SequenceNameList = slotSequence;
-            cj.CycleNumber = _cycleSetPoint;
+            cj.CycleNumber = SC.GetValue<int>("System.CycleCount");  // only for temperary debug
 
             Dictionary<string, bool[]> seqSlot = new Dictionary<string, bool[]>();
             Dictionary<string, List<Tuple<ModuleName, int>>> seqSlotWafers = new Dictionary<string, List<Tuple<ModuleName, int>>>();
@@ -1573,34 +1560,9 @@ namespace Venus_RT.Modules
                 }
             }
 
-            
-            if (true)
-            {
-                bool allControlJobComplete = _lstControlJobs.All(cj => cj.State == EnumControlJobState.Completed);
-                int totolCycleWafer = _lpCycleWafer.Sum(item => item.Value);
-                if (totolCycleWafer != _cycledWafer || _lpCycleCount.Sum(item => item.Value) > 0 && _throughput < 0.01)  // refresh _throughput in time
-                {
-                    _cycledWafer = totolCycleWafer;
-                    if (_cycledCount > 0 || allControlJobComplete)
-                    {
-                        _throughput = (float)(_cycledWafer / _cycleWatch.Elapsed.TotalHours);
-                    }
-                    else
-                    {
-                        _throughput = 0;
-                    }
-                }
-
-                if (allControlJobComplete)
-                {
-                    _cycledCount++;
-
-                    LOG.Write(eEvent.EV_ROUTER, ModuleName.System, $"Cycle Count: {_cycledCount}, Total Processed Wafer: {_cycledWafer}, Throughput: {_throughput}");
-                    if (_cycledCount >= _cycleSetPoint)
-                        _cycleState = RState.End;
-                }
-
-            }
+            // update system cycle status
+            if (_lstControlJobs.All(cj => cj.State == EnumControlJobState.Completed))
+                _cycleState = RState.End;
 
             List<ControlJobInfo> cjRemoveList = new List<ControlJobInfo>();
             foreach (var cj in _lstControlJobs)
@@ -1620,7 +1582,7 @@ namespace Venus_RT.Modules
                     if (pj.ControlJobName == cj.Name)
                         pjRemoveList.Add(pj);
                 }
-
+                
                 foreach (var pj in pjRemoveList)
                 {
                     _lstProcessJobs.Remove(pj);
@@ -1745,6 +1707,7 @@ namespace Venus_RT.Modules
             }
 
             // caculate processed wafer by control job
+            var lp = ModuleHelper.Converter(cj.Module);
             int countProcessed = 0;
             foreach (var pjName in cj.ProcessJobNameList)
             {
@@ -1756,7 +1719,7 @@ namespace Venus_RT.Modules
                 }
 
                 // caculate process wafer by process
-                if (_isCycleMode && _cycledCount < (_isCycleMode ? _cycleSetPoint : 0))
+                if (_lpCycleCount[lp] < cj.CycleNumber)
                 {
                     foreach (var pjSlotWafer in pj.SlotWafers)
                     {
@@ -1768,7 +1731,6 @@ namespace Venus_RT.Modules
             }
 
             int lpCycleWafer = 0;
-            var lp = ModuleHelper.Converter(cj.Module);
             if (cj.JetState == EnumJetCtrlJobState.Completed)
             {
                 cj.EndTime = DateTime.Now;
@@ -1776,11 +1738,13 @@ namespace Venus_RT.Modules
                 _faCallback.JobFinished(cj, GetFirstProcessJob(cj));
                 _dbCallback.LotFinished(cj);
 
-                if (!(_isCycleMode && _cycledCount < _cycleSetPoint))
+                if (_lpCycleCount[lp] >= cj.CycleNumber)
                     (Singleton<TransferModule>.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)
                 {
@@ -1817,15 +1781,19 @@ namespace Venus_RT.Modules
             }
             else
             {
-                lpCycleWafer = _lpCycleCount[ModuleHelper.Converter(cj.Module)] * cj.LotWafers.Count + countProcessed;
+                lpCycleWafer = _lpCycleCount[lp] * cj.LotWafers.Count + countProcessed;
             }
 
-            if (_lpCycleCount[ModuleHelper.Converter(cj.Module)] != _cycledCount || lpCycleWafer != _lpCycleWafer[ModuleHelper.Converter(cj.Module)])
+            if (lpCycleWafer != _lpCycleWafer[lp])
             {
-                _lpCycleWafer[ModuleHelper.Converter(cj.Module)] = lpCycleWafer;
+                _lpCycleWafer[lp] = lpCycleWafer;
+                if(_lpCycleCount[lp] > 0)
+                {
+                    _lpThroughput[lp] = (float)(lpCycleWafer /(DateTime.Now - cj.StartTime).TotalHours);
+                }
             }
 
-            return false;
+            return cj.State == EnumControlJobState.Completed;
         }
 
         private bool IsAllJobWaferProcessedOrProcessing(ControlJobInfo cj)

+ 229 - 127
Venus/Venus_RT/Modules/VenusDispatcher.cs

@@ -473,7 +473,7 @@ namespace Venus_RT.Modules
         private Dictionary<ModuleName, int> _lpCycleWafer = new Dictionary<ModuleName, int>();
         private Dictionary<ModuleName, int> _lpCycleCount = new Dictionary<ModuleName, int>();
         private Dictionary<ModuleName, int> _lpCycleSP = new Dictionary<ModuleName, int>();
-        private Dictionary<ModuleName, int> _lpThroughput = new Dictionary<ModuleName, int>();
+        private Dictionary<ModuleName, float> _lpThroughput = new Dictionary<ModuleName, float>();
         private Stopwatch _cycleWatch = new Stopwatch();
 
         private SchedulerFACallback _faCallback;
@@ -605,10 +605,11 @@ namespace Venus_RT.Modules
             cj.LotInnerId = Guid.NewGuid();
             cj.LotWafers = new List<WaferInfo>();
             cj.SetState(EnumControlJobState.WaitingForStart);
-            cj.JetState = EnumJetCtrlJobState.Created;
+            cj.JetState = EnumJetCtrlJobState.Quequed;
             cj.PreJobClean = preCleanRecipe;
             cj.PostJobClean = postCleanRecipe;
             cj.SequenceNameList = slotSequence;
+            cj.CycleNumber = SC.GetValue<int>("System.CycleCount");  // only for temperary debug
 
             Dictionary<string, bool[]> seqSlot = new Dictionary<string, bool[]>();
             Dictionary<string, List<Tuple<ModuleName, int>>> seqSlotWafers = new Dictionary<string, List<Tuple<ModuleName, int>>>();
@@ -1204,7 +1205,7 @@ namespace Venus_RT.Modules
                 }
                 else
                 {
-                    if(emptyAndReadyIn20sPMs.Count >= 2 && waitInWafers.Count >= 2)
+                    if(emptyAndReadyIn20sPMs.Count >= 2 && waitInWafers.Count >= 2 && waitInWafers[0].currentMod == waitInWafers[1].currentMod)
                     {
                         var doublePickActions = new List<MoveItem>()
                         {
@@ -1621,146 +1622,191 @@ namespace Venus_RT.Modules
             if (_lstControlJobs.Count == 0 || _tmRobotStatus == RState.Running)
                 return;
 
-            bool allControlJobComplete = true;
-            List<ControlJobInfo> cjRemoveList = new List<ControlJobInfo>();
-
-            var runingJobs = _lstControlJobs.FindAll(cj => cj.State == EnumControlJobState.Executing);
-            foreach(var runingjob in  runingJobs) 
+            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)
             {
-                int countProcessed = 0;
-                foreach (var pjName in runingjob.ProcessJobNameList)
+                if (queueJobs.Count() > 0)
                 {
-                    var pj = _lstProcessJobs.Find(x => x.Name == pjName);
-                    if (pj == null)
-                    {
-                        LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Not find pj named {pjName} in {runingjob.Name}");
-                        continue;
-                    }
+                    // start the first waiting job
+                    RunControlJob(queueJobs.First());
+                }
+            }
+            else
+            {
+                foreach (var runningjob in runningJobs)
+                {
+                    // update the running job
+                    RunControlJob(runningjob);
+                }
 
-                    // caculate process wafer by process
-                    if (_isCycleMode && _cycledCount < (_isCycleMode ? _cycleSetPoint : 0))
+                foreach (var queueJob in queueJobs)
+                {
+                    if (!runningJobs.Exists(cj => IsControlJobIntersect(cj, queueJob)))
                     {
-                        foreach (var pjSlotWafer in pj.SlotWafers)
-                        {
-                            WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2);
-                            if (!wafer.IsEmpty && wafer.ProcessState == EnumWaferProcessStatus.Completed)
-                                countProcessed++;
-                        }
+                        // parallelly start the not intersect job
+                        RunControlJob(queueJob);
                     }
                 }
+            }
 
-                int lpCycleWafer = _lpCycleCount[ModuleHelper.Converter(runingjob.Module)] * runingjob.LotWafers.Count + (runingjob.State ==  EnumControlJobState.Completed&& runingjob.LotWafers.Count == countProcessed ? 0 : countProcessed);
-                if (_lpCycleCount[ModuleHelper.Converter(runingjob.Module)] != _cycledCount || lpCycleWafer != _lpCycleWafer[ModuleHelper.Converter(runingjob.Module)])
+            // update system cycle status
+            if (_lstControlJobs.All(cj => cj.State == EnumControlJobState.Completed))
+                _cycleState = RState.End;
+
+            List<ControlJobInfo> cjRemoveList = new List<ControlJobInfo>();
+            foreach (var cj in _lstControlJobs)
+            {
+                LoadportCassetteState state = (LoadportCassetteState)DATA.Poll($"{cj.Module}.CassetteState");
+                if (cj.State == EnumControlJobState.Completed && state != LoadportCassetteState.Normal)
                 {
-                    _lpCycleWafer[ModuleHelper.Converter(runingjob.Module)] = lpCycleWafer;
+                    cjRemoveList.Add(cj);
                 }
+            }
 
-                if (IsAllProcessJobComplete(runingjob))
+            foreach (var cj in cjRemoveList)
+            {
+                List<ProcessJobInfo> pjRemoveList = new List<ProcessJobInfo>();
+                foreach (var pj in _lstProcessJobs)
                 {
-                    var PMs = GetWaitPreCleanPMsByCtrlJob(runingjob);
-                    if (runingjob.JetState == EnumJetCtrlJobState.Processing)
+                    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:
                     {
-                        // subscribe clean task
-                        foreach (var pm in PMs)
+                        if (IsCtrlJobNeedPreClean(cj))
                         {
-                            var pmTask = _dictModuleTask[pm] as VenusPMTask;
-                            if (pmTask != null && pmTask.Scheduler.IsOnline)
+                            cj.JetState = EnumJetCtrlJobState.PreJobClean;
+
+                            var PMs = GetWaitPreCleanPMsByCtrlJob(cj);
+                            foreach (var pm in PMs)
                             {
-                                pmTask.SubscribeLotCleanTask(runingjob, VenusPMTask.RecipeJobType.PostClean);
+                                var pmTask = _dictModuleTask[pm] as VenusPMTask;
+                                if (pmTask != null && pmTask.Scheduler.IsOnline)
+                                {
+                                    pmTask.SubscribeLotCleanTask(cj, VenusPMTask.RecipeJobType.PreClean);
+                                }
                             }
                         }
+                        else
+                        {
+                            ActiveControlJob(cj);
+                        }
 
-                        runingjob.JetState = EnumJetCtrlJobState.PostJobClean;
+                        cj.SetState(EnumControlJobState.Executing);
                     }
-                    else if (runingjob.JetState == EnumJetCtrlJobState.PostJobClean)
+                    break;
+                case EnumJetCtrlJobState.PreJobClean:
                     {
-                        foreach (var pm in PMs)
+                        if (IsPreJobCleanDone(cj))
                         {
-                            var pmTask = _dictModuleTask[pm] as VenusPMTask;
-                            if (pmTask != null && pmTask.Scheduler.IsOnline && !pmTask.IsAllJobCompleted())
-                                return;
+                            ActiveControlJob(cj);
                         }
-
-                        runingjob.JetState = EnumJetCtrlJobState.Completed;
-                        runingjob.SetState(EnumControlJobState.Completed);
-                        runingjob.EndTime = DateTime.Now;
-
-                        _faCallback.JobFinished(runingjob, GetFirstProcessJob(runingjob));
-                        _dbCallback.LotFinished(runingjob);
-
-                        if (!(_isCycleMode && _cycledCount < _cycleSetPoint))
-                            (Singleton<TransferModule>.Instance.GetScheduler(ModuleHelper.Converter(runingjob.Module)) as SchedulerLoadPort).NoteJobComplete();
-
-                        _lpCycleCount[ModuleHelper.Converter(runingjob.Module)]++;
                     }
-                }
-            }
-
-            var pendingJobs = _lstControlJobs.FindAll(cj => cj.State == EnumControlJobState.Queued).OrderBy(cj => cj.StartTime);
-            if(pendingJobs.Count() > 0)
-            {
-                if (runingJobs.Count == 0)
-                {
-                    pendingJobs.First().JetState = EnumJetCtrlJobState.Processing;
-                    ActiveControlJob(pendingJobs.First());
-
-                    // subscribe clean task
-                    var PMs = GetWaitPreCleanPMsByCtrlJob(pendingJobs.First());
-                    foreach (var pm in PMs)
+                    break;
+                case EnumJetCtrlJobState.Processing:
                     {
-                        var pmTask = _dictModuleTask[pm] as VenusPMTask;
-                        if (pmTask != null && pmTask.Scheduler.IsOnline)
+                        if (IsCtrlJobNeedPostClean(cj))
                         {
-                            pmTask.SubscribeLotCleanTask(pendingJobs.First(), VenusPMTask.RecipeJobType.PreClean);
+                            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;
             }
 
-            foreach(var cj in _lstControlJobs)
+            // caculate processed wafer by control job
+            var lp = ModuleHelper.Converter(cj.Module);
+            int countProcessed = 0;
+            foreach (var pjName in cj.ProcessJobNameList)
             {
-                LoadportCassetteState state = (LoadportCassetteState)DATA.Poll($"{cj.Module}.CassetteState");
-                if (cj.State == EnumControlJobState.Completed && state != LoadportCassetteState.Normal)
+                var pj = _lstProcessJobs.Find(x => x.Name == pjName);
+                if (pj == null)
                 {
-                    cjRemoveList.Add(cj);
+                    LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Not find pj named {pjName} in {cj.Name}");
+                    continue;
                 }
 
-                allControlJobComplete = allControlJobComplete && cj.State == EnumControlJobState.Completed;
-            }
-
-
-            if (_isCycleMode && _cycledCount < (_isCycleMode ? _cycleSetPoint : 0))
-            {
-                int totolCycleWafer = _lpCycleWafer.Sum(item => item.Value);
-                if (totolCycleWafer != _cycledWafer || _lpCycleCount.Sum(item => item.Value) > 0 && _throughput < 0.01)  // refresh _throughput in time
+                // caculate process wafer by process
+                if (_lpCycleCount[lp] < cj.CycleNumber)
                 {
-                    _cycledWafer = totolCycleWafer;
-                    if (_cycledCount > 0 || allControlJobComplete)
+                    foreach (var pjSlotWafer in pj.SlotWafers)
                     {
-                        _throughput = (float)(_cycledWafer / _cycleWatch.Elapsed.TotalHours);
-                    }
-                    else
-                    {
-                        _throughput = 0;
+                        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<TransferModule>.Instance.GetScheduler(lp) as SchedulerLoadPort).NoteJobComplete();
 
-                if (allControlJobComplete)
+                _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)
                 {
-                    _cycledCount++;
+                    cj.SetState(EnumControlJobState.Executing);
+                    cj.JetState = EnumJetCtrlJobState.Quequed;
 
-                    LOG.Write(eEvent.EV_ROUTER, ModuleName.System, $"Cycle Count: {_cycledCount}, Total Processed Wafer: {_cycledWafer}, Throughput: {_throughput}");
-                    if (_cycledCount < _cycleSetPoint)
-                    {
-                        foreach (var cj in _lstControlJobs)
-                        {
-                            cj.SetState(EnumControlJobState.Queued);
+                    cj.LotInnerId = Guid.NewGuid();
 
-                            cj.LotInnerId = Guid.NewGuid();
+                    _dbCallback.LotCreated(cj);
 
-                            _dbCallback.LotCreated(cj);
-                        }
-                        foreach (var pj in _lstProcessJobs)
+                    foreach (var pj in _lstProcessJobs)
+                    {
+                        if (pj.ControlJobName == cj.Name)
                         {
                             pj.SetState(EnumProcessJobState.Queued);
                             pj.InnerId = Guid.NewGuid();
@@ -1773,33 +1819,73 @@ namespace Venus_RT.Modules
                                 wafer.ProcessState = EnumWaferProcessStatus.Idle;
                             }
                         }
-
-                        _lstWaferTasks.Clear();
-                    }
-                    else
-                    {
-                        _cycleState = RState.End;
                     }
-                        
+
+                    _lstWaferTasks.RemoveAll(wt => wt.currentMod == lp);
+                }
+                else
+                {
+                    cj.SetState(EnumControlJobState.Completed);
                 }
             }
+            else
+            {
+                lpCycleWafer = _lpCycleCount[lp] * cj.LotWafers.Count + countProcessed;
+            }
 
-            foreach (var cj in cjRemoveList)
+            if (lpCycleWafer != _lpCycleWafer[lp])
             {
-                List<ProcessJobInfo> pjRemoveList = new List<ProcessJobInfo>();
-                foreach (var pj in _lstProcessJobs)
+                _lpCycleWafer[lp] = lpCycleWafer;
+                if (_lpCycleCount[lp] > 0)
                 {
-                    if (pj.ControlJobName == cj.Name)
-                        pjRemoveList.Add(pj);
+                    _lpThroughput[lp] = (float)(lpCycleWafer / (DateTime.Now - cj.StartTime).TotalHours);
                 }
+            }
 
-                foreach (var pj in pjRemoveList)
-                {
-                    _lstProcessJobs.Remove(pj);
-                }
+            return cj.State == EnumControlJobState.Completed;
+        }
 
-                _lstControlJobs.Remove(cj);
+        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)
@@ -1826,7 +1912,7 @@ namespace Venus_RT.Modules
 
         private bool ActiveControlJob(ControlJobInfo cj)
         {
-            cj.SetState(EnumControlJobState.Executing);
+            cj.JetState = EnumJetCtrlJobState.Processing;
             foreach (var pjName in cj.ProcessJobNameList)
             {
                 var pj = _lstProcessJobs.Find(x => x.Name == pjName);
@@ -1923,20 +2009,36 @@ namespace Venus_RT.Modules
 
         private void CreateNewWaferTask()
         {
-            var cj = _lstControlJobs.Find(job => job.State == EnumControlJobState.Executing);
-            if (cj != null)
+            var runningJobs = _lstControlJobs.FindAll(cj => cj.JetState == EnumJetCtrlJobState.PreJobClean || cj.JetState == EnumJetCtrlJobState.Processing);
+            var onDutyPms = new Dictionary<ModuleName, int>();
+            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)
             {
-                var pm = GetComingAvailablePM(cj);
-                if (pm != ModuleName.System)
+                foreach (var runningJob in runningJobs)
                 {
-                    foreach (var wafer in cj.LotWafers)
+                    foreach (var wafer in runningJob.LotWafers)
                     {
                         if (wafer.IsEmpty || wafer.ProcessJob == null)
                             continue;
 
-                        if (wafer.ProcessJob.Sequence.PMs.Contains(pm) && wafer.NextSequenceStep == 0 && !_lstWaferTasks.Exists(task => task.sourceMod == (ModuleName)wafer.OriginStation && task.sourceSlot == wafer.OriginSlot))
+                        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, pm);
+                            CreateWaferTasks(wafer, oderedPMs.First().Key);
                             return;
                         }
                     }