浏览代码

TM Robot Pick/Place/Swap simulation ready.

sangwq 2 年之前
父节点
当前提交
bdee4a05ba

+ 5 - 0
Venus/Framework/Common/Routine/IRoutine.cs

@@ -34,6 +34,11 @@ namespace Aitex.Core.RT.Routine
         protected readonly int _delay_5s = 5000;
         protected readonly int _delay_10s = 10000;
         protected readonly int _delay_20s = 20000;
+        protected readonly int _delay_30s = 30000;
+        protected readonly int _delay_60s = 60000;
+        protected readonly int _delay_2m = 120000;
+        protected readonly int _delay_3m = 180000;
+        protected readonly int _delay_5m = 300000;
 
         public ModuleRoutineBase(ModuleName module)
         {

+ 2 - 0
Venus/Venus_Core/EventDefine.cs

@@ -56,6 +56,8 @@ namespace Aitex.Core.RT.Log{
 		ERR_TM = 2000,
 		INFO_TM = 2001,
 		WARN_TM = 2002,
+		ERR_TM_ROBOT = 2100,
+		INFO_TM_ROBOT = 2101,
 		EV_EFEM_COMMON_INFO = 4000,
 		WARN_EFEM_COMMON_WARN = 4001,
 		ERR_EFEM_COMMON_FAILED = 4002,

+ 18 - 0
Venus/Venus_RT/Config/LogDefine.json

@@ -496,6 +496,24 @@
     "Note": "TM warning"
   },
   {
+    "Id": 2100,
+    "Level": "Error",
+    "LogEnum": "ERR_TM_ROBOT",
+    "GlobalDescription_zh": "TM Robot receive an error:{0}",
+    "GlobalDescription_en": "TM Robot receive an error:{0}",
+    "Module": "TM",
+    "Note": "TM Alarm"
+  },
+  {
+    "Id": 2101,
+    "Level": "Info",
+    "LogEnum": "INFO_TM_ROBOT",
+    "GlobalDescription_zh": "{0}",
+    "GlobalDescription_en": "{0}",
+    "Module": "TM",
+    "Note": "TM Info"
+  },
+  {
     "Id": 4000,
     "Level": "Info",
     "LogEnum": "EV_EFEM_COMMON_INFO",

+ 8 - 3
Venus/Venus_RT/Config/System.sccfg

@@ -14,7 +14,8 @@
 		<config default="1" name="MaxTemperatureToleranceToTarget" nameView="Max Temperature Tolerance To Target" description="工艺最大温度差" max="100" min="1" paramter="" tag="" unit="℃" type="Double" />
 		<!--<config default="30" name="DefaultCoolingTime" nameView="Default Cooling Time" description="晶圆默认冷却时间" max="300" min="1" paramter="" tag="" unit="s" type="Integer" />-->
 		<config default="true" name="DisplayPopDialogWhenJobComplete" nameView="Pop Dialog When Job Complete" description="是否弹出Job结束对话框" max="" min="" paramter="" tag="" unit="" type="Bool" />
-		<config default="100" name="PMLLMaxPressureDifference" nameView="Max Pressure difference for open slit valve" description="" max="500" min="20" paramter="" tag="" unit="mtorr" type="Double" />
+		<config default="100" name="PMLLMaxPressureDifference" nameView="Max PM/LL Pressure difference for open slit valve" description="" max="500" min="20" paramter="" tag="" unit="mtorr" type="Double" />
+		<config default="100" name="TMLLMaxPressureDifference" nameView="Max TM/LL Pressure difference for open slit valve" description="" max="500" min="20" paramter="" tag="" unit="mtorr" type="Double" />
 		<config default="LLA,PMA" name="InstalledModules" description="安装的模块" max="" min="" paramter="" tag="" unit="" visible="false" type="String" />
 		<config default="2" name="MaxInternalWaferCount" nameView="Max Internal wafer count" description="系统可允许进入的wafer数" max="8" min="1" paramter="" tag="" unit="" visible="true" type="Integer" />
 		<config default="100" name="TurboN2FlowSetPoint" nameView="TurboN2SetPoint" description="" max="200" min="0" paramter="" tag="" unit="mtorr" type="Double" />
@@ -132,6 +133,12 @@
 		<config default="180" name="LeakCheckPumpTime" description="Leak Check Pump Time" max="7200" min="0" paramter="" tag="" unit="second" type="Integer" />
 		<config default="300" name="LeakCheckWaitTime" description="Leak Check Wait Time" max="7200" min="0" paramter="" tag="" unit="second" type="Integer" />
 		<config default="30" name="LeakRate" description="Leak Rate" max="756000" min="0" paramter="" tag="" unit="mTorrPerMin" type="Double" />
+
+		<config default="120" name="PickTimeout" nameView="Pick Timeout" description="" max="300" min="0" paramter="" tag="" unit="s" type="Integer" />
+		<config default="120" name="PlaceTimeout" nameView="Place Timeout" description="" max="300" min="0" paramter="" tag="" unit="s" type="Integer" />
+		<config default="60" name="ExtendTimeout" nameView="Extend Timeout" description="" max="300" min="0" paramter="" tag="" unit="s" type="Integer" />
+		<config default="60" name="RetractTimeout" nameView="Retract Timeout" description="" max="300" min="0" paramter="" tag="" unit="s" type="Integer" />
+		<config default="180" name="SwapTimeout" nameView="Swap Timeout" description="" max="300" min="0" paramter="" tag="" unit="s" type="Integer" />
 		
 		<configs name="TM_MFC1" nameView="MFC1" >
 			<config default="true" name="Enable" nameView="Enable" description="Enable gas 1 or not" tag="" unit="" type="Bool" />
@@ -146,8 +153,6 @@
 			<config default="10" name="MfcWarningRange" nameView="MFC Warning Range" description="" max="200" min="0" paramter="" tag="" unit="sccm" type="Integer" />
 			<config default="10" name="MfcWarningTime" nameView="MFC Warning Time" description="" max="60" min="0" paramter="" tag="" unit="second" type="Integer" />
 		</configs>
-
-
 	</configs>
 
 	<!--LLA-->

+ 1 - 0
Venus/Venus_RT/Devices/TM/ITransferRobot.cs

@@ -7,6 +7,7 @@ namespace Venus_RT.Devices
     public interface ITransferRobot
     {
         RState Status { get;}
+        bool IsHomed { get; }
         bool Home();
         bool Halt();
         bool CheckLoad(Hand hand = Hand.Blade1);

+ 71 - 0
Venus/Venus_RT/Devices/TM/JetTM.cs

@@ -453,5 +453,76 @@ namespace Venus_RT.Devices
 
             return true;
         }
+
+        public bool TurnMFSlitDoor(ModuleName loadlock, bool open, out string reason)
+        {
+            if (open)
+            {
+                bool _isATMMode = SC.GetValue<bool>("System.IsATMMode");
+                if (_isATMMode)
+                {
+                    if (!IsTMATM)
+                    {
+                        reason = $"TM is not ATM, can not open slit door";
+                        LOG.Write(eEvent.ERR_DEVICE_INFO, Module, reason);
+                        return false;
+                    }
+
+                    if (!IsModuleATM(loadlock))
+                    {
+                        reason = $"{loadlock} is not ATM, can not open slit door";
+                        LOG.Write(eEvent.ERR_DEVICE_INFO, Module, reason);
+                        return false;
+                    }
+                }
+                else
+                {
+                    double maxPressureDifference = SC.GetValue<double>("System.TMLLMaxPressureDifference");
+                    if (Math.Abs(GetModulePressure(loadlock) - GetModulePressure(ModuleName.TM)) > maxPressureDifference)
+                    {
+                        reason = $"{loadlock} and TM pressure difference exceeds the max limit {maxPressureDifference}";
+                        LOG.Write(eEvent.ERR_DEVICE_INFO, Module, reason);
+                        return false;
+                    }
+                }
+
+            }
+
+            switch(loadlock)
+            {
+                case ModuleName.LLA:
+                    return _LLATSlitDoor.SetCylinder(open, out reason);
+                case ModuleName.LLB:
+                    return _LLBTSlitDoor.SetCylinder(open, out reason);
+
+            }
+
+            reason = $"Invalid module {loadlock}";
+            LOG.Write(eEvent.ERR_DEVICE_INFO, Module, reason);
+            return false;
+        }
+
+        public bool TurnEFEMSlitDoor(ModuleName loadlock, bool open, out string reason)
+        {
+            if(open && !IsModuleATM(loadlock))
+            {
+                reason = $"{loadlock} is not ATM, can not open EFEM side slit door";
+                LOG.Write(eEvent.ERR_DEVICE_INFO, Module, reason);
+                return false;
+            }
+
+            switch (loadlock)
+            {
+                case ModuleName.LLA:
+                    return _LLAESlitDoor.SetCylinder(open, out reason);
+                case ModuleName.LLB:
+                    return _LLBESlitDoor.SetCylinder(open, out reason);
+
+            }
+
+            reason = $"Invalid module {loadlock}";
+            LOG.Write(eEvent.ERR_DEVICE_INFO, Module, reason);
+            return false;
+        }
     }
 }

+ 340 - 29
Venus/Venus_RT/Devices/TM/SIASUNRobot.cs

@@ -32,7 +32,9 @@ namespace Venus_RT.Devices
         }
 
         private RState _status;
+        private bool _IsHomed;
         public RState Status { get { return _status; } }
+        public bool IsHomed { get { return _IsHomed; } }
 
         private readonly AsyncSocket _socket;
         private OPStep _currentOP = OPStep.Idle;
@@ -42,6 +44,7 @@ namespace Venus_RT.Devices
         private string Hand2Arm(Hand hand) => hand == Hand.Blade1 ? "A" : "B";
 
         private readonly Regex _rex_check_load = new Regex(@"LOAD\s+(A|B)\s+(\w+)\s*");
+        private readonly Regex _rex_error_code = new Regex(@"_ERR\s+(\d+)\s*");
 
         public SIASUNRobot()
         {
@@ -51,6 +54,7 @@ namespace Venus_RT.Devices
             _socket.OnErrorHappened += OnErrorHappen;
 
             _status = RState.Init;
+            _IsHomed = false;
 
             _StationNumbers[ModuleName.LLA] = SC.GetValue<int>("TM.LLAStationNumber");
             _StationNumbers[ModuleName.LLB] = SC.GetValue<int>("TM.LLBStationNumber");
@@ -68,13 +72,12 @@ namespace Venus_RT.Devices
         {
             _status = RState.Running;
             _currentOP = OPStep.Home;
-            _socket.Write("HOME ALL\r");
-            return true;
+
+            return _SendCommand("HOME ALL\r");
         }
         public bool Halt()
         {
-            _socket.Write("HALT\r");
-            return true;
+            return _SendCommand("HALT\r");
         }
         public bool CheckLoad(Hand hand = Hand.Blade1)
         {
@@ -83,8 +86,7 @@ namespace Venus_RT.Devices
 
             _currentOP = hand == Hand.Blade2 ? OPStep.CheckLoad_ArmB : OPStep.CheckLoad_ArmA;
             _status = RState.Running;
-            _socket.Write($"CHECK LOAD {_checkLoadStation} ARM {Hand2Arm(hand)}\r");
-            return true;
+            return _SendCommand($"CHECK LOAD {_checkLoadStation} ARM {Hand2Arm(hand)}\r");
         }
         public bool Goto(ModuleName station, int slot, Hand hand)
         {
@@ -93,8 +95,7 @@ namespace Venus_RT.Devices
 
             _currentOP = OPStep.Goto;
             _status = RState.Running;
-            _socket.Write($"GOTO N {_StationNumbers[station]} SLOT {slot} ARM {Hand2Arm(hand)}\r");
-            return true;
+            return _SendCommand($"GOTO N {_StationNumbers[station]} SLOT {slot} ARM {Hand2Arm(hand)}\r");
         }
         public bool MoveTo(ModuleName stnFrom, ModuleName stnTo, Hand hand)
         {
@@ -103,8 +104,7 @@ namespace Venus_RT.Devices
 
             _currentOP = OPStep.MoveTo;
             _status = RState.Running;
-            _socket.Write($"XFER ARM {Hand2Arm(hand)} {_StationNumbers[stnFrom]} {_StationNumbers[stnTo]}\r");
-            return true;
+            return _SendCommand($"XFER ARM {Hand2Arm(hand)} {_StationNumbers[stnFrom]} {_StationNumbers[stnTo]}\r");
         }
         public bool PickExtend(ModuleName chamber, int slot, Hand hand)
         {
@@ -113,8 +113,7 @@ namespace Venus_RT.Devices
 
             _currentOP = OPStep.PickExtend;
             _status = RState.Running;
-            _socket.Write($"PICK {_StationNumbers[chamber]} SLOT {slot} ARM {Hand2Arm(hand)} ENRT\r");
-            return true;
+            return _SendCommand($"PICK {_StationNumbers[chamber]} SLOT {slot} ARM {Hand2Arm(hand)} ENRT\r");
         }
         public bool PickRetract(ModuleName chamber, int slot, Hand hand)
         {
@@ -123,8 +122,7 @@ namespace Venus_RT.Devices
 
             _currentOP = OPStep.PickRetract;
             _status = RState.Running;
-            _socket.Write($"PICK {_StationNumbers[chamber]} SLOT {slot} ARM {Hand2Arm(hand)} STRT\r");
-            return true;
+            return _SendCommand($"PICK {_StationNumbers[chamber]} SLOT {slot} ARM {Hand2Arm(hand)} STRT\r");
         }
         public bool PlaceExtend(ModuleName chamber, int slot, Hand hand)
         {
@@ -133,8 +131,7 @@ namespace Venus_RT.Devices
 
             _currentOP = OPStep.PlaceExtent;
             _status = RState.Running;
-            _socket.Write($"PLACE {_StationNumbers[chamber]} SLOT {slot} ARM {Hand2Arm(hand)} ENRT\r");
-            return true;
+            return _SendCommand($"PLACE {_StationNumbers[chamber]} SLOT {slot} ARM {Hand2Arm(hand)} ENRT\r");
         }
         public bool PlaceRetract(ModuleName chamber, int slot, Hand hand)
         {
@@ -143,8 +140,7 @@ namespace Venus_RT.Devices
 
             _currentOP = OPStep.PlaceExtent;
             _status = RState.Running;
-            _socket.Write($"PLACE {_StationNumbers[chamber]} SLOT {slot} ARM {Hand2Arm(hand)} STRT\r");
-            return true;
+            return _SendCommand($"PLACE {_StationNumbers[chamber]} SLOT {slot} ARM {Hand2Arm(hand)} STRT\r");
         }
         public bool Pick(ModuleName station, int slot, Hand hand)
         {
@@ -153,8 +149,7 @@ namespace Venus_RT.Devices
 
             _currentOP = OPStep.Pick;
             _status = RState.Running;
-            _socket.Write($"PICK {_StationNumbers[station]} SLOT {slot} ARM {Hand2Arm(hand)}\r");
-            return true;
+            return _SendCommand($"PICK {_StationNumbers[station]} SLOT {slot} ARM {Hand2Arm(hand)}\r");
         }
         public bool Place(ModuleName station, int slot, Hand hand)
         {
@@ -163,25 +158,30 @@ namespace Venus_RT.Devices
 
             _currentOP = OPStep.Place;
             _status = RState.Running;
-            _socket.Write($"PLACE {_StationNumbers[station]} SLOT {slot} ARM {Hand2Arm(hand)}\r");
-            return true;
+            return _SendCommand($"PLACE {_StationNumbers[station]} SLOT {slot} ARM {Hand2Arm(hand)}\r");
+        }
+
+        private bool _SendCommand(string cmd)
+        {
+            LOG.Write(eEvent.INFO_TM_ROBOT, ModuleName.TM, $"Send Command to SIASUN TM Robot: {cmd}");
+            return _socket.Write(cmd);
         }
 
         private bool CheckRobotStatus()
         {
-            if(Status != RState.Init)
+            if(Status == RState.Init)
             {
-                LOG.Write(eEvent.ERR_TM, ModuleName.TM, "TM Robot is not homed, please home first.");
+                LOG.Write(eEvent.ERR_TM_ROBOT, ModuleName.TM, "TM Robot is not homed, please home first.");
                 return false;
             }
             else if(Status == RState.Running)
             {
-                LOG.Write(eEvent.ERR_TM, ModuleName.TM, "TM Robot is busy, please wait a minute");
+                LOG.Write(eEvent.ERR_TM_ROBOT, ModuleName.TM, "TM Robot is busy, please wait a minute");
                 return false;
             }
             else if(Status == RState.Failed || Status == RState.Timeout)
             {
-                LOG.Write(eEvent.ERR_TM, ModuleName.TM, "TM Robot has a error, please check and fix the hardware issue and home it");
+                LOG.Write(eEvent.ERR_TM_ROBOT, ModuleName.TM, "TM Robot has a error, please check and fix the hardware issue and home it");
                 return false;
             }
 
@@ -190,7 +190,20 @@ namespace Venus_RT.Devices
 
         private void OnReceiveMessage(string RevMsg)
         {
-            switch(_currentOP)
+            if(_rex_error_code.IsMatch(RevMsg))
+            {
+                _IsHomed = false;
+                _status = RState.Failed;
+
+                var results = _rex_error_code.Match(RevMsg);
+                ErrorMessageHandler(results.Groups[1].Value);
+
+                return;
+            }
+
+            LOG.Write(eEvent.INFO_TM_ROBOT, ModuleName.TM, $"Receive message from SIASUN TM Robot: {RevMsg}, while{_currentOP}");
+
+            switch (_currentOP)
             {
                 case OPStep.Goto:
                 case OPStep.MoveTo:
@@ -240,6 +253,7 @@ namespace Venus_RT.Devices
 
                             _currentOP = OPStep.Idle;
                             _status = RState.End;
+                            _IsHomed = true;
                         }
                     }
                     break;
@@ -264,12 +278,309 @@ namespace Venus_RT.Devices
         private void OnErrorHappen(ErrorEventArgs args)
         {
             Singleton<RouteManager>.Instance.TM.PostMsg(TMEntity.MSG.Error);
-            LOG.Write(eEvent.ERR_TM, ModuleName.TM, $"SIASUN TM Robot Error: {args.Reason} while {_currentOP}");
+            LOG.Write(eEvent.ERR_TM_ROBOT, ModuleName.TM, $"SIASUN TM Robot Error: {args.Reason} while {_currentOP}");
         }
 
         private void ReportWrongMsg(string revMsg)
         {
-            LOG.Write(eEvent.ERR_TM, ModuleName.TM, $"Receive wrong message:{revMsg} while {_currentOP}");
+            LOG.Write(eEvent.ERR_TM_ROBOT, ModuleName.TM, $"Receive wrong message:{revMsg} while {_currentOP}");
+        }
+
+        private void ErrorMessageHandler(string errCode)
+        {
+            int ErrCode;
+            string ErrorInfo;
+            if(int.TryParse(errCode, out ErrCode))
+            {
+                switch (ErrCode)
+                {
+                    // 系统及硬件错误信息
+                    case 901:
+                        ErrorInfo = $"_Err {errCode}: 主电柜急停启动";
+                        break;
+                    case 902:
+                        ErrorInfo = $"_Err {errCode}: 示教盒急停启动";
+                        break;
+                    case 862:
+                        ErrorInfo = $"_Err {errCode}: 驱动器 RDY 信号断开";
+                        break;
+
+                    // 执行错误信息
+                    case 3001:
+                        ErrorInfo = $"_Err {errCode}: 系统发生碰撞,按取消恢复";
+                        break;
+                    case 7300:
+                        ErrorInfo = $"_Err {errCode}: 旋转信号不允许";
+                        break;
+                    case 7301:
+                        ErrorInfo = $"_Err {errCode}: 伸缩信号不允许";
+                        break;
+                    case 2200:
+                        ErrorInfo = $"_Err {errCode}: 输出端口 NO.不存在";
+                        break;
+                    case 3100:
+                        ErrorInfo = $"_Err {errCode}: 关节 N 位置超界";
+                        break;
+                    case 3120:
+                        ErrorInfo = $"_Err {errCode}: 关节 N 速度超界";
+                        break;
+                    case 100:
+                        ErrorInfo = $"_Err {errCode}: 手臂电源上电失败(手臂电源未打开)";
+                        break;
+
+                    // 通信错误信息
+                    case 7307:
+                        ErrorInfo = $"_Err {errCode}: GOTO 工位号超范围";
+                        break;
+                    case 7308:
+                        ErrorInfo = $"_Err {errCode}: 不支持的传感器类型";
+                        break;
+                    case 7312:
+                        ErrorInfo = $"_Err {errCode}: PICK工位号超范围";
+                        break;
+                    case 7313:
+                        ErrorInfo = $"_Err {errCode}: PLACE工位号超范围";
+                        break;
+                    case 7314:
+                        ErrorInfo = $"_Err {errCode}: XFER工位号超范围";
+                        break;
+                    case 7315:
+                        ErrorInfo = $"_Err {errCode}: REMOVE不支持的IO类型";
+                        break;
+                    case 7316:
+                        ErrorInfo = $"_Err {errCode}: RQ INTLCK不识别的参数";
+                        break;
+                    case 7317:
+                        ErrorInfo = $"_Err {errCode}: RQ IO不识别的参数";
+                        break;
+                    case 7319:
+                        ErrorInfo = $"_Err {errCode}: RQ STN工位号超范围";
+                        break;
+                    case 7320:
+                        ErrorInfo = $"_Err {errCode}: wafre(WAF_SEN)参数未设置";
+                        break;
+                    case 7321:
+                        ErrorInfo = $"_Err {errCode}: wafex(RETRACT_PIN)参数未设置";
+                        break;
+                    case 7322:
+                        ErrorInfo = $"_Err {errCode}: svlv(SBIT_SVLV_SEN)参数未设置";
+                        break;
+                    case 7323:
+                        ErrorInfo = $"_Err {errCode}: ens(EX_ENABLE)参数未设置";
+                        break;
+                    case 7324:
+                        ErrorInfo = $"_Err {errCode}: RQ命令不支持的参数";
+                        break;
+                    case 7325:
+                        ErrorInfo = $"_Err {errCode}: SET INTLOCK WAF_SEN参数错";
+                        break;
+                    case 7326:
+                        ErrorInfo = $"_Err {errCode}: SET INTLOCK RZ参数错";
+                        break;
+                    case 7327:
+                        ErrorInfo = $"_Err {errCode}: SET INTLOCK参数错";
+                        break;
+                    case 7328:
+                        ErrorInfo = $"_Err {errCode}: SET IO ECHO参数错";
+                        break;
+                    case 7329:
+                        ErrorInfo = $"_Err {errCode}: SET IO STATE不支持";
+                        break;
+                    case 7330:
+                        ErrorInfo = $"_Err {errCode}: SET IO不支持的参数";
+                        break;
+                    case 7331:
+                        ErrorInfo = $"_Err {errCode}: SET STN工位号超范围";
+                        break;
+                    case 7332:
+                        ErrorInfo = $"_Err {errCode}: 手臂参数读取错误";
+                        break;
+                    case 7333:
+                        ErrorInfo = $"_Err {errCode}: WAF_SEN不识别的参数";
+                        break;
+                    case 7334:
+                        ErrorInfo = $"_Err {errCode}: SET不支持的传感器类型";
+                        break;
+                    case 7335:
+                        ErrorInfo = $"_Err {errCode}: SET 指令输入不完整";
+                        break;
+                    case 7336:
+                        ErrorInfo = $"_Err {errCode}: STORE IO命令不支持该参数";
+                        break;
+                    case 7337:
+                        ErrorInfo = $"_Err {errCode}: STORE LOAD命令不支持该参数";
+                        break;
+                    case 7338:
+                        ErrorInfo = $"_Err {errCode}: STORE STN指令工位号大于20";
+                        break;
+                    case 7339:
+                        ErrorInfo = $"_Err {errCode}: STORE STN命令手臂参数错误";
+                        break;
+                    case 7340:
+                        ErrorInfo = $"_Err {errCode}: STORE不支持的传感器类型";
+                        break;
+                    case 7341:
+                        ErrorInfo = $"_Err {errCode}: STORE指令输入不完整";
+                        break;
+                    case 7342:
+                        ErrorInfo = $"_Err {errCode}: 无法识别的命令";
+                        break;
+                    case 7343:
+                        ErrorInfo = $"_Err {errCode}: HOME参数未指定";
+                        break;
+                    case 7344:
+                        ErrorInfo = $"_Err {errCode}: GOTO指令R轴参数未指定";
+                        break;
+                    case 7345:
+                        ErrorInfo = $"_Err {errCode}: GOTO指令Z轴参数未指定";
+                        break;
+                    case 7346:
+                        ErrorInfo = $"_Err {errCode}: ARM参数错误";
+                        break;
+                    case 7347:
+                        ErrorInfo = $"_Err {errCode}: GOTO指令未指定参数";
+                        break;
+                    case 7349:
+                        ErrorInfo = $"_Err {errCode}: MOVE指令未指定模式或轴";
+                        break;
+                    case 7350:
+                        ErrorInfo = $"_Err {errCode}: MOVE 指令中字段名字错";
+                        break;
+                    case 7351:
+                        ErrorInfo = $"_Err {errCode}: PICK未指定参数";
+                        break;
+                    case 7352:
+                        ErrorInfo = $"_Err {errCode}: PLACE未指定参数";
+                        break;
+                    case 7353:
+                        ErrorInfo = $"_Err {errCode}: REMOVE未指定参数";
+                        break;
+                    case 7354:
+                        ErrorInfo = $"_Err {errCode}: 指令执行未结束";
+                        break;
+                    case 7355:
+                        ErrorInfo = $"_Err {errCode}: GOTO指令未指定工位号";
+                        break;
+                    case 7356:
+                        ErrorInfo = $"_Err {errCode}: PICK指令未指定工位号";
+                        break;
+                    case 7357:
+                        ErrorInfo = $"_Err {errCode}: PLACE指令未指定工位号";
+                        break;
+                    case 7358:
+                        ErrorInfo = $"_Err {errCode}: ABS未指定数值";
+                        break;
+                    case 7359:
+                        ErrorInfo = $"_Err {errCode}: REL未指定数值";
+                        break;
+                    case 7360:
+                        ErrorInfo = $"_Err {errCode}: 没有指定主程序";
+                        break;
+                    case 7361:
+                        ErrorInfo = $"_Err {errCode}: 当前没有打开作业";
+                        break;
+                    case 7362:
+                        ErrorInfo = $"_Err {errCode}: 当前作业不是主作业";
+                        break;
+                    case 7363:
+                        ErrorInfo = $"_Err {errCode}: ex_ena(EX_ENABLE_SEN)参数未设置";
+                        break;
+                    case 7364:
+                        ErrorInfo = $"_Err {errCode}: stable(STABLE_ SIGNAL)参数未设置";
+                        break;
+                    case 7365:
+                        ErrorInfo = $"_Err {errCode}: VIA参数设置有误";
+                        break;
+                    case 7366:
+                        ErrorInfo = $"_Err {errCode}: 系统处于非启动状态";
+                        break;
+                    case 7367:
+                        ErrorInfo = $"_Err {errCode}: extend(EX_SIGNAL)参数未设置";
+                        break;
+                    case 7368:
+                        ErrorInfo = $"_Err {errCode}: retract(RE_ SIGNAL)参数未设置";
+                        break;
+                    case 7371:
+                        ErrorInfo = $"_Err {errCode}: place动作前:未检测到晶圆";
+                        break;
+                    case 7372:
+                        ErrorInfo = $"_Err {errCode}: pick动作前:检测到晶圆";
+                        break;
+                    case 7373:
+                        ErrorInfo = $"_Err {errCode}: place动作后:检测到晶圆";
+                        break;
+                    case 7374:
+                        ErrorInfo = $"_Err {errCode}: pick动作后:未检测到晶圆";
+                        break;
+                    case 7375:
+                        ErrorInfo = $"_Err {errCode}: 系统未上电或当前不是执行模式";
+                        break;
+                    case 7376:
+                        ErrorInfo = $"_Err {errCode}: 参数中存在非数字";
+                        break;
+                    case 7385:
+                        ErrorInfo = $"_Err {errCode}: 驱动器异常停止";
+                        break;
+                    case 7387:
+                        ErrorInfo = $"_Err {errCode}: 驱动器ID1报警";
+                        break;
+                    case 7388:
+                        ErrorInfo = $"_Err {errCode}: 驱动器ID2报警";
+                        break;
+                    case 7389:
+                        ErrorInfo = $"_Err {errCode}: 驱动器ID3报警";
+                        break;
+                    case 7391:
+                        ErrorInfo = $"_Err {errCode}: AWC工位号超范围";
+                        break;
+                    case 7392:
+                        ErrorInfo = $"_Err {errCode}: 偏差过大AWC报警";
+                        break;
+                    case 7393:
+                        ErrorInfo = $"_Err {errCode}: 标定失败,请重新标定";
+                        break;
+                    case 7398:
+                        ErrorInfo = $"_Err {errCode}: 触发点计算半径有误";
+                        break;
+                    case 7399:
+                        ErrorInfo = $"_Err {errCode}: 驱动器锁存AWC数据个数有误";
+                        break;
+                    case 7401:
+                        ErrorInfo = $"_Err {errCode}: 手指上可能有晶圆";
+                        break;
+                    case 7402:
+                        ErrorInfo = $"_Err {errCode}: 手指上可能无晶圆";
+                        break;
+                    case 7403:
+                        ErrorInfo = $"_Err {errCode}: load当前状态为ON,不正确";
+                        break;
+                    case 7404:
+                        ErrorInfo = $"_Err {errCode}: load当前状态为OFF,不正确";
+                        break;
+                    case 7405:
+                        ErrorInfo = $"_Err {errCode}: 当前slot不存在!";
+                        break;
+                    case 7495:
+                        ErrorInfo = $"_Err {errCode}: ID1码盘反馈错误";
+                        break;
+                    case 7496:
+                        ErrorInfo = $"_Err {errCode}: ID2码盘反馈错误";
+                        break;
+                    case 7497:
+                        ErrorInfo = $"_Err {errCode}: ID3码盘反馈错误";
+                        break;
+                    default:
+                        ErrorInfo = $"_Err {errCode}: 不能识别的错误码";
+                        break;
+
+                }
+
+                LOG.Write(eEvent.ERR_TM_ROBOT, ModuleName.TM, ErrorInfo);
+            }
+            else
+            {
+                LOG.Write(eEvent.ERR_TM_ROBOT, ModuleName.TM, $"Try Parse the receive error code faild:{errCode}");
+            }
         }
     }
 }

+ 4 - 0
Venus/Venus_RT/Modules/LLs/LLEntity.cs

@@ -3,9 +3,11 @@ using Aitex.Core.Util;
 using Aitex.Core.RT.Fsm;
 using Aitex.Core.RT.Log;
 using MECF.Framework.Common.Equipment;
+using MECF.Framework.Common.SubstrateTrackings;
 using Venus_RT.Modules.TM;
 using Venus_RT.Devices;
 using Venus_Core;
+
 namespace Venus_RT.Modules
 {
     class LLEntity : Entity, IEntity, IModuleEntity
@@ -97,6 +99,8 @@ namespace Venus_RT.Modules
             _leakCheckRoutine   = new MFLeakCheckRoutine(_JetTM, Module);
             _purgeRoutine       = new MFPurgeRoutine(_JetTM, Module);
 
+            WaferManager.Instance.SubscribeLocation(Module, 4);
+
             InitFsmMap();
         }
 

+ 2 - 2
Venus/Venus_RT/Modules/PMs/PMEntity.cs

@@ -474,8 +474,8 @@ namespace Venus_RT.Modules.PMs
 
             Running = true;
 
-            WaferManager.Instance.SubscribeLocation(ModuleName.PMA, 1);
-            WaferManager.Instance.SubscribeLocation(ModuleName.LLA, 1);
+            WaferManager.Instance.SubscribeLocation(Module, 1);
+            //WaferManager.Instance.SubscribeLocation(ModuleName.LLA, 1);
         }
         protected override bool Init()
         {

+ 157 - 6
Venus/Venus_RT/Modules/TM/MFPickRoutine.cs

@@ -1,12 +1,163 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using Aitex.Core.RT.Routine;
+using Aitex.Core.RT.SCCore;
+using Aitex.Sorter.Common;
+using Venus_RT.Devices;
+using MECF.Framework.Common.Routine;
+using MECF.Framework.Common.Equipment;
+using MECF.Framework.Common.SubstrateTrackings;
+using Venus_Core;
+using Aitex.Core.RT.Log;
+using Aitex.Core.Util;
 
 namespace Venus_RT.Modules.TM
 {
-    class MFPickRoutine
+    class MFPickRoutine : ModuleRoutineBase, IRoutine
     {
+        private enum PickStep
+        {
+            WaitModuleReady,
+            ModulePrepare,
+            OpenSlitDoor,
+            Picking,
+            CloseSlitDoor,
+            NotifyDone,
+        }
+
+        private readonly JetTM _JetTM;
+        private readonly ITransferRobot _robot;
+
+        private int _pickingTimeout = 120 * 1000;
+        private ModuleName _targetModule;
+        private LLEntity _llModule;
+        int _targetSlot;
+        Hand _hand;
+        
+        public MFPickRoutine(JetTM tm, ITransferRobot robot) :base(ModuleName.TM)
+        {
+            _JetTM = tm;
+            _robot = robot;
+            Name = "Pick";
+        }
+        public RState Start(params object[] objs)
+        {
+            if(!_robot.IsHomed)
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"TM Robot is not homed, please home it first");
+                return RState.Failed;
+            }
+
+            _targetModule = (ModuleName)objs[0];
+            _targetSlot = (int)objs[1];
+            _hand = (Hand)objs[2];
+
+            if(ModuleHelper.IsLoadLock(_targetModule) && ModuleHelper.IsInstalled(_targetModule))
+            {
+                _llModule = _targetModule == ModuleName.LLA ? Singleton<RouteManager>.Instance.LLA : Singleton<RouteManager>.Instance.LLB;
+            }
+            else
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Invalid target module : {_targetModule} for picking action");
+                return RState.Failed;
+            }
+
+            if (WaferManager.Instance.CheckHasWafer(ModuleName.TM, (int)_hand))
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Cannot pick as TM Robot Arm: {_hand} already has a wafer");
+                return RState.Failed;
+            }
+
+            if(WaferManager.Instance.CheckNoWafer(_targetModule, _targetSlot))
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Cannot pick as {_targetModule} Slot {_targetSlot} has no wafer");
+                return RState.Failed;
+            }
+
+            Reset();
+            _pickingTimeout = SC.GetValue<int>($"{Module}.PickTimeout") * 1000;
+
+            return Runner.Start(Module, Name);
+        }
+
+        public RState Monitor()
+        {
+            Runner.Wait((int)PickStep.WaitModuleReady,  () => _llModule.IsIdle,     _delay_60s)
+                .Run((int)PickStep.ModulePrepare,       ModulePrepare,              IsModulePrepareReady)
+                .Run((int)PickStep.OpenSlitDoor,        OpenSlitDoor,               IsSlitDoorOpen)
+                .Run((int)PickStep.Picking,             Picking,                    WaitPickDone)
+                .Run((int)PickStep.CloseSlitDoor,       CloseSlitDoor,              IsSlitDoorClosed)
+                .End((int)PickStep.NotifyDone,          NotifyLLDone,               _delay_50ms);         
+
+            return Runner.Status;
+        }
+
+        private bool ModulePrepare()
+        {
+            _llModule.PostMsg(LLEntity.MSG.Prepare_TM);
+            return true;
+        }
+
+        private bool IsModulePrepareReady()
+        {
+            return _llModule.Status == LLEntity.LLStatus.Ready_For_TM;
+        }
+
+        private bool OpenSlitDoor()
+        {
+            return _JetTM.TurnMFSlitDoor(_targetModule, true, out _);
+        }
+
+        private bool CloseSlitDoor()
+        {
+            return _JetTM.TurnMFSlitDoor(_targetModule, false, out _);
+        }
+
+        private bool IsSlitDoorOpen()
+        {
+            if (_targetModule == ModuleName.LLA) 
+                return _JetTM.IsLLASlitDoorOpen;
+            else 
+                return _JetTM.IsLLBSlitDoorOpen;
+        }
+
+        private bool IsSlitDoorClosed()
+        {
+            if (_targetModule == ModuleName.LLA)
+                return _JetTM.IsLLASlitDoorClosed;
+            else
+                return _JetTM.IsLLBSlitDoorClosed;
+        }
+
+        private bool Picking()
+        {
+            return _robot.Pick(_targetModule, _targetSlot, _hand);
+        }
+
+        private bool WaitPickDone()
+        {
+            if (_robot.Status == RState.Running)
+            {
+                return false;
+            }
+            else if (_robot.Status == RState.End)
+            {
+                return true;
+            }
+            else
+            {
+                Runner.Stop($"TM Robot Picking failed, {_robot.Status}");
+                return true;
+            }
+        }
+
+        private bool NotifyLLDone()
+        {
+            _llModule.PostMsg(LLEntity.MSG.TM_Exchange_Ready);
+            return true;
+        }
+
+        public void Abort()
+        {
+            _robot.Halt();
+        }
     }
 }

+ 158 - 6
Venus/Venus_RT/Modules/TM/MFPlaceRoutine.cs

@@ -1,12 +1,164 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using Aitex.Core.RT.Routine;
+using Aitex.Core.RT.SCCore;
+using Aitex.Sorter.Common;
+using Venus_RT.Devices;
+using MECF.Framework.Common.Routine;
+using MECF.Framework.Common.Equipment;
+using MECF.Framework.Common.SubstrateTrackings;
+using Venus_Core;
+using Aitex.Core.RT.Log;
+using Aitex.Core.Util;
 
 namespace Venus_RT.Modules.TM
 {
-    class MFPlaceRoutine
+    class MFPlaceRoutine : ModuleRoutineBase, IRoutine
     {
+        private enum PlaceStep
+        {
+            WaitModuleReady,
+            ModulePrepare,
+            OpenSlitDoor,
+            Placing,
+            CloseSlitDoor,
+            NotifyDone,
+        }
+
+        private readonly JetTM _JetTM;
+        private readonly ITransferRobot _robot;
+
+        private int _placingTimeout = 120 * 1000;
+        private ModuleName _targetModule;
+        private LLEntity _llModule;
+        int _targetSlot;
+        Hand _hand;
+
+        public MFPlaceRoutine(JetTM tm, ITransferRobot robot) : base(ModuleName.TM)
+        {
+            _JetTM = tm;
+            _robot = robot;
+
+            Name = "Place";
+        }
+        public RState Start(params object[] objs)
+        {
+            if (!_robot.IsHomed)
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"TM Robot is not homed, please home it first");
+                return RState.Failed;
+            }
+
+            _targetModule = (ModuleName)objs[0];
+            _targetSlot = (int)objs[1];
+            _hand = (Hand)objs[2];
+
+            if (ModuleHelper.IsLoadLock(_targetModule) && ModuleHelper.IsInstalled(_targetModule))
+            {
+                _llModule = _targetModule == ModuleName.LLA ? Singleton<RouteManager>.Instance.LLA : Singleton<RouteManager>.Instance.LLB;
+            }
+            else
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Invalid target module : {_targetModule} for place action");
+                return RState.Failed;
+            }
+
+            if (WaferManager.Instance.CheckNoWafer(ModuleName.TM, (int)_hand))
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Cannot place as TM Robot Arm: {_hand} has no wafer");
+                return RState.Failed;
+            }
+
+            if (WaferManager.Instance.CheckHasWafer(_targetModule, _targetSlot))
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Cannot place as {_targetModule} Slot {_targetSlot} already has a wafer");
+                return RState.Failed;
+            }
+
+            Reset();
+            _placingTimeout = SC.GetValue<int>($"{Module}.PlaceTimeout") * 1000;
+
+            return Runner.Start(Module, Name);
+        }
+
+        public RState Monitor()
+        {
+            Runner.Wait((int)PlaceStep.WaitModuleReady, () => _llModule.IsIdle,     _delay_60s)
+                .Run((int)PlaceStep.ModulePrepare,      ModulePrepare,              IsModulePrepareReady)
+                .Run((int)PlaceStep.OpenSlitDoor,       OpenSlitDoor,               IsSlitDoorOpen)
+                .Run((int)PlaceStep.Placing,            Placing,                    WaitPlaceDone)
+                .Run((int)PlaceStep.CloseSlitDoor,      CloseSlitDoor,              IsSlitDoorClosed)
+                .End((int)PlaceStep.NotifyDone,         NotifyLLDone,               _delay_50ms);
+
+            return Runner.Status;
+        }
+
+        private bool ModulePrepare()
+        {
+            _llModule.PostMsg(LLEntity.MSG.Prepare_TM);
+            return true;
+        }
+
+        private bool IsModulePrepareReady()
+        {
+            return _llModule.Status == LLEntity.LLStatus.Ready_For_TM;
+        }
+
+        private bool OpenSlitDoor()
+        {
+            return _JetTM.TurnMFSlitDoor(_targetModule, true, out _);
+        }
+
+        private bool CloseSlitDoor()
+        {
+            return _JetTM.TurnMFSlitDoor(_targetModule, false, out _);
+        }
+
+        private bool IsSlitDoorOpen()
+        {
+            if (_targetModule == ModuleName.LLA)
+                return _JetTM.IsLLASlitDoorOpen;
+            else
+                return _JetTM.IsLLBSlitDoorOpen;
+        }
+
+        private bool IsSlitDoorClosed()
+        {
+            if (_targetModule == ModuleName.LLA)
+                return _JetTM.IsLLASlitDoorClosed;
+            else
+                return _JetTM.IsLLBSlitDoorClosed;
+        }
+
+        private bool Placing()
+        {
+            return _robot.Place(_targetModule, _targetSlot, _hand);
+        }
+
+        private bool WaitPlaceDone()
+        {
+            if (_robot.Status == RState.Running)
+            {
+                return false;
+            }
+            else if (_robot.Status == RState.End)
+            {
+                return true;
+            }
+            else
+            {
+                Runner.Stop($"TM Robot Place failed, {_robot.Status}");
+                return true;
+            }
+        }
+
+        private bool NotifyLLDone()
+        {
+            _llModule.PostMsg(LLEntity.MSG.TM_Exchange_Ready);
+            return true;
+        }
+
+        public void Abort()
+        {
+            _robot.Halt();
+        }
     }
 }

+ 201 - 0
Venus/Venus_RT/Modules/TM/MFSwapRoutine.cs

@@ -0,0 +1,201 @@
+using Aitex.Core.RT.Routine;
+using Aitex.Core.RT.SCCore;
+using Aitex.Sorter.Common;
+using Venus_RT.Devices;
+using MECF.Framework.Common.Routine;
+using MECF.Framework.Common.Equipment;
+using MECF.Framework.Common.SubstrateTrackings;
+using Venus_Core;
+using Aitex.Core.RT.Log;
+using Aitex.Core.Util;
+
+namespace Venus_RT.Modules.TM
+{
+    class MFSwapRoutine : ModuleRoutineBase, IRoutine
+    {
+        private enum SwapStep
+        {
+            WaitModuleReady,
+            ModulePrepare,
+            OpenSlitDoor,
+            Picking,
+            Placing,
+            CloseSlitDoor,
+            NotifyDone,
+        }
+
+        private readonly JetTM _JetTM;
+        private readonly ITransferRobot _robot;
+
+        private int _swapTimeout = 120 * 1000;
+        private ModuleName _targetModule;
+        private LLEntity _llModule;
+        int _pickSlot, _placeSlot;
+        Hand _pickHand, _placeHand;
+
+        public MFSwapRoutine(JetTM tm, ITransferRobot robot) : base(ModuleName.TM)
+        {
+            _JetTM = tm;
+            _robot = robot;
+
+            Name = "Swap";
+        }
+        public RState Start(params object[] objs)
+        {
+            if (!_robot.IsHomed)
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"TM Robot is not homed, please home it first");
+                return RState.Failed;
+            }
+
+            _targetModule = (ModuleName)objs[0];
+            _pickSlot = (int)objs[1];
+            _placeSlot = (int)objs[2];
+            _pickHand = (Hand)objs[3];
+            _placeHand = _pickHand == Hand.Blade1 ? Hand.Blade2 : Hand.Blade1;
+
+            if (ModuleHelper.IsLoadLock(_targetModule) && ModuleHelper.IsInstalled(_targetModule))
+            {
+                _llModule = _targetModule == ModuleName.LLA ? Singleton<RouteManager>.Instance.LLA : Singleton<RouteManager>.Instance.LLB;
+            }
+            else
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Invalid target module : {_targetModule} for picking action");
+                return RState.Failed;
+            }
+
+            if (WaferManager.Instance.CheckHasWafer(ModuleName.TM, (int)_pickHand))
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Cannot pick as TM Robot Arm: {_pickHand} already has a wafer");
+                return RState.Failed;
+            }
+
+            if (WaferManager.Instance.CheckNoWafer(_targetModule, _pickSlot))
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Cannot pick as {_targetModule} Slot {_pickSlot} has no wafer");
+                return RState.Failed;
+            }
+
+            if (WaferManager.Instance.CheckNoWafer(ModuleName.TM, (int)_placeHand))
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Cannot place as TM Robot Arm: {_placeHand} has no wafer");
+                return RState.Failed;
+            }
+
+            if (WaferManager.Instance.CheckHasWafer(_targetModule, _placeSlot))
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Cannot place as {_targetModule} Slot {_placeSlot} already has a wafer");
+                return RState.Failed;
+            }
+
+            Reset();
+            _swapTimeout = SC.GetValue<int>($"{Module}.SwapTimeout") * 1000;
+
+            return Runner.Start(Module, Name);
+        }
+
+        public RState Monitor()
+        {
+            Runner.Wait((int)SwapStep.WaitModuleReady,  () => _llModule.IsIdle,     _delay_60s)
+                .Run((int)SwapStep.ModulePrepare,       ModulePrepare,              IsModulePrepareReady)
+                .Run((int)SwapStep.OpenSlitDoor,        OpenSlitDoor,               IsSlitDoorOpen)
+                .Run((int)SwapStep.Picking,             Picking,                    WaitPickDone)
+                .Run((int)SwapStep.Placing,             Placing,                    WaitPlaceDone)
+                .Run((int)SwapStep.CloseSlitDoor,       CloseSlitDoor,              IsSlitDoorClosed)
+                .End((int)SwapStep.NotifyDone,          NotifyLLDone,               _delay_50ms);
+
+            return Runner.Status;
+        }
+
+        private bool ModulePrepare()
+        {
+            _llModule.PostMsg(LLEntity.MSG.Prepare_TM);
+            return true;
+        }
+
+        private bool IsModulePrepareReady()
+        {
+            return _llModule.Status == LLEntity.LLStatus.Ready_For_TM;
+        }
+
+        private bool OpenSlitDoor()
+        {
+            return _JetTM.TurnMFSlitDoor(_targetModule, true, out _);
+        }
+
+        private bool CloseSlitDoor()
+        {
+            return _JetTM.TurnMFSlitDoor(_targetModule, false, out _);
+        }
+
+        private bool IsSlitDoorOpen()
+        {
+            if (_targetModule == ModuleName.LLA)
+                return _JetTM.IsLLASlitDoorOpen;
+            else
+                return _JetTM.IsLLBSlitDoorOpen;
+        }
+
+        private bool IsSlitDoorClosed()
+        {
+            if (_targetModule == ModuleName.LLA)
+                return _JetTM.IsLLASlitDoorClosed;
+            else
+                return _JetTM.IsLLBSlitDoorClosed;
+        }
+
+        private bool Picking()
+        {
+            return _robot.Pick(_targetModule, _pickSlot, _pickHand);
+        }
+
+        private bool WaitPickDone()
+        {
+            if (_robot.Status == RState.Running)
+            {
+                return false;
+            }
+            else if (_robot.Status == RState.End)
+            {
+                return true;
+            }
+            else
+            {
+                Runner.Stop($"TM Robot Picking failed, {_robot.Status}");
+                return true;
+            }
+        }
+        private bool Placing()
+        {
+            return _robot.Place(_targetModule, _placeSlot, _placeHand);
+        }
+
+        private bool WaitPlaceDone()
+        {
+            if (_robot.Status == RState.Running)
+            {
+                return false;
+            }
+            else if (_robot.Status == RState.End)
+            {
+                return true;
+            }
+            else
+            {
+                Runner.Stop($"TM Robot Place failed, {_robot.Status}");
+                return true;
+            }
+        }
+
+        private bool NotifyLLDone()
+        {
+            _llModule.PostMsg(LLEntity.MSG.TM_Exchange_Ready);
+            return true;
+        }
+
+        public void Abort()
+        {
+            _robot.Halt();
+        }
+    }
+}

+ 111 - 2
Venus/Venus_RT/Modules/TM/TMEntity.cs

@@ -5,6 +5,7 @@ using Aitex.Core.Util;
 using Venus_Core;
 using Aitex.Sorter.Common;
 using MECF.Framework.Common.Equipment;
+using MECF.Framework.Common.SubstrateTrackings;
 using Venus_RT.Devices;
 using Venus_RT.Modules.TM;
 
@@ -24,7 +25,8 @@ namespace Venus_RT.Modules
             Purging,
             Leakchecking,
             Picking,         
-            Placing,       
+            Placing,  
+            Swaping,
             Aligning,      
             Mapping,     
             Extending,    
@@ -45,6 +47,7 @@ namespace Venus_RT.Modules
             LeakCheck,
             Pick, 
             Place,
+            Swap,
             Extend,
             Retract,
             Error,
@@ -81,17 +84,27 @@ namespace Venus_RT.Modules
         private readonly MFVentRoutine _ventingRoutine;
         private readonly MFLeakCheckRoutine _leakCheckRoutine;
         private readonly MFPurgeRoutine _purgeRoutine;
+        private readonly MFPickRoutine _pickRoutine;
+        private readonly MFPlaceRoutine _placeRoutine;
+        private readonly MFSwapRoutine _swapRoutine;
         public TMEntity()
         {
             _tm                 = Singleton<JetTM>.Instance;
             _robot              = new SIASUNRobot();
 
-            _homeRoutine = new MFHomeRoutine(_tm, _robot);
+            _homeRoutine    = new MFHomeRoutine(_tm, _robot);
+            _pickRoutine    = new MFPickRoutine(_tm, _robot);
+            _placeRoutine   = new MFPlaceRoutine(_tm, _robot);
+            _swapRoutine    = new MFSwapRoutine(_tm, _robot);
+
             _pumpingRoutine     = new MFPumpRoutine(_tm, ModuleName.TM);
             _ventingRoutine     = new MFVentRoutine(_tm, ModuleName.TM);
             _leakCheckRoutine   = new MFLeakCheckRoutine(_tm, ModuleName.TM);
             _purgeRoutine       = new MFPurgeRoutine(_tm, ModuleName.TM);
 
+
+            WaferManager.Instance.SubscribeLocation(ModuleName.TM, 2);
+
             InitFsmMap();
         }
 
@@ -138,6 +151,21 @@ namespace Venus_RT.Modules
             Transition(STATE.Leakchecking,      FSM_MSG.TIMER,      FnLeakCheckTimeout, STATE.Idle);
             Transition(STATE.Leakchecking,      MSG.Abort,          FnAbortLeakCheck,   STATE.Idle);
 
+            // Pick wafer from LL sequence
+            Transition(STATE.Idle,              MSG.Pick,           FnStartPick,        STATE.Picking);
+            Transition(STATE.Picking,           FSM_MSG.TIMER,      FnPickTimeout,      STATE.Idle);
+            Transition(STATE.Picking,           MSG.Abort,          FnAbortPick,        STATE.Idle);
+
+            // Place wafer to LL sequence
+            Transition(STATE.Idle,              MSG.Place,          FnStartPlace,       STATE.Placing);
+            Transition(STATE.Placing,           FSM_MSG.TIMER,      FnPlaceTimeout,     STATE.Idle);
+            Transition(STATE.Placing,           MSG.Abort,          FnAbortPlace,       STATE.Idle);
+
+            // Swap wafer with LL sequence
+            Transition(STATE.Idle,              MSG.Swap,           FnStartSwap,        STATE.Swaping);
+            Transition(STATE.Swaping,           FSM_MSG.TIMER,      FnSwapTimeout,      STATE.Idle);
+            Transition(STATE.Swaping,           MSG.Abort,          FnAbortSwap,        STATE.Idle);
+
             Running = true;
         }
 
@@ -280,6 +308,75 @@ namespace Venus_RT.Modules
             _leakCheckRoutine.Abort();
             return true;
         }
+
+        private bool FnStartPick(object[] param)
+        {
+            return _pickRoutine.Start(param) == RState.Running;
+        }
+
+        private bool FnPickTimeout(object[] param)
+        {
+            RState ret = _pickRoutine.Monitor();
+            if (ret == RState.Failed || ret == RState.Timeout)
+            {
+                PostMsg(MSG.Error);
+                return false;
+            }
+
+            return ret == RState.End;
+        }
+
+        private bool FnAbortPick(object[] param)
+        {
+            _pickRoutine.Abort();
+            return true;
+        }
+        private bool FnStartPlace(object[] param)
+        {
+            return _placeRoutine.Start(param) == RState.Running;
+        }
+
+        private bool FnPlaceTimeout(object[] param)
+        {
+            RState ret = _placeRoutine.Monitor();
+            if (ret == RState.Failed || ret == RState.Timeout)
+            {
+                PostMsg(MSG.Error);
+                return false;
+            }
+
+            return ret == RState.End;
+        }
+
+        private bool FnAbortPlace(object[] param)
+        {
+            _placeRoutine.Abort();
+            return true;
+        }
+
+        private bool FnStartSwap(object[] param)
+        {
+            return _swapRoutine.Start(param) == RState.Running;
+        }
+
+        private bool FnSwapTimeout(object[] param)
+        {
+            RState ret = _swapRoutine.Monitor();
+            if (ret == RState.Failed || ret == RState.Timeout)
+            {
+                PostMsg(MSG.Error);
+                return false;
+            }
+
+            return ret == RState.End;
+        }
+
+        private bool FnAbortSwap(object[] param)
+        {
+            _swapRoutine.Abort();
+            return true;
+        }
+
         public bool Check(int msg, out string reason, params object[] args)
         {
             reason = "";
@@ -333,6 +430,18 @@ namespace Venus_RT.Modules
             {
                 PostMsg(MSG.Pump);
             }
+            else if(flag == 4)
+            {
+                PostMsg(MSG.Pick, ModuleName.LLA, 0, 0);
+            }
+            else if(flag == 5)
+            {
+                PostMsg(MSG.Place, ModuleName.LLA, 0, 0);
+            }
+            else if(flag == 6)
+            {
+                PostMsg(MSG.Swap, ModuleName.LLA, 0, 2, 0);
+            }
             //else if (flag == 4)
             //{
             //    PostMsg(MSG.PumpLoadLock);

+ 1 - 0
Venus/Venus_RT/Venus_RT.csproj

@@ -216,6 +216,7 @@
     <Compile Include="Modules\TM\MFPlaceRoutine.cs" />
     <Compile Include="Modules\TM\MFPumpRoutine.cs" />
     <Compile Include="Modules\TM\MFPurgeRoutine.cs" />
+    <Compile Include="Modules\TM\MFSwapRoutine.cs" />
     <Compile Include="Modules\TM\MFVentRoutine.cs" />
     <Compile Include="Modules\TM\TMEntity.cs" />
     <Compile Include="Properties\AssemblyInfo.cs">

+ 3 - 1
Venus/Venus_Simulator/Devices/TMSimulatorServer.cs

@@ -17,7 +17,7 @@ namespace Venus_Simulator.Devices
         private PeriodicJob _HwThread;
         public TMSimulatorServer() : base(1102, -1, "\r", ' ')
         {
-            _HwThread = new PeriodicJob(5000, OnSendEvent, "EfemHardware", true);
+            _HwThread = new PeriodicJob(500, OnSendEvent, "EfemHardware", true);
         }
 
         private bool OnSendEvent()
@@ -51,6 +51,7 @@ namespace Venus_Simulator.Devices
                 string arm = result.Groups[4].Value;
                 string dir = result.Groups[5].Value;
 
+                Thread.Sleep(5000);
                 OnWriteMessage("_RDY\r");
             }
             else if(_move_wafer.IsMatch(str))
@@ -63,6 +64,7 @@ namespace Venus_Simulator.Devices
                 string slot = result.Groups[3].Value;
                 string arm = result.Groups[4].Value;
 
+                Thread.Sleep(5000);
                 OnWriteMessage("_RDY\r");
             }
 

+ 75 - 0
Venus/Venus_Simulator/Instances/SimulatorSystem.cs

@@ -184,6 +184,18 @@ namespace Venus_Simulator.Instances
             IO.DI[$"{mod}.DI_LLB_PCW_Flow_Switch"].Value = true;
             IO.DI[$"{mod}.DI_TM_CHB_Door_Closed"].Value = true;
 
+            IO.DI[$"{mod}.DI_LLA_Lid_Door_Closed"].Value = true;
+            IO.DI[$"{mod}.DI_LLB_Lid_Door_Closed"].Value = true;
+
+            IO.DI[$"{mod}.DI_LLA_E_Slit_Door_open_Position"].Value = false;
+            IO.DI[$"{mod}.DI_LLA_E_Slit_Door_close_Position"].Value = true;
+            IO.DI[$"{mod}.DI_LLA_T_Slit_Door_open_Position"].Value = false;
+            IO.DI[$"{mod}.DI_LLA_T_Slit_Door_close_Position"].Value = true;
+            IO.DI[$"{mod}.DI_LLB_E_Slit_Door_open_Position"].Value = false;
+            IO.DI[$"{mod}.DI_LLB_E_Slit_Door_close_Position"].Value = true;
+            IO.DI[$"{mod}.DI_LLB_T_Slit_Door_open_Position"].Value = false;
+            IO.DI[$"{mod}.DI_LLB_T_Slit_Door_close_Position"].Value = true;
+                                                                                             
             IO.DI[$"{mod}.DI_CDA_Pressure_Switch"].Value = true;
             IO.DI[$"{mod}.DI_Vaccum_Pressure_Switch"].Value = true;
             IO.DI[$"{mod}.DI_N2_Pressure_Switch"].Value = true;
@@ -221,6 +233,8 @@ namespace Venus_Simulator.Instances
                 //ChangeTime(ModuleName.PMB);
                 //MonitorIOPumpCtrl(ModuleName.PMB);
 
+                MonitorMFSlitDoor();
+
 
             }
             catch (Exception e)
@@ -260,6 +274,67 @@ namespace Venus_Simulator.Instances
             }
         }
 
+        void MonitorMFSlitDoor()
+        {
+            ModuleName mod = ModuleName.TM;
+
+            // LLA T door open
+            if (IO.DO[$"{mod}.DO_LLA_Slit_Door_T_Open"].Value)
+            {
+                IO.DI[$"{mod}.DI_LLA_T_Slit_Door_open_Position"].Value = true;
+                IO.DI[$"{mod}.DI_LLA_T_Slit_Door_close_Position"].Value = false;
+            }
+
+            // LLB T door open
+            if (IO.DO[$"{mod}.DO_LLB_Slit_Door_T_Open"].Value)
+            {
+                IO.DI[$"{mod}.DI_LLB_T_Slit_Door_open_Position"].Value = true;
+                IO.DI[$"{mod}.DI_LLB_T_Slit_Door_close_Position"].Value = false;
+            }
+
+            // LLA E door open
+            if (IO.DO[$"{mod}.DO_LLA_Slit_Door_E_Open"].Value)
+            {
+                IO.DI[$"{mod}.DI_LLA_E_Slit_Door_open_Position"].Value = true;
+                IO.DI[$"{mod}.DI_LLA_E_Slit_Door_close_Position"].Value = false;
+            }
+
+            // LLB E door open
+            if (IO.DO[$"{mod}.DO_LLB_Slit_Door_E_Open"].Value)
+            {
+                IO.DI[$"{mod}.DI_LLB_E_Slit_Door_open_Position"].Value = true;
+                IO.DI[$"{mod}.DI_LLB_E_Slit_Door_close_Position"].Value = false;
+            }
+
+            // LLA T door close
+            if (IO.DO[$"{mod}.DO_LLA_Slit_Door_T_Close"].Value)
+            {
+                IO.DI[$"{mod}.DI_LLA_T_Slit_Door_open_Position"].Value = false;
+                IO.DI[$"{mod}.DI_LLA_T_Slit_Door_close_Position"].Value = true;
+            }
+
+            // LLB T door close
+            if (IO.DO[$"{mod}.DO_LLB_Slit_Door_T_Close"].Value)
+            {
+                IO.DI[$"{mod}.DI_LLB_T_Slit_Door_open_Position"].Value = false;
+                IO.DI[$"{mod}.DI_LLB_T_Slit_Door_close_Position"].Value = true;
+            }
+
+            // LLA E door close
+            if (IO.DO[$"{mod}.DO_LLA_Slit_Door_E_Close"].Value)
+            {
+                IO.DI[$"{mod}.DI_LLA_E_Slit_Door_open_Position"].Value = false;
+                IO.DI[$"{mod}.DI_LLA_E_Slit_Door_close_Position"].Value = true;
+            }
+
+            // LLB E door close
+            if (IO.DO[$"{mod}.DO_LLB_Slit_Door_E_Close"].Value)
+            {
+                IO.DI[$"{mod}.DI_LLB_E_Slit_Door_open_Position"].Value = false;
+                IO.DI[$"{mod}.DI_LLB_E_Slit_Door_close_Position"].Value = true;
+            }
+        }
+
         void MonitorPin(ModuleName mod)
         {
             // lift pin up