1047 lines
43 KiB
C#
1047 lines
43 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
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.SubstrateTrackings;
|
||
using SicModules.PMs.Routines.Base;
|
||
using static Aitex.Core.RT.Device.PmDevices.DicMode;
|
||
|
||
namespace SicModules.PMs.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;
|
||
|
||
#region Parse
|
||
|
||
private bool _isPSUHeaterJumpMode;
|
||
private bool _isSCRHeaterJumpMode;
|
||
private bool _isMFCJumpMode;
|
||
|
||
#endregion
|
||
|
||
#region Check
|
||
|
||
private PeriodicJob _thread;
|
||
|
||
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, PMModule pm1) : base(module, pm1)
|
||
{
|
||
Module = module.ToString();
|
||
Name = "Process";
|
||
|
||
_dbCallback = new RecipeDBCallback();
|
||
_fdc = new Fdc(Module);
|
||
_pmInterLock = DEVICE.GetDevice<IoInterLock>($"{Module}.PMInterLock");
|
||
|
||
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);
|
||
}
|
||
|
||
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();
|
||
|
||
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;
|
||
Grow();
|
||
}
|
||
|
||
//ToleranceChecker();
|
||
|
||
SkipStepForHeat();
|
||
|
||
break;
|
||
|
||
case RecipeRunningState.ConditionWait:
|
||
{
|
||
if (_stepTimer.IsTimeout())
|
||
{
|
||
_state = RecipeRunningState.StepCompleted;
|
||
Grow();
|
||
}
|
||
}
|
||
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 void Grow()
|
||
{
|
||
var wi = WaferManager.Instance.GetWafer(ModuleHelper.Converter(Module), 0);
|
||
OP.DoOperation($"CoatingManager.Grow",
|
||
new object[] {
|
||
(wi.TrayOriginSlot+1).ToString(),
|
||
PmDevice.RecipeRunningInfo.GrowthRate,
|
||
PmDevice.RecipeRunningInfo.StepTime ,
|
||
Module });
|
||
}
|
||
}
|
||
}
|