Browse Source

Loadlock/TM Robot slot In/Out Options.

sangwq 1 year ago
parent
commit
a05e9eaa1b

+ 2 - 2
Venus/Venus_RT/Config/System.sccfg

@@ -23,7 +23,7 @@
 		<config default="1000"  name="DataCollectionInterval" nameView="DataCollectionInterval" description="插入数据时间间隔" max="2000" min="200" paramter="" tag="" unit="ms" type="Integer"/>
 		<config default="false" name="IsEnableEthercat" nameView="Is Enable Ethercat" description="是否开启凌华Ethercat" max="" min="" paramter="" tag="" unit="" type="Bool" visible="false"/>
 		<config default="false" name="IsLogExcludeInfoType" nameView="Log Exclude Info Type" description="TopView Log排除info类型信息" max="" min="" paramter="" tag="" unit="" type="Bool" />
-		<config default="Kepler" name="Name" nameView="Name" description="Name" tag="" unit="" type="String" />
+		<config default="0" name="LoadlockSlotInOutOption"  nameView="Loadlock Slot In Out Option" description="0, All slots in All slot Out; 1, Upper slots in lower slots out; 2, Lower slots in Upper slot out " max="2" min="0" paramter="" tag="" unit="" type="Integer"/>
 		<configs name="SetUp" nameView="Set Up" visible="false">
 			<config default="true" name="EPDInstalled" nameView="Is EPD installed" description="EPD是否安装" max="" min="" paramter="" tag="" unit="" type="Bool" />
 		</configs>
@@ -160,7 +160,7 @@
 		<config default="90"  name="ControlPressureSetPoint"   nameView="Control Pressure SetPoint"   description="TM Chamber 控压 设定值" max="200" min="0" paramter="" tag="" unit="mTorr" type="Integer" />
 		<config default="50"  name="WithLLPressureDifference"   nameView="TM LL Pressure Difference"   description="TM和LL压差" max="100" min="0" paramter="" tag="" unit="mTorr" type="Integer" />
 		<config default="50"  name="WithPMPressureDifference"   nameView="TM PM Pressure Difference"   description="TM和PM压差" max="100" min="0" paramter="" tag="" unit="mTorr" type="Integer" />
-		<config default="0" name="SingleArmOption"   nameView="Single Arm Option"   description="0, both; 1, Blade1; 2, Blade2" max="2" min="0" paramter="" tag="" unit="" type="Integer" />
+		<config default="0" name="SingleArmOption"   nameView="Single Arm Option"   description="0, Both; 1, Blade1; 2, Blade2; 3, Blade1 In Blade2 Out; 4, Blade2 In Blade1 Out" max="4" min="0" paramter="" tag="" unit="" type="Integer" />
 		<config default="0" name="QueryAWCOption"   nameView="Query AWC Option"   description="0, None; 1, Only TMPick Open; 2, Only TMPlace Open; 3, Both Open" max="3" min="0" paramter="" tag="" unit="" type="Integer" />
 
 		<config default="0" name="LLAutoVentInWaferOpt" nameView="Loadlock Aut Vent Option For Raw Wafer" description="Auto Venting Loadlock while raw wafer number less or equal than this number" max="10" min="0" paramter="" tag="" unit="" type="Integer" />

+ 2 - 2
Venus/Venus_RT/Config/System_Kepler2200.sccfg

@@ -23,7 +23,7 @@
 		<config default="10"  name="CheckResourceInterval" nameView="CheckResourceInterval" description="进程资源监视间隔,单位为分钟,0为不监视" max="60" min="0" paramter="" tag="" unit="min" type="Integer"/>
 		<config default="1000"  name="DataCollectionInterval" nameView="DataCollectionInterval" description="插入数据时间间隔" max="2000" min="200" paramter="" tag="" unit="ms" type="Integer"/>
 		<config default="false" name="IsLogExcludeInfoType" nameView="Log Exclude Info Type" description="TopView Log排除info类型信息" max="" min="" paramter="" tag="" unit="" type="Bool" />
-		<config default="Kepler" name="Name" nameView="Name" description="Name" tag="" unit="" type="String" />
+		<config default="0" name="LoadlockSlotInOutOption"  nameView="Loadlock Slot In Out Option" description="0, All slots in All slot Out; 1, Upper slots in lower slots out; 2, Lower slots in Upper slot out " max="2" min="0" paramter="" tag="" unit="" type="Integer"/>
 		
 		<!--<configs name="SetUp" nameView="Set Up" visible="false">
 			<config default="true" name="EPDInstalled" nameView="Is EPD installed" description="EPD是否安装" max="" min="" paramter="" tag="" unit="" type="Bool" />
@@ -98,7 +98,7 @@
 		<config default="false" name="IgnoreWaterFlowError" nameView="Ignore water flow alarm" description="是否忽略冷却水报警" max="" min="" paramter="" tag="" unit="" type="Bool" />
 
 		<config default="40" name="DelayTimeBeforeLiftDown" nameView="Delay Time before lift down" description="冷却前,lift pin停留多久再落下" max="600" min="0" paramter="" tag="" unit="second" type="Integer" />
-		<config default="0" name="SingleArmOption"   nameView="Single Arm Option"   description="0, both; 1, Blade1; 2, Blade2" max="2" min="0" paramter="" tag="" unit="" type="Integer" />
+		<config default="0" name="SingleArmOption"   nameView="Single Arm Option"   description="0, Both; 1, Blade1; 2, Blade2; 3, Blade1 In Blade2 Out; 4, Blade2 In Blade1 Out" max="4" min="0" paramter="" tag="" unit="" type="Integer" />
 
 		<config default="4" name="LLAutoPumpInWaferOpt" nameView="Loadlock Auto Pump Option for Raw Wafer" description="Auto Pumping Loadlock while raw wafer number big or equal then this number" max="10" min="0" paramter="" tag="" unit="" type="Integer" />
 		<config default="0" name="LLAutoPumpOutWaferOpt" nameView="Loadlock Auto Pump Option for Processed Wafer" description="Auto Pumping Loadlock while processed wafer number less or equal than this number" max="10" min="0" paramter="" tag="" unit="" type="Integer" />

+ 2 - 2
Venus/Venus_RT/Config/System_Kepler2300.sccfg

@@ -23,7 +23,7 @@
 		<config default="1000"  name="DataCollectionInterval" nameView="DataCollectionInterval" description="插入数据时间间隔" max="2000" min="200" paramter="" tag="" unit="ms" type="Integer"/>
 		<config default="false" name="IsEnableEthercat" nameView="Is Enable Ethercat" description="是否开启凌华Ethercat" max="" min="" paramter="" tag="" unit="" type="Bool" visible="false"/>
 		<config default="false" name="IsLogExcludeInfoType" nameView="Log Exclude Info Type" description="TopView Log排除info类型信息" max="" min="" paramter="" tag="" unit="" type="Bool" />
-		<config default="Kepler" name="Name" nameView="Name" description="Name" tag="" unit="" type="String" />
+		<config default="0" name="LoadlockSlotInOutOption"  nameView="Loadlock Slot In Out Option" description="0, All slots in All slots Out; 1, Upper slots in lower slots out; 2, Lower slots in Upper slot out " max="2" min="0" paramter="" tag="" unit="" type="Integer"/>
 		<configs name="SetUp" nameView="Set Up" visible="false">
 			<config default="true" name="EPDInstalled" nameView="Is EPD installed" description="EPD是否安装" max="" min="" paramter="" tag="" unit="" type="Bool" />
 		</configs>
@@ -160,7 +160,7 @@
 		<config default="90"  name="ControlPressureSetPoint"   nameView="Control Pressure SetPoint"   description="TM Chamber 控压 设定值" max="200" min="0" paramter="" tag="" unit="mTorr" type="Integer" />
 		<config default="50"  name="WithLLPressureDifference"   nameView="TM LL Pressure Difference"   description="TM和LL压差" max="100" min="0" paramter="" tag="" unit="mTorr" type="Integer" />
 		<config default="50"  name="WithPMPressureDifference"   nameView="TM PM Pressure Difference"   description="TM和PM压差" max="100" min="0" paramter="" tag="" unit="mTorr" type="Integer" />
-		<config default="0" name="SingleArmOption"   nameView="Single Arm Option"   description="0, both; 1, Blade1; 2, Blade2" max="2" min="0" paramter="" tag="" unit="" type="Integer" />
+		<config default="0" name="SingleArmOption"   nameView="Single Arm Option"   description="0, Both; 1, Blade1; 2, Blade2; 3, Blade1 In Blade2 Out; 4, Blade2 In Blade1 Out" max="4" min="0" paramter="" tag="" unit="" type="Integer" />
 		<config default="0" name="QueryAWCOption"   nameView="Query AWC Option"   description="0, None; 1, Only TMPick Open; 2, Only TMPlace Open; 3, Both Open" max="3" min="0" paramter="" tag="" unit="" type="Integer" />
 
 		<config default="0" name="LLAutoVentInWaferOpt" nameView="Loadlock Aut Vent Option For Raw Wafer" description="Auto Venting Loadlock while raw wafer number less or equal than this number" max="10" min="0" paramter="" tag="" unit="" type="Integer" />

+ 0 - 1
Venus/Venus_RT/Config/System_VenusDE.sccfg

@@ -23,7 +23,6 @@
 		<config default="1000"  name="DataCollectionInterval" nameView="DataCollectionInterval" description="插入数据时间间隔" max="2000" min="200" paramter="" tag="" unit="ms" type="Integer"/>
 		<config default="false" name="IsEnableEthercat" nameView="Is Enable Ethercat" description="是否开启凌华Ethercat" max="" min="" paramter="" tag="" unit="" type="Bool" visible="false"/>
 		<config default="false" name="IsLogExcludeInfoType" nameView="Log Exclude Info Type" description="TopView Log排除info类型信息" max="" min="" paramter="" tag="" unit="" type="Bool" />
-		<config default="Kepler" name="Name" nameView="Name" description="Name" tag="" unit="" type="String" />
 		<configs name="SetUp" nameView="Set Up" visible="false">
 			<config default="true" name="EPDInstalled" nameView="Is EPD installed" description="EPD是否安装" max="" min="" paramter="" tag="" unit="" type="Bool" />
 		</configs>

+ 0 - 1
Venus/Venus_RT/Config/System_VenusSE.sccfg

@@ -23,7 +23,6 @@
     <config default="1000"  name="DataCollectionInterval" nameView="DataCollectionInterval" description="插入数据时间间隔" max="2000" min="200" paramter="" tag="" unit="ms" type="Integer"/>
     <config default="false" name="IsEnableEthercat" nameView="Is Enable Ethercat" description="是否开启凌华Ethercat" max="" min="" paramter="" tag="" unit="" type="Bool" visible="false"/>
 	<config default="false" name="IsLogExcludeInfoType" nameView="Log Exclude Info Type" description="TopView Log排除info类型信息" max="" min="" paramter="" tag="" unit="" type="Bool" />
-    <config default="Kepler" name="Name" nameView="Name" description="Name" tag="" unit="" type="String" />
     <configs name="SetUp" nameView="Set Up" visible="false">
       <config default="true" name="EPDInstalled" nameView="Is EPD installed" description="EPD是否安装" max="" min="" paramter="" tag="" unit="" type="Bool" />
     </configs>

+ 506 - 20
Venus/Venus_RT/Modules/SystemDispatcher.cs

@@ -699,6 +699,13 @@ namespace Venus_RT.Modules
         }
     }
 
+    enum LLSlotInOutOpt
+    {
+        AllInAllOut,
+        UpperInLowerOut,
+        LowerInUpperOut,
+    }
+
     class SystemDispatcher : ICycle
     {
         private List<ControlJobInfo> _lstControlJobs = new List<ControlJobInfo>();
@@ -732,6 +739,7 @@ namespace Venus_RT.Modules
 
         private int _efemRobotSingleArmOption = 0;
         private int _tmRobotSingleArmOption = 0;
+        private LLSlotInOutOpt _LLSlotInOutOption = 0;
         private Dictionary<ModuleName, int> _lpCycleWafer = new Dictionary<ModuleName, int>();
         private Dictionary<ModuleName, int> _lpCycleCount = new Dictionary<ModuleName, int>();
         private Stopwatch _cycleWatch = new Stopwatch();
@@ -804,6 +812,12 @@ namespace Venus_RT.Modules
             _cycledCount                = 0;
             _throughput                 = 0;
 
+            // rounding TM robot single arm option
+            if(_tmRobotSingleArmOption > 2 && _LLSlotInOutOption == LLSlotInOutOpt.AllInAllOut)
+            {
+                _tmRobotSingleArmOption = 0;
+            }
+
             _cycleWatch.Stop();
             _lpCycleWafer.Clear();
             _lpCycleCount.Clear();
@@ -830,8 +844,6 @@ namespace Venus_RT.Modules
         {
         }
 
-
-
         public bool CreateJob(Dictionary<string, object> param, out string reason)
         {
             reason = "";
@@ -1176,10 +1188,6 @@ namespace Venus_RT.Modules
 
             if (RtInstance.ConfigType == ConfigType.Kepler2200)
             {
-                //if (!CheckSequenceKepler2200TemperatureReady(GetFirstProcessJob(cj).Sequence))
-                //{
-                //    return false;
-                //}
                 foreach (var item in _lstProcessJobs)
                 {
                     if (item.ControlJobName == cj.Name && !CheckSequenceKepler2200TemperatureReady(item.Sequence))
@@ -1397,7 +1405,11 @@ namespace Venus_RT.Modules
 
         private void prelude()
         {
-            
+            bool available = true;
+            foreach(var mod in _dictModuleTask)
+            {
+                available = mod.Value.Scheduler.IsAvailable; // force scheduler update
+            }
         }
 
         private void epilogue()
@@ -1940,7 +1952,7 @@ namespace Venus_RT.Modules
             var waferStaus = GetLLReadyInOutSlots(ll);
             var atmWafers = _lstWaferTasks.Where(wafer => (wafer.movingStatus == RState.End ||wafer.movingStatus == RState.Init) && (ModuleHelper.IsEFEMRobot(wafer.currentMod) || ModuleHelper.IsAligner(wafer.currentMod))).ToList();
             var notAlignedWafer = atmWafers.Where(wafer => !wafer.IsAligned).ToList();
-            var readyInWafers = _lstWaferTasks.Where(wafer => ModuleHelper.IsLoadPort(wafer.currentMod) && wafer.movingStatus == RState.Init && wafer.pressureStatus == RState.Init).OrderBy(wafer => wafer.currentSlot).ToArray();
+            var readyInWafers = _lstWaferTasks.Where(wafer => ModuleHelper.IsLoadPort(wafer.currentMod) && wafer.movingStatus == RState.Init && wafer.pressureStatus == RState.Init && CanWaferGotoLL(wafer, ll)).OrderBy(wafer => wafer.currentSlot).ToArray();
             var freeHands = GetEFEMFreeHand();
 
             if ((atmWafers.Count >= 2 && atmWafers.Count(wafer => !wafer.IsAligned) == 0) || freeHands.Count == 0 || readyInWafers.Length == 0)
@@ -1992,7 +2004,7 @@ namespace Venus_RT.Modules
                 (atmWafers.Count(wafer => ModuleHelper.IsEFEMRobot(wafer.currentMod)) == 0 || waferStaus.emptySlot.Count >= 2))
             {
                 var alignerWafer = atmWafers.Where(wafer => ModuleHelper.IsAligner(wafer.currentMod)).First();
-                if(CanWaferGotoLL(alignerWafer, ll))
+                if (CanWaferGotoLL(alignerWafer, ll))
                 {
                     alignerWafer.RouteTo(ll, waferStaus.emptySlot[placeCount]);
 
@@ -2006,7 +2018,7 @@ namespace Venus_RT.Modules
             var armWafers = atmWafers.Where(wafer => ModuleHelper.IsEFEMRobot(wafer.currentMod)).ToArray();
             foreach(var wafer in armWafers)
             {
-                if(CanWaferGotoLL(wafer, ll) && placeCount < waferStaus.emptySlot.Count)
+                if (CanWaferGotoLL(wafer, ll) && placeCount < waferStaus.emptySlot.Count)
                 {
                     wafer.RouteTo(ll, waferStaus.emptySlot[placeCount]);
 
@@ -2066,13 +2078,20 @@ namespace Venus_RT.Modules
             if (_efemRobotStatus != RState.End)
                 return;
 
-            if(_LLASlotNumber == 2)
+            if(_LLSlotInOutOption == LLSlotInOutOpt.AllInAllOut)
             {
-                Routing2SlotATMSystem();
+                if (_LLASlotNumber == 2)
+                {
+                    Routing2SlotATMSystem();
+                }
+                else if (_LLASlotNumber == 4)
+                {
+                    Routing4SlotATMSystem();
+                }
             }
-            else if(_LLASlotNumber == 4)
+            else
             {
-                Routing4SlotATMSystem();
+                RoutingFixedSlotATMSystem();
             }
         }
 
@@ -2167,6 +2186,41 @@ namespace Venus_RT.Modules
             }
         }
 
+        private void RoutingFixedSlotATMSystem()
+        {
+            var atmWaferCount = _lstWaferTasks.Where(wafer => ModuleHelper.IsEFEMRobot(wafer.currentMod) || ModuleHelper.IsAligner(wafer.currentMod)).Count();
+            var lls = _dictModuleTask.Where(mod => ModuleHelper.IsLoadLock(mod.Key) && mod.Value.Scheduler.IsOnline);
+
+            if ( atmWaferCount == 0 || (_efemRobotSingleArmOption == 0 && atmWaferCount == 1 ))
+            {
+                foreach(var ll in lls)
+                {
+                    if (ForwardATMWafers(ll.Key))
+                        return;
+                }
+            }
+
+            // try to match a ll swap action
+            if(_LLASlotNumber == 2)
+            {
+                var readyReturnLL = lls.Where(ll => GetLLFixedReadyInOutSlots(ll.Key).eOutSlot.Count > 0).OrderBy(ll => ll.Value.TimeToReady);
+                foreach(var ll in readyReturnLL)
+                {
+                    if (ExchangeWaferWithLL(ll.Key))
+                        return;
+                }
+            }
+            else
+            {
+                var readyReturnLL = lls.Where(ll => GetLLFixedReadyInOutSlots(ll.Key).eOutSlot.Count > 0).OrderByDescending(ll => GetLLFixedReadyInOutSlots(ll.Key).eOutSlot.Count);
+                foreach (var ll in readyReturnLL)
+                {
+                    if (ExchangeWaferWithLL(ll.Key))
+                        return;
+                }
+            }
+        }
+
         private void RoutingSameLLInOutATMSystem()
         {
             var atmWaferCount = _lstWaferTasks.Where(wafer => ModuleHelper.IsEFEMRobot(wafer.currentMod) || ModuleHelper.IsAligner(wafer.currentMod)).Count();
@@ -2621,6 +2675,19 @@ namespace Venus_RT.Modules
             return (returnWafers[preferLL], preferLL, llSwapActions[preferLL]);
         }
 
+        ModuleName FindTheBestMovePathWithFixSlot(IEnumerable<KeyValuePair<ModuleName, ModuleTask>> lls, List<WaferTask> pmWafers)
+        {
+            Dictionary<ModuleName, int> moveValues = new Dictionary<ModuleName, int>();
+            foreach(var ll in lls)
+            {
+                var llWaferStatus = GetLLFixedReadyInOutSlots(ll.Key);
+                moveValues[ll.Key] = Math.Min(pmWafers.Where(wt => CanWaferGotoLL(wt, ll.Key)).Count(), GetLLFixedReadyInOutSlots(ll.Key).tInSlot.Count) + llWaferStatus.tOutSlot.Count;
+            }
+
+            var preferLL = moveValues.OrderByDescending(v => v.Value).First().Key;
+            return moveValues[preferLL] > 0 ? preferLL : ModuleName.System;
+        }
+
         void RoutingTMSwapActions(List<MoveItem> actions)
         {
             foreach (var action in actions)
@@ -2681,13 +2748,20 @@ namespace Venus_RT.Modules
                         return;
                 }
 
-                if(_LLASlotNumber == 2)
+                if(_LLSlotInOutOption == LLSlotInOutOpt.AllInAllOut)
                 {
-                    Routing2SlotVacSystem();
+                    if (_LLASlotNumber == 2)
+                    {
+                        Routing2SlotVacSystem();
+                    }
+                    else if (_LLASlotNumber == 4)
+                    {
+                        Routing4SlotVacSystem();
+                    }
                 }
-                else if(_LLASlotNumber == 4)
+                else
                 {
-                    Routing4SlotVacSystem();
+                    RoutingFixedSlotVacSystem();
                 }
             }
         }
@@ -3060,7 +3134,6 @@ namespace Venus_RT.Modules
                     }
                 }
             }
-            
         }
 
         private void Routing4SlotVacSystem()
@@ -3185,6 +3258,375 @@ namespace Venus_RT.Modules
             }
         }
 
+        private void RoutingFixedSlotVacSystem()
+        {
+            var robotWafers = _lstWaferTasks.Where(wt => ModuleHelper.IsTMRobot(wt.currentMod));
+            var lls = _dictModuleTask.Where(mod => ModuleHelper.IsLoadLock(mod.Key) && mod.Value.Scheduler.IsOnline);
+            if (_LLASlotNumber == 2)
+            {
+                if (robotWafers.Count() == 1)
+                {
+                    if(ModuleHelper.IsPm(robotWafers.First().destMod))
+                    {
+                        var destPM = robotWafers.First().destMod;
+                        if(_dictModuleTask[destPM].TimeToReady <= 5)
+                        {
+                            if(_lstWaferTasks.Exists(wt => wt.currentMod == destPM))
+                            {
+                                var pickHand = SelectTMPickArm(destPM);
+                                if(pickHand != Hand.None)
+                                {
+                                    var pmSwap = new List<MoveItem>();
+                                    pmSwap.Add(new MoveItem(destPM, 0, ModuleName.TMRobot, (int)pickHand, pickHand));
+                                    pmSwap.Add(new MoveItem(ModuleName.TMRobot, robotWafers.First().currentSlot, destPM, 0, (Hand)robotWafers.First().currentSlot));
+                                    _tmSchdActions.Enqueue(pmSwap);
+                                    return;
+                                }
+                                
+                            }
+                            else
+                            {
+                                _tmSchdActions.Enqueue(new List<MoveItem> { new MoveItem(ModuleName.TMRobot, robotWafers.First().currentSlot, destPM, 0, (Hand)robotWafers.First().currentSlot) });
+                                return;
+                            }
+                        }
+                        else
+                        {
+                            var readySwapLL = lls.Where(ll => CanWaferGotoLL(robotWafers.First(), ll.Key) &&
+                                GetLLFixedReadyInOutSlots(ll.Key).tOutSlot.Count == 1 &&
+                                GetLLFixedReadyInOutSlots(ll.Key).tInSlot.Count == 1 &&
+                                ll.Value.TimeToReady <= 10);
+                            if (readySwapLL.Count() > 0)
+                            {
+                                var destLL = readySwapLL.First().Key;
+                                Hand pickHand = SelectTMPickArm(destLL);
+                                if (pickHand != Hand.None)
+                                {
+                                    Hand placeHand = (Hand)robotWafers.First().currentSlot;
+                                    var llSwap = new List<MoveItem>();
+                                    var llSlots = GetLLFixedReadyInOutSlots(destLL);
+                                    llSwap.Add(new MoveItem(destLL, llSlots.tOutSlot.First(), ModuleName.TMRobot, (int)pickHand, pickHand));
+                                    llSwap.Add(new MoveItem(ModuleName.TMRobot, (int)placeHand, destLL, llSlots.tInSlot.First(), placeHand));
+                                    _tmSchdActions.Enqueue(llSwap);
+                                    return;
+                                }
+                            }
+
+                            var emptyLL = lls.Where(ll => CanWaferGotoLL(robotWafers.First(), ll.Key) &&
+                                                            GetLLFixedReadyInOutSlots(ll.Key).tInSlot.Count == 1 &&
+                                                            ll.Value.TimeToReady <= 2);
+                            if (emptyLL.Count() > 0)
+                            {
+                                var destLL = emptyLL.First().Key;
+                                _tmSchdActions.Enqueue(new List<MoveItem> { new MoveItem( ModuleName.TMRobot,
+                                                                                            robotWafers.First().currentSlot,
+                                                                                            destLL,
+                                                                                            GetLLFixedReadyInOutSlots(destLL).tInSlot.First(),
+                                                                                            (Hand)robotWafers.First().currentSlot) });
+
+                                return;
+                            }
+                        }
+                    }
+                }
+                else if (robotWafers.Count() == 0)
+                {
+                    var readyReturnWafers = _lstWaferTasks.Where(wt => ModuleHelper.IsPm(wt.currentMod) && ModuleHelper.IsLoadPort(wt.destMod)).ToList();
+                    if(readyReturnWafers.Count > 0)
+                    {
+                        // try to do a LL swap action
+                        var readySwapLL = lls.Where(ll => GetLLFixedReadyInOutSlots(ll.Key).tOutSlot.Count == 1 && GetLLFixedReadyInOutSlots(ll.Key).tInSlot.Count == 1 && ll.Value.TimeToReady <= 10);
+                        foreach (var pmWafer in readyReturnWafers)
+                        {
+                            foreach (var ll in readySwapLL)
+                            {
+                                if (CanWaferGotoLL(pmWafer, ll.Key))
+                                {
+                                    var returnHand = SelectTMPickArm(pmWafer.currentMod);
+                                    var pickHand = SelectTMPickArm(ll.Key);
+                                    var llWaferStatus = GetLLFixedReadyInOutSlots(ll.Key);
+                                    _tmSchdActions.Enqueue(new List<MoveItem> { new MoveItem(pmWafer.currentMod, pmWafer.currentSlot, ModuleName.TMRobot, (int)returnHand, returnHand) });
+
+                                    var llSwapActions = new List<MoveItem>();
+                                    llSwapActions.Add(new MoveItem(ModuleName.TMRobot, (int)returnHand, ll.Key, llWaferStatus.tInSlot.First(), returnHand));
+                                    llSwapActions.Add(new MoveItem(ll.Key, llWaferStatus.tOutSlot.First(), ModuleName.TMRobot, (int)pickHand, pickHand));
+                                    _tmSchdActions.Enqueue(llSwapActions);
+                                    return;
+                                }
+                            }
+                        }
+
+                        // just return to LL
+                        var readyInLL = lls.Where(ll => GetLLFixedReadyInOutSlots(ll.Key).tInSlot.Count == 1 && ll.Value.TimeToReady <= 5);
+                        foreach(var pmWafer in readyReturnWafers)
+                        {
+                            foreach(var ll in readyInLL)
+                            {
+                                if(CanWaferGotoLL(pmWafer, ll.Key))
+                                {
+                                    var returnHand = SelectTMPickArm(pmWafer.currentMod);
+                                    _tmSchdActions.Enqueue(new List<MoveItem> { new MoveItem(pmWafer.currentMod, pmWafer.currentSlot, ModuleName.TMRobot, (int)returnHand, returnHand) });
+                                    _tmSchdActions.Enqueue(new List<MoveItem> { new MoveItem(ModuleName.TMRobot, (int)returnHand, ll.Key, GetLLFixedReadyInOutSlots(ll.Key).tInSlot.First(), returnHand) });
+                                    return;
+                                }
+                            }
+                        }
+                    }
+
+                    var readyPushInLLs = lls.Where(ll => GetLLFixedReadyInOutSlots(ll.Key).tOutSlot.Count == 1 && ll.Value.TimeToReady <= 2);
+                    if(readyPushInLLs.Count() > 0)
+                    {
+                        var destLL = readyPushInLLs.First().Key;
+                        if(readyPushInLLs.Count() == 2 && readyPushInLLs.First().Value.Scheduler.IsAtm && readyPushInLLs.Last().Value.Scheduler.IsVac)
+                        {
+                            destLL = readyPushInLLs.Last().Key;
+                        }
+
+                        var pickHand = SelectTMPickArm(destLL);
+                        _tmSchdActions.Enqueue(new List<MoveItem> { new MoveItem(destLL, GetLLFixedReadyInOutSlots(destLL).tOutSlot.First(), ModuleName.TMRobot, (int)pickHand, pickHand) });
+                        return;
+                    }
+                }
+                else
+                {
+                    // should not go here
+                    LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, "Had better avoid picking two wafers on TM robot arms under Loadlock fixed slot pattern");
+                    foreach(var wafer in robotWafers)
+                    {
+                        if(ModuleHelper.IsPm(wafer.destMod))
+                        {
+                            if(!_lstWaferTasks.Exists(wt => wt.currentMod == wafer.destMod) && _dictModuleTask[wafer.destMod].TimeToReady  <= 2)
+                            {
+                                _tmSchdActions.Enqueue(new List<MoveItem> { new MoveItem(ModuleName.TMRobot, wafer.currentSlot, wafer.destMod, 0, (Hand)wafer.currentSlot) });
+                                return;
+                            }
+                        }
+                        else
+                        {
+                            var readyInLL = lls.Where(ll => GetLLFixedReadyInOutSlots(ll.Key).tInSlot.Count == 1 && CanWaferGotoLL(wafer, ll.Key) && ll.Value.TimeToReady <= 2);
+                            if(readyInLL.Count() > 0)
+                            {
+                                _tmSchdActions.Enqueue(new List<MoveItem> { new MoveItem(ModuleName.TMRobot, wafer.currentSlot, readyInLL.First().Key, GetLLFixedReadyInOutSlots(readyInLL.First().Key).tInSlot.First(), (Hand)wafer.currentSlot) });
+                                return;
+                            }
+                        }
+                    }
+                }
+            }
+            else //  4 slot Loadlock
+            {
+                if(robotWafers.Count() > 0)
+                {
+                    if (robotWafers.Count() == 2)
+                    {
+                        if (robotWafers.All(wt => ModuleHelper.IsLoadPort(wt.destMod)))
+                        {
+                            var destLL = lls.Where(ll => robotWafers.All(wt => CanWaferGotoLL(wt, ll.Key)) &&
+                                                    ll.Value.TimeToReady <= 10 &&
+                                                    GetLLFixedReadyInOutSlots(ll.Key).tInSlot.Count == 2).
+                                                    OrderByDescending(ll => GetLLFixedReadyInOutSlots(ll.Key).tOutSlot.Count).
+                                                    OrderBy(ll => ll.Value.TimeToReady);
+
+                            if (destLL.Count() > 0)
+                            {
+                                var swapLL = destLL.First().Key;
+                                var llWaferStatus = GetLLFixedReadyInOutSlots(swapLL);
+
+                                var swapActions = new List<MoveItem>();
+                                swapActions.Add(new MoveItem(ModuleName.TMRobot, robotWafers.First().currentSlot, swapLL, llWaferStatus.tInSlot.First(), (Hand)robotWafers.First().currentSlot));
+                                swapActions.Add(new MoveItem(ModuleName.TMRobot, robotWafers.Last().currentSlot, swapLL, llWaferStatus.tInSlot.Last(), (Hand)robotWafers.Last().currentSlot));
+
+                                int pickCount = 0;
+                                foreach (var slot in llWaferStatus.eOutSlot)
+                                {
+                                    swapActions.Add(new MoveItem(swapLL, slot, ModuleName.TMRobot, pickCount, (Hand)pickCount));
+                                }
+                                _tmSchdActions.Enqueue(swapActions);
+                                return;
+                            }
+                        }
+
+                        foreach (var wafer in robotWafers)
+                        {
+                            if (ModuleHelper.IsPm(wafer.destMod))
+                            {
+                                if (!_lstWaferTasks.Exists(wt => wt.currentMod == wafer.destMod) && _dictModuleTask[wafer.destMod].TimeToReady <= 5)
+                                {
+                                    _tmSchdActions.Enqueue(new List<MoveItem> { new MoveItem(ModuleName.TMRobot, wafer.currentSlot, wafer.destMod, 0, (Hand)wafer.currentSlot) });
+                                    return;
+                                }
+                            }
+                            else
+                            {
+                                var placeLL = lls.Where(ll => CanWaferGotoLL(wafer, ll.Key) && ll.Value.TimeToReady <= 5 && GetLLFixedReadyInOutSlots(ll.Key).tInSlot.Count > 0).OrderBy(ll => ll.Value.TimeToReady);
+                                if (placeLL.Count() > 0)
+                                {
+                                    _tmSchdActions.Enqueue(new List<MoveItem> { new MoveItem(ModuleName.TMRobot, wafer.currentSlot, placeLL.First().Key, GetLLFixedReadyInOutSlots(placeLL.First().Key).tInSlot.First(), (Hand)wafer.currentSlot) });
+                                    return;
+                                }
+                            }
+                        }
+                    }
+                    else if (robotWafers.Count() == 1)
+                    {
+                        var robotWafer = robotWafers.First();
+                        if (ModuleHelper.IsPm(robotWafer.destMod))
+                        {
+                            if (_dictModuleTask[robotWafer.destMod].TimeToReady <= 5)
+                            {
+                                if (_lstWaferTasks.Exists(wt => wt.currentMod == robotWafer.destMod))
+                                {
+                                    var pmSwap = new List<MoveItem>();
+                                    pmSwap.Add(new MoveItem(robotWafer.destMod, 0, ModuleName.TMRobot, 1 - robotWafer.currentSlot, (Hand)(1 - robotWafer.currentSlot)));
+                                    pmSwap.Add(new MoveItem(ModuleName.TMRobot, robotWafer.currentSlot, robotWafer.destMod, 0, (Hand)robotWafer.currentSlot));
+                                    _tmSchdActions.Enqueue(pmSwap);
+                                    return;
+                                }
+                                else
+                                {
+                                    _tmSchdActions.Enqueue(new List<MoveItem> { new MoveItem(ModuleName.TMRobot, robotWafer.currentSlot, robotWafer.destMod, 0, (Hand)robotWafer.currentSlot) });
+                                    return;
+                                }
+                            }
+                        }
+                        else
+                        {
+                            var destLL = lls.Where(ll => CanWaferGotoLL(robotWafer, ll.Key) &&
+                                                    ll.Value.TimeToReady <= 10 &&
+                                                    GetLLFixedReadyInOutSlots(ll.Key).tInSlot.Count > 0).
+                                                    OrderByDescending(ll => GetLLFixedReadyInOutSlots(ll.Key).tOutSlot.Count).
+                                                    OrderBy(ll => ll.Value.TimeToReady);
+
+                            if (destLL.Count() > 0)
+                            {
+                                var swapLL = destLL.First().Key;
+                                var llWaferStatus = GetLLFixedReadyInOutSlots(swapLL);
+                                var llSwap = new List<MoveItem>();
+                                llSwap.Add(new MoveItem(ModuleName.TMRobot, robotWafer.currentSlot, swapLL, llWaferStatus.tInSlot.First(), (Hand)robotWafer.currentSlot));
+                                int pickCount = 0;
+                                foreach (var slot in llWaferStatus.tOutSlot)
+                                {
+                                    llSwap.Add(new MoveItem(swapLL, slot, ModuleName.TMRobot, pickCount, (Hand)pickCount));
+                                    pickCount++;
+                                }
+
+                                _tmSchdActions.Enqueue(llSwap);
+                                return;
+                            }
+                        }
+                    }
+                }
+                else // robot empty
+                {
+                    var nearlyReadyReturnWafers = _lstWaferTasks.Where(wt => ModuleHelper.IsPm(wt.currentMod) && (ModuleHelper.IsLoadPort(wt.destMod) || _dictModuleTask[wt.currentMod].TimeToReady <= 10)).OrderBy(wt => _dictModuleTask[wt.currentMod].TimeToReady).ToList();
+                    var readyLL = lls.Where(ll => ll.Value.TimeToReady <= 10);
+                    if(readyLL.Count() > 0)
+                    {
+                        var llPath = FindTheBestMovePathWithFixSlot(readyLL, nearlyReadyReturnWafers);
+                        if(ModuleHelper.IsLoadLock(llPath))
+                        {
+                            int returnCount = 0;
+                            var llWaferStatus = GetLLFixedReadyInOutSlots(llPath);
+                            var llSwapActions = new List<MoveItem>();
+                            foreach(var wafer in nearlyReadyReturnWafers)
+                            {
+                                if(CanWaferGotoLL(wafer, llPath) && returnCount < Math.Min(llWaferStatus.tInSlot.Count, 2))
+                                {
+                                    _tmSchdActions.Enqueue(new List<MoveItem> { new MoveItem(wafer.currentMod, 0, ModuleName.TMRobot, returnCount, (Hand)returnCount) });
+                                    llSwapActions.Add(new MoveItem(ModuleName.TMRobot, returnCount, llPath, llWaferStatus.tInSlot[returnCount], (Hand)returnCount));
+                                    returnCount++;
+                                }
+                            }
+
+                            int pickCount = 0;
+                            foreach(var slot in llWaferStatus.tOutSlot)
+                            {
+                                if (pickCount < 2)
+                                {
+                                    llSwapActions.Add(new MoveItem(llPath, slot, ModuleName.TMRobot, pickCount, (Hand)pickCount));
+                                    pickCount++;
+                                }
+                            }
+
+                            if(llSwapActions.Count > 0)
+                            {
+                                _tmSchdActions.Enqueue(llSwapActions);
+                            }
+
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+
+        private bool IsTMRobotArmNotReserved(Hand hand)
+        {
+            if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, (int)hand) || _lstWaferTasks.Exists(wt => wt.currentMod == ModuleName.TMRobot && wt.currentSlot == (int)hand))
+                return false;
+
+            foreach(var acs in _tmSchdActions)
+            {
+                foreach(var ac in acs)
+                {
+                    if ((ac.DestinationModule == ModuleName.TMRobot || ac.SourceModule == ModuleName.TMRobot) && ac.DestinationSlot == (int)hand)
+                        return false;
+                }
+            }
+
+            return true;
+        }
+
+        private Hand SelectTMPickArm(ModuleName mod)
+        {
+            switch (_tmRobotSingleArmOption)
+            {
+                case 0:
+                    {
+                        if (IsTMRobotArmNotReserved(Hand.Blade1))
+                            return Hand.Blade1;
+
+                        if (IsTMRobotArmNotReserved(Hand.Blade2))
+                            return Hand.Blade2;
+                    }
+                    break;
+                case 1:
+                    {
+                        if (IsTMRobotArmNotReserved(Hand.Blade1))
+                            return Hand.Blade1;
+                    }
+                    break;
+                case 2:
+                    {
+                        if (IsTMRobotArmNotReserved(Hand.Blade2))
+                            return Hand.Blade2;
+                    }
+                    break;
+                // Blade1 In Blade2 Out
+                case 3: 
+                    {
+                        if (ModuleHelper.IsLoadLock(mod) && IsTMRobotArmNotReserved(Hand.Blade1))
+                            return Hand.Blade1;
+
+                        if (ModuleHelper.IsPm(mod) && IsTMRobotArmNotReserved(Hand.Blade2))
+                            return Hand.Blade2;
+                    }
+                    break;
+                // Blade2 In Blade1 Out
+                case 4:
+                    {
+                        if (ModuleHelper.IsLoadLock(mod) && IsTMRobotArmNotReserved(Hand.Blade2))
+                            return Hand.Blade2;
+
+                        if (ModuleHelper.IsPm(mod) && IsTMRobotArmNotReserved(Hand.Blade1))
+                            return Hand.Blade1;
+                    }
+                    break;
+            }
+
+            return Hand.None;
+        }
+
         private List<Hand> GetTMFreeHand()
         {
             var lstHands = new List<Hand>();
@@ -3279,6 +3721,50 @@ namespace Venus_RT.Modules
             return (readInSlots, readyOutSlots, emptySlots);
         }
 
+        private (List<int> eInSlot, List<int> eOutSlot, List<int> tInSlot, List<int> tOutSlot) GetLLFixedReadyInOutSlots(ModuleName ll)
+        {
+            var eInSlot = new List<int>();
+            var eOutSlot = new List<int>();
+            var tInSlot = new List<int>();
+            var tOutSlot = new List<int>();
+
+            int SlotNumber = ll == ModuleName.LLA ? _LLASlotNumber : _LLBSlotNumber;
+            for (int slot = 0; slot < SlotNumber; slot++)
+            {
+                if (WaferManager.Instance.CheckNoWafer(ll, slot) && !_lstWaferTasks.Exists(wafer => (wafer.routedMod == ll && wafer.routedSlot == slot) || (wafer.currentMod == ll && wafer.currentSlot == slot) || (wafer.nextMod == ll && wafer.nextSlot == slot)))
+                {
+                    if ((slot >= SlotNumber / 2 && _LLSlotInOutOption == LLSlotInOutOpt.UpperInLowerOut) || 
+                        (slot < SlotNumber / 2 && _LLSlotInOutOption == LLSlotInOutOpt.LowerInUpperOut))
+                    {
+                        eInSlot.Add(slot);
+                    }
+                    else
+                    {
+                        tInSlot.Add(slot);
+                    }
+                }
+
+                if (WaferManager.Instance.CheckHasWafer(ll, slot))
+                {
+                    var wafer = WaferManager.Instance.GetWafer(ll, slot);
+                    var waferTask = _lstWaferTasks.Find(wt => wt.waferId == wafer.InnerId && wt.currentMod == ll && wt.currentSlot == slot);
+                    if(waferTask == null)
+                    {
+                        LOG.Write(eEvent.ERR_ROUTER, ll, $"Routing failed, cannot find inner task associated with {wafer.WaferOrigin}");
+                    }
+                    else if (waferTask.movingStatus == RState.End)
+                    {
+                        if (ModuleHelper.IsPm(waferTask.destMod))
+                            tOutSlot.Add(slot);
+                        else if (_dictModuleTask[ll].Scheduler.WaferArrivedTicks(slot) > waferTask.llDelayTime * 1000)
+                            eOutSlot.Add(slot);
+                    }
+                }
+            }
+            
+            return (eInSlot, eOutSlot, tInSlot, tOutSlot);
+        }
+
         private (List<WaferInfo> wafers, List<int> emptySlots) GetLLWaferExistance(ModuleName ll)
         {
             var lstWafers = new List<WaferInfo>();
@@ -3598,7 +4084,7 @@ namespace Venus_RT.Modules
                             }
                             if (recipe.Header.Temperature != null && recipe.Header.Temperature != "" && (currentChamberTemperature > Convert.ToSingle(recipe.Header.Temperature) + 10 || currentChamberTemperature < Convert.ToSingle(recipe.Header.Temperature) - 10))
                             {
-                                LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Start job失败,由于{seq.Name}里{module}腔体温度{currentChamberTemperature}与{recipeName} Recipe温度{recipe.Header.Temperature}不匹配");
+                                LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Start job失败,由于{module}腔体温度{currentChamberTemperature}与{recipeName} Recipe温度{recipe.Header.Temperature}不匹配");
                                 return false;
                             }
                         }