Sic10/Modules/SicPM2/RecipeExecutions/Process.cs

1061 lines
44 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Aitex.Core.Common;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Device;
using Aitex.Core.RT.Device.Devices;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.RT.Routine;
using Aitex.Core.RT.SCCore;
using Aitex.Core.RT.Tolerance;
using Aitex.Core.Util;
using MECF.Framework.Common.DBCore;
using MECF.Framework.Common.Equipment;
using MECF.Framework.Common.OperationCenter;
using MECF.Framework.Common.SubstrateTrackings;
using SicPM2.Routines;
using System;
using System.Collections.Generic;
using System.Xml;
using static Aitex.Core.RT.Device.PmDevices.DicMode;
using MECF.Framework.RT.Core.DBProviderEx;
using MECF.Framework.RT.Core.DBProviderEx;
namespace SicPM2.RecipeExecutions
{
public enum RecipeContinueMode
{
None,
WaferReturnAndJobStop,
RecipeCompleted,
StepContinue,
StepRestart,
RecipeRestart,
NextStep,
}
public partial class Process : PMBaseRoutine
{
enum RoutineStep
{
WaitProcess,
}
enum RecipeRunningState
{
Error,
RecipeCompleted,
ExecStep,
TimeWait,
ConditionWait,
StepCompleted,
Paused,
}
private object _recipeLocker = new object();
private bool _hasRecordRunTime = false;
private RecipeRunningState _state = RecipeRunningState.ExecStep;
private RecipeRunningState _pausedState = RecipeRunningState.ExecStep;
private DeviceTimer _estimatedTimeCalcTimer = new DeviceTimer();//用于定时计算工艺程序估计的结束时间
private double _curStepElpasedTimeBeforePaused;
private double _curStepElpasedTimeBeforePaused2;
private List<int> _lstSkipSteps = new List<int>();
public RecipeContinueMode ContinueAction { get; set; }
public DateTime _recipeStartTime
{
get;
private set;
}
public string CurrentRecipeContent { get; private set; }
private int _currentStepNumber;
private int _dummyStepCount;
public int CurStepTotalLoopCount
{
get;
private set;
}
public double CurStepTotalTime
{
get
{
if (PmDevice.RecipeRunningInfo.RecipeStepList == null || PmDevice.RecipeRunningInfo.RecipeStepList.Count == 0 || _state == RecipeRunningState.RecipeCompleted || _state == RecipeRunningState.Error)
return 0;
return PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepTime * 1000;
}
}
public int CurrentLoopCount
{
get;
private set;
}
private DeviceTimer _stepTimer = new DeviceTimer();
private DeviceTimer _stepTimer2 = new DeviceTimer();
private DeviceTimer _recipeTimer = new DeviceTimer();
public bool IsPaused
{
private set;
get;
}
private double CurStepLeftTime
{
get
{
return _stepTimer.GetTotalTime() - _stepTimer.GetElapseTime();
}
}
public double EstimatedTotalLeftTime
{
get;
private set;
}
private RecipeDBCallback _dbCallback;
private Fdc _fdc;
private bool _isDryRun;
private int _delayTimeDryRun;
private double _tempOffset;
private int _currentStepIndex = 99;
private IoInterLock _pmInterLock;
private SicServo _pmSicServo = null;
private bool bAlarm = false; //True-Alarm, False-Warning
private double fThreshold = 3.0f; //阈值,从配置文件里取
private int iTimes = -1; //超限次数,设定值
private int iT = 0; //超限次数计数,实际值
#region Parse
private bool _isPSUHeaterJumpMode;
private bool _isSCRHeaterJumpMode;
private bool _isMFCJumpMode;
#endregion
#region Check
private PeriodicJob _thread;
private PeriodicJob _threadRotationAlarm;
private DeviceTimer _rampCalcTimer = new DeviceTimer();//用于定时获取PC的Ramp
protected ToleranceChecker[] _mfcGapChecker = new ToleranceChecker[32];
protected R_TRIG[] _mfcTrig = new R_TRIG[32];
protected ToleranceChecker[] _pcGapChecker = new ToleranceChecker[7];
protected R_TRIG[] _pcTrig = new R_TRIG[7];
protected R_TRIG[] _pcTrig2 = new R_TRIG[7];
protected double[] _pressurePrevious = new double[7];
#endregion
public Process(ModuleName module, PM2Module pm1) : base(module, pm1)
{
Module = module.ToString();
Name = "Process";
_dbCallback = new RecipeDBCallback();
_fdc = new Fdc(Module);
_pmInterLock = DEVICE.GetDevice<IoInterLock>($"{Module}.PMInterLock");
this._pmSicServo = DEVICE.GetDevice<SicServo>($"{Module}.PMServo");
if (this._pmSicServo != null)
{
this.bAlarm = SC.GetValue<bool>($"PM.{Module}.RotationAlarm.WarningOrAlarm");
this.fThreshold = SC.GetValue<double>($"PM.{Module}.RotationAlarm.Threshold");
this.iTimes = SC.GetValue<int>($"PM.{Module}.RotationAlarm.Times");
}
for (int i = 0; i < _mfcGapChecker.Length; i++)
{
_mfcGapChecker[i] = new ToleranceChecker();
_mfcTrig[i] = new R_TRIG();
}
for (int i = 0; i < _pcGapChecker.Length; i++)
{
_pcGapChecker[i] = new ToleranceChecker();
_pcTrig[i] = new R_TRIG();
_pcTrig2[i] = new R_TRIG();
}
Initialize();
Calculte();
_thread = new PeriodicJob(10 * 1000, Calculte, "Calculte Standard Deviation", false);
_threadRotationAlarm = new PeriodicJob(1000, CalculteRotationAlarm, "Calculte Rotation Alarm Deviation", false);
}
public override Result Start(params object[] param)
{
Reset();
_hasRecordRunTime = false;
if (!_pmInterLock.SetPMProcessRunning(true, out string reason))
{
EV.PostAlarmLog(Module, $"can not run Process, {reason}");
return Result.FAIL;
}
_lstSkipSteps = new List<int>();
_currentStepIndex = 99;
_currentStepNumber = CurStepTotalLoopCount = 0;
_dummyStepCount = 0;
_estimatedTimeCalcTimer.Start(1000);
_rampCalcTimer.Start(1000);
PmDevice.RecipeRunningInfo.InnerId = Guid.NewGuid();
PmDevice.RecipeRunningInfo.BeginTime = DateTime.Now;
PmDevice.RecipeRunningInfo.TotalTime = CalcRecipeTime();
PmDevice.RecipeRunningInfo.IsRoutineAbort = false;
_state = RecipeRunningState.ExecStep;
_recipeTimer.Start(int.MaxValue);
_dbCallback.RecipeStart(PmDevice.Module, 0, PmDevice.RecipeRunningInfo.InnerId.ToString(), PmDevice.RecipeRunningInfo.RecipeName);
_dbCallback.RecipeUpdateStatus(PmDevice.RecipeRunningInfo.InnerId.ToString(), "InProcess");
WaferManager.Instance.UpdateWaferProcessStatus(ModuleHelper.Converter(Module), 0, EnumWaferProcessStatus.InProcess);
WaferManager.Instance.GetWafer(ModuleHelper.Converter(Module), 0).TrayProcessCount--;
_fdc.Reset();
_isDryRun = SC.GetValue<bool>($"PM.{Module}.DryRun.IsDryRun");
_delayTimeDryRun = SC.GetValue<int>($"PM.{Module}.DryRun.DryRunDelayTime");
_tempOffset = SC.GetValue<double>($"PM.{Module}.Process.TempOffset");
_thread.Start();
_threadRotationAlarm.Start();
Notify($"Start");
return Result.RUN;
}
public override Result Monitor()
{
if (!PmDevice.CheckEnableRunProcess(out string reason))
{
EV.PostAlarmLog(Module, reason);
return Result.FAIL;
}
if (_isDryRun) // 空跑工艺
{
try
{
DryRunProcess((int)RoutineStep.WaitProcess, $"Chamber:{Name}:WaitProcess", _delayTimeDryRun);
}
catch (RoutineBreakException)
{
return Result.RUN;
}
catch (RoutineFaildException)
{
return Result.FAIL;
}
Notify("End");
return Result.DONE;
}
else
{
MonitorRecipeEndTime();
MonitorRecipeRunInfo();
lock (_recipeLocker)
{
try
{
switch (_state)
{
case RecipeRunningState.ExecStep:
{
PmDevice.ResetToleranceChecker();
//int stepTime = (int)PMDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepTime * 1000;
if (ContinueAction != RecipeContinueMode.StepContinue)
{
_curStepElpasedTimeBeforePaused = 0;
_curStepElpasedTimeBeforePaused2 = 0;
}
ContinueAction = RecipeContinueMode.None;
if (PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].IsLoopStartStep)
{
CurStepTotalLoopCount = PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].LoopCount;
if (CurStepTotalLoopCount == 0)
{
CurrentLoopCount = 0;
}
else
{
CurrentLoopCount++;
}
}
//stepTime = (int)(PMDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepTime - _curStepElpasedTimeBeforePaused / 1000);
_stepTimer.Start(PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepTime * 1000 - _curStepElpasedTimeBeforePaused);
if (!_stepTimer2.IsIdle())
{
_curStepElpasedTimeBeforePaused2 += _stepTimer2.GetElapseTime();
_stepTimer2.Stop();
}
_stepTimer2.Start(int.MaxValue);
Notify($"Running step {_currentStepNumber + 1} : {PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepName}");
if (PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].IsDummyStep)
{
_dummyStepCount++;
}
//执行工艺程序命令
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 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 - _curStepElpasedTimeBeforePaused);// (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)
{
if (_lstSkipSteps.Count>0 && _lstSkipSteps.Contains(previousStepNumber)) //上一步是跳步过来的,这一步和上一步的值是相同的不用设置
{
continue;
}
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;
//ResetChecker();
_dbCallback.RecipeStepStart(PmDevice.RecipeRunningInfo.InnerId.ToString(), _currentStepNumber, PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepName, (float)PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepTime);
_fdc.Start(PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].RecipeCommands);
}
break;
case RecipeRunningState.TimeWait:
if (IsPaused)
{
_state = RecipeRunningState.Paused;
}
if (_stepTimer.IsTimeout())
{
_state = RecipeRunningState.StepCompleted;
this.Grow(PmDevice.RecipeRunningInfo.GrowthRate, PmDevice.RecipeRunningInfo.StepTime);
}
//ToleranceChecker();
SkipStepForHeat();
break;
case RecipeRunningState.ConditionWait:
{
if (_stepTimer.IsTimeout())
{
_state = RecipeRunningState.StepCompleted;
this.Grow(PmDevice.RecipeRunningInfo.GrowthRate, PmDevice.RecipeRunningInfo.StepTime);
}
}
break;
case RecipeRunningState.Paused:
PmDevice.PauseRecipe(out reason);
if (!_stepTimer.IsIdle())
{
_curStepElpasedTimeBeforePaused += _stepTimer.GetElapseTime();
_stepTimer.Stop();
}
switch (ContinueAction)
{
case RecipeContinueMode.None:
break;
case RecipeContinueMode.WaferReturnAndJobStop:
//Singleton<RouteManager>.Instance.CheckToPostMessage((int)RouteManager.MSG.StopJob);
_state = RecipeRunningState.Error;
break;
case RecipeContinueMode.RecipeCompleted:
_state = RecipeRunningState.RecipeCompleted;
break;
case RecipeContinueMode.StepContinue:
_state = RecipeRunningState.ExecStep;
break;
case RecipeContinueMode.StepRestart:
_state = RecipeRunningState.ExecStep;
break;
case RecipeContinueMode.RecipeRestart:
_currentStepNumber = 0;
_state = RecipeRunningState.ExecStep;
break;
case RecipeContinueMode.NextStep:
_state = RecipeRunningState.StepCompleted;
break;
}
break;
case RecipeRunningState.StepCompleted:
{
//放在前面stepnumber后面会被更新
_stepTimer2.Stop();
_dbCallback.RecipeStepEnd(PmDevice.RecipeRunningInfo.InnerId.ToString(), _currentStepNumber, _fdc.DataList);
_fdc.Stop();
if (PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].IsLoopEndStep)
{
//重新读取循环的设定次数
for (int nn = _currentStepNumber; nn >= 0; nn--)
{
if (PmDevice.RecipeRunningInfo.RecipeStepList[nn].IsLoopStartStep)
{
CurStepTotalLoopCount = PmDevice.RecipeRunningInfo.RecipeStepList[nn].LoopCount;
break;
}
}
if (CurrentLoopCount >= CurStepTotalLoopCount)
{
CurrentLoopCount = CurStepTotalLoopCount = 0;
_currentStepNumber++;
}
else
{
int n = _currentStepNumber - 1;
int next = -1;
while (n >= 0)
{
if (PmDevice.RecipeRunningInfo.RecipeStepList[n].IsLoopStartStep)
{
next = n;
break;
}
n--;
}
if (next == -1)
throw new Exception("Loop End control error");
_currentStepNumber = next;
}
}
else
{
_currentStepNumber++;
}
if (_currentStepNumber >= PmDevice.RecipeRunningInfo.RecipeStepList.Count)
{
_currentStepNumber = PmDevice.RecipeRunningInfo.RecipeStepList.Count - 1;
_state = RecipeRunningState.RecipeCompleted;
}
else
{
_state = RecipeRunningState.ExecStep;
}
}
break;
case RecipeRunningState.RecipeCompleted:
{
//更新PM的Runtime
if (!_hasRecordRunTime)
{
_hasRecordRunTime = true;
RuntimeDataRecorder.UpdateElapseTimePM(Module + " Process", (int)(_recipeTimer.GetElapseTime() / 60000));
}
_recipeTimer.Stop();
Notify("Finished");
return Result.DONE;
}
case RecipeRunningState.Error:
{
//更新PM的Runtime
if (!_hasRecordRunTime)
{
_hasRecordRunTime = true;
RuntimeDataRecorder.UpdateElapseTimePM(Module+" Process", (int)(_recipeTimer.GetElapseTime() / 60000));
}
return Result.DONE;
}
default:
break;
}
}
catch (Exception ex)
{
PmDevice.SetHeaterStopRamp();
PmDevice.SetMfcStopRamp(PmDevice.GetMfcListByGroupName(MfcGroupName.All));
PmDevice.SetRotationStopRamp();
LOG.Write(ex);
return Result.FAIL;
}
}
return Result.RUN;
}
}
private void MonitorRecipeRunInfo()
{
try
{
PmDevice.RecipeRunningInfo.StepNumber = _currentStepNumber + 1 - _dummyStepCount; //CurStepNum start from 0, ignore dummy step
PmDevice.RecipeRunningInfo.StepName = PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepName;
PmDevice.RecipeRunningInfo.StepTime = PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepTime;
PmDevice.RecipeRunningInfo.GrowthRate = PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].GrowthRate;
PmDevice.RecipeRunningInfo.StepElapseTime = _stepTimer.IsIdle() ? _curStepElpasedTimeBeforePaused / 1000 : (_stepTimer.GetElapseTime() + _curStepElpasedTimeBeforePaused) / 1000;
PmDevice.RecipeRunningInfo.TotalElapseTime = CalcElapseRecipeTime();
PmDevice.RecipeRunningInfo.StepElapseTime2 = _stepTimer2.IsIdle() ? _curStepElpasedTimeBeforePaused2 / 1000 : (_stepTimer2.GetElapseTime() + _curStepElpasedTimeBeforePaused2) / 1000;
PmDevice.RecipeRunningInfo.TotalElapseTime2 = _recipeTimer.GetElapseTime() / 1000;
if (PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].RecipeCommands.ContainsKey("SHArH2Switch.SetValve"))
{
PmDevice.RecipeRunningInfo.ArH2Switch = PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].RecipeCommands["SHArH2Switch.SetValve"];
}
if (PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].RecipeCommands.ContainsKey("N2Dilution.SetValve"))
{
PmDevice.RecipeRunningInfo.N2FlowMode = PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].RecipeCommands["N2Dilution.SetValve"];
}
if (_currentStepIndex != PmDevice.RecipeRunningInfo.StepNumber)
{
Notify($"Start Recipe: Step:{PmDevice.RecipeRunningInfo.StepNumber} Name:{PmDevice.RecipeRunningInfo.StepName} ");
_currentStepIndex = PmDevice.RecipeRunningInfo.StepNumber;
}
}
catch (Exception ex)
{
}
}
public override void Abort()
{
//更新PM的Runtime
PmDevice.RecipeRunningInfo.IsRoutineAbort = true;
if (!_hasRecordRunTime)
{
_hasRecordRunTime = true;
RuntimeDataRecorder.UpdateElapseTimePM(Module + " Process", (int)(_recipeTimer.GetElapseTime() / 60000));
}
_state = RecipeRunningState.RecipeCompleted;
_dbCallback.RecipeFailed(PmDevice.RecipeRunningInfo.InnerId.ToString());
_fdc.Stop();
PmDevice.AbortRunProcess(out string reason);
PmDevice.RecipeRunningInfo.StepName = string.Empty;
PmDevice.RecipeRunningInfo.StepNumber = 0;
PmDevice.RecipeRunningInfo.StepTime = 0;
PmDevice.RecipeRunningInfo.StepElapseTime = 0;
PmDevice.RecipeRunningInfo.TotalTime = 0;
PmDevice.RecipeRunningInfo.TotalElapseTime = 0;
//PMDevice.Rf.SetPowerOnOff(false, out _);
//PMDevice.Microwave.SetPowerOnOff(false, out _);
//PMDevice.GasLine1.SetFlow(out _, 0, 0);
//PMDevice.GasLine2.SetFlow(out _, 0, 0);
//PMDevice.GasLine3.SetFlow(out _, 0, 0);
}
public void ExitProcess()
{
if (_state == RecipeRunningState.RecipeCompleted)
{
_dbCallback.RecipeComplete(PmDevice.RecipeRunningInfo.InnerId.ToString());
_fdc.Stop();
}
else
{
_dbCallback.RecipeFailed(PmDevice.RecipeRunningInfo.InnerId.ToString());
_fdc.Stop();
}
_thread.Stop();
}
public void PauseRecipe()
{
if (_state != RecipeRunningState.TimeWait && _state != RecipeRunningState.ConditionWait)
return;
if (!IsPaused)
{
IsPaused = true;
_pausedState = _state;
_state = RecipeRunningState.Paused;
}
}
public void SkipCurrentRecipeStep()
{
if (_state == RecipeRunningState.ConditionWait || _state == RecipeRunningState.TimeWait)
{
_lstSkipSteps.Add(_currentStepNumber);
_state = RecipeRunningState.StepCompleted;
}
}
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;
}
protected int CalcElapseRecipeTime()
{
double total = 0;
for (int i = 0; i < _currentStepNumber; i++)
{
if (!PmDevice.RecipeRunningInfo.RecipeStepList[i].IsDummyStep) // 不统计虚拟Step的时间
{
total += PmDevice.RecipeRunningInfo.RecipeStepList[i].StepTime;
}
}
total += PmDevice.RecipeRunningInfo.StepElapseTime;
return (int)total;
}
protected void MonitorRecipeEndTime()
{
try
{
if (!_estimatedTimeCalcTimer.IsTimeout())
return;
_estimatedTimeCalcTimer.Start(1000);
EstimatedTotalLeftTime = 0;
if (_state == RecipeRunningState.RecipeCompleted)
return;
if (!(_currentStepNumber >= 0 && _currentStepNumber <= PmDevice.RecipeRunningInfo.RecipeStepList.Count - 1))
return;
if (CurStepLeftTime > 0)
{
EstimatedTotalLeftTime = CurStepLeftTime;
}
int nextBegin = _currentStepNumber;
bool IsInLoop = false;
int iNum1 = 0;
int iNum2 = 0;
//int j=i;
for (int j = _currentStepNumber; j < PmDevice.RecipeRunningInfo.RecipeStepList.Count; j++)
{
if (PmDevice.RecipeRunningInfo.RecipeStepList[j].IsLoopEndStep)
{
iNum2 = j;
IsInLoop = true;
break;
}
else if (j > _currentStepNumber && PmDevice.RecipeRunningInfo.RecipeStepList[j].IsLoopStartStep)
{
IsInLoop = false;
break;
}
}
if (IsInLoop)
{
iNum1 = _currentStepNumber;
for (int j = _currentStepNumber; j >= 0; j--)
{
if (PmDevice.RecipeRunningInfo.RecipeStepList[j].IsLoopStartStep)
{
iNum1 = j;
break;
}
}
for (int j = _currentStepNumber + 1; j <= iNum2; j++)
{
EstimatedTotalLeftTime += PmDevice.RecipeRunningInfo.RecipeStepList[j].StepTime * 1000 * (PmDevice.RecipeRunningInfo.RecipeStepList[iNum1].LoopCount - CurrentLoopCount + 1);
}
for (int j = iNum1; j <= _currentStepNumber; j++)
{
EstimatedTotalLeftTime += PmDevice.RecipeRunningInfo.RecipeStepList[j].StepTime * 1000 * (PmDevice.RecipeRunningInfo.RecipeStepList[iNum1].LoopCount - CurrentLoopCount);
}
nextBegin = iNum2 + 1;
}
else
{
nextBegin++;
}
for (int j = nextBegin; j < PmDevice.RecipeRunningInfo.RecipeStepList.Count; j++)
{
if (PmDevice.RecipeRunningInfo.RecipeStepList[j].IsLoopStartStep)
{
//j=i;
iNum1 = j;
iNum2 = j + 1;
double lr1 = 0;
for (int m = j; m < PmDevice.RecipeRunningInfo.RecipeStepList.Count; m++)
{
lr1 += PmDevice.RecipeRunningInfo.RecipeStepList[m].StepTime * 1000;
if (PmDevice.RecipeRunningInfo.RecipeStepList[m].IsLoopEndStep)
{
iNum2 = m;
break;
}
}
EstimatedTotalLeftTime += lr1 * PmDevice.RecipeRunningInfo.RecipeStepList[iNum1].LoopCount;
j = iNum2;
}
else
{
EstimatedTotalLeftTime += PmDevice.RecipeRunningInfo.RecipeStepList[j].StepTime * 1000;
}
}
}
catch (Exception ex)
{
LOG.Write(ex);
}
}
public void SetContinue(string continueMode)
{
switch (continueMode)
{
case "Step continue":
ContinueAction = RecipeContinueMode.StepContinue;
break;
case "Step restart":
ContinueAction = RecipeContinueMode.StepRestart;
break;
case "Next step":
ContinueAction = RecipeContinueMode.NextStep;
break;
case "Recipe restart":
ContinueAction = RecipeContinueMode.RecipeRestart;
break;
case "Recipe complete":
ContinueAction = RecipeContinueMode.RecipeCompleted;
break;
case "Wafer return and job stop":
ContinueAction = RecipeContinueMode.WaferReturnAndJobStop;
break;
}
IsPaused = false;
PmDevice.ResetToleranceChecker();
}
public void DryRunProcess(int id, string stepName, int delayTime)
{
Tuple<bool, Result> ret = Delay(id, () =>
{
Notify($"Dry run process {delayTime} seconds");
_stepSpan = new TimeSpan(0, 0, 0, (int)delayTime);
_stepStartTime = DateTime.Now;
_stepName = stepName;
return true;
}, delayTime * 1000);
if (ret.Item1)
{
if (ret.Item2 == Result.FAIL)
{
throw (new RoutineFaildException());
}
throw new RoutineBreakException();
}
}
public void ToleranceChecker()
{
#region MFC
for (int i = 0; i < _mfcGapChecker.Length; i++)
{
_mfcGapChecker[i].Monitor(PmDevice._mfcList[i].FeedBack, PmDevice._mfcList[i].SetPoint * (1 - 2 / 100), PmDevice._mfcList[i].SetPoint * (1 + 2 / 100), 5 * 60);
if (_mfcGapChecker[i].Trig)
{
EV.PostWarningLog(Module, $"{PmDevice._mfcList[i].Name} flow gap > 2%, over 5 minites");
}
}
for (int i = 0; i < _mfcTrig.Length; i++)
{
double gap = Convert.ToDouble(dbMfcGapResults[i].StdDev);
double setPoint = Convert.ToDouble(dbMfcSetPointResults[i].Mean);
_mfcTrig[i].CLK = gap / setPoint > 1 / 100;
if (_mfcTrig[i].Q)
{
EV.PostWarningLog(Module, $"{PmDevice._mfcList[i].Name} flow standard deviation > 1%, for 5 minites");
}
}
#endregion
#region PC
for (int i = 0; i < _pcGapChecker.Length; i++)
{
_pcGapChecker[i].Monitor(PmDevice._pcList[i].FeedBack, PmDevice._pcList[i].SetPoint - 60, PmDevice._pcList[i].SetPoint + 60, 5 * 60);
if (_pcGapChecker[i].Trig)
{
EV.PostWarningLog(Module, $"{PmDevice._pcList[i].Name} flow gap > 60 mbar over 5 minites");
}
}
for (int i = 0; i < _pcTrig.Length; i++)
{
double gap = Convert.ToDouble(dbPcGapResults[i].StdDev);
//double setPoint = Convert.ToDouble(dbPcSetPointResults[i].StdDev);
_pcTrig[i].CLK = gap > 10;
if (_pcTrig[i].Q)
{
EV.PostWarningLog(Module, $"{PmDevice._mfcList[i].Name} flow standard deviation > 1%, for 5 minites");
}
}
if (_rampCalcTimer.IsTimeout())
{
for (int i = 0; i < _pcTrig2.Length; i++)
{
_pcTrig2[i].CLK = Math.Abs(PmDevice._pcList[i].FeedBack - _pressurePrevious[i]) > 90;
if (_pcTrig2[i].Q)
{
EV.PostWarningLog(Module, $"{PmDevice._mfcList[i].Name} pressure ramp > 90 mbar");
}
_pressurePrevious[i] = PmDevice._pcList[i].FeedBack;
}
_rampCalcTimer.Start(1000);
}
#endregion
}
public void ResetChecker()
{
#region MFC
for (int i = 0; i < _mfcGapChecker.Length; i++)
{
_mfcGapChecker[i].Reset(5 * 60);
}
for (int i = 0; i < _mfcTrig.Length; i++)
{
_mfcTrig[i].RST = true;
}
#endregion
#region PC
for (int i = 0; i < _pcGapChecker.Length; i++)
{
_pcGapChecker[i].Reset(5 * 60);
}
for (int i = 0; i < _pcTrig.Length; i++)
{
_pcTrig[i].RST = true;
}
#endregion
}
public void SkipStepForHeat()
{
int stepCount = PmDevice.RecipeRunningInfo.RecipeStepList.Count;
if (_currentStepNumber + 1 < stepCount)
{
var currentStep = PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber];
var nextStep = PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber + 1];
if (currentStep.IsDummyStep)
return;
if (nextStep.IsDummyStep)
nextStep = PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber + 2];
if (!currentStep.RecipeCommands.ContainsKey("TC1.SetHeaterMode") || !nextStep.RecipeCommands.ContainsKey("TC1.SetHeaterMode"))
return;
HeaterControlMode currentPSUMode = (HeaterControlMode)Enum.Parse(typeof(HeaterControlMode), currentStep.RecipeCommands["TC1.SetHeaterMode"]);
HeaterControlMode nextPSUMode = (HeaterControlMode)Enum.Parse(typeof(HeaterControlMode), nextStep.RecipeCommands["TC1.SetHeaterMode"]);
if (currentPSUMode == HeaterControlMode.Power && nextPSUMode != HeaterControlMode.Power)
{
float PSUL2InputTemp =(float)DATA.Poll($"{Module}.TC1.L2InputTempSetPoint"); // (float)DATA.Poll($"{Module}.TC1.L1PVFeedBack"); 测试用
float nextL2Temp = Convert.ToSingle(nextStep.RecipeCommands["TC1.SetL2TargetSP"]);
if (nextL2Temp > PSUL2InputTemp && nextL2Temp - PSUL2InputTemp < Math.Abs(_tempOffset))
{
SkipCurrentRecipeStep();
EV.PostInfoLog(Module, $"Current PSU middle temperature is {PSUL2InputTemp}℃ and Power mode, next step PSU middle temperature setpoint is {nextL2Temp}℃ and {nextPSUMode} mode." +
$" TempOffset is {_tempOffset}℃. Need jump step!");
}
}
}
}
public bool Grow(double GrowthRate, double Seconds)
{
var wi = WaferManager.Instance.GetWafer(ModuleHelper.Converter(Module), 0);
return WaferDataRecorderEx.Grow(wi.InnerId.ToString(), GrowthRate, Seconds);
}
}
}