407 lines
16 KiB
C#
407 lines
16 KiB
C#
using Aitex.Core.Common.DeviceData;
|
|
using Aitex.Core.RT.Event;
|
|
using Aitex.Core.RT.Log;
|
|
using Aitex.Core.RT.OperationCenter;
|
|
using Aitex.Core.RT.Routine;
|
|
using Aitex.Core.Util;
|
|
using MECF.Framework.Common.Equipment;
|
|
using MECF.Framework.Common.SubstrateTrackings;
|
|
using SicPM2.Routines;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.PMs;
|
|
|
|
namespace SicPM2.RecipeExecutions
|
|
{
|
|
public class CleanRecipe : PMBaseRoutine
|
|
{
|
|
public enum RoutineStep
|
|
{
|
|
SetServoUp,
|
|
SetChamberPressure,
|
|
EnableRotation,
|
|
OpenH2Valve,
|
|
EnableHeater,
|
|
PcCalibartion,
|
|
RunStepOne,
|
|
Wait,
|
|
CheckCondition,
|
|
WaitTempReach,
|
|
}
|
|
|
|
enum RecipeRunningState
|
|
{
|
|
Error,
|
|
RecipeCompleted,
|
|
ExecStep,
|
|
TimeWait,
|
|
ConditionWait,
|
|
StepCompleted,
|
|
Paused,
|
|
}
|
|
|
|
private string _recipeName;
|
|
|
|
private object _recipeLocker = new object();
|
|
private bool _isPSUHeaterJumpMode;
|
|
private bool _isSCRHeaterJumpMode;
|
|
private bool _isMFCJumpMode;
|
|
|
|
private int _currentStepNumber;
|
|
|
|
private DeviceTimer _stepTimer = new DeviceTimer();
|
|
private DeviceTimer _recipeTimer = new DeviceTimer();
|
|
private RecipeRunningState _state = RecipeRunningState.ExecStep;
|
|
|
|
private PMServoToPressure _pmServoToPressure;
|
|
|
|
|
|
|
|
public CleanRecipe(ModuleName module, PM2Module pm1) : base(module, pm1)
|
|
{
|
|
Module = module.ToString();
|
|
Name = "CleanRecipe";
|
|
_pmServoToPressure = new PMServoToPressure(module, pm1);
|
|
|
|
}
|
|
|
|
public void Init(string recipeName)
|
|
{
|
|
if (!recipeName.Contains("Sic\\Clean\\") && recipeName!="")
|
|
{
|
|
recipeName = "Sic\\Clean\\" + recipeName;
|
|
}
|
|
_recipeName = recipeName;
|
|
}
|
|
|
|
public override Result Start(params object[] objs)
|
|
{
|
|
Reset();
|
|
|
|
if (!RecipeParser.Parse(_recipeName, Module, out var recipeHead, out var recipeSteps, out string reason))
|
|
{
|
|
Stop($"Load recipe {_recipeName} failed, {reason}");
|
|
return Result.FAIL;
|
|
}
|
|
|
|
if (WaferManager.Instance.CheckHasWafer(PmDevice.Module, 0))
|
|
{
|
|
EV.PostWarningLog(Module, $"can not run clean recipe, Has wafer at {Module}");
|
|
return Result.FAIL;
|
|
}
|
|
if (!PmDevice.Pump.IsOn || PmDevice.Pump.IsError)
|
|
{
|
|
EV.PostWarningLog(Module, $"can not run process, pump state is not correct");
|
|
return Result.FAIL;
|
|
}
|
|
if (!PmDevice._ioThrottleValve.TVValveEnable)
|
|
{
|
|
EV.PostWarningLog(Module, $"can not run process, TV is not enable");
|
|
return Result.FAIL;
|
|
}
|
|
|
|
if (PmDevice._ioThrottleValve.ControlModeFeedback != PressureCtrlMode.TVPressureCtrl)
|
|
{
|
|
EV.PostWarningLog(Module, $"can not run process, TV is not Pressure Ctrol Mode");
|
|
return Result.FAIL;
|
|
}
|
|
|
|
if (!PmDevice.CheckIOValueByGroup(IoGroupName.I, true))
|
|
{
|
|
EV.PostAlarmLog(Module, $"can not run process,I valves is not open!");
|
|
return Result.FAIL;
|
|
}
|
|
if (!PmDevice.CheckIOValueByGroup(IoGroupName.E, false))
|
|
{
|
|
EV.PostAlarmLog(Module, $"can not run process,E valves is not closed!");
|
|
return Result.FAIL;
|
|
}
|
|
if (!PmDevice.CheckIOValueByGroup(IoGroupName.K, false))
|
|
{
|
|
EV.PostAlarmLog(Module, $"can not run process,K valves is not closed!");
|
|
return Result.FAIL;
|
|
}
|
|
if (!PmDevice.CheckIOValueByGroup(IoGroupName.VentPump, true))
|
|
{
|
|
EV.PostAlarmLog(Module, $"can not run process,V72 is not open!");
|
|
return Result.FAIL;
|
|
}
|
|
if (!PmDevice.PSU1.AllHeatEnable)
|
|
{
|
|
EV.PostAlarmLog(Module, $"can not run process,Heater is disable!");
|
|
return Result.FAIL;
|
|
}
|
|
_currentStepNumber = 0;
|
|
PmDevice.RecipeRunningInfo.RecipeName = _recipeName;
|
|
PmDevice.RecipeRunningInfo.Head = recipeHead;
|
|
PmDevice.RecipeRunningInfo.RecipeStepList = recipeSteps;
|
|
PmDevice.RecipeRunningInfo.TotalTime = CalcRecipeTime();
|
|
_state = RecipeRunningState.ExecStep;
|
|
|
|
return Result.RUN;
|
|
}
|
|
|
|
public override Result Monitor()
|
|
{
|
|
try
|
|
{
|
|
SetConfinementRingUpAndWait((int)RoutineStep.SetServoUp, 30);
|
|
|
|
ExecuteRoutine((int)RoutineStep.SetChamberPressure, _pmServoToPressure);
|
|
|
|
EnableRotation((int)RoutineStep.EnableRotation, 5);
|
|
|
|
OpenH2Valve((int)RoutineStep.OpenH2Valve, PmDevice, 2);
|
|
|
|
EnableHeater((int)RoutineStep.EnableHeater, PmDevice, 5);
|
|
|
|
MonitorRecipeRunInfo();
|
|
|
|
lock (_recipeLocker)
|
|
{
|
|
try
|
|
{
|
|
switch (_state)
|
|
{
|
|
case RecipeRunningState.ExecStep:
|
|
|
|
if (_currentStepNumber == 0)
|
|
{
|
|
_recipeTimer.Start(int.MaxValue);
|
|
}
|
|
|
|
_stepTimer.Start(PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepTime * 1000);
|
|
|
|
Notify($"Running step {_currentStepNumber + 1} : {PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepName}");
|
|
|
|
|
|
//执行工艺程序命令
|
|
foreach (var recipeCmd in PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].RecipeCommands.Keys)
|
|
{
|
|
if (recipeCmd == "SusHeaterSetMode")
|
|
{
|
|
if (PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].RecipeCommands[recipeCmd] == "Jump")
|
|
_isPSUHeaterJumpMode = true;
|
|
else
|
|
_isPSUHeaterJumpMode = false;
|
|
|
|
continue;
|
|
}
|
|
if (recipeCmd == "WWHeaterSetMode")
|
|
{
|
|
if (PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].RecipeCommands[recipeCmd] == "Jump")
|
|
_isSCRHeaterJumpMode = true;
|
|
else
|
|
_isSCRHeaterJumpMode = false;
|
|
|
|
continue;
|
|
}
|
|
if (recipeCmd == "FlowSetMode")
|
|
{
|
|
if (PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].RecipeCommands[recipeCmd] == "Jump")
|
|
_isMFCJumpMode = true;
|
|
else
|
|
_isMFCJumpMode = false;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (IsCmdSkip(recipeCmd)) // 不是注册的方法,需要跳过
|
|
continue;
|
|
|
|
if (!OP.CanDoOperation($"{Module}.{recipeCmd}", out string reason, PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].RecipeCommands[recipeCmd]))
|
|
{
|
|
EV.PostAlarmLog(Module, $"Can not execute {recipeCmd}, {reason}");
|
|
return Result.FAIL;
|
|
}
|
|
else
|
|
{
|
|
int time = (int)PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepTime * 1000;
|
|
|
|
if (recipeCmd.StartsWith("TC1") && _isPSUHeaterJumpMode)
|
|
{
|
|
time = 1;
|
|
}
|
|
|
|
if (recipeCmd.StartsWith("TC2") && _isSCRHeaterJumpMode)
|
|
{
|
|
time = 1;
|
|
}
|
|
|
|
if (recipeCmd.StartsWith("Mfc") && recipeCmd.EndsWith(".Ramp") && !PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].IsDummyStep)
|
|
{
|
|
if (_isMFCJumpMode)
|
|
{
|
|
time = 1;
|
|
}
|
|
}
|
|
|
|
if ((recipeCmd == "TV.SetPressure"
|
|
|| recipeCmd == "PMServo.SetActualSpeed"
|
|
|| recipeCmd.StartsWith("Pressure") && recipeCmd.EndsWith(".Ramp")
|
|
|| recipeCmd.StartsWith("Mfc") && recipeCmd.EndsWith(".Ramp"))
|
|
&& !PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].IsDummyStep)
|
|
{
|
|
if (_currentStepNumber >= 1)
|
|
{
|
|
int previousStepNumber = _currentStepNumber - 1;
|
|
|
|
if (PmDevice.RecipeRunningInfo.RecipeStepList[previousStepNumber].IsDummyStep)
|
|
previousStepNumber = _currentStepNumber - 2;
|
|
|
|
if (PmDevice.RecipeRunningInfo.RecipeStepList[previousStepNumber].RecipeCommands.ContainsKey(recipeCmd))
|
|
{
|
|
string previousValue = PmDevice.RecipeRunningInfo.RecipeStepList[previousStepNumber].RecipeCommands[recipeCmd];
|
|
string currentValue = PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].RecipeCommands[recipeCmd];
|
|
if (previousValue == currentValue)
|
|
{
|
|
time = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OP.DoOperation($"{Module}.{recipeCmd}", out string reason1, time, PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].RecipeCommands[recipeCmd]);
|
|
}
|
|
}
|
|
|
|
if (PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].EndBy == EnumEndByCondition.ByTime)
|
|
_state = RecipeRunningState.TimeWait;
|
|
else
|
|
_state = RecipeRunningState.ConditionWait;
|
|
break;
|
|
|
|
case RecipeRunningState.TimeWait:
|
|
if (_stepTimer.IsTimeout())
|
|
{
|
|
_state = RecipeRunningState.StepCompleted;
|
|
}
|
|
break;
|
|
case RecipeRunningState.ConditionWait:
|
|
{
|
|
if (_stepTimer.IsTimeout())
|
|
{
|
|
_state = RecipeRunningState.StepCompleted;
|
|
}
|
|
}
|
|
break;
|
|
case RecipeRunningState.StepCompleted:
|
|
_currentStepNumber++;
|
|
if (_currentStepNumber >= PmDevice.RecipeRunningInfo.RecipeStepList.Count)
|
|
{
|
|
_currentStepNumber = PmDevice.RecipeRunningInfo.RecipeStepList.Count - 1;
|
|
_state = RecipeRunningState.RecipeCompleted;
|
|
}
|
|
else
|
|
{
|
|
_state = RecipeRunningState.ExecStep;
|
|
}
|
|
break;
|
|
case RecipeRunningState.RecipeCompleted:
|
|
{
|
|
_recipeTimer.Stop();
|
|
Notify("Finished");
|
|
return Result.DONE;
|
|
}
|
|
}
|
|
|
|
return Result.RUN;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LOG.Write(ex);
|
|
return Result.FAIL;
|
|
}
|
|
}
|
|
}
|
|
catch (RoutineBreakException)
|
|
{
|
|
return Result.RUN;
|
|
}
|
|
catch (RoutineFaildException)
|
|
{
|
|
return Result.FAIL;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LOG.Write(ex, String.Format("CleanRecipe has exception"));
|
|
throw (ex);
|
|
}
|
|
|
|
Notify("Finished");
|
|
|
|
return Result.DONE;
|
|
}
|
|
|
|
protected void EnableHeater(int id, PMModuleBase pm, int timeout)
|
|
{
|
|
Tuple<bool, Result> ret = ExecuteAndWait(id, () =>
|
|
{
|
|
Notify($"Set {pm.Name} Heater Enable");
|
|
|
|
if (!pm.EnableHeater(true, out string reason))
|
|
{
|
|
Stop($"Set {pm.Name} Heater Enable failed, {reason}");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}, () =>
|
|
{
|
|
return pm.CheckHeaterEnable();
|
|
}, timeout * 1000);
|
|
|
|
if (ret.Item1)
|
|
{
|
|
if (ret.Item2 == Result.FAIL)
|
|
{
|
|
throw (new RoutineFaildException());
|
|
}
|
|
if (ret.Item2 == Result.TIMEOUT)
|
|
{
|
|
Stop($"Set {pm.Name} Heater Enable timeout, over {timeout} seconds");
|
|
throw (new RoutineFaildException());
|
|
}
|
|
else
|
|
{
|
|
throw (new RoutineBreakException());
|
|
}
|
|
}
|
|
}
|
|
|
|
protected int CalcRecipeTime()
|
|
{
|
|
double total = 0;
|
|
for (int i = 0; i < PmDevice.RecipeRunningInfo.RecipeStepList.Count; i++)
|
|
{
|
|
if (!PmDevice.RecipeRunningInfo.RecipeStepList[i].IsDummyStep) // 不统计虚拟Step的时间
|
|
{
|
|
total += PmDevice.RecipeRunningInfo.RecipeStepList[i].StepTime;
|
|
}
|
|
}
|
|
|
|
return (int)total;
|
|
}
|
|
|
|
private void MonitorRecipeRunInfo()
|
|
{
|
|
try
|
|
{
|
|
PmDevice.RecipeRunningInfo.StepNumber = _currentStepNumber + 1;
|
|
PmDevice.RecipeRunningInfo.StepName = PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepName;
|
|
PmDevice.RecipeRunningInfo.StepTime = PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepTime;
|
|
PmDevice.RecipeRunningInfo.TotalElapseTime = _recipeTimer.GetElapseTime() / 1000;
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|