SicMultiplate/Modules/Mainframe/PMs/RecipeExecutions/Process.cs

1175 lines
48 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 System;
using System.Collections.Generic;
using Aitex.Core.Common;
using Aitex.Core.Common.DeviceData.IoDevice;
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, WaferProcessStatus.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");
ResetHeaterResCheckResult();
_thread.Start();
Notify($"Start");
return Result.RUN;
}
#region Heater Resistance Monitor
private readonly R_TRIG _trigHeaterResOutOfRange = new();
private readonly R_TRIG _trigHeaterResMonitorBegin = new();
private readonly R_TRIG _trigHeaterResMonitorEnd = new();
private void ResetHeaterResCheckResult()
{
_trigHeaterResOutOfRange.RST = true;
_trigHeaterResMonitorBegin.RST = true;
_trigHeaterResMonitorEnd.RST = true;
}
private void MonitorHeaterResistance()
{
var reason = "";
var totalElapseTime = PmDevice.RecipeRunningInfo.TotalElapseTime;//Recipe已经执行的时间
var totalTime = PmDevice.RecipeRunningInfo.TotalTime;//Recipe总时间
if (totalElapseTime == 0) //当开始执行后再去判断时间和电阻
return;
// 工艺刚开始和快结束的一段时间内,不需要检测电阻值。
var ignoredDurationSec = SC.GetValue<double>($"PM.{Module}.Heater.InProcessResistanceMonitorIgnoreDuration");
// 开始监测和节结束监测电阻时,输出信息,方便调试。
_trigHeaterResMonitorBegin.CLK = totalElapseTime >= ignoredDurationSec;
_trigHeaterResMonitorEnd.CLK = totalTime - totalElapseTime < ignoredDurationSec;
if (_trigHeaterResMonitorBegin.Q)
Notify("Begin Heater Resistance Monitor");
if (_trigHeaterResMonitorEnd.Q)
Notify("End Heater Resistance Monitor");
// 工艺刚开始和快结束的一段时间内,不需要检测电阻值。
if (!_trigHeaterResMonitorBegin.M || _trigHeaterResMonitorEnd.M)
return;
// 如果在工艺中检测Heater电阻
for (var i = 1; i <= 3; i++) //检测出所有的加热丝是否有断开的,不要提前结束循环
{
var ioPsuData = (IoPsuData)DATA.Poll($"{Module}.PSU{i}.DeviceData");
if (ioPsuData != null)
{
if (ioPsuData.IsResistanceOutOfRange)//PSU加热丝断开,后续可能增加条件判断
reason += $"PSU{i} resistance is out of range\r\n ";
}
var ioScrData = (IoPsuData)DATA.Poll($"{Module}.SCR{i}.DeviceData");
if (ioScrData != null)
{
if (ioScrData.IsResistanceOutOfRange)//SCR加热丝断开,后续可能增加条件判断
reason += $"SCR{i} resistance is out of range\r\n ";
}
}
if (reason.Length > 0)
{
// 某个加热器电阻超标
_trigHeaterResOutOfRange.CLK = true;
if (_trigHeaterResOutOfRange.Q)
{
var alarmLevel = SC.SafeGetStringValue($"PM.{Module}.Heater.InProcessResistanceFailAlarmLevel", "Alarm");
if (alarmLevel == "Alarm")
Stop(reason);
else
EV.PostWarningLog(Module, reason);
}
}
}
#endregion
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();
MonitorHeaterResistance();
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();
}
if (PmDevice.RecipeRunningInfo.NeedReloadRecipe)
{
if (string.IsNullOrEmpty(PmDevice.RecipeRunningInfo.XmlRecipeToReload))
{
EV.PostWarningLog(Module, "Recipe is required to be reloaded but no new recipe is received");
PmDevice.RecipeRunningInfo.NeedReloadRecipe = false;
}
else
{ //重新加载后面的Recipe内容
if (RecipeParser.ParseXmlString(PmDevice.RecipeRunningInfo.XmlRecipeToReload, Module, out var recipeHead, out var recipeSteps, out var reason1))
{
PmDevice.RecipeRunningInfo.RecipeStepList = recipeSteps;
PmDevice.RecipeRunningInfo.TotalTime = CalcRecipeTime();
PmDevice.RecipeRunningInfo.NeedReloadRecipe = false;
}
else
{
EV.PostWarningLog(Module, $"Reloading Recipe failed, {reason1}");
}
}
}
//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");
GrowCheck();
return Result.DONE;
}
case RecipeRunningState.Error:
{
//更新PM的Runtime
if (!_hasRecordRunTime)
{
_hasRecordRunTime = true;
RuntimeDataRecorder.UpdateElapseTimePM(Module+" Process", (int)(_recipeTimer.GetElapseTime() / 60000));
}
GrowCheck();
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()
{
try
{
var wi = WaferManager.Instance.GetWafer(ModuleHelper.Converter(Module), 0);
OP.DoOperation($"HistoryCoatingManager.Grow",
new object[] {
(wi.WaferInnerID).ToString(),
PmDevice.RecipeRunningInfo.GrowthRate,
PmDevice.RecipeRunningInfo.StepTime ,
Module });
}
catch (Exception ex)
{
LOG.Error(ex.Message, ex);
}
}
public void GrowCheck()
{
try
{
var wi = WaferManager.Instance.GetWafer(ModuleHelper.Converter(Module), 0);
OP.DoOperation($"HistoryCoatingManager.GrowCheck",
new object[] {
(wi.WaferInnerID).ToString(),
Module });
}
catch (Exception ex)
{
LOG.Error(ex.Message, ex);
}
}
}
}