2026-03-24 15:04:02 +08:00
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.Device.PmDevices ;
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.Gem ;
using MECF.Framework.Common.SubstrateTrackings ;
using SicModules.PMs.Routines.Base ;
using MECF.Framework.Common.MECF.Framework.Common.RecipeCenter.Recipe ;
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 ;
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 ; //超限次数计数,实际值
private double _pressureDifferenceUpperLimit ;
#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 , PMModule 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 ( ) ;
SC . RegisterValueChangedCallback ( $"PM.{Module}.PT1PT2PressureDifferenceUpperLimit" , ( obj ) = > { _pressureDifferenceUpperLimit = ( double ) obj ; } ) ;
_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 ;
}
_stepTimer = new DeviceTimer ( ) ;
_curStepElpasedTimeBeforePaused = 0 ;
//_stepTimer2 = new DeviceTimer();
//_recipeTimer = new DeviceTimer();
//_stepTimer.Start(0);
_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 ;
PmDevice . RecipeRunningInfo . StepNumber = 1 ;
PmDevice . RecipeRunningInfo . StepName =
PmDevice . RecipeRunningInfo . RecipeStepList [ _currentStepNumber ] . StepName ;
_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" ) ;
//手动工艺时LotID和SequenceID可能是string.Empty
//补全路径
string recipeID = PmDevice . RecipeRunningInfo . RecipeName + ".rcp" ;
string squenceID = $"Sequence\\{PmDevice.GetWaferSequenceID()}.seq" ;
GemManager . Instance . Equipment ? . TriggerEvent ( "PMRecipeStart" , new string [ ] { "ChamberID" , "RecipeID" , "LotID" , "SequenceID" } , new object [ ] { PmDevice . Module , recipeID , PmDevice . GetWaferLotID ( ) , squenceID } ) ;
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" ) ;
_pressureDifferenceUpperLimit = SC . GetValue < double > ( $"PM.{Module}.PT1PT2PressureDifferenceUpperLimit" ) ;
ResetHeaterResCheckResult ( ) ;
_thread . Start ( ) ;
_threadRotationAlarm . 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 readonly R_TRIG _trigPressureDifference = new ( ) ;
private void ResetHeaterResCheckResult ( )
{
_trigHeaterResOutOfRange . RST = true ;
_trigHeaterResMonitorBegin . RST = true ;
_trigHeaterResMonitorEnd . RST = true ;
_trigPressureDifference . 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:{ioPsuData.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:{ioScrData.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 ) ;
}
}
}
private void PressureDifferenceDetection ( )
{
double pt1 = PmDevice . GetChamberPressure ( ) ;
double pt2 = PmDevice . GetForelinePressure ( ) ;
double d = Math . Abs ( pt1 - pt2 ) ;
if ( d > = _pressureDifferenceUpperLimit )
_trigPressureDifference . CLK = true ;
if ( _trigPressureDifference . Q )
{
string reason = $"PT1={pt1} PT2={pt2},Difference {d} over DifferenceMax={_pressureDifferenceUpperLimit} " ;
var alarmLevel = SC . SafeGetStringValue ( $"PM.{Module}.PT1PT2PressureDifferenceMaxAlarmLevel" , "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 ( ) ; //加热丝断开检测
PressureDifferenceDetection ( ) ; //腔体和管道压力检测
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 ) ;
2026-06-10 15:41:00 +08:00
Notify ( $"Running step {_currentStepNumber + 1} : {PmDevice.RecipeRunningInfo.RecipeStepList[_currentStepNumber].StepName}" ) ;
GemManager . Instance ? . TriggerEvent ( $"{Module}RecipeStepStart" , new string [ ] { $"{Module}.SelectedRecipeName" , $"{Module}.RecipeStepNumber" , $"{Module}.RecipeStepName" } ,
new object [ ] { PmDevice . RecipeRunningInfo . RecipeName , _currentStepNumber + 1 ,
PmDevice . RecipeRunningInfo . RecipeStepList [ _currentStepNumber ] . StepName } ) ;
2026-03-24 15:04:02 +08:00
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 ) )
{
//记录高亮部分
foreach ( var item in PmDevice . RecipeRunningInfo . HighlightDic )
{
var s = recipeSteps . Find ( i = > i . StepUid = = item . Key ) ;
if ( s ! = null )
{
s . HighLightList = item . Value ;
}
}
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 :
{
2026-06-10 15:41:00 +08:00
//放在前面, stepnumber后面会被更新
GemManager . Instance ? . TriggerEvent ( $"{Module}RecipeStepComplete" ,
new string [ ] { $"{Module}.SelectedRecipeName" , $"{Module}.RecipeStepNumber" , $"{Module}.RecipeStepName" } ,
new object [ ] { PmDevice . RecipeRunningInfo . RecipeName , _currentStepNumber + 1 , PmDevice . RecipeRunningInfo . RecipeStepList [ _currentStepNumber ] . StepName } ) ;
2026-03-24 15:04:02 +08:00
2026-06-10 15:41:00 +08:00
_stepTimer2 . Stop ( ) ;
2026-03-24 15:04:02 +08:00
_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 ,
( int ) _recipeTimer . GetElapseTime ( ) / 1000 ) ;
}
_recipeTimer . Stop ( ) ;
Notify ( "Finished" ) ;
GrowCheck ( ) ;
//手动工艺时LotID和SequenceID可能是string.Empty
//补全路径
string recipeID = PmDevice . RecipeRunningInfo . RecipeName + ".rcp" ;
string squenceID = $"Sequence\\{PmDevice.GetWaferSequenceID()}.seq" ;
GemManager . Instance . Equipment ? . TriggerEvent ( "PMRecipeComplete" , new string [ ] { "ChamberID" , "RecipeID" , "LotID" , "SequenceID" } , new object [ ] { PmDevice . Module , recipeID , PmDevice . GetWaferLotID ( ) , squenceID } ) ;
return Result . DONE ;
}
case RecipeRunningState . Error :
{
//更新PM的Runtime
if ( ! _hasRecordRunTime )
{
_hasRecordRunTime = true ;
RuntimeDataRecorder . UpdateElapseTimePM ( Module ,
( int ) _recipeTimer . GetElapseTime ( ) / 1000 ) ;
}
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 ( )
{
//Abort后设置加热比列为0
PmDevice . TC1 . SetRatio ( 0 , 0 , 0 ) ;
PmDevice . TC2 . SetRatio ( 0 , 0 , 0 ) ;
//更新PM的Runtime
PmDevice . RecipeRunningInfo . IsRoutineAbort = true ;
if ( ! _hasRecordRunTime )
{
_hasRecordRunTime = true ;
RuntimeDataRecorder . UpdateElapseTimePM ( Module , ( int ) _recipeTimer . GetElapseTime ( ) / 1000 ) ;
}
_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 ;
//手动工艺时LotID和SequenceID可能是string.Empty
//补全路径
string recipeID = PmDevice . RecipeRunningInfo . RecipeName + ".rcp" ;
string squenceID = $"Sequence\\{PmDevice.GetWaferSequenceID()}.seq" ;
GemManager . Instance . Equipment ? . TriggerEvent ( "PMRecipeAbort" , new string [ ] { "ChamberID" , "RecipeID" , "LotID" , "SequenceID" } , new object [ ] { PmDevice . Module , recipeID , PmDevice . GetWaferLotID ( ) , squenceID } ) ;
//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 double 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 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 ;
var currentPSUMode = ( HeatStrategy ) Enum . Parse ( typeof ( HeatStrategy ) ,
currentStep . RecipeCommands [ "TC1.SetHeaterMode" ] ) ;
var nextPSUMode = ( HeatStrategy ) Enum . Parse ( typeof ( HeatStrategy ) ,
nextStep . RecipeCommands [ "TC1.SetHeaterMode" ] ) ;
if ( currentPSUMode = = HeatStrategy . Power & & nextPSUMode ! = HeatStrategy . 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 . TrayInnerID ) . 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 ) ;
}
}
}
}