Browse Source

update vpw recipe

chenkui 3 days ago
parent
commit
9ed5517270

+ 1 - 1
PunkHPX8_Core/RtState.cs

@@ -124,9 +124,9 @@ namespace PunkHPX8_Core
         Disable,
         Error,
         Init,
-        Initializing,
         Initialized,
         Idle,
+        Initializing,
         Purgeing
     }
     public enum VPWCellState

+ 1 - 1
PunkHPX8_RT/Config/Devices/Beckhoffcfg - plctask.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <BeckhoffCfg>
-	<Controller Name="MASTER" IPAddress="10.4.6.75.1.1" PortAddress="851">
+	<Controller Name="MASTER" IPAddress="192.168.0.200.1.1" PortAddress="851">
 
 		<!-- Need to have at least one input and one output before Axis stuff -->
 

+ 12 - 5
PunkHPX8_RT/Config/System.sccfg

@@ -229,9 +229,12 @@
 		<config default="true" name="SRD2EnablePresenceCheckvalue" nameView="SRD2EnablePresenceCheckvalue" description="SRD2 Enable Presence Test" max="" min="" paramter="" tag="" unit="" type="Bool"/>
 	</configs>
 	<configs name="VPWMain">
-		<config default="5.1" name="PumpKp" nameView="PumpKp" description="PumpKp" max="1000" min="0" paramter="" tag="" unit="s" type="Double"/>
-		<config default="0" name="PumpKi" nameView="PumpKi" description="PumpKi" max="1000" min="0" paramter="" tag="" unit="s" type="Double"/>
-		<config default="0" name="PumpKd" nameView="PumpKd" description="PumpKd" max="1000" min="0" paramter="" tag="" unit="s" type="Double"/>
+		<config default="5.1" name="PumpKp" nameView="PumpKp" description="Pump Kp factor for target pressure " max="1000" min="0" paramter="" tag="" unit="s" type="Double"/>
+		<config default="0" name="PumpKi" nameView="PumpKi" description="Pump Ki factor for target pressure " max="1000" min="0" paramter="" tag="" unit="s" type="Double"/>
+		<config default="0" name="PumpKd" nameView="PumpKd" description="Pump Kd factor for target pressure " max="1000" min="0" paramter="" tag="" unit="s" type="Double"/>
+		<config default="5.1" name="PumpFlowKp" nameView="PumpFlowKp" description="Pump Kp factor for target flow" max="1000" min="0" paramter="" tag="" unit="s" type="Double"/>
+		<config default="0" name="PumpFlowKi" nameView="PumpFlowKi" description="Pump Ki factor for target flow" max="1000" min="0" paramter="" tag="" unit="s" type="Double"/>
+		<config default="0" name="PumpFlowKd" nameView="PumpFlowKd" description="Pump Kd factor for target flow" max="1000" min="0" paramter="" tag="" unit="s" type="Double"/>
 		<config default="42" name="PressureTarget" nameView="PressureTarget" description="PressureTarget" max="100" min="0" paramter="" tag="" unit="s" type="Double"/>
 		<config default="7300" name="MaxPumpSpeed" nameView="MaxPumpSpeed" description="Max Pump Speed" max="32768" min="0" paramter="" tag="" unit="" type="Integer"/>
 		<config default="300" name="MinPumpSpeed" nameView="MinPumpSpeed" description="Min Pump Speed" max="32768" min="0" paramter="" tag="" unit="" type="Integer"/>
@@ -255,8 +258,12 @@
 		</configs>
 	</configs>
 	<configs name="VPW1">
-		<config default="2000" name="PutDownAfterDripClose" nameView="PutDownAfterDripClose" description="Delay time for put down chamber after drip valve close" max="10000" min="1" paramter="" tag="" unit="ms" type="Integer"></config>
-		<config default="300" name="WaitForWaferTime" nameView="WaitForWaferTime" description="Wait for wafer to place into VPW" max="3600" min="1" paramter="" tag="" unit="s" type="Integer" visible="false"></config>
+		<config default="2000" name="PutDownAfterDripClose" nameView="PutDownAfterDripClose" description="Delay time for put down chamber after drip valve close" max="60000" min="0" paramter="" tag="" unit="ms" type="Integer"></config>
+		<config default="300" name="WaitForWaferTime" nameView="WaitForWaferTime" description="Wait for wafer to place into VPW" max="3600" min="0" paramter="" tag="" unit="s" type="Integer" visible="false"></config>
+		<config default="60" name="PumpDownWarningTime" nameView="PumpDownWarningTime" description="waring time of vacuum pump down" max="3600" min="0" paramter="" tag="" unit="s" type="Integer"></config>
+		<config default="80" name="PumpDownTimeout" nameView="PumpDownTimeout" description="time out of vacuum pump down" max="3600" min="0" paramter="" tag="" unit="s" type="Integer"></config>
+		<config default="730" name="LidReleasePressure" nameView="LidReleasePressure" description="lid release pressure" max="10000" min="0" paramter="" tag="" unit="Torr" type="Integer"></config>
+		<config default="10" name="LidReleasePressureTimeout" nameView="LidReleasePressureTimeout" description="time out of lid release pressure" max="3600" min="0" paramter="" tag="" unit="s" type="Integer"></config>
 	</configs>
 	<configs name="Linmot" nameView="Linmot">
 		<config default="5000" name="LinmotHoldoffMilliseconds" nameView="LinmotHoldoffMilliseconds" description="Linmot Hold off Milliseconds" max="10000" min="1" paramter="" tag="" unit="ms" type="Integer"></config>

+ 5 - 0
PunkHPX8_RT/Modules/VpwCell/VpwCellEntity.cs

@@ -256,6 +256,11 @@ namespace PunkHPX8_RT.Modules.VpwMain
             Transition(VPWCellState.Retrying, FSM_MSG.TIMER, VpwCellRetry, VPWCellState.Retrying);
             Transition(VPWCellState.Retrying, VpwCellMsg.Prepare, RetryPrepare, VPWCellState.Preparing);
             Transition(VPWCellState.Retrying, VpwCellMsg.RunRecipe, RetryRunRecipe, VPWCellState.RunReciping);
+
+
+            EnumLoop<VPWCellState>.ForEach((item) => { fsm.MapState((int)item, item.ToString()); });
+
+            EnumLoop<VpwCellMsg>.ForEach((item) => { fsm.MapMessage((int)item, item.ToString()); });
         }
         /// <summary>
         /// 初始化数据

+ 30 - 6
PunkHPX8_RT/Modules/VpwCell/VpwManualPrepareRoutine.cs

@@ -2,6 +2,7 @@
 using Aitex.Core.RT.Log;
 using Aitex.Core.RT.Routine;
 using Aitex.Core.RT.SCCore;
+using Aitex.Core.Util;
 using MECF.Framework.Common.Equipment;
 using MECF.Framework.Common.RecipeCenter;
 using MECF.Framework.Common.Routine;
@@ -10,6 +11,7 @@ using PunkHPX8_Core;
 using PunkHPX8_RT.Devices.AXIS;
 using PunkHPX8_RT.Devices.VpwCell;
 using PunkHPX8_RT.Devices.VpwMain;
+using PunkHPX8_RT.Modules.VpwMain;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -22,10 +24,10 @@ namespace PunkHPX8_RT.Modules.VpwCell
     {
         private enum PrepareStep
         {
-            CheckPreCondition,
+            CheckPreCondition=0,
             Purge,
             WaitPurge,
-            RotationPositionOffset,
+            RotationPositionOffset=1,
             WaitRotation,
             CloseDrip,
             Delay,
@@ -53,6 +55,10 @@ namespace PunkHPX8_RT.Modules.VpwCell
         /// </summary>
         private VpwMainDevice _mainDevice;
         /// <summary>
+        /// VPW Entity
+        /// </summary>
+        private VpwMainEntity _vpwMainEntity;
+        /// <summary>
         /// 延迟时间
         /// </summary>
         private int _putDownAfterDripClose = 2000;
@@ -101,7 +107,24 @@ namespace PunkHPX8_RT.Modules.VpwCell
         /// <returns></returns>
         private bool Purge()
         {
-            return true;
+            if (_vpwMainEntity == null)
+            {
+                NotifyError(eEvent.ERR_VPW, "VPW Main not exist", -1);
+                return false;
+            }
+            if (_vpwMainEntity.IsIdle)
+            {
+                return _vpwMainEntity.CheckToPostMessage<VPWMainState, VPWMainMsg>(eEvent.ERR_VPW, Module, (int)VPWMainMsg.Purge);
+            }
+            else if (_vpwMainEntity.State == VPWMainState.Purgeing)
+            {
+                return true;
+            }
+            else
+            {
+                NotifyError(eEvent.ERR_VPW, $"State {_vpwMainEntity.State} cannot purge", -1);
+                return false;
+            }
         }
         /// <summary>
         /// 检验Purge执行是否结束
@@ -109,7 +132,7 @@ namespace PunkHPX8_RT.Modules.VpwCell
         /// <returns></returns>
         private bool CheckPurgeStatus()
         {
-            return true;
+            return _vpwMainEntity.IsIdle;
         }
         /// <summary>
         /// 检验Purger执行是否出现异常
@@ -117,7 +140,7 @@ namespace PunkHPX8_RT.Modules.VpwCell
         /// <returns></returns>
         private bool CheckPurgeStopStatus()
         {
-            return false;
+            return _vpwMainEntity.IsError;
         }
         /// <summary>
         /// Position运行至offset
@@ -209,6 +232,7 @@ namespace PunkHPX8_RT.Modules.VpwCell
             _mainDevice = DEVICE.GetDevice<VpwMainDevice>(ModuleName.VPWMain1.ToString());
             _putDownAfterDripClose = SC.GetValue<int>($"{Module}.PutDownAfterDripClose");
             _waitForWaferTime = SC.GetValue<int>($"{Module}.WaitForWaferTime") * 1000;
+            _vpwMainEntity = Singleton<RouteManager>.Instance.GetModule<VpwMainEntity>(ModuleName.VPWMain1.ToString());
             return Runner.Start(Module, $"{Module} prepare");
         }
         /// <summary>
@@ -227,7 +251,7 @@ namespace PunkHPX8_RT.Modules.VpwCell
                 NotifyError(eEvent.ERR_VPW, "rotaion is not homed", -1);
                 return false;
             }
-            return false;
+            return true;
         }
 
         /// <summary>

+ 49 - 0
PunkHPX8_RT/Modules/VpwCell/VpwManualRecipeRoutine.cs

@@ -1,5 +1,7 @@
 using Aitex.Core.RT.Routine;
+using MECF.Framework.Common.RecipeCenter;
 using MECF.Framework.Common.Routine;
+using MECF.Framework.Common.Utilities;
 using PunkHPX8_Core;
 using System;
 using System.Collections.Generic;
@@ -11,12 +13,35 @@ namespace PunkHPX8_RT.Modules.VpwCell
 {
     public class VpwManualRecipeRoutine : RoutineBase, IRoutine
     {
+        private enum RecipeStep
+        {
+            Prepare,
+            WaitPrepare,
+            VacuumPrewet,
+            WaitVacuumPrewet,
+            End
+        }
+        #region 内部变量
+        /// <summary>
+        /// 手动Prepare routine
+        /// </summary>
+        private VpwManualPrepareRoutine _manualPrepareRoutine;
+        /// <summary>
+        /// Vacuum prewet routine
+        /// </summary>
+        private VpwVacuumPrewetRoutine _vacuumPrewetRoutine;
+        /// <summary>
+        /// recipe
+        /// </summary>
+        private VpwRecipe _recipe;
+        #endregion
         /// <summary>
         /// 构造函数
         /// </summary>
         /// <param name="module"></param>
         public VpwManualRecipeRoutine(string module) : base(module)
         {
+            _manualPrepareRoutine = new VpwManualPrepareRoutine(module);
         }
 
         /// <summary>
@@ -32,15 +57,39 @@ namespace PunkHPX8_RT.Modules.VpwCell
         /// <returns></returns>
         public RState Monitor()
         {
+            Runner.Run(RecipeStep.Prepare, Prepare, _delay_1ms)
+                .WaitWithStopCondition(RecipeStep.WaitPrepare, ()=>CommonFunction.CheckRoutineEndState(_manualPrepareRoutine),
+                    ()=>CommonFunction.CheckRoutineStopState(_manualPrepareRoutine))
+                .Run(RecipeStep.VacuumPrewet,VacuumPrewet)
+                 .WaitWithStopCondition(RecipeStep.WaitVacuumPrewet, () => CommonFunction.CheckRoutineEndState(_vacuumPrewetRoutine),
+                    () => CommonFunction.CheckRoutineStopState(_vacuumPrewetRoutine))
+                .End(RecipeStep.End, NullFun, _delay_1ms);
             return Runner.Status;
         }
         /// <summary>
+        /// Prepare
+        /// </summary>
+        /// <returns></returns>
+        private bool Prepare()
+        {
+            return _manualPrepareRoutine.Start(_recipe) == RState.Running;
+        }
+        /// <summary>
+        /// Vacuum Prewet
+        /// </summary>
+        /// <returns></returns>
+        private bool VacuumPrewet()
+        {
+            return _vacuumPrewetRoutine.Start(_recipe) == RState.Running;
+        }
+        /// <summary>
         /// 启动
         /// </summary>
         /// <param name="objs"></param>
         /// <returns></returns>
         public RState Start(params object[] objs)
         {
+            _recipe = objs[0] as VpwRecipe;
             return Runner.Start(Module, "start run recipe");
         }
     }

+ 27 - 46
PunkHPX8_RT/Modules/VpwCell/VpwPrepareRoutine.cs

@@ -2,6 +2,7 @@
 using Aitex.Core.RT.Log;
 using Aitex.Core.RT.Routine;
 using Aitex.Core.RT.SCCore;
+using Aitex.Core.Util;
 using MECF.Framework.Common.Equipment;
 using MECF.Framework.Common.RecipeCenter;
 using MECF.Framework.Common.Routine;
@@ -10,6 +11,7 @@ using PunkHPX8_Core;
 using PunkHPX8_RT.Devices.AXIS;
 using PunkHPX8_RT.Devices.VpwCell;
 using PunkHPX8_RT.Devices.VpwMain;
+using PunkHPX8_RT.Modules.VpwMain;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -56,6 +58,10 @@ namespace PunkHPX8_RT.Modules.VpwCell
         /// Main设备
         /// </summary>
         private VpwMainDevice _mainDevice;
+        /// <summary>
+        /// VPW Entity
+        /// </summary>
+        private VpwMainEntity _vpwMainEntity;
         #endregion
         /// <summary>
         /// 构造函数
@@ -93,7 +99,24 @@ namespace PunkHPX8_RT.Modules.VpwCell
         /// <returns></returns>
         private bool Purge()
         {
-            return true;
+            if (_vpwMainEntity == null)
+            {
+                NotifyError(eEvent.ERR_VPW, "VPW Main not exist", -1);
+                return false;
+            }
+            if (_vpwMainEntity.IsIdle)
+            {
+                return _vpwMainEntity.CheckToPostMessage<VPWMainState, VPWMainMsg>(eEvent.ERR_VPW, Module, (int)VPWMainMsg.Purge);
+            }
+            else if (_vpwMainEntity.State == VPWMainState.Purgeing)
+            {
+                return true;
+            }
+            else
+            {
+                NotifyError(eEvent.ERR_VPW, $"State {_vpwMainEntity.State} cannot purge", -1);
+                return false;
+            }
         }
         /// <summary>
         /// 检验Purge执行是否结束
@@ -101,7 +124,7 @@ namespace PunkHPX8_RT.Modules.VpwCell
         /// <returns></returns>
         private bool CheckPurgeStatus()
         {
-            return true;
+            return _vpwMainEntity.IsIdle;
         }
         /// <summary>
         /// 检验Purger执行是否出现异常
@@ -109,7 +132,7 @@ namespace PunkHPX8_RT.Modules.VpwCell
         /// <returns></returns>
         private bool CheckPurgeStopStatus()
         {
-            return false;
+            return _vpwMainEntity.IsError;
         }
         /// <summary>
         /// Position运行至offset
@@ -146,49 +169,6 @@ namespace PunkHPX8_RT.Modules.VpwCell
             return result;
         }
         /// <summary>
-        /// 检验是否存在Wafer
-        /// </summary>
-        /// <returns></returns>
-        private bool CheckWaferExsit()
-        {
-            return WaferManager.Instance.CheckHasWafer(Module, 0);
-        }
-        /// <summary>
-        /// chamber up
-        /// </summary>
-        /// <returns></returns>
-        private bool ChamberUp()
-        {
-            bool result=_mainDevice.ChamberUp();
-            if (!result)
-            {
-                NotifyError(eEvent.ERR_VPW, "chamber up failed", -1);
-            }
-            return result;
-        }
-        /// <summary>
-        /// 检验Chamber是否关闭
-        /// </summary>
-        /// <returns></returns>
-        private bool CheckChamberClosed()
-        {
-            return _mainDevice.CommonData.ChamberClosed && !_mainDevice.CommonData.ChamberOpened;
-        }
-        /// <summary>
-        /// Check LoopDO数值
-        /// </summary>
-        /// <returns></returns>
-        private bool CheckLoopDO()
-        {
-            double loopDoValue = _vpwCellDevice.LoopDOValue;
-            bool result = loopDoValue < _recipe.DiwLoopDoSet;
-            if (!result) 
-            {
-                NotifyError(eEvent.ERR_VPW, $"LoopDO value {loopDoValue} is less than {_recipe.DiwLoopDoSet}", -1);
-            }
-            return result;
-        }
-        /// <summary>
         /// 启动
         /// </summary>
         /// <param name="objs"></param>
@@ -201,6 +181,7 @@ namespace PunkHPX8_RT.Modules.VpwCell
             _mainDevice = DEVICE.GetDevice<VpwMainDevice>(ModuleName.VPWMain1.ToString());
             _putDownAfterDripClose = SC.GetValue<int>($"{Module}.PutDownAfterDripClose");
             _waitForWaferTime = SC.GetValue<int>($"{Module}.WaitForWaferTime") * 1000;
+            _vpwMainEntity = Singleton<RouteManager>.Instance.GetModule<VpwMainEntity>(ModuleName.VPWMain1.ToString());
             return Runner.Start(Module, $"{Module} prepare");
         }
         /// <summary>

+ 53 - 3
PunkHPX8_RT/Modules/VpwCell/VpwRecipeRoutine.cs

@@ -4,6 +4,7 @@ using Aitex.Core.RT.Routine;
 using MECF.Framework.Common.Equipment;
 using MECF.Framework.Common.RecipeCenter;
 using MECF.Framework.Common.Routine;
+using MECF.Framework.Common.Utilities;
 using PunkHPX8_Core;
 using PunkHPX8_RT.Devices.VpwCell;
 using PunkHPX8_RT.Devices.VpwMain;
@@ -22,6 +23,8 @@ namespace PunkHPX8_RT.Modules.VpwCell
             ChamberUp,
             CloseDrain,
             CheckLoopDO,
+            VacuumPrewet,
+            WaitVacuumPrewet,
             End
         }
         #region 内部变量
@@ -37,6 +40,10 @@ namespace PunkHPX8_RT.Modules.VpwCell
         /// Main设备
         /// </summary>
         private VpwMainDevice _mainDevice;
+        /// <summary>
+        /// Vacuum prewet routine
+        /// </summary>
+        private VpwVacuumPrewetRoutine _vacuumPrewetRoutine;
         #endregion
         /// <summary>
         /// 构造函数
@@ -44,6 +51,7 @@ namespace PunkHPX8_RT.Modules.VpwCell
         /// <param name="module"></param>
         public VpwRecipeRoutine(string module) : base(module)
         {
+            _vacuumPrewetRoutine = new VpwVacuumPrewetRoutine(Module);
         }
 
         /// <summary>
@@ -62,6 +70,8 @@ namespace PunkHPX8_RT.Modules.VpwCell
             Runner.Run(RecipeStep.ChamberUp, ChamberUp, CheckChamberClosed)
                 .Run(RecipeStep.CloseDrain, _vpwCellDevice.DrainValveOff, _delay_1ms)
                 .Run(RecipeStep.CheckLoopDO, CheckLoopDO, _delay_1ms)
+                .Run(RecipeStep.VacuumPrewet,VacuumPrewet,_delay_1ms)
+                .WaitWithStopCondition(RecipeStep.WaitVacuumPrewet,CheckVacuumPrewetEndStatus,CheckVacuumPrewetStopStatus)
                 .End(RecipeStep.End, NullFun, _delay_1ms);
             return Runner.Status;
         }
@@ -74,7 +84,7 @@ namespace PunkHPX8_RT.Modules.VpwCell
             bool result = _mainDevice.ChamberUp();
             if (!result)
             {
-                NotifyError(eEvent.ERR_VPW, "chamber up failed", -1);
+                NotifyError(eEvent.ERR_VPW, "chamber up failed", 0);
             }
             return result;
         }
@@ -84,7 +94,12 @@ namespace PunkHPX8_RT.Modules.VpwCell
         /// <returns></returns>
         private bool CheckChamberClosed()
         {
-            return _mainDevice.CommonData.ChamberClosed && !_mainDevice.CommonData.ChamberOpened;
+            bool result= _mainDevice.CommonData.ChamberClosed && !_mainDevice.CommonData.ChamberOpened;
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_VPW, $"Chamber Closed is {_mainDevice.CommonData.ChamberClosed} and opened is {_mainDevice.CommonData.ChamberOpened}",0);
+            }
+            return result;
         }
         /// <summary>
         /// Check LoopDO数值
@@ -96,7 +111,42 @@ namespace PunkHPX8_RT.Modules.VpwCell
             bool result = loopDoValue < _recipe.DiwLoopDoSet;
             if (!result)
             {
-                NotifyError(eEvent.ERR_VPW, $"LoopDO value {loopDoValue} is less than {_recipe.DiwLoopDoSet}", -1);
+                NotifyError(eEvent.ERR_VPW, $"LoopDO value {loopDoValue} is less than {_recipe.DiwLoopDoSet}", 0);
+            }
+            return result;
+        }
+        /// <summary>
+        /// Vacuum Prewet
+        /// </summary>
+        /// <returns></returns>
+        private bool VacuumPrewet()
+        {
+            bool result = _vacuumPrewetRoutine.Start(_recipe) == RState.Running;
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_VPW, _vacuumPrewetRoutine.ErrorMsg, 1);
+            }
+            return result;
+        }
+        /// <summary>
+        /// 检验Vacuum Prewet结束状态
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckVacuumPrewetEndStatus()
+        {
+            bool result = CommonFunction.CheckRoutineEndState(_vacuumPrewetRoutine);
+            return result;
+        }
+        /// <summary>
+        /// 检验Vacuum Prewet异常
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckVacuumPrewetStopStatus()
+        {
+            bool result=CommonFunction.CheckRoutineStopState(_vacuumPrewetRoutine);
+            if (result)
+            {
+                NotifyError(eEvent.ERR_VPW, _vacuumPrewetRoutine.ErrorMsg, 1);
             }
             return result;
         }

+ 386 - 0
PunkHPX8_RT/Modules/VpwCell/VpwVacuumPrewetRoutine.cs

@@ -0,0 +1,386 @@
+using Aitex.Core.RT.Device;
+using Aitex.Core.RT.Log;
+using Aitex.Core.RT.Routine;
+using Aitex.Core.RT.SCCore;
+using Aitex.Core.Util;
+using MECF.Framework.Common.Alarm;
+using MECF.Framework.Common.Equipment;
+using MECF.Framework.Common.RecipeCenter;
+using MECF.Framework.Common.Routine;
+using MECF.Framework.Common.SubstrateTrackings;
+using PunkHPX8_Core;
+using PunkHPX8_RT.Devices.AXIS;
+using PunkHPX8_RT.Devices.VpwCell;
+using PunkHPX8_RT.Devices.VpwMain;
+using PunkHPX8_RT.Modules.VpwMain;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PunkHPX8_RT.Modules.VpwCell
+{
+    public class VpwVacuumPrewetRoutine : RoutineBase, IRoutine
+    {
+        private enum PrepareStep
+        {
+            OpenVacuumValve,
+            EnableVacuumPump,
+            CheckVacuum,
+            CloseVacuumValve,
+            OpenVentValve,
+            CheckLidReleaseVacuum,
+            ChamberDown,
+            CloseVentValve,
+            ChamberUp,
+            LastOpenVacuumValve,
+            LastCheckVacuum,
+            DryerHoldTime,
+            OpenCellValve,
+            End
+        }
+        #region 内部变量
+        /// <summary>
+        /// recipe
+        /// </summary>
+        private VpwRecipe _recipe;
+        /// <summary>
+        /// 设备
+        /// </summary>
+        private VpwCellDevice _vpwCellDevice;
+        /// <summary>
+        /// Main设备
+        /// </summary>
+        private VpwMainDevice _mainDevice;
+        /// <summary>
+        /// Pump DownWarn时间
+        /// </summary>
+        private int _pumpDownWarningTime = 60000;
+        /// <summary>
+        /// Pump Down超时
+        /// </summary>
+        private int _pumpDownTimeOut = 80000;
+        /// <summary>
+        /// 开始Pump Down时间
+        /// </summary>
+        private DateTime _pumpDownTime=DateTime.MinValue;
+        /// <summary>
+        /// 是否需要重试
+        /// </summary>
+        private bool _isNeedRetry = false;
+        /// <summary>
+        /// Lid Release Pressure
+        /// </summary>
+        private int _lidReleasePressure = 730;
+        /// <summary>
+        /// Lid Release Pressure 
+        /// </summary>
+        private int _lidReleasePressureTimeout = 10000;
+        #endregion
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="module"></param>
+        public VpwVacuumPrewetRoutine(string module) : base(module)
+        {
+        }
+        /// <summary>
+        /// 中止
+        /// </summary>
+        public void Abort()
+        {
+            Runner.Stop("Manual abort");
+        }
+        /// <summary>
+        /// 监控
+        /// </summary>
+        /// <returns></returns>
+        public RState Monitor()
+        {
+            Runner.Run(PrepareStep.OpenVacuumValve, PumpValveOn, _delay_1ms)
+                .Run(PrepareStep.EnableVacuumPump, PumpEnable, _delay_1ms)
+                .Wait(PrepareStep.CheckVacuum, CheckVacuumValue, _pumpDownTimeOut + 1000)
+                .RunIf(PrepareStep.CloseVacuumValve, _isNeedRetry, PumpValveOff, _delay_1ms)
+                .RunIf(PrepareStep.OpenVentValve, _isNeedRetry, OpenVentValve, _delay_1ms)
+                .WaitIf(PrepareStep.CheckLidReleaseVacuum, _isNeedRetry, CheckLidReleaseVacuum, _lidReleasePressureTimeout)
+                .RunIf(PrepareStep.ChamberDown, _isNeedRetry, ChamberDown, CheckChamberOpened, _delay_10s)
+                .RunIf(PrepareStep.CloseVentValve, _isNeedRetry, CloseVentValve, _delay_1ms)
+                .RunIf(PrepareStep.ChamberUp, _isNeedRetry, ChamberUp, CheckChamberClosed, _delay_10s)
+                .RunIf(PrepareStep.LastOpenVacuumValve, _isNeedRetry, PumpValveOn, _delay_1ms)
+                .WaitIf(PrepareStep.LastCheckVacuum, _isNeedRetry,LastCheckVacuumValue, _pumpDownTimeOut)
+                .Delay(PrepareStep.DryerHoldTime,_recipe.DryHoldTime*1000)
+                .Run(PrepareStep.OpenCellValve,OpenCellValve,_delay_1ms)
+                .End(PrepareStep.End,NullFun,_delay_1ms);
+            return Runner.Status;
+        }
+        /// <summary>
+        /// pump valve on
+        /// </summary>
+        /// <returns></returns>
+        private bool PumpValveOn()
+        {
+            bool result = _vpwCellDevice.VacuumValveOn();
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_VPW, "pump valve on failed", 0);
+            }
+            return result;
+        }
+        /// <summary>
+        /// Pump valve off
+        /// </summary>
+        /// <returns></returns>
+        private bool PumpValveOff()
+        {
+            bool result = _vpwCellDevice.VacuumValveOff();
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_VPW, "pump valve off failed", 0);
+            }
+            return result;
+        }
+        /// <summary>
+        /// Pump Enable
+        /// </summary>
+        /// <returns></returns>
+        private bool PumpEnable()
+        {
+            bool result =_mainDevice.VacuumPumpEnable();
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_VPW, "pump enable failed", 0);
+            }
+            else
+            {
+                _pumpDownTime = DateTime.Now;
+            }
+            return result;
+        }
+        /// <summary>
+        /// 检验真空
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckVacuumValue()
+        {
+            double vacuumValue = _vpwCellDevice.CommonData.VacuumPressure;
+            if (vacuumValue >= _recipe.VacuumTarget)
+            {
+                return true;
+            }
+            if (DateTime.Now.Subtract(_pumpDownTime).TotalMilliseconds >= _pumpDownWarningTime)
+            {
+                AlarmListManager.Instance.AddWarn(Module, "vacuum value", $"vacuum value {vacuumValue} is less than {_recipe.VacuumTarget}");
+            }
+            if (DateTime.Now.Subtract(_pumpDownTime).TotalMilliseconds >= _pumpDownTimeOut)
+            {
+                _isNeedRetry = true;
+            }
+            return false;
+        }
+        /// <summary>
+        /// 检验Lid Release真空数值
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckLidReleaseVacuum()
+        {
+            double vacuumValue = _vpwCellDevice.CommonData.VacuumPressure;
+            return vacuumValue >= _lidReleasePressure;
+        }
+        /// <summary>
+        /// open vent valve
+        /// </summary>
+        /// <returns></returns>
+        private bool OpenVentValve()
+        {
+            bool result = _vpwCellDevice.VentValveOn();
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_VPW, "open vent valve failed", 0);
+            }
+            return result;
+        }
+        /// <summary>
+        /// close vent valve
+        /// </summary>
+        /// <returns></returns>
+        private bool CloseVentValve()
+        {
+            bool result = _vpwCellDevice.VentValveOff();
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_VPW, "close vent valve failed", 0);
+            }
+            return result;
+        }
+        /// <summary>
+        /// Chamber down
+        /// </summary>
+        /// <returns></returns>
+        private bool ChamberDown()
+        {
+            bool result= _mainDevice.ChamberDown();
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_VPW, "chamber down failed",0);
+            }
+            return result;
+        }
+        /// <summary>
+        /// 检验Chamber是否打开 
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckChamberOpened()
+        {
+            return _mainDevice.CommonData.ChamberOpened && !_mainDevice.CommonData.ChamberClosed;
+        }
+        /// <summary>
+        /// Chamber up
+        /// </summary>
+        /// <returns></returns>
+        private bool ChamberUp()
+        {
+            bool result = _mainDevice.ChamberUp();
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_VPW, "chamber up failed", 0);
+            }
+            return result;
+        }
+        /// <summary>
+        /// 检验Chamber是否关闭
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckChamberClosed()
+        {
+            return !_mainDevice.CommonData.ChamberOpened && _mainDevice.CommonData.ChamberClosed;
+        }
+        /// <summary>
+        /// 检验真空
+        /// </summary>
+        /// <returns></returns>
+        private bool LastCheckVacuumValue()
+        {
+            double vacuumValue = _vpwCellDevice.CommonData.VacuumPressure;
+            if (vacuumValue >= _recipe.VacuumTarget)
+            {
+                return true;
+            }
+            if (DateTime.Now.Subtract(_pumpDownTime).TotalMilliseconds >= _pumpDownWarningTime)
+            {
+                AlarmListManager.Instance.AddWarn(Module, "vacuum value", $"vacuum value {vacuumValue} is less than {_recipe.VacuumTarget}");
+            }
+            return false;
+        }
+        /// <summary>
+        /// 打开相应的cell valve
+        /// </summary>
+        /// <returns></returns>
+        private bool OpenCellValve()
+        {
+            int count = 0;
+            int enableCount = 0;
+            if (_recipe.VacuumPrewetDripEnable)
+            {
+                count += _vpwCellDevice.FlowDripOn()?1:0;
+                enableCount++;
+            }
+            if (_recipe.VacuumPrewetLargeEnable)
+            {
+                count += _vpwCellDevice.FlowLargeOn() ? 1 : 0;
+                enableCount++;
+            }
+            if (_recipe.VacuumPrewetSmallEnable)
+            {
+                count += _vpwCellDevice.FlowSmallOn() ? 1 : 0;
+                enableCount++;
+            }
+            bool result = StartRotation();
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_VPW, "start rotation failed", 0);
+                return false;
+            }
+
+            result= count == enableCount;
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_VPW, "open cell valve failed", 0);
+            }
+            return result;
+        }
+        /// <summary>
+        /// 同时旋转
+        /// </summary>
+        /// <returns></returns>
+        private bool StartRotation()
+        {
+            int targetPosition = 0;
+            int maxSpeed = 0;
+            int second = 0;
+            foreach(var item in _recipe.VacuumRinseStep)
+            {
+                second += item.DurationSeconds;
+                int speed = item.RotationSpeed;
+                if (maxSpeed < speed)
+                {
+                    maxSpeed = speed;
+                }
+            }
+
+            foreach(var item in _recipe.VentRinseStep)
+            {
+                second += item.DurationSeconds;
+                int speed = item.RotationSpeed;
+                if (maxSpeed < speed)
+                {
+                    maxSpeed = speed;
+                }
+            }
+
+            foreach(var item in _recipe.ExtendCleanRinseStep)
+            {
+                second += item.DurationSeconds;
+                int speed = item.RotationSpeed;
+                if (maxSpeed < speed)
+                {
+                    maxSpeed = speed;
+                }
+            }
+            targetPosition = maxSpeed * (second + 60 * 1000);//按最大速度*(时间+多出一分钟)
+            _vpwCellDevice.SetRotationSpeed(_recipe.VacuumRinseStep[0].RotationSpeed / 6);
+            return _vpwCellDevice.RotationProfilePosition(targetPosition);
+        }
+        /// <summary>
+        /// 启动
+        /// </summary>
+        /// <param name="objs"></param>
+        /// <returns></returns>
+        public RState Start(params object[] objs)
+        {
+            _recipe=(VpwRecipe)objs[0];
+            _vpwCellDevice = DEVICE.GetDevice<VpwCellDevice>(Module);
+            _mainDevice = DEVICE.GetDevice<VpwMainDevice>(ModuleName.VPWMain1.ToString());
+            _pumpDownWarningTime = SC.GetValue<int>($"{Module}.PumpDownWarningTime")*1000;
+            _pumpDownTimeOut = SC.GetValue<int>($"{Module}.PumpDownTimeout")*1000;
+            _lidReleasePressure = SC.GetValue<int>($"{Module}.LidReleasePressure");
+            _lidReleasePressureTimeout = SC.GetValue<int>($"{Module}.LidReleasePressureTimeout");
+            _isNeedRetry = false;
+            return Runner.Start(Module, $"{Module} prepare");
+        }
+        /// <summary>
+        /// 重试
+        /// </summary>
+        /// <param name="step"></param>
+        public RState Retry(int step)
+        {
+            if (_recipe == null)
+            {
+                NotifyError(eEvent.ERR_RINSE, "recipe is null", -1);
+                return RState.Failed;
+            }
+            List<Enum> preStepIds = new List<Enum>();
+            return Runner.Retry(PrepareStep.OpenVacuumValve, preStepIds, Module, "Vacuum Prewet Retry");
+        }
+    }
+}

+ 1 - 1
PunkHPX8_RT/Modules/VpwMain/VPWHomeRoutine.cs

@@ -569,7 +569,7 @@ namespace PunkHPX8_RT.Modules.VpwMain
                     _cellFlowOk[lstDevice[i].Module]=false;
                 }
             }
-            _mainDevice = DEVICE.GetDevice<VpwMainDevice>(Module.ToString());
+            _mainDevice = DEVICE.GetDevice<VpwMainDevice>(ModuleName.VPWMain1.ToString());
             _n2PurgeTime = SC.GetValue<int>("VPWMain.Plumbing.N2PurgeTime");
             _flowFaultHolderoffTime = SC.GetValue<int>("VPWMain.Plumbing.FlowFaultHoldoffTime");
             double purgeMotorSpeed = SC.GetValue<double>("VPWMain.Plumbing.PurgeMotorSpeed");

+ 9 - 1
PunkHPX8_RT/Modules/VpwMain/VpwMainEntity.cs

@@ -89,7 +89,14 @@ namespace PunkHPX8_RT.Modules.VpwMain
         /// </summary>
         public bool IsBusy
         {
-            get { return fsm.State == (int)VPWMainState.Initializing; }
+            get { return fsm.State > (int)VPWMainState.Idle; }
+        }
+        /// <summary>
+        /// 状态
+        /// </summary>
+        public VPWMainState State
+        {
+            get { return (VPWMainState)fsm.State; }
         }
 
         /// <summary>
@@ -156,6 +163,7 @@ namespace PunkHPX8_RT.Modules.VpwMain
             Transition(VPWMainState.Idle, VPWMainMsg.Init, NullFunc, VPWMainState.Init);
             //Purge
             Transition(VPWMainState.Idle, VPWMainMsg.Purge, Purge, VPWMainState.Purgeing);
+            Transition(VPWMainState.Purgeing, VPWMainMsg.Purge, NullFunc, VPWMainState.Purgeing);
             Transition(VPWMainState.Purgeing, FSM_MSG.TIMER, PurgeMonitor, VPWMainState.Idle);
 
             EnumLoop<VPWMainState>.ForEach((item) => { fsm.MapState((int)item, item.ToString()); });

+ 1 - 0
PunkHPX8_RT/PunkHPX8_RT.csproj

@@ -315,6 +315,7 @@
     <Compile Include="Modules\VpwCell\VpwLotTrackUtil.cs" />
     <Compile Include="Modules\VpwCell\VpwCellEntity.cs" />
     <Compile Include="Modules\VpwCell\VpwCycleManualProcessRecipeRoutine.cs" />
+    <Compile Include="Modules\VpwCell\VpwVacuumPrewetRoutine.cs" />
     <Compile Include="Modules\VpwCell\VpwPrepareRoutine.cs" />
     <Compile Include="Modules\VpwCell\VpwManualPrepareRoutine.cs" />
     <Compile Include="Modules\VpwCell\VpwRecipeRoutine.cs" />