Explorar o código

PM TM Wafer Transfer Ready.

sangwq %!s(int64=2) %!d(string=hai) anos
pai
achega
a54944a308

+ 6 - 0
Venus/Framework/Common/Log/LOG.cs

@@ -66,6 +66,12 @@ namespace Aitex.Core.RT.Log
         {
             Write(eEvent.ERR_EXCEPTION, ModuleName.System, prefix + GetFormatStackFrameInfo(ex));
         }
+        public static void WriteSingeLine(eEvent id, ModuleName module, string log)
+        {
+            string newLog = log.Replace("\r", "<回车>");
+            newLog = newLog.Replace("\n", "<换行>");
+            Write(id, module, newLog);
+        }
         public static void Write(eEvent id, ModuleName module, params string[] values)
         {
 

+ 12 - 0
Venus/Venus_Core/RtState.cs

@@ -85,5 +85,17 @@ namespace Venus_Core
         ClosingLid,
         MFCVerification,
         AllMFCVerification,
+
+        // exchange wafer with TM
+        PreparePick,
+        PreparePlace,
+        ReadyForPick,
+        ReadyForPlace,
+        DropDownWafer,
+        LiftUpWafer,
+        DropDownReady,
+        LiftUpReady,
+        FinishPick,
+        FinishPlace,
     }
 }

+ 4 - 4
Venus/Venus_RT/Devices/IODevices/IoCylinder.cs

@@ -208,16 +208,16 @@ namespace Venus_RT.Devices
                         if (_operation == CylinderState.Open)
                         {
                             if (!_doON.Check(true, out var reason))
-                                LOG.Write(eEvent.ERR_DEVICE_INFO, Module, "气缸信号无法打开, interlock, " + reason);
+                                LOG.Write(eEvent.ERR_DEVICE_INFO, Module, $"{Name}气缸信号无法打开, interlock, " + reason);
                             else
-                                LOG.Write(eEvent.ERR_DEVICE_INFO, Module, "气缸信号仍然关闭");
+                                LOG.Write(eEvent.ERR_DEVICE_INFO, Module, $"{Name}气缸信号仍然关闭");
                         }
                         else
                         {
                             if (!_doON.Check(false, out var reason))
-                                LOG.Write(eEvent.ERR_DEVICE_INFO, Module, "气缸信号无法关闭, interlock, " + reason);
+                                LOG.Write(eEvent.ERR_DEVICE_INFO, Module, $"{Name}气缸信号无法关闭, interlock, " + reason);
                             else
-                                LOG.Write(eEvent.ERR_DEVICE_INFO, Module, "气缸信号仍然打开");
+                                LOG.Write(eEvent.ERR_DEVICE_INFO, Module, $"{Name}气缸信号仍然打开");
                         }
                     }
                     _operation = (CylinderState)SetPoint;

+ 32 - 32
Venus/Venus_RT/Devices/JetPM.cs

@@ -826,43 +826,43 @@ namespace Venus_RT.Devices
             return true;
         }
 
-        public void SetSlitDoor(bool open, out string reason)
+        public bool SetSlitDoor(bool open, out string reason)
         {
             reason = string.Empty;
 
-            if(open)
-            {
-                bool _isATMMode = SC.GetValue<bool>("System.IsATMMode");
-                if(_isATMMode)
-                {
-                    if(!IsATM)
-                    {
-                        reason = $"{Module} is not ATM, can not open slit door";
-                        LOG.Write(eEvent.ERR_DEVICE_INFO, Module, reason);
-                        return;
-                    }
-
-                    if(!IsATMLoadlock)
-                    {
-                        reason = $"LoadLock is not ATM, can not open slit door";
-                        LOG.Write(eEvent.ERR_DEVICE_INFO, Module, reason);
-                        return;
-                    }
-                }
-                else
-                {
-                    double maxPressureDifference = SC.GetValue<double>("System.PMLLMaxPressureDifference");
-                    if (Math.Abs(LoadlockPressure - ChamberPressure) > maxPressureDifference)
-                    {
-                        reason = $"{Module} and Loadlock pressure difference exceeds the max limit {maxPressureDifference}";
-                        LOG.Write(eEvent.ERR_DEVICE_INFO, Module, reason);
-                        return;
-                    }
-                }
+            //if(open)
+            //{
+            //    bool _isATMMode = SC.GetValue<bool>("System.IsATMMode");
+            //    if(_isATMMode)
+            //    {
+            //        if(!IsATM)
+            //        {
+            //            reason = $"{Module} is not ATM, can not open slit door";
+            //            LOG.Write(eEvent.ERR_DEVICE_INFO, Module, reason);
+            //            return false;
+            //        }
+
+            //        if(!IsATMLoadlock)
+            //        {
+            //            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.PMLLMaxPressureDifference");
+            //        if (Math.Abs(LoadlockPressure - ChamberPressure) > maxPressureDifference)
+            //        {
+            //            reason = $"{Module} and Loadlock pressure difference exceeds the max limit {maxPressureDifference}";
+            //            LOG.Write(eEvent.ERR_DEVICE_INFO, Module, reason);
+            //            return false;
+            //        }
+            //    }
                 
-            }
+            //}
 
-            _slitDoor.SetCylinder(open, out reason);
+            return _slitDoor.SetCylinder(open, out reason);
         }
 
         public bool RetractWafer()

+ 2 - 2
Venus/Venus_RT/Devices/TM/SIASUNRobot.cs

@@ -163,7 +163,7 @@ namespace Venus_RT.Devices
 
         private bool _SendCommand(string cmd)
         {
-            LOG.Write(eEvent.INFO_TM_ROBOT, ModuleName.TM, $"Send Command to SIASUN TM Robot: {cmd}");
+            LOG.WriteSingeLine(eEvent.INFO_TM_ROBOT, ModuleName.TM, $"Send Command to SIASUN TM Robot: {cmd}");
             return _socket.Write(cmd);
         }
 
@@ -201,7 +201,7 @@ namespace Venus_RT.Devices
                 return;
             }
 
-            LOG.Write(eEvent.INFO_TM_ROBOT, ModuleName.TM, $"Receive message from SIASUN TM Robot: {RevMsg}, while{_currentOP}");
+            LOG.WriteSingeLine(eEvent.INFO_TM_ROBOT, ModuleName.TM, $"Receive message from SIASUN TM Robot: {RevMsg}, while {_currentOP}");
 
             switch (_currentOP)
             {

+ 235 - 3
Venus/Venus_RT/Modules/PMs/PMEntity.cs

@@ -50,6 +50,14 @@ namespace Venus_RT.Modules.PMs
 
     public class PMEntity : Entity, IModuleEntity
     {
+        public enum PMStatus
+        {
+            Not_Ready,
+            Ready_For_Pick,
+            Ready_For_Place,
+            Exchange_Ready,
+        }
+
         public enum MSG
         {
             Home,
@@ -100,12 +108,21 @@ namespace Venus_RT.Modules.PMs
             StopGasFlow,
             RfPower,
             MFCVerification,
+
+            // exchange wafer with TM
+            PreparePick,
+            PreparePlace,
+            LiftUpWafer,
+            DropDownWafer,
+            PickReady,
+            PlaceReady,
             MaxMsg
         }
 
         private readonly JetPM _chamber;
 
         public ModuleName Module { get; }
+        public PMStatus Status { get; private set; }
         public Action<bool, bool> TransferPrepared;
 
         private readonly PMHomeRoutine              _home;
@@ -170,6 +187,10 @@ namespace Venus_RT.Modules.PMs
             get { return _isOnline; }
         }
         public bool IsAutoMode => _AutoMode == AutoFlag.Auto;
+        public bool IsSlitDoorOpen => _chamber.CheckSlitDoorOpen();
+        public bool IsSlitDoorClose => _chamber.CheckSlitDoorClose();
+        public bool LiftPinIsDown => _chamber.LiftPinIsDown;
+        public bool LiftPinIsUp => _chamber.LiftPinIsUp;
 
         public bool Check(int msg, out string reason, object[] objs)
         {
@@ -355,8 +376,13 @@ namespace Venus_RT.Modules.PMs
             fsm                     = new StateMachine<PMEntity>(Module.ToString(), (int)PMState.Init, 50);
 
             //Idle
-            EnterExitTransition((int)PMState.Idle, FnIdle,          (int)FSM_MSG.NONE,          null);
-            EnterExitTransition<PMState, FSM_MSG>(PMState.Error,    fEnterError,                FSM_MSG.NONE,   null);
+            EnterExitTransition((int)PMState.Idle,                          FnIdle,                 (int)FSM_MSG.NONE,      null);
+            EnterExitTransition<PMState, FSM_MSG>(PMState.Error,            fEnterError,            FSM_MSG.NONE,           null);
+
+            EnterExitTransition<PMState, FSM_MSG>(PMState.ReadyForPick,     fnEnterPickReady,       FSM_MSG.NONE,   fnExitPickReady);
+            EnterExitTransition<PMState, FSM_MSG>(PMState.ReadyForPlace,    fnEnterPlaceReady,      FSM_MSG.NONE,   fnExitPlaceReady);
+            EnterExitTransition<PMState, FSM_MSG>(PMState.DropDownReady,    fnEnterDropDownReady,   FSM_MSG.NONE,   fnExitDropDownReady);
+            EnterExitTransition<PMState, FSM_MSG>(PMState.LiftUpReady,      fnEnterLiftUpReady,     FSM_MSG.NONE,   fnExitLiftUpReady);
 
 
             //Home
@@ -472,6 +498,32 @@ namespace Venus_RT.Modules.PMs
             Transition(PMState.MFCVerification, FSM_MSG.TIMER,          FnMFCVerificationTimeout,   PMState.Idle);
             Transition(PMState.MFCVerification, MSG.Abort,              FnAbortMFCVerification,     PMState.Idle);
 
+            //////////////////////// Exchange Wafer with TM ///////////////////////////////////////////////////////
+            // Pick Wafer from PM
+            Transition(PMState.Idle,            MSG.PreparePick,    FnStartPreparePick,     PMState.PreparePick);
+            Transition(PMState.PreparePick,     MSG.Abort,          FnAbortPreparePick,     PMState.Idle);
+            Transition(PMState.PreparePick,     FSM_MSG.TIMER,      FnPreparePickTimeout,   PMState.ReadyForPick);
+            Transition(PMState.ReadyForPick,    MSG.Abort,          FnAbortPick,            PMState.Idle);
+            Transition(PMState.ReadyForPick,    MSG.DropDownWafer,  FnStartDropDownWafer,   PMState.DropDownWafer);
+            Transition(PMState.DropDownWafer,   FSM_MSG.TIMER,      FnDropDownTimeout,      PMState.DropDownReady);
+            Transition(PMState.DropDownReady,   MSG.PickReady,      FnStartFinishPick,      PMState.FinishPick);
+            Transition(PMState.FinishPick,      FSM_MSG.TIMER,      FnFinishPickTimeout,    PMState.Idle);
+            
+            
+            // Place Wafer to PM
+            Transition(PMState.Idle,            MSG.PreparePlace,   FnStartPreparePlace,    PMState.PreparePlace);
+            Transition(PMState.PreparePlace,    MSG.Abort,          FnAbortPreparePlace,    PMState.Idle);
+            Transition(PMState.PreparePlace,    FSM_MSG.TIMER,      FnPreparePlaceTimeout,  PMState.ReadyForPlace);
+            Transition(PMState.ReadyForPlace,   MSG.LiftUpWafer,    FnStartLiftUpWafer,     PMState.LiftUpWafer);
+            Transition(PMState.LiftUpWafer,     FSM_MSG.TIMER,      FnLiftUpTimeout,        PMState.LiftUpReady);
+            Transition(PMState.LiftUpReady,     MSG.PlaceReady,     FnStartFinishPlace,     PMState.FinishPlace);
+            Transition(PMState.ReadyForPlace,   MSG.Abort,          FnAortPlace,            PMState.Idle);
+            Transition(PMState.FinishPlace,     FSM_MSG.TIMER,      FnFinishPlaceTimeout,   PMState.Idle);
+            
+
+            // Swap Wafer With PM
+            Transition(PMState.DropDownReady,   MSG.PreparePlace,   FnStartSwapPlace,       PMState.PreparePlace);
+
             Running = true;
 
             WaferManager.Instance.SubscribeLocation(Module, 1);
@@ -514,9 +566,57 @@ namespace Venus_RT.Modules.PMs
 
         private bool fEnterError(object[] objs)
         {
+            return true;
+        }
+
+        private bool fnEnterPickReady(object[] objs)
+        {
+            Status = PMStatus.Ready_For_Pick;
+            return true;
+        }
+
+        private bool fnExitPickReady(object[] objs)
+        {
+            Status = PMStatus.Not_Ready;
+            return true;
+        }
+
+        private bool fnEnterPlaceReady(object[] objs)
+        {
+            Status = PMStatus.Ready_For_Place;
+            return true;
+        }
+
+        private bool fnExitPlaceReady(object[] objs)
+        {
+            Status = PMStatus.Not_Ready;
+            return true;
+        }
+
+        private bool fnEnterDropDownReady(object[] objs)
+        {
+            Status = PMStatus.Exchange_Ready;
+            return true;
+        }
+
+        private bool fnExitDropDownReady(object[] objs)
+        {
+            Status = PMStatus.Not_Ready;
+            return true;
+        }
+
+        private bool fnEnterLiftUpReady(object[] objs)
+        {
+            Status = PMStatus.Exchange_Ready;
+            return true;
+        }
 
+        private bool fnExitLiftUpReady(object[] objs)
+        {
+            Status = PMStatus.Not_Ready;
             return true;
         }
+
         private bool FnError(object[] objs)
         {
             Running = false;
@@ -963,7 +1063,6 @@ namespace Venus_RT.Modules.PMs
             return true;
         }
 
-
         private bool FnStartRfPower(object[] param)
         {
             return _rfPowerRoutine.Start(param) == RState.Running;
@@ -1014,6 +1113,139 @@ namespace Venus_RT.Modules.PMs
             return true;
         }
 
+        #region Exchange wafer with TM
+        private bool FnStartPreparePick(object[] param)
+        {
+            if(!_chamber.SetLiftPin(MovementPosition.Up, out string reason))
+            {
+                LOG.Write(eEvent.ERR_PM, Module, $"Set Lift Pin Up failed:{reason}");
+                return false;
+            }
+
+            if(!_chamber.SetSlitDoor(true, out reason))
+            {
+                LOG.Write(eEvent.ERR_PM, Module, $"Set Slit Door Open failed:{reason}");
+                return false;
+            }
+
+            return true;
+        }
+        private bool FnPreparePickTimeout(object[] param)
+        {
+            return _chamber.LiftPinIsUp && IsSlitDoorOpen;
+        }
+        private bool FnAbortPreparePick(object[] param)
+        {
+            return true;
+        }
+
+        private bool FnStartFinishPick(object[] param)
+        {
+            if (!_chamber.SetSlitDoor(false, out string reason))
+            {
+                LOG.Write(eEvent.ERR_PM, Module, $"Set Slit Door Close failed:{reason}");
+                return false;
+            }
+
+            return true;
+        }
+        private bool FnAbortPick(object[] param)
+        {
+            return true;
+        }
+        private bool FnFinishPickTimeout(object[] param)
+        {
+            return IsSlitDoorClose;
+        }
+
+        private bool FnStartDropDownWafer(object[] param)
+        {
+            if (!_chamber.SetLiftPin(MovementPosition.Down, out string reason))
+            {
+                LOG.Write(eEvent.ERR_PM, Module, $"Set Lift Pin Down failed:{reason}");
+                return false;
+            }
+
+            return _chamber.SetLiftPin(MovementPosition.Down, out string _);
+        }
+
+        private bool FnDropDownTimeout(object[] param)
+        {
+            return _chamber.LiftPinIsDown;
+        }
+
+        private bool FnStartPreparePlace(object[] param)
+        {
+            if (!_chamber.SetLiftPin(MovementPosition.Down, out string reason))
+            {
+                LOG.Write(eEvent.ERR_PM, Module, $"Set Lift Pin down failed:{reason}");
+                return false;
+            }
+
+            if (!_chamber.SetSlitDoor(true, out reason))
+            {
+                LOG.Write(eEvent.ERR_PM, Module, $"Set Slit Door Open failed:{reason}");
+                return false;
+            }
+
+            return true;
+        }
+        private bool FnPreparePlaceTimeout(object[] param)
+        {
+            return _chamber.LiftPinIsDown && IsSlitDoorOpen;
+        }
+        private bool FnAbortPreparePlace(object[] param)
+        {
+            return true;
+        }
+
+        private bool FnStartLiftUpWafer(object[] param)
+        {
+            if (!_chamber.SetLiftPin(MovementPosition.Up, out string reason))
+            {
+                LOG.Write(eEvent.ERR_PM, Module, $"Set Lift Pin Up failed:{reason}");
+                return false;
+            }
+
+            return true;
+        }
+
+        private bool FnLiftUpTimeout(object[] param)
+        {
+            return _chamber.LiftPinIsUp;
+        }
+
+        private bool FnStartFinishPlace(object[] param)
+        {
+            if (!_chamber.SetLiftPin(MovementPosition.Down, out string reason))
+            {
+                LOG.Write(eEvent.ERR_PM, Module, $"Set Lift Pin Down failed:{reason}");
+                return false;
+            }
+
+            if (!_chamber.SetSlitDoor(false, out reason))
+            {
+                LOG.Write(eEvent.ERR_PM, Module, $"Set Slit Door Close failed:{reason}");
+                return false;
+            }
+
+            return true;
+        }
+        private bool FnAortPlace(object[] param)
+        {
+            return true;
+        }
+        private bool FnFinishPlaceTimeout(object[] param)
+        {
+            return _chamber.LiftPinIsDown && IsSlitDoorClose;
+        }
+        private bool FnStartSwapPlace(object[] param)
+        {
+            return true;
+        }
+
+        #endregion
+
         private void _debugRoutine()
         {
             int flag = 0;

+ 28 - 0
Venus/Venus_RT/Modules/RouteManager.cs

@@ -60,6 +60,8 @@ namespace Venus_RT.Modules
 
         public PMEntity PMA { get; private set; }
         public PMEntity PMB { get; private set; }
+        public PMEntity PMC { get; private set; }
+        public PMEntity PMD { get; private set; }
 
         public TMEntity TM { get; private set; }
 
@@ -81,6 +83,12 @@ namespace Venus_RT.Modules
             if (ModuleHelper.IsInstalled(ModuleName.PMB))
                 PMB = new PMEntity(ModuleName.PMB);
 
+            if (ModuleHelper.IsInstalled(ModuleName.PMC))
+                PMC = new PMEntity(ModuleName.PMC);
+
+            if (ModuleHelper.IsInstalled(ModuleName.PMD))
+                PMD = new PMEntity(ModuleName.PMD);
+
             if (ModuleHelper.IsInstalled(ModuleName.TM))
                 TM = new TMEntity();
 
@@ -181,5 +189,25 @@ namespace Venus_RT.Modules
 
             return true;
         }
+
+        public PMEntity GetPM(ModuleName mod)
+        {
+            if(ModuleHelper.IsInstalled(mod))
+            {
+                switch(mod)
+                {
+                    case ModuleName.PMA:
+                        return PMA;
+                    case ModuleName.PMB:
+                        return PMB;
+                    case ModuleName.PMC:
+                        return PMC;
+                    case ModuleName.PMD:
+                        return PMD;
+                }
+            }
+
+            return null;
+        }
     }
 }

+ 136 - 0
Venus/Venus_RT/Modules/TM/MFPMExtendRoutine.cs

@@ -0,0 +1,136 @@
+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;
+using Venus_RT.Modules.PMs;
+
+namespace Venus_RT.Modules.TM
+{
+    class MFPMExtendRoutine : ModuleRoutineBase, IRoutine
+    {
+        private enum ExtendStep
+        {
+            ArmExtend,
+            End,
+        }
+
+        private readonly JetTM _JetTM;
+        private readonly ITransferRobot _robot;
+
+        private int _extendingTimeout = 120 * 1000;
+        private ModuleName _targetModule;
+        private PMEntity _pmModule;
+        int _targetSlot;
+        Hand _hand;
+
+        public MFPMExtendRoutine(JetTM tm, ITransferRobot robot) : base(ModuleName.TM)
+        {
+            _JetTM = tm;
+            _robot = robot;
+            Name = "Extend to PM";
+        }
+        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.IsPm(_targetModule) && ModuleHelper.IsInstalled(_targetModule))
+            {
+                _pmModule = Singleton<RouteManager>.Instance.GetPM(_targetModule);
+            }
+            else
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Invalid target module : {_targetModule} for picking action");
+                return RState.Failed;
+            }
+
+            if(_pmModule.IsSlitDoorClose)
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"{_targetModule} slit door closed, can not extend robot arm");
+                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.CheckHasWafer(_targetModule, _targetSlot))
+            {
+                if(WaferManager.Instance.CheckHasWafer(ModuleName.TM, (int)_hand))
+                {
+                    LOG.Write(eEvent.ERR_TM, Module, $"Both {_targetModule} and robot arm {_hand} have wafers");
+                    return RState.Failed;
+                }
+
+                if(_pmModule.LiftPinIsDown)
+                {
+                    LOG.Write(eEvent.ERR_TM, Module, $"{_targetModule} has a wafer and Lift Pin is down, can not extend robot arm");
+                    return RState.Failed;
+                }
+            }
+
+            Reset();
+            _extendingTimeout = SC.GetValue<int>($"{Module}.ExtendTimeout") * 1000;
+
+            return Runner.Start(Module, Name);
+        }
+
+        public RState Monitor()
+        {
+            Runner.Run((int)ExtendStep.ArmExtend, ArmExtend, WaitRobotExtendDone)
+                .End((int)ExtendStep.End, NullFun, _delay_50ms);
+
+            return Runner.Status;
+        }
+
+        private bool ArmExtend()
+        {
+            if (WaferManager.Instance.CheckHasWafer(_targetModule, _targetSlot))
+            {
+                return _robot.PickExtend(_targetModule, _targetSlot, _hand);
+            }
+            else
+            {
+                return _robot.PickExtend(_targetModule, _targetSlot, _hand);
+            } 
+        }
+
+        private bool WaitRobotExtendDone()
+        {
+            if (_robot.Status == RState.Running)
+            {
+                return false;
+            }
+            else if (_robot.Status == RState.End)
+            {
+                return true;
+            }
+            else
+            {
+                Runner.Stop($"TM Robot Arm Extend failed, {_robot.Status}");
+                return true;
+            }
+        }
+
+        public void Abort()
+        {
+            _robot.Halt();
+        }
+    }
+}
+

+ 170 - 0
Venus/Venus_RT/Modules/TM/MFPMPickRoutine.cs

@@ -0,0 +1,170 @@
+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;
+using Venus_RT.Modules.PMs;
+
+namespace Venus_RT.Modules.TM
+{
+    class MFPMPickRoutine : ModuleRoutineBase, IRoutine
+    {
+        private enum PickStep
+        {
+            WaitPMReady,
+            PMPrepare,
+            ArmExtend,
+            DropDownWafer,
+            ArmRetract,
+            NotifyDone,
+        }
+
+        private readonly JetTM _JetTM;
+        private readonly ITransferRobot _robot;
+
+        private int _pickingTimeout = 120 * 1000;
+        private ModuleName _targetModule;
+        private PMEntity  _pmModule;
+        int _targetSlot;
+        Hand _hand;
+
+        public MFPMPickRoutine(JetTM tm, ITransferRobot robot) : base(ModuleName.TM)
+        {
+            _JetTM = tm;
+            _robot = robot;
+            Name = "Pick from PM";
+        }
+        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.IsPm(_targetModule) && ModuleHelper.IsInstalled(_targetModule))
+            {
+                _pmModule = Singleton<RouteManager>.Instance.GetPM(_targetModule);
+            }
+            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.WaitPMReady,  () => _pmModule.IsIdle,     _delay_60s)
+                .Run((int)PickStep.PMPrepare,       ModulePrepare,              IsModulePrepareReady)
+                .Run((int)PickStep.ArmExtend,       ArmExtend,                  WaitRobotExtendDone)
+                .Run((int)PickStep.DropDownWafer,   NotifyPMPickWafer,          WaitPMWaferDropDown)
+                .Run((int)PickStep.ArmRetract,      ArmRetract,                 WaitRobotRetractDone)
+                .End((int)PickStep.NotifyDone,      NotifyPMDone,               _delay_50ms);
+
+            return Runner.Status;
+        }
+
+        private bool ModulePrepare()
+        {
+            _pmModule.PostMsg(PMEntity.MSG.PreparePick);
+            return true;
+        }
+
+        private bool IsModulePrepareReady()
+        {
+            return _pmModule.Status == PMEntity.PMStatus.Ready_For_Pick && _pmModule.IsSlitDoorOpen;
+        }
+
+        private bool ArmExtend()
+        {
+            return _robot.PickExtend(_targetModule, _targetSlot, _hand);
+        }
+        private bool ArmRetract()
+        {
+            return _robot.PickRetract(_targetModule, _targetSlot, _hand);
+        }
+
+        private bool WaitRobotExtendDone()
+        {
+            if (_robot.Status == RState.Running)
+            {
+                return false;
+            }
+            else if (_robot.Status == RState.End)
+            {
+                return true;
+            }
+            else
+            {
+                Runner.Stop($"TM Robot Pick Extend failed, {_robot.Status}");
+                return true;
+            }
+        }
+
+        private bool NotifyPMPickWafer()
+        {
+            _pmModule.PostMsg(PMEntity.MSG.DropDownWafer);
+            return true;
+        }
+
+        private bool WaitPMWaferDropDown()
+        {
+            return _pmModule.Status == PMEntity.PMStatus.Exchange_Ready;
+        }
+
+        private bool WaitRobotRetractDone()
+        {
+            if (_robot.Status == RState.Running)
+            {
+                return false;
+            }
+            else if (_robot.Status == RState.End)
+            {
+                return true;
+            }
+            else
+            {
+                Runner.Stop($"TM Robot Pick Retract failed, {_robot.Status}");
+                return true;
+            }
+        }
+
+        private bool NotifyPMDone()
+        {
+            _pmModule.PostMsg(PMEntity.MSG.PickReady);
+            return true;
+        }
+
+        public void Abort()
+        {
+            _robot.Halt();
+        }
+    }
+}

+ 170 - 0
Venus/Venus_RT/Modules/TM/MFPMPlaceRoutine.cs

@@ -0,0 +1,170 @@
+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;
+using Venus_RT.Modules.PMs;
+
+namespace Venus_RT.Modules.TM
+{
+    class MFPMPlaceRoutine : ModuleRoutineBase, IRoutine
+    {
+        private enum PlaceStep
+        {
+            WaitPMReady,
+            PMPrepare,
+            ArmExtend,
+            LiftUpWafer,
+            ArmRetract,
+            NotifyDone,
+        }
+
+        private readonly JetTM _JetTM;
+        private readonly ITransferRobot _robot;
+
+        private int _placingTimeout = 120 * 1000;
+        private ModuleName _targetModule;
+        private PMEntity _pmModule;
+        int _targetSlot;
+        Hand _hand;
+
+        public MFPMPlaceRoutine(JetTM tm, ITransferRobot robot) : base(ModuleName.TM)
+        {
+            _JetTM = tm;
+            _robot = robot;
+            Name = "Place to PM";
+        }
+        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.IsPm(_targetModule) && ModuleHelper.IsInstalled(_targetModule))
+            {
+                _pmModule = Singleton<RouteManager>.Instance.GetPM(_targetModule);
+            }
+            else
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Invalid target module : {_targetModule} for picking 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.WaitPMReady, () => _pmModule.IsIdle, _delay_60s)
+                .Run((int)PlaceStep.PMPrepare,      ModulePrepare,          IsModulePrepareReady)
+                .Run((int)PlaceStep.ArmExtend,      ArmExtend,              WaitRobotExtendDone)
+                .Run((int)PlaceStep.LiftUpWafer,    NotifyPMPlaceWafer,     WaitPMWaferLiftUp)
+                .Run((int)PlaceStep.ArmRetract,     ArmRetract,             WaitRobotRetractDone)
+                .End((int)PlaceStep.NotifyDone,     NotifyPMDone,           _delay_50ms);
+
+            return Runner.Status;
+        }
+
+        private bool ModulePrepare()
+        {
+            _pmModule.PostMsg(PMEntity.MSG.PreparePlace);
+            return true;
+        }
+
+        private bool IsModulePrepareReady()
+        {
+            return _pmModule.Status == PMEntity.PMStatus.Ready_For_Place && _pmModule.IsSlitDoorOpen;
+        }
+
+        private bool ArmExtend()
+        {
+            return _robot.PlaceExtend(_targetModule, _targetSlot, _hand);
+        }
+        private bool ArmRetract()
+        {
+            return _robot.PlaceRetract(_targetModule, _targetSlot, _hand);
+        }
+
+        private bool WaitRobotExtendDone()
+        {
+            if (_robot.Status == RState.Running)
+            {
+                return false;
+            }
+            else if (_robot.Status == RState.End)
+            {
+                return true;
+            }
+            else
+            {
+                Runner.Stop($"TM Robot Place Extend failed, {_robot.Status}");
+                return true;
+            }
+        }
+
+        private bool NotifyPMPlaceWafer()
+        {
+            _pmModule.PostMsg(PMEntity.MSG.LiftUpWafer);
+            return true;
+        }
+
+        private bool WaitPMWaferLiftUp()
+        {
+            return _pmModule.Status == PMEntity.PMStatus.Exchange_Ready;
+        }
+
+        private bool WaitRobotRetractDone()
+        {
+            if (_robot.Status == RState.Running)
+            {
+                return false;
+            }
+            else if (_robot.Status == RState.End)
+            {
+                return true;
+            }
+            else
+            {
+                Runner.Stop($"TM Robot Place Retract failed, {_robot.Status}");
+                return true;
+            }
+        }
+
+        private bool NotifyPMDone()
+        {
+            _pmModule.PostMsg(PMEntity.MSG.PlaceReady);
+            return true;
+        }
+
+        public void Abort()
+        {
+            _robot.Halt();
+        }
+    }
+}

+ 115 - 0
Venus/Venus_RT/Modules/TM/MFPMRetractRoutine.cs

@@ -0,0 +1,115 @@
+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;
+using Venus_RT.Modules.PMs;
+
+namespace Venus_RT.Modules.TM
+{
+    class MFPMRetractRoutine : ModuleRoutineBase, IRoutine
+    {
+        private enum RetractStep
+        {
+            ArmRetract,
+            End,
+        }
+
+        private readonly JetTM _JetTM;
+        private readonly ITransferRobot _robot;
+
+        private int _extendingTimeout = 120 * 1000;
+        private ModuleName _targetModule;
+        private PMEntity _pmModule;
+        int _targetSlot;
+        Hand _hand;
+
+        public MFPMRetractRoutine(JetTM tm, ITransferRobot robot) : base(ModuleName.TM)
+        {
+            _JetTM = tm;
+            _robot = robot;
+            Name = "Retract from PM";
+        }
+        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.IsPm(_targetModule) && ModuleHelper.IsInstalled(_targetModule))
+            {
+                _pmModule = Singleton<RouteManager>.Instance.GetPM(_targetModule);
+            }
+            else
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Invalid target module : {_targetModule} for picking action");
+                return RState.Failed;
+            }
+
+            if (_pmModule.IsSlitDoorClose)
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"{_targetModule} slit door closed, can not extend robot arm");
+                return RState.Failed;
+            }
+
+            Reset();
+            _extendingTimeout = SC.GetValue<int>($"{Module}.ExtendTimeout") * 1000;
+
+            return Runner.Start(Module, Name);
+        }
+
+        public RState Monitor()
+        {
+            Runner.Run((int)RetractStep.ArmRetract, ArmRetract, WaitRobotExtendDone)
+                .End((int)RetractStep.End, NullFun, _delay_50ms);
+
+            return Runner.Status;
+        }
+
+        private bool ArmRetract()
+        {
+            if (WaferManager.Instance.CheckHasWafer(_targetModule, _targetSlot))
+            {
+                return _robot.PickRetract(_targetModule, _targetSlot, _hand);
+            }
+            else
+            {
+                return _robot.PlaceRetract(_targetModule, _targetSlot, _hand);
+            }
+        }
+
+        private bool WaitRobotExtendDone()
+        {
+            if (_robot.Status == RState.Running)
+            {
+                return false;
+            }
+            else if (_robot.Status == RState.End)
+            {
+                return true;
+            }
+            else
+            {
+                Runner.Stop($"TM Robot Arm Retract failed, {_robot.Status}");
+                return true;
+            }
+        }
+
+        public void Abort()
+        {
+            _robot.Halt();
+        }
+    }
+}
+

+ 216 - 0
Venus/Venus_RT/Modules/TM/MFPMSwapRoutine.cs

@@ -0,0 +1,216 @@
+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;
+using Venus_RT.Modules.PMs;
+
+namespace Venus_RT.Modules.TM
+{
+    class MFPMSwapRoutine : ModuleRoutineBase, IRoutine
+    {
+        private enum SwapStep
+        {
+            WaitPMReady,
+            PickPrepare,
+            PickExtend,
+            DropDownWafer,
+            PickRetract,
+            PlacePrepare,
+            PlaceExtend,
+            LiftUpWafer,
+            PlaceRetract,
+            NotifyDone,
+        }
+
+        private readonly JetTM _JetTM;
+        private readonly ITransferRobot _robot;
+
+        private int _swapingTimeout = 120 * 1000;
+        private ModuleName _targetModule;
+        private PMEntity _pmModule;
+        int _targetSlot;
+        Hand _pickHand;
+        Hand _placeHand;
+
+        public MFPMSwapRoutine(JetTM tm, ITransferRobot robot) : base(ModuleName.TM)
+        {
+            _JetTM = tm;
+            _robot = robot;
+            Name = "Swap with PM";
+        }
+        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];
+            _pickHand = (Hand)objs[2];
+            _placeHand = _pickHand == Hand.Blade2 ? Hand.Blade1 : Hand.Blade2;
+
+            if (ModuleHelper.IsPm(_targetModule) && ModuleHelper.IsInstalled(_targetModule))
+            {
+                _pmModule = Singleton<RouteManager>.Instance.GetPM(_targetModule);
+            }
+            else
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Invalid target module : {_targetModule} for picking action");
+                return RState.Failed;
+            }
+
+            if (WaferManager.Instance.CheckNoWafer(ModuleName.TM, (int)_placeHand))
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Cannot Swap Wafer as TM Robot Arm: {_placeHand} has no wafer");
+                return RState.Failed;
+            }
+
+            if (WaferManager.Instance.CheckHasWafer(ModuleName.TM, (int)_pickHand))
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Cannot Swap Wafer as TM Robot Arm: {_pickHand} has a wafer");
+                return RState.Failed;
+            }
+
+            if (WaferManager.Instance.CheckNoWafer(_targetModule, _targetSlot))
+            {
+                LOG.Write(eEvent.ERR_TM, Module, $"Cannot Swap Wafer as {_targetModule} Slot {_targetSlot} has no wafer");
+                return RState.Failed;
+            }
+
+            Reset();
+            _swapingTimeout = SC.GetValue<int>($"{Module}.SwapTimeout") * 1000;
+
+            return Runner.Start(Module, Name);
+        }
+
+        public RState Monitor()
+        {
+            Runner.Wait((int)SwapStep.WaitPMReady,  () => _pmModule.IsIdle, _delay_60s)
+                .Run((int)SwapStep.PickPrepare,     PickPrepare,            IsModuleReadyForPick)
+                .Run((int)SwapStep.PickExtend,      PickExtend,             WaitRobotExtendDone)
+                .Run((int)SwapStep.DropDownWafer,   NotifyPMPickWafer,      WaitPMWaferDropDown)
+                .Run((int)SwapStep.PickRetract,     PickRetract,            WaitRobotRetractDone)
+                .Run((int)SwapStep.PlacePrepare,    PlacePrepare,           IsModuleReadyForPlace)
+                .Run((int)SwapStep.PlaceExtend,     PlaceExtend,            WaitRobotExtendDone)
+                .Run((int)SwapStep.LiftUpWafer,     NotifyLiftUpWafer,      WaitPMWaferLiftUp)
+                .Run((int)SwapStep.PlaceRetract,    PlaceRetract,           WaitRobotRetractDone)
+                .End((int)SwapStep.NotifyDone,      NotifyPMDone,           _delay_50ms);
+
+            return Runner.Status;
+        }
+
+        private bool PickPrepare()
+        {
+            _pmModule.PostMsg(PMEntity.MSG.PreparePick);
+            return true;
+        }
+
+        private bool IsModuleReadyForPick()
+        {
+            return _pmModule.Status == PMEntity.PMStatus.Ready_For_Pick && _pmModule.IsSlitDoorOpen;
+        }
+
+        private bool PickExtend()
+        {
+            return _robot.PickExtend(_targetModule, _targetSlot, _pickHand);
+        }
+        private bool PickRetract()
+        {
+            return _robot.PickRetract(_targetModule, _targetSlot, _pickHand);
+        }
+
+        private bool PlacePrepare()
+        {
+            _pmModule.PostMsg(PMEntity.MSG.PreparePlace);
+            return true;
+        }
+
+        private bool IsModuleReadyForPlace()
+        {
+            return _pmModule.Status == PMEntity.PMStatus.Ready_For_Place && _pmModule.IsSlitDoorOpen;
+        }
+
+        private bool PlaceExtend()
+        {
+            return _robot.PlaceExtend(_targetModule, _targetSlot, _placeHand);
+        }
+        private bool PlaceRetract()
+        {
+            return _robot.PlaceRetract(_targetModule, _targetSlot, _placeHand);
+        }
+
+        private bool WaitRobotExtendDone()
+        {
+            if (_robot.Status == RState.Running)
+            {
+                return false;
+            }
+            else if (_robot.Status == RState.End)
+            {
+                return true;
+            }
+            else
+            {
+                Runner.Stop($"TM Robot Place Extend failed, {_robot.Status}");
+                return true;
+            }
+        }
+
+        private bool NotifyPMPickWafer()
+        {
+            _pmModule.PostMsg(PMEntity.MSG.DropDownWafer);
+            return true;
+        }
+
+        private bool WaitPMWaferDropDown()
+        {
+            return _pmModule.Status == PMEntity.PMStatus.Exchange_Ready;
+        }
+
+        private bool WaitRobotRetractDone()
+        {
+            if (_robot.Status == RState.Running)
+            {
+                return false;
+            }
+            else if (_robot.Status == RState.End)
+            {
+                return true;
+            }
+            else
+            {
+                Runner.Stop($"TM Robot Swap Retract failed, {_robot.Status}");
+                return true;
+            }
+        }
+
+        private bool NotifyLiftUpWafer()
+        {
+            _pmModule.PostMsg(PMEntity.MSG.LiftUpWafer);
+            return true;
+        }
+        private bool WaitPMWaferLiftUp()
+        {
+            return _pmModule.Status == PMEntity.PMStatus.Exchange_Ready;
+        }
+
+        private bool NotifyPMDone()
+        {
+            _pmModule.PostMsg(PMEntity.MSG.PlaceReady);
+            return true;
+        }
+
+        public void Abort()
+        {
+            _robot.Halt();
+        }
+    }
+}

+ 110 - 0
Venus/Venus_RT/Modules/TM/TMEntity.cs

@@ -27,6 +27,9 @@ namespace Venus_RT.Modules
             Picking,         
             Placing,  
             Swaping,
+            PMPicking,
+            PMPlacing,
+            PMSwaping,
             Aligning,      
             Mapping,     
             Extending,    
@@ -48,6 +51,9 @@ namespace Venus_RT.Modules
             Pick, 
             Place,
             Swap,
+            PMPick,
+            PMPlace,
+            PMSwap,
             Extend,
             Retract,
             Error,
@@ -87,6 +93,9 @@ namespace Venus_RT.Modules
         private readonly MFPickRoutine _pickRoutine;
         private readonly MFPlaceRoutine _placeRoutine;
         private readonly MFSwapRoutine _swapRoutine;
+        private readonly MFPMPickRoutine _pmPickRoutine;
+        private readonly MFPMPlaceRoutine _pmPlaceRoutine;
+        private readonly MFPMSwapRoutine _pmSwapRoutine;
         public TMEntity()
         {
             _tm                 = Singleton<JetTM>.Instance;
@@ -97,6 +106,10 @@ namespace Venus_RT.Modules
             _placeRoutine   = new MFPlaceRoutine(_tm, _robot);
             _swapRoutine    = new MFSwapRoutine(_tm, _robot);
 
+            _pmPickRoutine  = new MFPMPickRoutine(_tm, _robot);
+            _pmPlaceRoutine = new MFPMPlaceRoutine(_tm, _robot);
+            _pmSwapRoutine  = new MFPMSwapRoutine(_tm, _robot);
+
             _pumpingRoutine     = new MFPumpRoutine(_tm, ModuleName.TM);
             _ventingRoutine     = new MFVentRoutine(_tm, ModuleName.TM);
             _leakCheckRoutine   = new MFLeakCheckRoutine(_tm, ModuleName.TM);
@@ -166,6 +179,21 @@ namespace Venus_RT.Modules
             Transition(STATE.Swaping,           FSM_MSG.TIMER,      FnSwapTimeout,      STATE.Idle);
             Transition(STATE.Swaping,           MSG.Abort,          FnAbortSwap,        STATE.Idle);
 
+            // Pick wafer from PM sequence
+            Transition(STATE.Idle,              MSG.PMPick,         FnStartPMPick,      STATE.PMPicking);
+            Transition(STATE.PMPicking,         FSM_MSG.TIMER,      FnPMPickTimeout,    STATE.Idle);
+            Transition(STATE.PMPicking,         MSG.Abort,          FnAbortPMPick,      STATE.Idle);
+
+            // Place wafer to PM sequence
+            Transition(STATE.Idle,              MSG.PMPlace,        FnStartPMPlace,     STATE.PMPlacing);
+            Transition(STATE.PMPlacing,         FSM_MSG.TIMER,      FnPMPlaceTimeout,   STATE.Idle);
+            Transition(STATE.PMPlacing,         MSG.Abort,          FnAbortPMPlace,     STATE.Idle);
+
+            // Swap wafer with PM sequence
+            Transition(STATE.Idle,              MSG.PMSwap,         FnStartPMSwap,      STATE.PMSwaping);
+            Transition(STATE.PMSwaping,         FSM_MSG.TIMER,      FnPMSwapTimeout,    STATE.Idle);
+            Transition(STATE.PMSwaping,         MSG.Abort,          FnAbortPMSwap,      STATE.Idle);
+
             Running = true;
         }
 
@@ -377,6 +405,75 @@ namespace Venus_RT.Modules
             return true;
         }
 
+
+        private bool FnStartPMPick(object[] param)
+        {
+            return _pmPickRoutine.Start(param) == RState.Running;
+        }
+
+        private bool FnPMPickTimeout(object[] param)
+        {
+            RState ret = _pmPickRoutine.Monitor();
+            if (ret == RState.Failed || ret == RState.Timeout)
+            {
+                PostMsg(MSG.Error);
+                return false;
+            }
+
+            return ret == RState.End;
+        }
+
+        private bool FnAbortPMPick(object[] param)
+        {
+            _pmPickRoutine.Abort();
+            return true;
+        }
+        private bool FnStartPMPlace(object[] param)
+        {
+            return _pmPlaceRoutine.Start(param) == RState.Running;
+        }
+
+        private bool FnPMPlaceTimeout(object[] param)
+        {
+            RState ret = _pmPlaceRoutine.Monitor();
+            if (ret == RState.Failed || ret == RState.Timeout)
+            {
+                PostMsg(MSG.Error);
+                return false;
+            }
+
+            return ret == RState.End;
+        }
+
+        private bool FnAbortPMPlace(object[] param)
+        {
+            _pmPlaceRoutine.Abort();
+            return true;
+        }
+
+        private bool FnStartPMSwap(object[] param)
+        {
+            return _pmSwapRoutine.Start(param) == RState.Running;
+        }
+
+        private bool FnPMSwapTimeout(object[] param)
+        {
+            RState ret = _pmSwapRoutine.Monitor();
+            if (ret == RState.Failed || ret == RState.Timeout)
+            {
+                PostMsg(MSG.Error);
+                return false;
+            }
+
+            return ret == RState.End;
+        }
+
+        private bool FnAbortPMSwap(object[] param)
+        {
+            _pmSwapRoutine.Abort();
+            return true;
+        }
+
         public bool Check(int msg, out string reason, params object[] args)
         {
             reason = "";
@@ -442,6 +539,19 @@ namespace Venus_RT.Modules
             {
                 PostMsg(MSG.Swap, ModuleName.LLA, 0, 2, 0);
             }
+            else if(flag == 7)
+            {
+                PostMsg(MSG.PMPick, ModuleName.PMA, 0, 0);
+            }
+            else if(flag == 8)
+            {
+                PostMsg(MSG.PMPlace, ModuleName.PMA, 0, 0);
+            }
+            else if(flag == 9)
+            {
+                PostMsg(MSG.PMSwap, ModuleName.PMA, 0, 0, 0, 0);
+            }
+
             //else if (flag == 4)
             //{
             //    PostMsg(MSG.PumpLoadLock);

+ 5 - 0
Venus/Venus_RT/Venus_RT.csproj

@@ -214,6 +214,11 @@
     <Compile Include="Modules\TM\MFLeakCheckRoutine.cs" />
     <Compile Include="Modules\TM\MFPickRoutine.cs" />
     <Compile Include="Modules\TM\MFPlaceRoutine.cs" />
+    <Compile Include="Modules\TM\MFPMExtendRoutine.cs" />
+    <Compile Include="Modules\TM\MFPMPickRoutine.cs" />
+    <Compile Include="Modules\TM\MFPMPlaceRoutine.cs" />
+    <Compile Include="Modules\TM\MFPMRetractRoutine.cs" />
+    <Compile Include="Modules\TM\MFPMSwapRoutine.cs" />
     <Compile Include="Modules\TM\MFPumpRoutine.cs" />
     <Compile Include="Modules\TM\MFPurgeRoutine.cs" />
     <Compile Include="Modules\TM\MFSwapRoutine.cs" />

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

@@ -206,6 +206,14 @@ namespace Venus_Simulator.Instances
             IO.DI[$"{mod}.DI_LLA_Foreline_VAC_Gauge_Alarm"].Value = true;
             IO.DI[$"{mod}.DI_LLB_Chamber_VAC_Gauge_Alarm"].Value = true;
             IO.DI[$"{mod}.DI_LLB_Foreline_VAC_Gauge_Alarm"].Value = true;
+
+            // Datetime
+            SetAiValue($"{mod}.AI_Year", DateTime.Today.Year);
+            SetAiValue($"{mod}.AI_Month", DateTime.Today.Month);
+            SetAiValue($"{mod}.AI_Day", DateTime.Today.Day);
+            SetAiValue($"{mod}.AI_Time", DateTime.Now.Hour);
+            SetAiValue($"{mod}.AI_Minute", DateTime.Now.Minute);
+            SetAiValue($"{mod}.AI_Second", DateTime.Now.Second);
         }
 
         private bool OnMonitor()
@@ -234,6 +242,7 @@ namespace Venus_Simulator.Instances
                 //MonitorIOPumpCtrl(ModuleName.PMB);
 
                 MonitorMFSlitDoor();
+                ChangeTime(ModuleName.TM);
 
 
             }