Преглед на файлове

add loader LoadSideRoutine

chenkui преди 3 месеца
родител
ревизия
5902a2565b

+ 3 - 0
CyberX8_RT/CyberX8_RT.csproj

@@ -264,6 +264,8 @@
     <Compile Include="Modules\EFEM\EfemRobotMapRoutine.cs" />
     <Compile Include="Modules\EFEM\RobotCycleRoutine.cs" />
     <Compile Include="Modules\FaModuleNotifier.cs" />
+    <Compile Include="Modules\Loader\LoaderLoadSideRoutine.cs" />
+    <Compile Include="Modules\Loader\LoaderUnloadSideRoutine.cs" />
     <Compile Include="Modules\Metal\CompactEmbranceInitializeRoutine.cs" />
     <Compile Include="Modules\Metal\CurrentShortTestRoutine.cs" />
     <Compile Include="Modules\Metal\CompactEmbranceRunRecipeRoutine.cs" />
@@ -426,6 +428,7 @@
     <Compile Include="Schedulers\EfemRobot\RobotMoveHelper.cs" />
     <Compile Include="Schedulers\EfemRobot\SchedulerEfemRobot.cs" />
     <Compile Include="Schedulers\EfemRobot\SchedulerRobot.cs" />
+    <Compile Include="Schedulers\Puf\PufSchedulerParameter.cs" />
     <Compile Include="Schedulers\SchedulerPostMsg.cs" />
     <Compile Include="Schedulers\Srd\SchedulerSrd.cs" />
     <Compile Include="Schedulers\Loader\SchedulerLoader.cs" />

+ 1 - 4
CyberX8_RT/Devices/Loader/LoaderUnloadRoutine.cs

@@ -264,10 +264,7 @@ namespace CyberX8_RT.Devices.Loader
         /// <exception cref="NotImplementedException"></exception>
         public RState Start(params object[] objs)
         {
-            if (SC.ContainsItem($"Loader1.{_side}WaferSize"))
-            {
-                _waferSize = SC.GetValue<int>($"Loader1.{_side}WaferSize");
-            }
+            _waferSize = SC.GetValue<int>($"Loader1.{_side}WaferSize");
             _shuttleAxis = GetShuttleAxis();
             _lsAxis = GetCrsAxis();
             _tiltAxis = GetTiltAxis();

+ 81 - 43
CyberX8_RT/Dispatch/WaferTaskManager.cs

@@ -21,6 +21,11 @@ namespace CyberX8_RT.Dispatch
 {
     public class WaferTaskManager : Singleton<WaferTaskManager>
     {
+        #region 常量
+        private const string SIDE_A = "SideA";
+        private const string SIDE_B = "SideB";
+        #endregion
+
         #region 内部变量
         /// <summary>
         /// FA回复
@@ -149,81 +154,88 @@ namespace CyberX8_RT.Dispatch
                     }
                 }
             }
-            if (wafers.Count == 1&&CheckWaferHolderHasTwoProductionWafers(waferHolderInfo))
+            if (wafers.Count == 1)
             {
                 WaferInfo waferInfo = wafers[0];
-                ModuleName pufModuleName = ModuleName.Unknown;
-                ModuleName dummyModuleName = ModuleName.Unknown;
+                ModuleName pufModuleName = ModuleName.PUF1;
+                string waferSide = "";
+                string dummySide = "";
                 bool lastSingleWaferToSideB = processJobInfos[0].SequenceRecipe.LastSingleWaferToSideB;
                 if (lastSingleWaferToSideB)
                 {
-                    pufModuleName = ModuleName.PUF2;
-                    dummyModuleName = ModuleName.PUF1;
+                    waferSide = SIDE_B;
+                    dummySide = SIDE_A;
                 }
                 else
                 {
-                    pufModuleName = ModuleName.PUF1;
-                    dummyModuleName = ModuleName.PUF2;
+                    waferSide = SIDE_A;
+                    dummySide = SIDE_B;
                 }
-                List<WaferInfo> dummyWafers = DummyWaferManager.Instance.LoadDummyWafersByWaferHolder(waferHolderInfo);
-                if (dummyWafers.Count != 0)
+                if (CheckWaferHolderHasSameTypeWafers(waferHolderInfo))
                 {
-                    CreateWaferTaskSchedulerSequence(waferInfo, pufModuleName, waferHolderInfo, waferInfo.ProcessJob.SequenceRecipe, "");
-                    WaferInfo dummyInfo = null;
-                    if (dummyWafers.Count > 1)
+                    List<WaferInfo> dummyWafers = DummyWaferManager.Instance.LoadDummyWafersByWaferHolder(waferHolderInfo);
+                    if (dummyWafers.Count != 0)
                     {
-                        foreach (WaferInfo info in dummyWafers)
+                        CreateWaferTaskSchedulerSequence(waferInfo, pufModuleName, waferHolderInfo, waferInfo.ProcessJob.SequenceRecipe, "",waferSide);
+                        WaferInfo dummyInfo = null;
+                        if (dummyWafers.Count > 1)
                         {
-                            //最后一片Wafer使用B面,那么dummy片使用0 slot
-                            if (lastSingleWaferToSideB && info.OriginSlot == 0)
+                            foreach (WaferInfo info in dummyWafers)
                             {
-                                dummyInfo = info;
-                                break;
+                                //最后一片Wafer使用B面,那么dummy片使用0 slot
+                                if (lastSingleWaferToSideB && info.OriginSlot == 0)
+                                {
+                                    dummyInfo = info;
+                                    break;
+                                }
+                                //最后一片Wafer使用A面,那么dummy片使用1 slot
+                                if (!lastSingleWaferToSideB && info.OriginSlot == 1)
+                                {
+                                    dummyInfo = info;
+                                    break;
+                                }
                             }
-                            //最后一片Wafer使用A面,那么dummy片使用1 slot
-                            if (!lastSingleWaferToSideB && info.OriginSlot == 1)
+                            if (dummyInfo == null)
                             {
-                                dummyInfo = info;
-                                break;
+                                dummyInfo = dummyWafers[0];
                             }
                         }
-                        if (dummyInfo == null)
+                        else
                         {
-                            dummyInfo=dummyWafers[0];
+                            dummyInfo = dummyWafers[0];
                         }
+                        CreateDummyWaferTaskSchedulerSequence(waferHolderInfo.SequenceRecipe, dummyInfo, pufModuleName, waferInfo.WaferID,dummySide);
+                        _waferTaskMatchDic[waferInfo.WaferID] = dummyWafers[0].WaferID;
+                        _waferTaskMatchDic[dummyWafers[0].WaferID] = waferInfo.WaferID;
+                        _waferWaferHolderTaskDic[waferInfo.WaferID] = waferHolderTask;
+                        _waferWaferHolderTaskDic[dummyWafers[0].WaferID] = waferHolderTask;
                     }
                     else
                     {
-                        dummyInfo = dummyWafers[0];
+                        CreateWaferTaskSchedulerSequence(waferInfo, pufModuleName, waferHolderInfo, waferInfo.ProcessJob.SequenceRecipe, "",waferSide);
+                        _waferWaferHolderTaskDic[waferInfo.WaferID] = waferHolderTask;
                     }
-                    CreateDummyWaferTaskSchedulerSequence(waferHolderInfo.SequenceRecipe, dummyInfo, dummyModuleName, waferInfo.WaferID);
-                    _waferTaskMatchDic[waferInfo.WaferID] = dummyWafers[0].WaferID;
-                    _waferTaskMatchDic[dummyWafers[0].WaferID] = waferInfo.WaferID;
-                    _waferWaferHolderTaskDic[waferInfo.WaferID] = waferHolderTask;
-                    _waferWaferHolderTaskDic[dummyWafers[0].WaferID] = waferHolderTask;
                 }
                 else
                 {
-                    CreateWaferTaskSchedulerSequence(waferInfo, pufModuleName, waferHolderInfo, waferInfo.ProcessJob.SequenceRecipe, "");
+                    CreateWaferTaskSchedulerSequence(waferInfo, pufModuleName, waferHolderInfo, waferInfo.ProcessJob.SequenceRecipe, "",waferSide);
                     _waferWaferHolderTaskDic[waferInfo.WaferID] = waferHolderTask;
                 }
             }
-            else
+            else if (wafers.Count >= 2)
             {
                 for (int i = 0; i < 2; i++)
                 {
                     WaferInfo waferInfo = wafers[i];
-                    ModuleName pufModuleName = ModuleName.Unknown;
+                    ModuleName pufModuleName = ModuleName.PUF1;
                     if (i == 0)
                     {
-                        pufModuleName = ModuleName.PUF1;
                         mateWaferTask = wafers[0].WaferID;
-                        CreateWaferTaskSchedulerSequence(waferInfo, pufModuleName, waferHolderInfo, waferInfo.ProcessJob.SequenceRecipe, "");
+                        CreateWaferTaskSchedulerSequence(waferInfo, pufModuleName, waferHolderInfo, waferInfo.ProcessJob.SequenceRecipe, "",SIDE_A);
                     }
                     else
                     {
-                        pufModuleName = ModuleName.PUF2;
-                        CreateWaferTaskSchedulerSequence(waferInfo, pufModuleName, waferHolderInfo, waferInfo.ProcessJob.SequenceRecipe, mateWaferTask);
+                        CreateWaferTaskSchedulerSequence(waferInfo, pufModuleName, waferHolderInfo, waferInfo.ProcessJob.SequenceRecipe, mateWaferTask,SIDE_B);
                     }
                 }
                 _waferTaskMatchDic[wafers[0].WaferID] = wafers[1].WaferID;
@@ -237,6 +249,31 @@ namespace CyberX8_RT.Dispatch
         /// </summary>
         /// <param name="waferHolderInfo"></param>
         /// <returns></returns>
+        private bool CheckWaferHolderHasSameTypeWafers(WaferHolderInfo waferHolderInfo)
+        {
+            int count = 0;
+            WaferInfo waferAInfo = WaferManager.Instance.GetWaferByWaferId(waferHolderInfo.WaferAId);
+            WaferType waferType = WaferType.Production;
+            if (waferAInfo != null)
+            {
+                waferType = waferAInfo.WaferType;
+                count++;
+            }
+            WaferInfo waferBInfo = WaferManager.Instance.GetWaferByWaferId(waferHolderInfo.WaferBId);
+            if (waferBInfo != null)
+            {
+                if (waferBInfo.WaferType == waferType)
+                {
+                    count++;
+                }
+            }
+            return count >= 2;
+        }
+        /// <summary>
+        /// 检验WaferHolder是否存在两片生产片
+        /// </summary>
+        /// <param name="waferHolderInfo"></param>
+        /// <returns></returns>
         private bool CheckWaferHolderHasTwoProductionWafers(WaferHolderInfo waferHolderInfo)
         {
             int count = 0;
@@ -259,9 +296,9 @@ namespace CyberX8_RT.Dispatch
         /// <param name="pufModuleName"></param>
         /// <param name="waferHolderInfo"></param>
         /// <param name="sequenceRecipe"></param>
-        private void CreateWaferTaskSchedulerSequence(WaferInfo waferInfo, ModuleName pufModuleName, WaferHolderInfo waferHolderInfo, SequenceRecipe sequenceRecipe, string mateWafeTask)
+        private void CreateWaferTaskSchedulerSequence(WaferInfo waferInfo, ModuleName pufModuleName, WaferHolderInfo waferHolderInfo, SequenceRecipe sequenceRecipe, string mateWafeTask,string side)
         {
-            List<SchedulerSequence> sequences = SchedulerSequenceManager.Instance.AnalyWaferAllSchedulerSequence(waferInfo, pufModuleName, waferHolderInfo, sequenceRecipe);
+            List<SchedulerSequence> sequences = SchedulerSequenceManager.Instance.AnalyWaferAllSchedulerSequence(waferInfo, pufModuleName,side, waferHolderInfo, sequenceRecipe);
             PUFEntity pufEntity = Singleton<RouteManager>.Instance.GetModule<PUFEntity>(pufModuleName.ToString());
             WaferTask waferTask = new WaferTask(waferInfo, pufEntity, sequences);
             if (!string.IsNullOrEmpty(mateWafeTask))
@@ -283,18 +320,19 @@ namespace CyberX8_RT.Dispatch
             {
                 WaferInfo waferInfo = waferInfos[i];
                 int orginalSlot = waferInfo.OriginSlot;
-                ModuleName pufModuleName = orginalSlot == 0 ? ModuleName.PUF1 : ModuleName.PUF2;
+                ModuleName pufModuleName = ModuleName.PUF1;
+                string side = orginalSlot == 0 ? SIDE_A : SIDE_B;
                 if (!Contains(waferInfo.WaferID))
                 {
                     if (i == 0)
                     {
                         mateWaferTask = waferInfo.WaferID;
-                        CreateDummyWaferTaskSchedulerSequence(sequenceRecipe,waferInfo, pufModuleName, "");
+                        CreateDummyWaferTaskSchedulerSequence(sequenceRecipe,waferInfo, pufModuleName,"", side);
                         _waferWaferHolderTaskDic[waferInfo.WaferID] = waferHolderTask;
                     }
                     else
                     {
-                        CreateDummyWaferTaskSchedulerSequence(sequenceRecipe,waferInfo, pufModuleName, mateWaferTask);
+                        CreateDummyWaferTaskSchedulerSequence(sequenceRecipe,waferInfo, pufModuleName, mateWaferTask,side);
                         _waferTaskMatchDic[mateWaferTask] = waferInfo.WaferID;
                         _waferTaskMatchDic[waferInfo.WaferID] = mateWaferTask;
                         _waferWaferHolderTaskDic[waferInfo.WaferID]= waferHolderTask;
@@ -309,9 +347,9 @@ namespace CyberX8_RT.Dispatch
         /// <param name="waferInfo"></param>
         /// <param name="pufModuleName"></param>
         /// <param name="mateWaferTask"></param>
-        private void CreateDummyWaferTaskSchedulerSequence(SequenceRecipe sequenceRecipe,WaferInfo waferInfo,ModuleName pufModuleName,string mateWaferTask)
+        private void CreateDummyWaferTaskSchedulerSequence(SequenceRecipe sequenceRecipe,WaferInfo waferInfo,ModuleName pufModuleName,string mateWaferTask,string side)
         {
-            List<SchedulerSequence> sequences = SchedulerSequenceManager.Instance.AnalyDummyWaferAllSchedulerSequence(sequenceRecipe,waferInfo, pufModuleName);
+            List<SchedulerSequence> sequences = SchedulerSequenceManager.Instance.AnalyDummyWaferAllSchedulerSequence(sequenceRecipe,waferInfo,pufModuleName, side);
             PUFEntity pufEntity = Singleton<RouteManager>.Instance.GetModule<PUFEntity>(pufModuleName.ToString());
             WaferTask waferTask = new WaferTask(waferInfo, pufEntity,sequences);
             if (!string.IsNullOrEmpty(mateWaferTask))

+ 63 - 63
CyberX8_RT/Modules/Loader/LoaderEntity.cs

@@ -171,8 +171,8 @@ namespace CyberX8_RT.Modules.Loader
         private LoaderHomeAllRoutine _homeAllRoutine;
         private LoaderSwitchAllOnRoutine _switchAllOnRoutine;
         private LoaderSwitchAllOffRoutine _switchAllOffRoutine;
-        private LoaderUnloadAllSideRoutine _unloadAllSideRoutine;
-        private LoaderLoadAllSideRoutine _loadAllSideRoutine;
+        private LoaderUnloadSideRoutine _unloadSideRoutine;
+        private LoaderLoadSideRoutine _loadSideRoutine;
         #endregion
 
         #region LotTrackDatas
@@ -269,8 +269,8 @@ namespace CyberX8_RT.Modules.Loader
             _homeAllRoutine=new LoaderHomeAllRoutine(Module.ToString());
             _switchAllOnRoutine=new LoaderSwitchAllOnRoutine(Module.ToString());
             _switchAllOffRoutine=new LoaderSwitchAllOffRoutine(Module.ToString());
-            _unloadAllSideRoutine = new LoaderUnloadAllSideRoutine(Module.ToString());
-            _loadAllSideRoutine=new LoaderLoadAllSideRoutine(Module.ToString());
+            _unloadSideRoutine = new LoaderUnloadSideRoutine(Module.ToString());
+            _loadSideRoutine=new LoaderLoadSideRoutine(Module.ToString());
         }
         /// <summary>
         /// 初始化数据
@@ -328,23 +328,23 @@ namespace CyberX8_RT.Modules.Loader
             //Prepare for Place
             Transition(LOADERSTATE.Idle, LoaderMSG.PrepareForPlace, PrePareForPlace, LOADERSTATE.PrepreForPlacing);
             Transition(LOADERSTATE.PrepreForPlacing, FSM_MSG.TIMER, PrepareForPlaceMonitor, LOADERSTATE.WaitForUnload);
-            Transition(LOADERSTATE.WaitForUnload, LoaderMSG.UnloadAll, UnloadAll, LOADERSTATE.Unloading);
-            Transition(LOADERSTATE.Unloading, FSM_MSG.TIMER, UnloadAllMonitor, LOADERSTATE.WaitForLoad);
-            Transition(LOADERSTATE.WaitForLoad, LoaderMSG.LoadAll, LoadAll, LOADERSTATE.Loading);
-            Transition(LOADERSTATE.Loading, FSM_MSG.TIMER, LoadAllMonitor, LOADERSTATE.Idle);
+            Transition(LOADERSTATE.WaitForUnload, LoaderMSG.UnloadSide, UnloadSide, LOADERSTATE.Unloading);
+            Transition(LOADERSTATE.Unloading, FSM_MSG.TIMER, UnloadSideMonitor, LOADERSTATE.WaitForLoad);
+            Transition(LOADERSTATE.WaitForLoad, LoaderMSG.LoadSide, LoadSide, LOADERSTATE.Loading);
+            Transition(LOADERSTATE.Loading, FSM_MSG.TIMER, LoadSideMonitor, LOADERSTATE.Idle);
             //Retry
             Transition(LOADERSTATE.Error, LoaderMSG.Retry, NullFunc, LOADERSTATE.Retrying);
             Transition(LOADERSTATE.Retrying,FSM_MSG.TIMER,LoaderRetry,LOADERSTATE.Retrying);
-            Transition(LOADERSTATE.Retrying, LoaderMSG.UnloadAll, RetryUnloadAll, LOADERSTATE.Unloading);
-            Transition(LOADERSTATE.Retrying, LoaderMSG.LoadAll, RetryLoadAll, LOADERSTATE.Loading);
+            Transition(LOADERSTATE.Retrying, LoaderMSG.UnloadSide, RetryUnloadSide, LOADERSTATE.Unloading);
+            Transition(LOADERSTATE.Retrying, LoaderMSG.LoadSide, RetryLoadSide, LOADERSTATE.Loading);
             //ConfirmComplete
             Transition(LOADERSTATE.Init, LoaderMSG.ConfirmComplete, ClearModuleAlarm, LOADERSTATE.Init);
             Transition(LOADERSTATE.Idle, LoaderMSG.ConfirmComplete, ClearModuleAlarm, LOADERSTATE.Idle);
             Transition(LOADERSTATE.Error, LoaderMSG.ConfirmComplete, NullFunc, LOADERSTATE.ConfirmCompleting);
             Transition(LOADERSTATE.ConfirmCompleting,FSM_MSG.TIMER,ConfirmComplete, LOADERSTATE.ConfirmCompleting);
             Transition(LOADERSTATE.ConfirmCompleting, LoaderMSG.PrepareForPlace, NullFunc, LOADERSTATE.WaitForUnload);
-            Transition(LOADERSTATE.ConfirmCompleting,LoaderMSG.UnloadAll,ConfirmUnloadAll, LOADERSTATE.WaitForLoad);
-            Transition(LOADERSTATE.ConfirmCompleting, LoaderMSG.LoadAll, ConfirmLoadAll, LOADERSTATE.Idle);
+            Transition(LOADERSTATE.ConfirmCompleting,LoaderMSG.UnloadSide,ConfirmUnloadSide, LOADERSTATE.WaitForLoad);
+            Transition(LOADERSTATE.ConfirmCompleting, LoaderMSG.LoadSide, ConfirmLoadSide, LOADERSTATE.Idle);
 
             EnumLoop<LOADERSTATE>.ForEach((item) => { fsm.MapState((int)item, item.ToString()); });
 
@@ -533,19 +533,19 @@ namespace CyberX8_RT.Modules.Loader
         }
         #endregion
 
-        #region Unload All
+        #region Unload Side
         /// <summary>
-        /// Unload All
+        /// Unload Side
         /// </summary>
         /// <param name="param"></param>
         /// <returns></returns>
-        private bool UnloadAll(object param)
+        private bool UnloadSide(object param)
         {
-            bool result= _unloadAllSideRoutine.Start(param) == RState.Running;
+            bool result= _unloadSideRoutine.Start(param) == RState.Running;
             if(result)
             {
                 LotTrackDataClear();
-                _currentRoutine = _unloadAllSideRoutine;                
+                _currentRoutine = _unloadSideRoutine;                
                 _unloadLotTrackDatas.Clear();
                 _unloadLotTrackDatasBuffer.Clear();
                 _unloadTimeList.Clear();
@@ -564,35 +564,35 @@ namespace CyberX8_RT.Modules.Loader
             return result;
         }
         /// <summary>
-        /// Retry UloadAll
+        /// Retry UloadSide
         /// </summary>
         /// <param name="param"></param>
         /// <returns></returns>
-        private bool RetryUnloadAll(object[] param)
+        private bool RetryUnloadSide(object[] param)
         {
             int stepIndex = (int)param[0];
-            bool result = _unloadAllSideRoutine.Retry(stepIndex)==RState.Running;
+            bool result = _unloadSideRoutine.Retry(stepIndex)==RState.Running;
             if (result)
             {
                 _unloadDatasStatus = LotTrackDatasStatus.Half;
                 _unloadLotTrackDatas.Clear();
-                _currentRoutine = _unloadAllSideRoutine;
+                _currentRoutine = _unloadSideRoutine;
             }
             return result;
         }
         /// <summary>
-        /// Unload All监控
+        /// Unload Side监控
         /// </summary>
         /// <param name="param"></param>
         /// <returns></returns>
-        private bool UnloadAllMonitor(object param)
+        private bool UnloadSideMonitor(object param)
         {
-            RState ret = _unloadAllSideRoutine.Monitor();
+            RState ret = _unloadSideRoutine.Monitor();
             if (ret == RState.End)
             {
                 AlarmListManager.Instance.CheckModuleAlamAndRemove(Module.ToString(), LOADERSTATE.Unloading.ToString());
-                _unloadLotTrackDatas = _unloadAllSideRoutine.UnloadLotTrackDatas;
-                _unloadLotTrackDatasBuffer.AddRange(_unloadAllSideRoutine.UnloadLotTrackDatas);
+                _unloadLotTrackDatas = _unloadSideRoutine.UnloadLotTrackDatas;
+                _unloadLotTrackDatasBuffer.AddRange(_unloadSideRoutine.UnloadLotTrackDatas);
                 _unloadTimeList.Add(DateTime.Now);
 
                 //Header信息
@@ -608,13 +608,13 @@ namespace CyberX8_RT.Modules.Loader
             {
                 if (Singleton<RouteManager>.Instance.IsAutoRunning)
                 {
-                    AlarmList alarmList = new AlarmList(Module.ToString(), ((LOADERSTATE)fsm.State).ToString(), (int)LoaderMSG.UnloadAll,
-                        _unloadAllSideRoutine.ErrorMsg, _unloadAllSideRoutine.ErrorStep, (int)AlarmType.Error);
+                    AlarmList alarmList = new AlarmList(Module.ToString(), ((LOADERSTATE)fsm.State).ToString(), (int)LoaderMSG.UnloadSide,
+                        _unloadSideRoutine.ErrorMsg, _unloadSideRoutine.ErrorStep, (int)AlarmType.Error);
                     AlarmListManager.Instance.AddAlarm(alarmList);
                 }
                 PostMsg(LoaderMSG.Error);
-                _unloadLotTrackDatas = _unloadAllSideRoutine.UnloadLotTrackDatas;
-                _unloadLotTrackDatasBuffer.AddRange(_unloadAllSideRoutine.UnloadLotTrackDatas);
+                _unloadLotTrackDatas = _unloadSideRoutine.UnloadLotTrackDatas;
+                _unloadLotTrackDatasBuffer.AddRange(_unloadSideRoutine.UnloadLotTrackDatas);
                 ////Header信息
                 WaferHolderInfo info = WaferHolderManager.Instance.GetWaferHolder(Module.ToString());
                 _headerdata.SequenceRecipe = ((info != null && info.SequenceRecipe != null) ? $"{info.SequenceRecipe.SequenceType}\\" + $"{info.SequenceRecipe.Ppid}.seq.rcp" : "");
@@ -630,16 +630,16 @@ namespace CyberX8_RT.Modules.Loader
         /// </summary>
         /// <param name="param"></param>
         /// <returns></returns>
-        private bool ConfirmUnloadAll(object[] param)
+        private bool ConfirmUnloadSide(object[] param)
         {
             int stepIdex=(int)param[0];
-            bool result = _unloadAllSideRoutine.CheckCompleteCondition(stepIdex);
+            bool result = _unloadSideRoutine.CheckCompleteCondition(stepIdex);
             if(!result)
             {
                 if (Singleton<RouteManager>.Instance.IsAutoRunning)
                 {
-                    AlarmList alarmList = new AlarmList(Module.ToString(), ((LOADERSTATE)fsm.State).ToString(), (int)LoaderMSG.UnloadAll,
-                    _unloadAllSideRoutine.ErrorMsg, _unloadAllSideRoutine.ErrorStep, (int)AlarmType.Error);
+                    AlarmList alarmList = new AlarmList(Module.ToString(), ((LOADERSTATE)fsm.State).ToString(), (int)LoaderMSG.UnloadSide,
+                    _unloadSideRoutine.ErrorMsg, _unloadSideRoutine.ErrorStep, (int)AlarmType.Error);
                     AlarmListManager.Instance.AddAlarm(alarmList);
                 }
                 PostMsg(LoaderMSG.Error);
@@ -657,18 +657,18 @@ namespace CyberX8_RT.Modules.Loader
         #endregion
 
 
-        #region Load All
+        #region Load Side
         /// <summary>
-        /// Load All
+        /// Load Side
         /// </summary>
         /// <param name="param"></param>
         /// <returns></returns>
-        private bool LoadAll(object param)
+        private bool LoadSide(object param)
         {
-            bool result= _loadAllSideRoutine.Start(param) == RState.Running;
+            bool result= _loadSideRoutine.Start(param) == RState.Running;
             if(result)
             {
-                _currentRoutine = _loadAllSideRoutine;
+                _currentRoutine = _loadSideRoutine;
                 //_loadTimeList.Clear();
                 _flowLotTrackdatas.Clear();
                 _loadLotTrackDatas.Clear();
@@ -681,39 +681,39 @@ namespace CyberX8_RT.Modules.Loader
         }
 
         /// <summary>
-        /// Retry LoadAll
+        /// Retry LoadSide
         /// </summary>
         /// <param name="param"></param>
         /// <returns></returns>
-        private bool RetryLoadAll(object[] param)
+        private bool RetryLoadSide(object[] param)
         {
             int stepIndex = (int)param[0];
-            bool result = _loadAllSideRoutine.Retry(stepIndex) == RState.Running;
+            bool result = _loadSideRoutine.Retry(stepIndex) == RState.Running;
             if (result)
             {
                 _unloadDatasStatus = LotTrackDatasStatus.Complete;
                 _unloadDatasBufferStatus = LotTrackDatasStatus.Complete;
                 SetLotTrackDatasStatus(stepIndex);               
-                _currentRoutine = _loadAllSideRoutine;
+                _currentRoutine = _loadSideRoutine;
             }
             return result;
         }
 
         /// <summary>
-        /// 监控LoadAll
+        /// 监控LoadSide
         /// </summary>
         /// <param name="param"></param>
         /// <returns></returns>
-        private bool LoadAllMonitor(object param)
+        private bool LoadSideMonitor(object param)
         {
-            RState ret = _loadAllSideRoutine.Monitor();
+            RState ret = _loadSideRoutine.Monitor();
             if (ret == RState.End)
             {
                 AlarmListManager.Instance.CheckModuleAlamAndRemove(Module.ToString(), LOADERSTATE.Loading.ToString());
 
-                _loadLotTrackDatas = _loadAllSideRoutine.LoadLotTrackDatas;
-                _loadTimeList = _loadAllSideRoutine.LoadTimeList;
-                _flowLotTrackdatas = _loadAllSideRoutine.FlowLotTrackDatas;
+                _loadLotTrackDatas = _loadSideRoutine.LoadLotTrackDatas;
+                _loadTimeList = _loadSideRoutine.LoadTimeList;
+                _flowLotTrackdatas = _loadSideRoutine.FlowLotTrackDatas;
                 bool clearFlag = _loadDatasStatus == LotTrackDatasStatus.None && _flowTestDatasStatus == LotTrackDatasStatus.None;
                 _loaderOperatingWaferInfosList["load"] = GetWaferInfo(clearFlag);
                 //Header信息
@@ -730,14 +730,14 @@ namespace CyberX8_RT.Modules.Loader
             {
                 if (Singleton<RouteManager>.Instance.IsAutoRunning)
                 {
-                    AlarmList alarmList = new AlarmList(Module.ToString(), ((LOADERSTATE)fsm.State).ToString(), (int)LoaderMSG.LoadAll,
-                    _loadAllSideRoutine.ErrorMsg, _loadAllSideRoutine.ErrorStep, (int)AlarmType.Error);
+                    AlarmList alarmList = new AlarmList(Module.ToString(), ((LOADERSTATE)fsm.State).ToString(), (int)LoaderMSG.LoadSide,
+                    _loadSideRoutine.ErrorMsg, _loadSideRoutine.ErrorStep, (int)AlarmType.Error);
                     AlarmListManager.Instance.AddAlarm(alarmList);
                 }
                 PostMsg(LoaderMSG.Error);
-                _loadLotTrackDatas = _loadAllSideRoutine.LoadLotTrackDatas;
-                _loadTimeList = _loadAllSideRoutine.LoadTimeList;
-                _flowLotTrackdatas = _loadAllSideRoutine.FlowLotTrackDatas;
+                _loadLotTrackDatas = _loadSideRoutine.LoadLotTrackDatas;
+                _loadTimeList = _loadSideRoutine.LoadTimeList;
+                _flowLotTrackdatas = _loadSideRoutine.FlowLotTrackDatas;
                 bool clearFlag = _loadDatasStatus == LotTrackDatasStatus.None && _flowTestDatasStatus == LotTrackDatasStatus.None;
                 _loaderOperatingWaferInfosList["load"] = GetWaferInfo(clearFlag);
                 //Header信息
@@ -751,20 +751,20 @@ namespace CyberX8_RT.Modules.Loader
             return false;
         }
         /// <summary>
-        /// 确认UnloadAll是否完成
+        /// 确认UnloadSide是否完成
         /// </summary>
         /// <param name="param"></param>
         /// <returns></returns>
-        private bool ConfirmLoadAll(object[] param)
+        private bool ConfirmLoadSide(object[] param)
         {
             int stepIdex = (int)param[0];
-            bool result = _loadAllSideRoutine.CheckCompleteCondition(stepIdex);
+            bool result = _loadSideRoutine.CheckCompleteCondition(stepIdex);
             if (!result)
             {
                 if (Singleton<RouteManager>.Instance.IsAutoRunning)
                 {
-                    AlarmList alarmList = new AlarmList(Module.ToString(), ((LOADERSTATE)fsm.State).ToString(), (int)LoaderMSG.LoadAll,
-                    _loadAllSideRoutine.ErrorMsg, _loadAllSideRoutine.ErrorStep, (int)AlarmType.Error);
+                    AlarmList alarmList = new AlarmList(Module.ToString(), ((LOADERSTATE)fsm.State).ToString(), (int)LoaderMSG.LoadSide,
+                    _loadSideRoutine.ErrorMsg, _loadSideRoutine.ErrorStep, (int)AlarmType.Error);
                     AlarmListManager.Instance.AddAlarm(alarmList);
                 }
                 PostMsg(LoaderMSG.Error);
@@ -865,11 +865,11 @@ namespace CyberX8_RT.Modules.Loader
             {
                 if(alarmList.ModuleState==LOADERSTATE.Unloading.ToString())
                 {
-                    CheckToPostMessage<LOADERSTATE, LoaderMSG>(eEvent.ERR_LOADER, Module.ToString(),(int)LoaderMSG.UnloadAll,alarmList.ModuleStep);
+                    CheckToPostMessage<LOADERSTATE, LoaderMSG>(eEvent.ERR_LOADER, Module.ToString(),(int)LoaderMSG.UnloadSide,alarmList.ModuleStep);
                 }
                 else if(alarmList.ModuleState==LOADERSTATE.Loading.ToString())
                 {
-                    CheckToPostMessage<LOADERSTATE, LoaderMSG>(eEvent.ERR_LOADER, Module.ToString(), (int)LoaderMSG.LoadAll,alarmList.ModuleStep);
+                    CheckToPostMessage<LOADERSTATE, LoaderMSG>(eEvent.ERR_LOADER, Module.ToString(), (int)LoaderMSG.LoadSide,alarmList.ModuleStep);
                 }
                 else if (alarmList.ModuleState == LOADERSTATE.PrepreForPlacing.ToString())
                 {
@@ -975,8 +975,8 @@ namespace CyberX8_RT.Modules.Loader
         Error,
         ClearError,
         PrepareForPlace,
-        UnloadAll,
-        LoadAll,
+        UnloadSide,
+        LoadSide,
         Retry,
         ConfirmComplete
     }

+ 454 - 0
CyberX8_RT/Modules/Loader/LoaderLoadSideRoutine.cs

@@ -0,0 +1,454 @@
+using Aitex.Core.RT.Device;
+using Aitex.Core.RT.Routine;
+using MECF.Framework.Common.Equipment;
+using MECF.Framework.Common.Routine;
+using CyberX8_Core;
+using CyberX8_RT.Devices.AXIS;
+using CyberX8_RT.Devices.Loader;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MECF.Framework.Common.Utilities;
+using Aitex.Core.RT.Log;
+using MECF.Framework.Common.CommonData.Loader;
+using CyberX8_RT.Modules.Dryer;
+using MECF.Framework.Common.Persistent.Temperature;
+using MECF.Framework.Common.WaferHolder;
+
+namespace CyberX8_RT.Modules.Loader
+{
+    public class LoaderLoadSideRoutine : RoutineBase, IRoutine
+    {
+        private enum LoadStep
+        {
+            SideLoad,
+            SideAllLoadWait,
+            LeakTest,
+            LeakTestWait,
+            RotationGoToTRNPA,
+            RotationGoToTRNPAWait,
+            UnclampWaferHolder,
+            End
+        }
+        #region 常量
+        private const string SIDE_A = "SideA";
+        private const string SIDE_B = "SideB";
+        private const int LOTTRACK_TIME = 1000;
+        #endregion
+        #region 内部变量
+        private JetAxisBase _rotationAxis;
+        private LoaderLoadRoutine _sideLoadRoutine;
+        private LoaderCommonDevice _loaderCommonDevice;
+        private bool _isSideLoaded = false;
+        private bool _isSideStop = false;
+        /// <summary>
+        /// lotTrack time
+        /// </summary>
+        private DateTime _lotTackTime = DateTime.Now;
+        /// <summary>
+        /// Loader Common
+        /// </summary>
+        private LoaderCommonDevice _loaderCommon;
+        /// <summary>
+        /// LoaderSide 
+        /// </summary>
+        private LoaderSideDevice _loaderSide;
+        /// <summary>
+        /// Loader LotTrackData
+        /// </summary>
+        private List<LoaderLotTrackData> _datas = new List<LoaderLotTrackData>();
+        /// <summary>
+        /// Flow LotTrackData
+        /// </summary>
+        private List<LoaderFlowLotTrackData> _flowDatas = new List<LoaderFlowLotTrackData>();
+        /// <summary>
+        /// Loader Start and Finish Time
+        /// </summary>
+        private List<DateTime> _loadTimeList = new List<DateTime>();
+        /// <summary>
+        /// 
+        /// </summary>
+        private string _side = "";
+        #endregion
+
+        #region 属性
+        /// <summary>
+        /// UnLoad LotTrackData
+        /// </summary>
+        public List<LoaderLotTrackData> LoadLotTrackDatas { get { return _datas; } }
+        /// <summary>
+        /// Flow LotTrackData
+        /// </summary>
+        public List<LoaderFlowLotTrackData> FlowLotTrackDatas { get { return _flowDatas; } }
+        /// <summary>
+        /// LoadTimeList
+        /// </summary>
+        public List<DateTime> LoadTimeList { get { return _loadTimeList; } }
+        #endregion
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="module"></param>
+        public LoaderLoadSideRoutine(string module) : base(module)
+        {
+        }
+
+        /// <summary>
+        /// 中止
+        /// </summary>
+        public void Abort()
+        {
+        }
+        /// <summary>
+        /// 监控
+        /// </summary>
+        /// <returns></returns>
+        public RState Monitor()
+        {
+            //记录Lot track
+            LottrackRecord();
+            Runner.Run(LoadStep.SideLoad, () => StartLoadRoutine(_sideLoadRoutine,_isSideLoaded), _delay_1ms)
+                .WaitWithStopCondition(LoadStep.SideAllLoadWait, CheckLoadAllRoutineEndStatus,CheckLoadAllRoutineStopStatus)
+                .Run(LoadStep.LeakTest, StartFlowTest, _delay_1ms)
+                .WaitWithStopCondition(LoadStep.LeakTestWait, CheckFlowTestEndStatus, CheckFlowTestStopStatus)
+                .Run(LoadStep.RotationGoToTRNPA,RotationGotoTransporterA,_delay_1s)
+                .WaitWithStopCondition(LoadStep.RotationGoToTRNPAWait,CheckRotationPositionStatus,CheckRotationPositionRunStop)
+                .Run(LoadStep.UnclampWaferHolder,WaferHolderClampOffAction,_delay_1ms)
+                .End(LoadStep.End, UpdateWaferHolderUse, _delay_1ms);
+            return Runner.Status;
+        }
+        /// <summary>
+        /// 检验Rotation移动状态
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckRotationPositionStatus()
+        {
+            return _rotationAxis.Status == RState.End;
+        }
+        /// <summary>
+        /// 检验Rotation是否还在运动
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckRotationPositionRunStop()
+        {
+            bool result= _rotationAxis.Status == RState.Failed||_rotationAxis.Status==RState.Timeout;
+            if (result)
+            {
+                NotifyError(eEvent.ERR_LOADER, "rotation goto position failed", 2);
+            }
+            return result;
+        }
+        /// <summary>
+        /// 启动load routine
+        /// </summary>
+        /// <param name="loadRoutine"></param>
+        /// <returns></returns>
+        private bool StartLoadRoutine(LoaderLoadRoutine loadRoutine,bool isSideLoaded)
+        {
+            if (isSideLoaded)
+            {
+                return true;
+            }
+            bool result= loadRoutine.Start()==RState.Running;
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_LOADER, loadRoutine.ErrorMsg, 0);
+            }
+            return result;
+        }
+        /// <summary>
+        /// 检验LoadAll完成状态
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckLoadAllRoutineEndStatus()
+        {
+            bool sideResult = true;
+            if (!_isSideLoaded)
+            {
+                sideResult = CheckLoadRoutineEndStatus(_sideLoadRoutine);
+            }
+            if (sideResult)
+            {
+                _loadTimeList.Add(DateTime.Now);
+            }
+            return sideResult;
+        }
+        /// <summary>
+        /// 检验LoadAll停止状态
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckLoadAllRoutineStopStatus()
+        {
+            bool sideComplete = false;
+            if(!_isSideLoaded&&!_isSideStop)
+            {
+                RState ret = _sideLoadRoutine.Monitor();
+                _isSideStop = ret == RState.Failed || ret == RState.Timeout;
+                sideComplete = (ret != RState.Running);
+                if (_isSideStop)
+                {
+                    NotifyError(eEvent.ERR_LOADER, $"load A failed\r\n{_sideLoadRoutine.ErrorMsg}", 0);
+                }
+            }
+            return _isSideStop;
+        }
+        /// <summary>
+        /// 检验routine完成状态
+        /// </summary>
+        /// <param name="loadRoutine"></param>
+        /// <returns></returns>
+        private bool CheckLoadRoutineEndStatus(LoaderLoadRoutine loadRoutine)
+        {
+            return loadRoutine.Monitor() == RState.End;
+        }
+        /// <summary>
+        /// 启动Flowtest
+        /// </summary>
+        /// <returns></returns>
+        private bool StartFlowTest()
+        {
+            bool result= _loaderCommonDevice.StartFlowTestAction();
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_LOADER, "Start Flow Test failed", 1);
+            }
+            return result;
+        }
+        /// <summary>
+        /// 检验FlowTest停止状态
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckFlowTestStopStatus()
+        {
+            if (_loaderCommonDevice.Status == RState.Failed || _loaderCommonDevice.Status == RState.Timeout)
+            {
+                NotifyError(eEvent.ERR_LOADER, "Flow Test failed",1);
+                return true;
+            }
+            return false;
+        }
+        /// <summary>
+        /// 检验FlowTest完成状态
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckFlowTestEndStatus()
+        {
+            bool result = _loaderCommonDevice.Status == RState.End;
+            if (result)
+            {
+                _flowDatas = _loaderCommonDevice.FlowLotTrackDatas;
+            }
+            return result;
+        }
+        /// <summary>
+        /// Rotation 运动至TRNPA
+        /// </summary>
+        /// <returns></returns>
+        private bool RotationGotoTransporterA()
+        {
+            bool result = _rotationAxis.PositionStation("TRNPA", false);
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_LOADER, "rotation start goto TRNPA failed", 2);
+            }
+            return result;
+        }
+        /// <summary>
+        /// Wafer Holder Clamp Off
+        /// </summary>
+        /// <returns></returns>
+        public bool WaferHolderClampOffAction()
+        {
+            bool result = _loaderCommonDevice.WaferHolderClampOffAction();
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_LOADER, "Wafer Shuttle Clamp Off failed", 2);
+            }
+            return result;
+        }
+        /// <summary>
+        /// 更新WaferHolder总用量
+        /// </summary>
+        /// <returns></returns>
+        private bool UpdateWaferHolderUse()
+        {
+            WaferHolderInfo waferHolderInfo = WaferHolderManager.Instance.GetWaferHolder("Loader");
+            if(waferHolderInfo != null)
+            {
+                waferHolderInfo.TotalUses += 1;
+                WaferHolderManager.Instance.UpdateWaferHolderInfo(waferHolderInfo);
+            }
+            return true;
+        }
+        /// <summary>
+        /// 启动
+        /// </summary>
+        /// <param name="objs"></param>
+        /// <returns></returns>
+        public RState Start(params object[] objs)
+        {
+            _side = objs[0].ToString();
+            InitializeParameters();
+            _loaderCommon = DEVICE.GetDevice<LoaderCommonDevice>($"{Module}.Common");
+            _loaderSide = DEVICE.GetDevice<LoaderSideDevice>($"{Module}.{_side}");
+            //清除lotTrack数据
+            _datas.Clear();
+            _flowDatas.Clear();
+            _loadTimeList.Clear();
+            _loadTimeList.Add(DateTime.Now);
+            
+            return Runner.Start(Module, "Start LoadAll");
+        }
+        /// <summary>
+        /// 初始化参数
+        /// </summary>
+        private void InitializeParameters()
+        {
+            _isSideLoaded = false;
+            _isSideStop = false;
+            _rotationAxis = DEVICE.GetDevice<JetAxisBase>($"{Module}.Rotation");
+            _sideLoadRoutine = new LoaderLoadRoutine(ModuleName.Loader1.ToString(), _side);
+            _loaderCommonDevice = DEVICE.GetDevice<LoaderCommonDevice>($"{ModuleName.Loader1}.Common");
+        }
+        /// <summary>
+        /// 重试
+        /// </summary>
+        /// <param name="step"></param>
+        public RState Retry(int step)
+        {
+            InitializeParameters();
+            List<Enum> preStepIds = new List<Enum>();
+            _datas.Clear();
+            if (step == 0 || step == -1)
+            {
+                string side = _side == SIDE_A ? "A" : "B";
+                _isSideLoaded = CheckSideLoadCondition(side, step,false);
+                return Runner.Retry(LoadStep.SideLoad, preStepIds, Module, "LoadAll Retry");
+            }
+            else if (step == 1)
+            {
+                AddPreSteps(LoadStep.LeakTest, preStepIds);
+                return Runner.Retry(LoadStep.LeakTest, preStepIds, Module, $"LoadAll step {LoadStep.LeakTest} start Retry");
+            }
+            else
+            {
+                AddPreSteps(LoadStep.RotationGoToTRNPA, preStepIds);
+                return Runner.Retry(LoadStep.RotationGoToTRNPA, preStepIds, Module, $"LoadAll step {LoadStep.RotationGoToTRNPA} start Retry");
+            }           
+        }
+
+        /// <summary>
+        /// 忽略前
+        /// </summary>
+        /// <param name="step"></param>
+        /// <param name="preStepIds"></param>
+        private void AddPreSteps(LoadStep step, List<Enum> preStepIds)
+        {
+            for (int i = 0; i < (int)step; i++)
+            {
+                preStepIds.Add((LoadStep)i);
+            }
+        }
+        /// <summary>
+        /// 检验前面Unload完成状态
+        /// </summary>
+        /// <returns></returns>
+        public bool CheckCompleteCondition(int index)
+        {
+            string side = _side == SIDE_A ? "A" : "B";
+            if (!CheckSideLoadCondition(side, index,true))
+            {
+                return false;
+            }
+            JetAxisBase loaderRotationAxis = DEVICE.GetDevice<JetAxisBase>($"{ModuleName.Loader1}.Rotation");
+            double loaderRotationPosition = loaderRotationAxis.MotionData.MotorPosition;
+            if (!loaderRotationAxis.CheckPositionIsInStation(loaderRotationPosition, "TRNPA"))
+            {
+                NotifyError(eEvent.ERR_LOADER, $"loader rotation {loaderRotationPosition} not in TRNPA", index);
+            }
+            return true;
+        }
+        /// <summary>
+        /// 检验Side Unload情况 
+        /// </summary>
+        /// <param name="side"></param>
+        /// <param name="index"></param>
+        /// <returns></returns>
+        private bool CheckSideLoadCondition(string side, int index,bool showError)
+        {
+            JetAxisBase shuttleAxis = DEVICE.GetDevice<JetAxisBase>($"{ModuleName.Loader1}.Shuttle{side}");
+            double shuttlePosition = shuttleAxis.MotionData.MotorPosition;
+            if (!shuttleAxis.CheckPositionInStationIgnoreWaferSize(shuttlePosition, "MID"))
+            {
+                if (showError)
+                {
+                    NotifyError(eEvent.ERR_LOADER, $"shuttle{side} {shuttlePosition} is not in mid", index);
+                }
+                return false;
+            }
+            JetAxisBase tiltAxis = DEVICE.GetDevice<JetAxisBase>($"{ModuleName.Loader1}.Tilt{side}");
+            double tiltPosition = tiltAxis.MotionData.MotorPosition;
+            if (tiltAxis.CheckPositionIsInStation(tiltPosition, "VERT"))
+            {
+                if (showError)
+                {
+                    NotifyError(eEvent.ERR_LOADER, $"tilt{side} {tiltPosition} is not in VERT", index);
+                }
+                return false;
+            }
+            JetAxisBase crsAxis = DEVICE.GetDevice<JetAxisBase>($"{ModuleName.Loader1}.LS{side}");
+            double crsPosition = crsAxis.MotionData.MotorPosition;
+            if (!crsAxis.CheckPositionIsInStation(crsPosition, "Setup"))
+            {
+                if (showError)
+                {
+                    NotifyError(eEvent.ERR_LOADER, $"LS{side} {crsPosition} is not in Setup", index);
+                }
+                return false;
+            }
+            return true;
+        }
+        /// <summary>
+        /// 记录Lottrack
+        /// </summary>
+        private void LottrackRecord()
+        {
+            //记录Lottrack
+            if (DateTime.Now.Subtract(_lotTackTime).TotalMilliseconds >= LOTTRACK_TIME)
+            {
+                AddLotTrackData();
+                _lotTackTime = DateTime.Now;
+            }
+        }
+        /// <summary>
+        /// 获取Lot Track数据
+        /// </summary>
+        /// <returns></returns>
+        private void AddLotTrackData()
+        {
+            LoadStep step = (LoadStep)Runner.CurrentStep;
+            if (step <= LoadStep.SideAllLoadWait)
+            {
+                LoaderLotTrackData data = new LoaderLotTrackData();
+                data.TimeStamp = DateTime.Now;
+
+                data.LoaderABernoulliBladderEnable = _loaderSide.SideData.BernoulliBladder;
+                data.LoaderABernoulliExtended = _loaderSide.SideData.BernoulliExtended;
+                data.LoaderABernoulliBladderPressure = _loaderSide.SideData.BernoulliBladderPressure;
+                data.LoaderABernoulliN2Pressure = _loaderSide.SideData.BernoulliPressure;                
+                data.LoaderACRSVacuum = _loaderSide.SideData.CRSVacuum;
+                data.LoaderACRSVacuumAnlg = _loaderSide.SideData.CRSVacuumValue;
+                data.LoaderAWHPressure = _loaderSide.SideData.WHBladderPressure;
+                data.LoaderATranslatePressure = _loaderSide.SideData.TransPressure;
+
+                data.LoaderWHClamped = _loaderCommon.CommonData.WaferHolderClamp;
+
+                _datas.Add(data);
+            }            
+        }
+        
+    }
+}

+ 323 - 0
CyberX8_RT/Modules/Loader/LoaderUnloadSideRoutine.cs

@@ -0,0 +1,323 @@
+using Aitex.Core.RT.Device;
+using Aitex.Core.RT.Routine;
+using MECF.Framework.Common.Equipment;
+using MECF.Framework.Common.Routine;
+using CyberX8_Core;
+using CyberX8_RT.Devices.AXIS;
+using CyberX8_RT.Devices.Loader;
+using System;
+using System.Collections.Generic;
+using Aitex.Core.RT.Log;
+using MECF.Framework.Common.Utilities;
+using MECF.Framework.Common.CommonData.Loader;
+using MECF.Framework.Common.CommonData;
+using Aitex.Core.RT.DataCenter;
+
+namespace CyberX8_RT.Modules.Loader
+{
+    public class LoaderUnloadSideRoutine : RoutineBase, IRoutine
+    {
+        private enum UnloadStep
+        {
+            RotationGoToLOADA,
+            RotationGoToLOADAWait,
+            SideUnload,
+            UnloadAllWait,
+            End
+        }
+
+        #region 常量
+        private const string SIDE_A = "SideA";
+        private const string SIDE_B = "SideB";
+        private const int LOTTRACK_TIME = 1000;
+        #endregion
+
+        #region 内部变量
+        private JetAxisBase _rotationAxis;
+        private string _side;
+        private LoaderUnloadRoutine _sideUnloadRoutine;
+        private bool _isSideUnloaded = false;
+        private bool _isSideStop = false;
+        /// <summary>
+        /// lotTrack time
+        /// </summary>
+        private DateTime _lotTackTime = DateTime.Now;
+        /// <summary>
+        /// Loader Common
+        /// </summary>
+        private LoaderCommonDevice _loaderCommon;
+        /// <summary>
+        /// LoaderSide
+        /// </summary>
+        private LoaderSideDevice _loaderSide;
+        /// <summary>
+        /// Loader LotTrackData
+        /// </summary>
+        private List<LoaderLotTrackData> _datas = new List<LoaderLotTrackData>();
+        #endregion
+
+        #region 属性
+        /// <summary>
+        /// UnLoad LotTrackData
+        /// </summary>
+        public List<LoaderLotTrackData> UnloadLotTrackDatas { get { return _datas; } }
+        #endregion
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="module"></param>
+        public LoaderUnloadSideRoutine(string module) : base(module)
+        {
+        }
+
+        /// <summary>
+        /// 中止
+        /// </summary>
+        public void Abort()
+        {
+        }
+        /// <summary>
+        /// 监控
+        /// </summary>
+        /// <returns></returns>
+        public RState Monitor()
+        {
+            LottrackRecord();
+            Runner.Run(UnloadStep.RotationGoToLOADA,RotationGotoLOADA,_delay_1ms)
+                .WaitWithStopCondition(UnloadStep.RotationGoToLOADAWait,CheckRotationPositionStatus,CheckRotationPositionRunStop)
+                .Run(UnloadStep.SideUnload, () => StartUnloadRoutine(_sideUnloadRoutine,_isSideUnloaded), _delay_1ms)
+                .WaitWithStopCondition(UnloadStep.UnloadAllWait, CheckUnloadAllRoutineEndStatus,CheckUnloadAllRoutineStopStatus)
+                .End(UnloadStep.End, NullFun, _delay_1ms);
+            return Runner.Status;
+        }
+        /// <summary>
+        /// Rotation Goto LOADA
+        /// </summary>
+        /// <returns></returns>
+        private bool RotationGotoLOADA()
+        {
+            bool result = _rotationAxis.PositionStation("LOADA", false);
+            if (!result)
+            {
+                NotifyError(eEvent.ERR_LOADER, "rotation start goto LOADA failed", 0);
+            }
+            return result;
+        }
+        /// <summary>
+        /// 检验Rotation移动状态
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckRotationPositionStatus()
+        {
+            return _rotationAxis.Status == RState.End;
+        }
+        /// <summary>
+        /// 检验Rotation是否还在运动
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckRotationPositionRunStop()
+        {
+            bool result = _rotationAxis.Status == RState.Failed || _rotationAxis.Status == RState.Timeout ;
+            if (result)
+            {
+                NotifyError(eEvent.ERR_LOADER, "rotation goto position failed",0);
+            }
+            return result;
+        }
+        /// <summary>
+        /// 启动Unload routine
+        /// </summary>
+        /// <param name="unloadRoutine"></param>
+        /// <returns></returns>
+        private bool StartUnloadRoutine(LoaderUnloadRoutine unloadRoutine,bool isSideUnloaded)
+        {
+            if (isSideUnloaded)
+            {
+                return true;
+            }
+            bool result= unloadRoutine.Start()==RState.Running;
+            if(!result)
+            {
+                NotifyError(eEvent.ERR_LOADER, unloadRoutine.ErrorMsg, 0);
+            }
+            return result;
+        }
+        /// <summary>
+        /// 检验UnloadAll完成状态
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckUnloadAllRoutineEndStatus()
+        {
+            bool sideResult = true;
+            if (!_isSideUnloaded)
+            {
+                sideResult = CheckUnloadRoutineEndStatus(_sideUnloadRoutine);
+            }
+            return sideResult;
+        }
+        /// <summary>
+        /// 检查UnloadAll停止状态
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckUnloadAllRoutineStopStatus()
+        {
+            bool sideComplete = false;
+            if (!_isSideUnloaded&&!_isSideStop)
+            {
+                RState ret = _sideUnloadRoutine.Monitor();
+                _isSideStop = ret == RState.Failed || ret == RState.Timeout;
+                sideComplete = (ret != RState.Running);
+                if (_isSideStop)
+                {
+                    NotifyError(eEvent.ERR_LOADER, $"unload A failed\r\n{_sideUnloadRoutine.ErrorMsg}", 1);
+                }
+            }
+            return _isSideStop;
+        }
+        /// <summary>
+        /// 检验routine完成状态
+        /// </summary>
+        /// <param name="unloadRoutine"></param>
+        /// <returns></returns>
+        private bool CheckUnloadRoutineEndStatus(LoaderUnloadRoutine unloadRoutine)
+        {
+            return unloadRoutine.Monitor() == RState.End;
+        }
+        /// <summary>
+        /// 检验Routine结束状态
+        /// </summary>
+        /// <param name="unloadRoutine"></param>
+        /// <returns></returns>
+        private bool CheckUnloadRoutineStopStatus(LoaderUnloadRoutine unloadRoutine,string side) 
+        {
+            RState state=unloadRoutine.Monitor();
+            if (state == RState.Failed || state == RState.Timeout)
+            {
+                NotifyError(eEvent.ERR_LOADER, $"{side} Unload failed", 0);
+                return true;
+            }
+            return false;
+        }
+        /// <summary>
+        /// 启动
+        /// </summary>
+        /// <param name="objs"></param>
+        /// <returns></returns>
+        public RState Start(params object[] objs)
+        {
+            _side = objs[0].ToString();
+            InitializeParameters();
+            _loaderCommon = DEVICE.GetDevice<LoaderCommonDevice>($"{Module}.Common");
+            _loaderSide = DEVICE.GetDevice<LoaderSideDevice>($"{Module}.{_side}");
+            return Runner.Start(Module, "Start UnloadAll");
+        }
+        /// <summary>
+        /// 初始化参数
+        /// </summary>
+        private void InitializeParameters()
+        {
+            _rotationAxis = DEVICE.GetDevice<JetAxisBase>($"{Module}.Rotation");
+            _sideUnloadRoutine = new LoaderUnloadRoutine(ModuleName.Loader1.ToString(), _side);
+            _isSideUnloaded = false;
+            _isSideStop = false;
+        }
+        /// <summary>
+        /// 重试
+        /// </summary>
+        /// <param name="step"></param>
+        public RState Retry(int step)
+        {
+            InitializeParameters();
+            List<Enum> preStepIds = new List<Enum>();
+            return Runner.Retry(UnloadStep.RotationGoToLOADA,preStepIds,Module,"UnloadAll Retry");
+        }
+
+        /// <summary>
+        /// 检验前面Unload完成状态
+        /// </summary>
+        /// <returns></returns>
+        public bool CheckCompleteCondition(int index)
+        {
+            string side = _side == SIDE_A ? "A" : "B";
+            if (!CheckSideUnloadCondition(side, index,true))
+            {
+                return false;
+            }
+            return true;
+        }
+        /// <summary>
+        /// 检验Side Unload情况 
+        /// </summary>
+        /// <param name="side"></param>
+        /// <param name="index"></param>
+        /// <returns></returns>
+        private bool CheckSideUnloadCondition(string side, int index,bool showError)
+        {
+            JetAxisBase shuttleAxis = DEVICE.GetDevice<JetAxisBase>($"{ModuleName.Loader1}.Shuttle{side}");
+            double shuttlePosition = shuttleAxis.MotionData.MotorPosition;
+            if (!shuttleAxis.CheckPositionInStationIgnoreWaferSize(shuttlePosition, "OUT"))
+            {
+                if (showError)
+                {
+                    NotifyError(eEvent.ERR_LOADER, $"shuttle{side} {shuttlePosition} is not in Out", index);
+                }
+                return false;
+            }
+            JetAxisBase tiltAxis = DEVICE.GetDevice<JetAxisBase>($"{ModuleName.Loader1}.Tilt{side}");
+            double tiltPosition = tiltAxis.MotionData.MotorPosition;
+            if (!tiltAxis.CheckPositionInStationIgnoreWaferSize(tiltPosition, "HORI"))
+            {
+                if (showError)
+                {
+                    NotifyError(eEvent.ERR_LOADER, $"tilt{side} {tiltPosition} is not in HORI", index);
+                    return false;
+                }
+            }
+            JetAxisBase crsAxis = DEVICE.GetDevice<JetAxisBase>($"{ModuleName.Loader1}.LS{side}");
+            double crsPosition = crsAxis.MotionData.MotorPosition;
+            if (!crsAxis.CheckPositionInStationIgnoreWaferSize(crsPosition, "Unlock"))
+            {
+                if (showError)
+                {
+                    NotifyError(eEvent.ERR_LOADER, $"LS{side} {crsPosition} is not in Unlock", index);
+                }
+                return false;
+            }
+            return true;
+        }
+        /// <summary>
+        /// 记录Lottrack
+        /// </summary>
+        private void LottrackRecord()
+        {
+            //记录Lottrack
+            if (DateTime.Now.Subtract(_lotTackTime).TotalMilliseconds >= LOTTRACK_TIME)
+            {
+                AddLotTrackData();
+                _lotTackTime = DateTime.Now;
+            }
+        }
+        /// <summary>
+        /// 获取Lot Track数据
+        /// </summary>
+        /// <returns></returns>
+        private void AddLotTrackData()
+        {
+            LoaderLotTrackData data = new LoaderLotTrackData();
+            data.TimeStamp = DateTime.Now;
+
+            data.LoaderABernoulliBladderEnable = _loaderSide.SideData.BernoulliBladder;
+            data.LoaderABernoulliExtended = _loaderSide.SideData.BernoulliExtended;
+            data.LoaderABernoulliBladderPressure = _loaderSide.SideData.BernoulliBladderPressure;
+            data.LoaderABernoulliN2Pressure = _loaderSide.SideData.BernoulliPressure;
+            data.LoaderACRSVacuum = _loaderSide.SideData.CRSVacuum;
+            data.LoaderACRSVacuumAnlg = _loaderSide.SideData.CRSVacuumValue;
+            data.LoaderAWHPressure = _loaderSide.SideData.WHBladderPressure;
+            data.LoaderATranslatePressure = _loaderSide.SideData.TransPressure;
+            data.LoaderWHClamped = _loaderCommon.CommonData.WaferHolderClamp;
+
+            _datas.Add(data);
+        }
+        
+    }
+}

+ 13 - 46
CyberX8_RT/Schedulers/Loader/SchedulerLoader.cs

@@ -14,6 +14,8 @@ using System.Text;
 using System.Threading.Tasks;
 using MECF.Framework.Common.SubstrateTrackings;
 using CyberX8_RT.Modules.Metal;
+using CyberX8_RT.Devices.AXIS;
+using Aitex.Core.RT.Device;
 
 namespace CyberX8_RT.Schedulers.Loader
 {
@@ -76,47 +78,23 @@ namespace CyberX8_RT.Schedulers.Loader
                 {
                     return false;
                 }
-                if (_puf1Entity.State == (int)PUFSTATE.AferSwapParkStation)
+                if (_puf1Entity.State == (int)PUFSTATE.AferSwapParkStation||(_puf1Entity.IsIdle && _puf1Entity.IsBackToParkStation))
                 {
-                    if (_puf2Entity.IsIdle&&_puf2Entity.IsBackToParkStation)
-                    {
-                        bool puf2AHasWafer = WaferManager.Instance.CheckHasWafer(ModuleName.PUF2, 0);
-                        bool puf2BHasWafer = WaferManager.Instance.CheckHasWafer(ModuleName.PUF2, 1);
-                        if (puf2AHasWafer || puf2BHasWafer)
-                        {
-                            return false;
-                        }
-                        bool result = _loaderEntity.CheckToPostMessage<LOADERSTATE, LoaderMSG>(eEvent.WARN_LOADER, Module.ToString(),
-                        (int)LoaderMSG.LoadAll);
-                        if (result)
-                        {
-                            _puf2Entity.CheckToPostMessage<PUFSTATE, PUFMSG>(eEvent.WARN_PUF, ModuleName.PUF2.ToString(), (int)PUFMSG.FlipSideA);
-                            _currentStep = SchedulerStep.Loading;
-                        }
-                    }
-                }
-                else if(_puf1Entity.IsIdle&&_puf1Entity.IsBackToParkStation && _puf2Entity.IsIdle && _puf2Entity.IsBackToParkStation)
-                {
-                    bool puf1AHasWafer = WaferManager.Instance.CheckHasWafer(ModuleName.PUF1, 0);
-                    bool puf1BHasWafer = WaferManager.Instance.CheckHasWafer(ModuleName.PUF1, 1);
-                    if (puf1AHasWafer || puf1BHasWafer)
+                    JetAxisBase tiltAAxis = DEVICE.GetDevice<JetAxisBase>($"{ModuleName.Loader1}.TiltA");
+                    JetAxisBase tiltBAxis = DEVICE.GetDevice<JetAxisBase>($"{ModuleName.Loader1}.TiltB");
+                    double tiltAPosition = tiltAAxis.MotionData.MotorPosition;
+                    double tiltBPosition= tiltBAxis.MotionData.MotorPosition;
+                    bool tiltAHori = tiltAAxis.CheckPositionIsInStation(tiltAPosition, "HORI");
+                    bool tiltBHori = tiltBAxis.CheckPositionIsInStation(tiltBPosition, "HORI");
+                    string side = tiltAHori ? "SideA" : (tiltBHori ? "SideB" : "");
+                    if (string.IsNullOrEmpty(side))
                     {
                         return false;
                     }
-
-                    bool puf2AHasWafer = WaferManager.Instance.CheckHasWafer(ModuleName.PUF2, 0);
-                    bool puf2BHasWafer = WaferManager.Instance.CheckHasWafer(ModuleName.PUF2, 1);
-                    if (puf2AHasWafer || puf2BHasWafer)
-                    {
-                        return false;
-                    }
-
                     bool result = _loaderEntity.CheckToPostMessage<LOADERSTATE, LoaderMSG>(eEvent.WARN_LOADER, Module.ToString(),
-                        (int)LoaderMSG.LoadAll);
+                    (int)LoaderMSG.LoadSide,side);
                     if (result)
                     {
-                        _puf2Entity.CheckToPostMessage<PUFSTATE, PUFMSG>(eEvent.WARN_PUF, ModuleName.PUF2.ToString(), (int)PUFMSG.FlipSideA);
-
                         _currentStep = SchedulerStep.Loading;
                     }
                 }
@@ -125,18 +103,7 @@ namespace CyberX8_RT.Schedulers.Loader
             {
                 if(_loaderEntity.IsIdle)
                 {
-                    if (_puf2Entity.IsBusy)
-                    {
-                        return false;
-                    }
-                    if (_puf2Entity.IsFlipSideA)
-                    {
-                        _state = RState.End;
-                    }
-                    else
-                    {
-                        _puf2Entity.CheckToPostMessage<PUFSTATE, PUFMSG>(eEvent.WARN_PUF, ModuleName.PUF2.ToString(), (int)PUFMSG.FlipSideA);
-                    }
+                    _state = RState.End;
                 }
             }
 

+ 15 - 0
CyberX8_RT/Schedulers/Puf/PufSchedulerParameter.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CyberX8_RT.Schedulers.Puf
+{
+    public class PufSchedulerParameter
+    {
+        public bool IsForward { get; set; }
+
+        public string Side { get; set; }
+    }
+}

+ 14 - 50
CyberX8_RT/Schedulers/Puf/SchedulerPuf.cs

@@ -44,6 +44,7 @@ namespace CyberX8_RT.Schedulers.Puf
         private OperationStep _currentOperation;
         private EfemEntity _efemEntity;
         private bool _forward = false;
+        private string _side = "";
         private bool _isProductionWafer = false;
         #endregion
 
@@ -82,7 +83,9 @@ namespace CyberX8_RT.Schedulers.Puf
         public override bool RunProcess(object recipe, object parameter, List<SchedulerSyncModuleMessage> syncMessages)
         {
             _isProductionWafer = false;
-            _forward = (bool)parameter;
+            PufSchedulerParameter pufSchedulerParameter = parameter as PufSchedulerParameter;
+            _forward = pufSchedulerParameter.IsForward;
+            _side = pufSchedulerParameter.Side;
             if (_forward)
             {
                 _currentOperation = OperationStep.ReadyForSwap;
@@ -105,7 +108,7 @@ namespace CyberX8_RT.Schedulers.Puf
         /// 监控执行
         /// </summary>
         /// <returns></returns>
-        public override bool MonitorProcess(SchedulerSequence schedulerSequence, bool hasMatchWafer)
+        public override bool MonitorProcess(SchedulerSequence schedulerSequence,bool hasMatchWafer)
         {
             //当前状态为ReadyForSwap
             if (_currentOperation == OperationStep.ReadyForSwap)
@@ -121,7 +124,7 @@ namespace CyberX8_RT.Schedulers.Puf
             else if (_currentOperation == OperationStep.WaitForLoaderUnloadComplete)
             {
                 //Loader Entity Unload all
-                if (CheckDualPufStateCanExecuteLoaderUnloadAll(hasMatchWafer))
+                if (CheckDualPufStateCanExecuteLoaderUnloadAll())
                 {
                     PostLoaderEntityUnloadAllSide();
                 }
@@ -193,31 +196,21 @@ namespace CyberX8_RT.Schedulers.Puf
         /// 检验Dual puf 是不是都处于WaitForSwap 状态,保证Loader可以UnloadAll
         /// </summary>
         /// <returns></returns>
-        private bool CheckDualPufStateCanExecuteLoaderUnloadAll(bool hasMatchWafer)
+        private bool CheckDualPufStateCanExecuteLoaderUnloadAll()
         {
             bool puf1ready = true;
             if(ModuleHelper.IsInstalled(ModuleName.PUF1))
             {
                 PUFEntity puf1Entity = Singleton<RouteManager>.Instance.GetModule<PUFEntity>(ModuleName.PUF1.ToString());
-                if(hasMatchWafer)
+                
+                //B面有Wafer
+                if (WaferManager.Instance.CheckHasWafer(ModuleName.PUF1,1))
                 {
-                    //B面有Wafer
-                    if (WaferManager.Instance.CheckHasWafer(ModuleName.PUF1,1))
-                    {
-                        puf1ready = puf1Entity.State == (int)PUFSTATE.WaitForSwap;
-                    }
-                    else
-                    {
-                        puf1ready = false;
-                    }
-                }                
+                    puf1ready = puf1Entity.State == (int)PUFSTATE.WaitForSwap;
+                }
                 else
                 {
-                    //任务仅一片Wafer,同时模块为PUF2
-                    if(Module==ModuleName.PUF2)
-                    {
-                        puf1ready = puf1Entity.IsIdle;
-                    }
+                    puf1ready = false;
                 }
             }
             if(!puf1ready)
@@ -225,35 +218,6 @@ namespace CyberX8_RT.Schedulers.Puf
                 return false;
             }
 
-            bool puf2ready = true;
-            if (ModuleHelper.IsInstalled(ModuleName.PUF2))
-            {
-                PUFEntity puf2Entity = Singleton<RouteManager>.Instance.GetModule<PUFEntity>(ModuleName.PUF2.ToString());
-                if(hasMatchWafer)
-                {
-                    //B面有Wafer
-                    if (WaferManager.Instance.CheckHasWafer(ModuleName.PUF2, 1))
-                    {
-                        puf2ready = puf2Entity.State == (int)PUFSTATE.WaitForSwap;
-                    }
-                    else
-                    {
-                        puf2ready = false;
-                    }
-                }                
-                else
-                {
-                    //任务仅一片Wafer,同时模块为PUF1
-                    if (Module == ModuleName.PUF1)
-                    {
-                        puf2ready = puf2Entity.IsIdle;
-                    }
-                }
-            }
-            if(!puf2ready)
-            {
-                return false;
-            }
             //Loader中没有WaferHolder
             if(_loaderEntity.WaferHolderInfo==null)
             {
@@ -280,7 +244,7 @@ namespace CyberX8_RT.Schedulers.Puf
             {
                 //触发loaderEntity UnloadAll
                 _loaderEntity.CheckToPostMessage<LOADERSTATE, LoaderMSG>(eEvent.WARN_LOADER, ModuleName.Loader1.ToString(),
-                    (int)LoaderMSG.UnloadAll);
+                    (int)LoaderMSG.UnloadSide,_side);
             }
             //若Loader Unload操作完成,状态变更为WaitForLoad,puf操作变更为Swap
             else if (_loaderEntity.State == (int)LOADERSTATE.WaitForLoad)

+ 13 - 7
CyberX8_RT/Schedulers/SchedulerSequenceManager.cs

@@ -32,6 +32,7 @@ using CyberX8_RT.Modules.Dryer;
 using CyberX8_RT.Modules.Prewet;
 using CyberX8_RT.Modules.SRD;
 using CyberX8_RT.Modules.Reservoir;
+using CyberX8_RT.Schedulers.Puf;
 
 namespace CyberX8_RT.Schedulers
 {
@@ -46,7 +47,7 @@ namespace CyberX8_RT.Schedulers
         /// </summary>
         /// <param name="sequenceRecipe"></param>
         /// <returns></returns>
-        public List<SchedulerSequence> AnalyWaferAllSchedulerSequence(WaferInfo waferInfo,ModuleName pufModule,WaferHolderInfo waferHolderInfo,SequenceRecipe sequenceRecipe)
+        public List<SchedulerSequence> AnalyWaferAllSchedulerSequence(WaferInfo waferInfo,ModuleName pufModule,string side,WaferHolderInfo waferHolderInfo,SequenceRecipe sequenceRecipe)
         {            
             List<SchedulerSequence> schedulerSequences = new List<SchedulerSequence>();
             int index = 0;
@@ -64,7 +65,7 @@ namespace CyberX8_RT.Schedulers
             MoveItem moveItem2 = new MoveItem(ModuleName.Aligner1, 0, pufModule, 1, Aitex.Sorter.Common.Hand.Blade1);
             SchedulerSequence secondEfemRobotSequence = CreateEfemRobotSequence(moveItem2,null,ref index);
             schedulerSequences.Add(secondEfemRobotSequence);
-            SchedulerSequence pufSequence=CreatePufSequence(pufModule,sequenceRecipe,true,ref index);
+            SchedulerSequence pufSequence=CreatePufSequence(pufModule,sequenceRecipe,side,true,ref index);
             schedulerSequences.Add(pufSequence);
             ////Loader
             //SchedulerSequence loaderSequence = CreateLoaderSequence(ref index);
@@ -82,7 +83,7 @@ namespace CyberX8_RT.Schedulers
             //SchedulerSequence backLoaderSequence = CreateLoaderSequence(ref index);
             //schedulerSequences.Add(backLoaderSequence);
             //puf模块
-            SchedulerSequence backPufSequence = CreatePufSequence(pufModule,sequenceRecipe,false, ref index);
+            SchedulerSequence backPufSequence = CreatePufSequence(pufModule,sequenceRecipe,"",false, ref index);
             schedulerSequences.Add(backPufSequence);
             //若经过srd
             if(SequenceRecipeManager.Instance.IsContainedSrd(sequenceRecipe))
@@ -183,7 +184,7 @@ namespace CyberX8_RT.Schedulers
         /// </summary>
         /// <param name="sequenceRecipe"></param>
         /// <returns></returns>
-        public List<SchedulerSequence> AnalyDummyWaferAllSchedulerSequence(SequenceRecipe sequenceRecipe,WaferInfo waferInfo, ModuleName pufModule)
+        public List<SchedulerSequence> AnalyDummyWaferAllSchedulerSequence(SequenceRecipe sequenceRecipe,WaferInfo waferInfo, ModuleName pufModule,string side)
         {
             List<SchedulerSequence> schedulerSequences = new List<SchedulerSequence>();
             int index = 0;
@@ -206,7 +207,7 @@ namespace CyberX8_RT.Schedulers
             MoveItem alignerToPufMoveItem = new MoveItem(ModuleName.Aligner1, 0, pufModule, 1, Aitex.Sorter.Common.Hand.Blade1);
             SchedulerSequence alignerToPufEfemRobotSequence = CreateEfemRobotSequence(alignerToPufMoveItem, null, ref index);
             schedulerSequences.Add(alignerToPufEfemRobotSequence);
-            SchedulerSequence pufSequence = CreatePufSequence(pufModule,sequenceRecipe, true, ref index);
+            SchedulerSequence pufSequence = CreatePufSequence(pufModule,sequenceRecipe, side,true, ref index);
             schedulerSequences.Add(pufSequence);            
             return schedulerSequences;
         }
@@ -391,7 +392,7 @@ namespace CyberX8_RT.Schedulers
         /// <param name="pufModuleName"></param>
         /// <param name="index"></param>
         /// <returns></returns>
-        private SchedulerSequence CreatePufSequence(ModuleName pufModuleName,SequenceRecipe sequenceRecipe,bool forward,ref int index)
+        private SchedulerSequence CreatePufSequence(ModuleName pufModuleName,SequenceRecipe sequenceRecipe,string side,bool forward,ref int index)
         {
             SchedulerSequence sequence = new SchedulerSequence();
             sequence.SchedulerModule = SchedulerManager.Instance.GetScheduler(pufModuleName);
@@ -399,7 +400,12 @@ namespace CyberX8_RT.Schedulers
             sequence.ModuleName = pufModuleName;
             sequence.State = RState.Init;
             sequence.Recipe = sequenceRecipe;
-            sequence.Parameters = forward;
+            PufSchedulerParameter parameter = new PufSchedulerParameter()
+            {
+                IsForward = forward,
+                Side = side,
+            };
+            sequence.Parameters = (forward,side);
             sequence.IsWaitNotify = !forward;
             sequence.ModuleType = ModuleType.PUF;
             sequence.MaterialType = MaterialType.Wafer;