1035 lines
42 KiB
C#
1035 lines
42 KiB
C#
|
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;
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
#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, PM2Module 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;
|
|||
|
}
|
|||
|
|
|||
|
//ToleranceChecker();
|
|||
|
|
|||
|
SkipStepForHeat();
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case RecipeRunningState.ConditionWait:
|
|||
|
{
|
|||
|
if (_stepTimer.IsTimeout())
|
|||
|
{
|
|||
|
_state = RecipeRunningState.StepCompleted;
|
|||
|
}
|
|||
|
}
|
|||
|
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.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!");
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|