Browse Source

Parallel cycle mode

sangwq 9 months ago
parent
commit
5469baaa63

+ 2 - 1
Venus/Framework/Common/Jobs/ControlJob.cs

@@ -54,7 +54,8 @@ namespace MECF.Framework.Common.Jobs
         public string PreJobClean { get; set; }
         [DataMember]
         public string PostJobClean { get; set; }
-
+        [DataMember]
+        public int CycleNumber { get; set; }    
 
         public ControlJobInfo()
         {

+ 232 - 155
Venus/Venus_RT/Modules/SystemDispatcher.cs

@@ -24,6 +24,8 @@ using Venus_Core;
 using Venus_RT.Modules.Schedulers;
 using Venus_RT.Scheduler;
 using Venus_Unity;
+using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.PMs;
+using System.Linq.Expressions;
 
 namespace Venus_RT.Modules
 {
@@ -786,6 +788,8 @@ namespace Venus_RT.Modules
         private LLSlotInOutOpt _LLSlotInOutOption = 0;
         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 Stopwatch _cycleWatch = new Stopwatch();
 
         private SequenceLLInOutPath _LLInOutPath = SequenceLLInOutPath.DInDOut;
@@ -812,20 +816,19 @@ namespace Venus_RT.Modules
 
             InitModules();
 
-            DATA.Subscribe("Scheduler.CycledCount", () => _cycledCount, SubscriptionAttribute.FLAG.IgnoreSaveDB);
-            DATA.Subscribe("Scheduler.CycledWafer", () => _cycledWafer, SubscriptionAttribute.FLAG.IgnoreSaveDB);
-            DATA.Subscribe("Scheduler.CycleSetPoint", () => _cycleSetPoint, SubscriptionAttribute.FLAG.IgnoreSaveDB);
-            DATA.Subscribe("Scheduler.Throughput", () => _throughput, SubscriptionAttribute.FLAG.IgnoreSaveDB);
-
-
             DATA.Subscribe("Scheduler.PjIdList", () => Array.ConvertAll(_lstProcessJobs.ToArray(), x => x.InnerId.ToString()).ToList());
             DATA.Subscribe("Scheduler.PjNameList", () => Array.ConvertAll(_lstProcessJobs.ToArray(), x => x.LotName.ToString()).ToList());
             DATA.Subscribe("Scheduler.InUsingRecipe", () => InUseRecipes);
-            for (int i = 1; i <= 3; i++)
+
+            foreach(var lp in new List<ModuleName> { ModuleName.LP1, ModuleName.LP2, ModuleName.LP3})
             {
-                _loadportControlJobDic[$"LP{i}"] = null;
-                string lp = $"LP{i}";
-                DATA.Subscribe($"{lp}.CurrentControlJob", () => _loadportControlJobDic[lp], SubscriptionAttribute.FLAG.IgnoreSaveDB);
+                _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);
             }
         }
 
@@ -952,6 +955,7 @@ namespace Venus_RT.Modules
             cj.PreJobClean = preCleanRecipe;
             cj.PostJobClean = postCleanRecipe;
             cj.SequenceNameList = slotSequence;
+            cj.CycleNumber = _cycleSetPoint;
 
             Dictionary<string, bool[]> seqSlot = new Dictionary<string, bool[]>();
             Dictionary<string, List<Tuple<ModuleName, int>>> seqSlotWafers = new Dictionary<string, List<Tuple<ModuleName, int>>>();
@@ -1528,132 +1532,51 @@ namespace Venus_RT.Modules
             }
         }
 
+        private bool IsControlJobIntersect(ControlJobInfo cj1, ControlJobInfo cj2)
+        {
+            var cj1Pms = GetWaitPreCleanPMsByCtrlJob(cj1);
+            var cj2Pms = GetWaitPreCleanPMsByCtrlJob(cj2);
+
+            return cj1Pms.Intersect(cj2Pms).Count() > 0;
+        }
+
         private void UpdateControlJobStatus()
         {
             if (_lstControlJobs.Count == 0 || _efemRobotStatus == RState.Running || _tmRobotStatus == RState.Running)
                 return;
 
-            bool allControlJobComplete = true;
-            List<ControlJobInfo> cjRemoveList = new List<ControlJobInfo>();
-            foreach (var cj in _lstControlJobs)
+            var runningJobs = _lstControlJobs.FindAll(cj => IsCtrlJobRuning(cj));
+            var queueJobs = _lstControlJobs.FindAll(cj => cj.JetState == EnumJetCtrlJobState.Quequed).OrderBy(cj => cj.StartTime);
+            if (runningJobs.Count == 0)
             {
-                if (cj.JetState == EnumJetCtrlJobState.Quequed)  // active quequed control job: need prejob clean or don need prejob clean
+                if(queueJobs.Count() > 0)
                 {
-                    var runingJobs = _lstControlJobs.FindAll(job => IsCtrlJobRuning(job));
-                    if (runingJobs.Count == 0 || (runingJobs.Count == 1 && IsCtrlJobEndingState(runingJobs.First())))
-                    {
-                        var quequedJobs = _lstControlJobs.FindAll(job => job.JetState == EnumJetCtrlJobState.Quequed);
-                        var firstQuequeJob = quequedJobs.OrderBy(job => job.StartTime).First();
-                        if (firstQuequeJob.InnerId == cj.InnerId)
-                        {
-                            if (IsCtrlJobNeedPreClean(cj))
-                            {
-                                cj.JetState = EnumJetCtrlJobState.PreJobClean;
-
-                                var PMs = GetWaitPreCleanPMsByCtrlJob(cj);
-                                foreach (var pm in PMs)
-                                {
-                                    var pmTask = _dictModuleTask[pm] as PMTask;
-                                    if(pmTask != null && pmTask.Scheduler.IsOnline)
-                                    {
-                                        pmTask.InvokePreJobClean(cj.PreJobClean);
-                                    }
-                                }
-                            }
-                            else
-                            {
-                                ActiveControlJob(cj);
-                            }
-                        }
-                    }
+                    // start the first waiting job
+                    RunControlJob(queueJobs.First());
                 }
-                else if (IsCtrlJobRuning(cj))
+            }
+            else
+            {
+                foreach(var runningjob in runningJobs)
                 {
-                    if (cj.JetState == EnumJetCtrlJobState.PreJobClean)
-                    {
-                        if (IsPreJobCleanDone(cj))
-                        {
-                            ActiveControlJob(cj);
-                        }
-                    }
-                    else
-                    {
-                        if (cj.JetState == EnumJetCtrlJobState.Processing && IsCtrlJobNeedPostClean(cj))  // active post job clean
-                        {
-                            if (IsAllJobWaferProcessedOrProcessing(cj))
-                            {
-                                cj.JetState = EnumJetCtrlJobState.PostJobClean;
-                                var PMs = GetWaitPreCleanPMsByCtrlJob(cj);
-                                foreach (var pm in PMs)
-                                {
-                                    var pmTask = _dictModuleTask[pm] as PMTask;
-                                    if (pmTask != null && pmTask.Scheduler.IsOnline)
-                                    {
-                                        pmTask.InvokePostJobClean(cj.PostJobClean);
-                                    }
-                                }
-                            }
-                        }
-
-                        // control job complete
-                        if (IsAllProcessJobComplete(cj) && (!IsCtrlJobNeedPostClean(cj) || cj.JetState == EnumJetCtrlJobState.PostJobClean && IsPostJobCleanDone(cj)))
-                        {
-                            cj.JetState = EnumJetCtrlJobState.Completed;
-
-                            cj.SetState(EnumControlJobState.Completed);
-                            cj.EndTime = DateTime.Now;
-
-                            _faCallback.JobFinished(cj, GetFirstProcessJob(cj));
-                            _dbCallback.LotFinished(cj);
-
-                            if (!(_isCycleMode && _cycledCount < _cycleSetPoint))
-                                (Singleton<TransferModule>.Instance.GetScheduler(ModuleHelper.Converter(cj.Module)) as SchedulerLoadPort).NoteJobComplete();
-
-                            _lpCycleCount[ModuleHelper.Converter(cj.Module)]++;
-                        }
-                    }
-
-                    int countProcessed = 0;
-                    foreach (var pjName in cj.ProcessJobNameList)
-                    {
-                        var pj = _lstProcessJobs.Find(x => x.Name == pjName);
-                        if (pj == null)
-                        {
-                            LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Not find pj named {pjName} in {cj.Name}");
-                            continue;
-                        }
-
-                        // caculate process wafer by process
-                        if (_isCycleMode && _cycledCount < (_isCycleMode ? _cycleSetPoint : 0))
-                        {
-                            foreach (var pjSlotWafer in pj.SlotWafers)
-                            {
-                                WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2);
-                                if (!wafer.IsEmpty && wafer.ProcessState == EnumWaferProcessStatus.Completed)
-                                    countProcessed++;
-                            }
-                        }
-                    }
-
-                    int lpCycleWafer = _lpCycleCount[ModuleHelper.Converter(cj.Module)] * cj.LotWafers.Count + (cj.JetState == EnumJetCtrlJobState.Completed && cj.LotWafers.Count == countProcessed ? 0 : countProcessed);
-                    if (_lpCycleCount[ModuleHelper.Converter(cj.Module)] != _cycledCount || lpCycleWafer != _lpCycleWafer[ModuleHelper.Converter(cj.Module)])
-                    {
-                        _lpCycleWafer[ModuleHelper.Converter(cj.Module)] = lpCycleWafer;
-                    }
-
+                    // update the running job
+                    RunControlJob(runningjob);
                 }
 
-                LoadportCassetteState state = (LoadportCassetteState)DATA.Poll($"{cj.Module}.CassetteState");
-                if (cj.State == EnumControlJobState.Completed && state != LoadportCassetteState.Normal && cj.JetState == EnumJetCtrlJobState.Completed)
+                foreach(var queueJob in queueJobs)
                 {
-                    cjRemoveList.Add(cj);
+                    if(!runningJobs.Exists(cj => IsControlJobIntersect(cj, queueJob)))
+                    {
+                        // parallelly start the not intersect job
+                        RunControlJob(queueJob);
+                    }
                 }
-
-                allControlJobComplete = allControlJobComplete && cj.State == EnumControlJobState.Completed;
             }
 
-            if (_isCycleMode && _cycledCount < (_isCycleMode ? _cycleSetPoint : 0))
+            
+            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
                 {
@@ -1673,39 +1596,22 @@ namespace Venus_RT.Modules
                     _cycledCount++;
 
                     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.Executing);
-                            cj.JetState = EnumJetCtrlJobState.Quequed;
-
-                            cj.LotInnerId = Guid.NewGuid();
-
-                            _dbCallback.LotCreated(cj);
-                        }
-                        foreach (var pj in _lstProcessJobs)
-                        {
-                            pj.SetState(EnumProcessJobState.Queued);
-                            pj.InnerId = Guid.NewGuid();
-                            foreach (var pjSlotWafer in pj.SlotWafers)
-                            {
-                                WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2);
-
-                                wafer.ProcessJob = null;
-                                wafer.NextSequenceStep = 0;
-                                wafer.ProcessState = EnumWaferProcessStatus.Idle;
-                            }
-                        }
-
-                        _lstWaferTasks.Clear();
-                    }
-                    else
+                    if (_cycledCount >= _cycleSetPoint)
                         _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)
+                {
+                    cjRemoveList.Add(cj);
+                }
+            }
+
             foreach (var cj in cjRemoveList)
             {
                 List<ProcessJobInfo> pjRemoveList = new List<ProcessJobInfo>();
@@ -1767,6 +1673,161 @@ namespace Venus_RT.Modules
             return true;
         }
 
+        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 PMTask;
+                                if (pmTask != null && pmTask.Scheduler.IsOnline)
+                                {
+                                    pmTask.InvokePreJobClean(cj.PreJobClean);
+                                }
+                            }
+                        }
+                        else
+                        {
+                            ActiveControlJob(cj);
+                        }
+                    }
+                    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 PMTask;
+                                    if (pmTask != null && pmTask.Scheduler.IsOnline)
+                                    {
+                                        pmTask.InvokePostJobClean(cj.PostJobClean);
+                                    }
+                                }
+                            }
+                        }
+                        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
+            int countProcessed = 0;
+            foreach (var pjName in cj.ProcessJobNameList)
+            {
+                var pj = _lstProcessJobs.Find(x => x.Name == pjName);
+                if (pj == null)
+                {
+                    LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Not find pj named {pjName} in {cj.Name}");
+                    continue;
+                }
+
+                // caculate process wafer by process
+                if (_isCycleMode && _cycledCount < (_isCycleMode ? _cycleSetPoint : 0))
+                {
+                    foreach (var pjSlotWafer in pj.SlotWafers)
+                    {
+                        WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2);
+                        if (!wafer.IsEmpty && wafer.ProcessState == EnumWaferProcessStatus.Completed)
+                            countProcessed++;
+                    }
+                }
+            }
+
+            int lpCycleWafer = 0;
+            var lp = ModuleHelper.Converter(cj.Module);
+            if (cj.JetState == EnumJetCtrlJobState.Completed)
+            {
+                cj.EndTime = DateTime.Now;
+
+                _faCallback.JobFinished(cj, GetFirstProcessJob(cj));
+                _dbCallback.LotFinished(cj);
+
+                if (!(_isCycleMode && _cycledCount < _cycleSetPoint))
+                    (Singleton<TransferModule>.Instance.GetScheduler(lp) as SchedulerLoadPort).NoteJobComplete();
+
+                _lpCycleCount[lp]++;
+                lpCycleWafer = _lpCycleCount[lp] * cj.LotWafers.Count;
+
+                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[ModuleHelper.Converter(cj.Module)] * cj.LotWafers.Count + countProcessed;
+            }
+
+            if (_lpCycleCount[ModuleHelper.Converter(cj.Module)] != _cycledCount || lpCycleWafer != _lpCycleWafer[ModuleHelper.Converter(cj.Module)])
+            {
+                _lpCycleWafer[ModuleHelper.Converter(cj.Module)] = lpCycleWafer;
+            }
+
+            return false;
+        }
+
         private bool IsAllJobWaferProcessedOrProcessing(ControlJobInfo cj)
         {
             List<ModuleName> allModules = _dictModuleTask.Keys.ToList();
@@ -1965,20 +2026,36 @@ namespace Venus_RT.Modules
 
         private void CreateNewWaferTask()
         {
-            var cj = _lstControlJobs.Find(c => (c.JetState == EnumJetCtrlJobState.PreJobClean || c.JetState == EnumJetCtrlJobState.Processing) && c.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;
                         }
                     }

+ 11 - 10
Venus/Venus_RT/Modules/VenusDispatcher.cs

@@ -472,6 +472,8 @@ namespace Venus_RT.Modules
         private int _tmRobotSingleArmOption = 0;
         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 Stopwatch _cycleWatch = new Stopwatch();
 
         private SchedulerFACallback _faCallback;
@@ -493,20 +495,19 @@ namespace Venus_RT.Modules
 
             InitModules();
 
-            DATA.Subscribe("Scheduler.CycledCount", () => _cycledCount, SubscriptionAttribute.FLAG.IgnoreSaveDB);
-            DATA.Subscribe("Scheduler.CycledWafer", () => _cycledWafer, SubscriptionAttribute.FLAG.IgnoreSaveDB);
-            DATA.Subscribe("Scheduler.CycleSetPoint", () => _cycleSetPoint, SubscriptionAttribute.FLAG.IgnoreSaveDB);
-            DATA.Subscribe("Scheduler.Throughput", () => _throughput, SubscriptionAttribute.FLAG.IgnoreSaveDB);
-
-
             DATA.Subscribe("Scheduler.PjIdList", () => Array.ConvertAll(_lstProcessJobs.ToArray(), x => x.InnerId.ToString()).ToList());
             DATA.Subscribe("Scheduler.PjNameList", () => Array.ConvertAll(_lstProcessJobs.ToArray(), x => x.LotName.ToString()).ToList());
             DATA.Subscribe("Scheduler.InUsingRecipe", () => InUseRecipes);
-            for (int i = 1; i <= 3; i++)
+
+            foreach (var lp in new List<ModuleName> { ModuleName.LP1, ModuleName.LP2, ModuleName.LP3 })
             {
-                _loadportControlJobDic[$"LP{i}"] = null;
-                string lp = $"LP{i}";
-                DATA.Subscribe($"{lp}.CurrentControlJob", () => _loadportControlJobDic[lp], SubscriptionAttribute.FLAG.IgnoreSaveDB);
+                _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);
             }
         }