using System; using System.Collections.Generic; using Aitex.Core.RT.Routine; using Aitex.Core.RT.SCCore; using Aitex.Core.Common; using Aitex.Core.RT.RecipeCenter; using Venus_RT.Devices; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.SubstrateTrackings; using Venus_Core; using System.Diagnostics; using MECF.Framework.Common.DBCore; using System.Threading.Tasks; using Aitex.Core.RT.Event; namespace Venus_RT.Modules.PMs { class PMProcessRoutine : PMRoutineBase, IRoutine { private enum ProcessStep { PreparePressure, PrepareTemperature, RunChuckRecipe, RunProcessRecipe, RunDechuckRecipe, RunCleanRecipe, End, } public string CurrentRunningRecipe { get; set; } public string ProcessRecipeName { get; set; } public string ChuckRecipeName { get; set; } public string DechuckRecipeName { get; set; } public string CleanRecipeName { get; set; } public RecipeHead ProcessRecipeHead { get; set; } public DateTime RecipeStartTime { get; private set; } public DateTime RecipeEndTime { get; private set; } public string WaferId { get; set; } public string SlotID { get; set; } public string LotID { get; set; } public string RecipeId { get; set; } //public bool CurrentRecipeType { get; set; } //int Timeall; private Stopwatch Recipetime = new Stopwatch(); private readonly PumpDownRoutine _pumpDownRoutine; private readonly ProcessHelper _processHelper; private bool _withWafer = true; //private bool _needPumpDown = false; public Recipe _currentRecipe = null; private int _currentStep = 0; private List<Recipe> Recipelist = new List<Recipe>(); private double _tolerance; private double _OffsetTemp = 0.0f; private bool _bLoopMode = false; private int _loopStartStep = 0; private int _loopCounter = 0; private int _recipeRunningMode = 0; private Stopwatch _stepTime = new Stopwatch(); public RecipeResult currentRecipeResult; public bool isMaualEndStep; private RecipeFACallback _faCallback; private JetChamber _jetChamber; private RecipeType _recipeType; private Recipe ProcessRecipe; private Recipe ChuckRecipe; private Recipe DechuckRecipe; private Recipe CleanRecipe; private bool needprocess; private bool needdechuck; private bool needchuck; private bool needclean; private double BaseTemperature { get { if (ProcessRecipeHead != null) { if (!string.IsNullOrEmpty(ProcessRecipeHead.BaseTemperature)) { double setpoint = Convert.ToDouble(ProcessRecipeHead.BaseTemperature); if (setpoint > 0 && setpoint < 600) return setpoint; } } return 0; } } private double BasePressure { get { if (ProcessRecipeHead != null) { if (!string.IsNullOrEmpty(ProcessRecipeHead.BasePressure)) { double setpoint = Convert.ToDouble(ProcessRecipeHead.BasePressure); if (setpoint > 0 && setpoint < 750000) return setpoint; } } return SC.GetValue<int>($"{Module}.Pump.PumpBasePressure"); } } public double EstimatedTotalLeftTime { get; private set; } public PMProcessRoutine(JetPMBase chamber, PumpDownRoutine pdRoutine) : base(chamber) { Name = "Process"; _pumpDownRoutine = pdRoutine; _processHelper = new ProcessHelper(chamber); _faCallback = new RecipeFACallback(); _jetChamber = (JetChamber)SC.GetValue<int>($"{chamber.Module}.ChamberType"); } private bool AssignFuns(Recipe recipe) { foreach (var step in recipe.Steps) { if (_processHelper.LoadStepFuns(step) == false) return false; foreach (ProcessUnitBase unit in step.LstUnit) { if (_processHelper.LoadMethods(unit) == false) { Stop($"Cannot find the process routine for unit:{unit.GetType()}"); return false; } } } return true; } public RState Start(params object[] objs) { needprocess = false; needchuck = false; needclean = false; needdechuck = false; Recipelist.Clear(); string recipeName = (string)objs[0]; if (objs.Length >= 2) { _recipeType = (RecipeType)Enum.Parse(typeof(RecipeType), objs[2].ToString()); if (_recipeType == RecipeType.Clean) { _withWafer = false; } else { _withWafer = true; } } if (objs.Length >= 4) { LotID = objs[3].ToString(); } else { LotID = ""; } if (_withWafer && WaferManager.Instance.CheckNoWafer(Module, 0)) { Stop($"can not run process, no wafer at {Module}"); EV.PostAlarmLog(Module.ToString(), $"can not run process, no wafer at {Module}"); return RState.Failed; } else if (!_withWafer && WaferManager.Instance.CheckHasWafer(Module, 0)) { Stop($"can not run clean recipe{recipeName}, a wafer at {Module}"); EV.PostAlarmLog(Module.ToString(), $"can not run clean recipe, a wafer at {Module}"); return RState.Failed; } if (!_chamber.IsRFGInterlockOn) { Stop("射频电源 Interlock条件不满足"); EV.PostAlarmLog(Module.ToString(), "射频电源 Interlock条件不满足"); return RState.Failed; } if (!CheckSlitDoor() || !CheckDryPump() || !CheckTurboPump()) { return RState.Failed; } currentRecipeResult = new RecipeResult(); currentRecipeResult.RecipeName = recipeName; string recipeContent = RecipeFileManager.Instance.LoadRecipe(_chamber.Name, recipeName, false, objs[2].ToString()); Recipe recipe = Recipe.Load(recipeContent); currentRecipeResult.RecipeStepCount = recipe.Steps.Count; if (recipe == null) { Stop($"Load Recipe:{recipeName} failed"); EV.PostAlarmLog(Module.ToString(), $"Load Recipe:{recipeName} failed"); return RState.Failed; } _recipeRunningMode = SC.GetValue<int>($"{Module}.RecipeRunningMode"); _processHelper.m_RecipeHead = recipe.Header; switch (recipe.Header.Type) { case RecipeType.Process: if (_recipeRunningMode == 1 && recipe.Header.ChuckRecipe != null && !string.IsNullOrEmpty(recipe.Header.ChuckRecipe)) { string chuckcontent = RecipeFileManager.Instance.LoadRecipe(_chamber.Name, recipe.Header.ChuckRecipe, false, RecipeType.Chuck.ToString()); ChuckRecipe = Recipe.Load(chuckcontent); if (ChuckRecipe != null) { ChuckRecipeName = recipe.Header.ChuckRecipe; needchuck = true; Recipelist.Add(ChuckRecipe); } } needprocess = true; ProcessRecipeHead = recipe.Header; ProcessRecipe = recipe; ProcessRecipeName = recipeName; Recipelist.Add(recipe); if (_recipeRunningMode == 1 && recipe.Header.DechuckRecipe != null && !string.IsNullOrEmpty(recipe.Header.DechuckRecipe)) { string dechuckcontent = RecipeFileManager.Instance.LoadRecipe(_chamber.Name, recipe.Header.DechuckRecipe, false, RecipeType.DeChuck.ToString()); DechuckRecipe = Recipe.Load(dechuckcontent); if (DechuckRecipe != null) { DechuckRecipeName = recipe.Header.DechuckRecipe; needdechuck = true; Recipelist.Add(DechuckRecipe); } } break; case RecipeType.Chuck: ChuckRecipeName = recipeName; ChuckRecipe = recipe; Recipelist.Add(ChuckRecipe); needchuck = true; break; case RecipeType.DeChuck: DechuckRecipeName = recipeName; Recipelist.Add(DechuckRecipe); DechuckRecipe = recipe; needdechuck = true; break; case RecipeType.Clean: CleanRecipe = recipe; CleanRecipeName = recipeName; Recipelist.Add(CleanRecipe); needclean = true; break; } foreach (Recipe rcp in Recipelist) { if (AssignFuns(rcp) == false) { Stop($"Associate process alogrithm with recipe:{rcp.Header.Name} failed"); EV.PostAlarmLog(Module.ToString(), $"Associate process alogrithm with recipe:{rcp.Header.Name} failed"); return RState.Failed; } } _bLoopMode = false; _loopStartStep = 0; _loopCounter = 0; _processHelper.isLoop = false; _processHelper.loopsteps = 0; _processHelper.currentStepIndex = 0; if (_jetChamber == JetChamber.Venus) { _tolerance = SC.GetValue<double>($"System.MaxTemperatureToleranceToTarget"); _OffsetTemp = SC.GetValue<double>($"{Module}.Chiller.ChillerTemperatureOffset"); } _faCallback.RecipeStart(_chamber.Module.ToString(), recipeName); WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.InProcess); return Runner.Start(Module, Name); } public RState Monitor() { Runner .Run(ProcessStep.PreparePressure, PreparePressure, IsPressureReady) .Run(ProcessStep.PrepareTemperature, PrepareTemp, IsTempReady) .RunIf(ProcessStep.RunChuckRecipe, needchuck, StartChuckRecipe, CheckRecipeDone, 5 * 60 * 60 * 1000) .RunIf(ProcessStep.RunProcessRecipe, needprocess, StartProcessRecipe, CheckRecipeDone, 5 * 60 * 60 * 1000) .RunIf(ProcessStep.RunDechuckRecipe, needdechuck, StartDechuckRecipe, CheckRecipeDone, 5 * 60 * 60 * 1000) .RunIf(ProcessStep.RunCleanRecipe, needclean, StartCleanRecipe, CheckRecipeDone, 5 * 60 * 60 * 1000) .End(ProcessStep.End, ProcessDone, _delay_1s); return Runner.Status; } private bool PreparePressure() { //2023/09/02 tps remove //if (_chamber.ChamberPressure < 5 && _chamber.ProcessPressure < BasePressure) //{ // _needPumpDown = false; // return true; //} //_needPumpDown = true; return _pumpDownRoutine.Start(BasePressure) == RState.Running; } private bool IsPressureReady() { //if (_needPumpDown == false) // return true; var status = _pumpDownRoutine.Monitor(); if (status == RState.End) { return true; } else if (status == RState.Failed || status == RState.Timeout) { Runner.Stop($"Pump down to {BasePressure} failed."); EV.PostAlarmLog(Module.ToString(), $"Pump down to {BasePressure} failed."); return true; } return false; } private bool PrepareTemp() { if (_jetChamber == JetChamber.Venus) { return SetCoolantTemp(BaseTemperature, _OffsetTemp); } else { return true; } } private bool IsTempReady() { if (_jetChamber == JetChamber.Venus) { return CheckCoolantTemp(BaseTemperature, _tolerance); } else { return true; } } public RState StartNewStep() { ProcessDataRecorder.StepStart(RecipeId, _currentRecipe.Steps[_currentStep].StepNo, $"{Module}:{_currentRecipe.Header.Name}:{_currentRecipe.Steps[_currentStep].Description}", _currentRecipe.Steps[_currentStep].Time); _stepTime.Restart(); var state = _currentRecipe.Steps[_currentStep].Start(); if (state != RState.Running) return state; //if (_bLoopMode == true) //{ //} if (_currentRecipe.Steps[_currentStep].CycleStart) { if (!_bLoopMode) { _bLoopMode = true; _loopStartStep = _currentStep; _loopCounter = _currentRecipe.Steps[_currentStep].CycleNumber - 1; currentRecipeResult.RecipeAllCounters = _currentRecipe.Steps[_currentStep].CycleNumber; currentRecipeResult.RecipeCurrentCounter = _loopCounter == 0 ? 0 : 1; _processHelper.isLoop = true; _processHelper.loopsteps = _currentRecipe.Steps[_currentStep].CycleNumber; } else { _processHelper.currentStepIndex += 1; } } return RState.Running; } private bool startRecipe(Recipe _recipe) { _currentRecipe = _recipe; if (_currentRecipe != null) { _currentStep = 0; CurrentRunningRecipe = _currentRecipe.Header.Name; RecipeId = Guid.NewGuid().ToString(); RecipeStartTime = DateTime.Now; Recipetime.Restart(); Notify($"Recipe:{CurrentRunningRecipe} start"); _chamber.EPDRecipeStart(CurrentRunningRecipe); return StartNewStep() == RState.Running; } else { return false; } } private bool StartProcessRecipe() { return startRecipe(ProcessRecipe); } private bool StartChuckRecipe() { return startRecipe(ChuckRecipe); } private bool StartDechuckRecipe() { return startRecipe(DechuckRecipe); } private bool StartCleanRecipe() { return startRecipe(CleanRecipe); } private bool CheckRecipeDone() { var step = _currentRecipe.Steps[_currentStep]; currentRecipeResult.RecipeStepCount = _currentRecipe.Steps.Count; currentRecipeResult.RecipeName = _currentRecipe.Header.Name; currentRecipeResult.RecipeType = _currentRecipe.Header.Type.ToString(); currentRecipeResult.RecipeStepNumber = step.StepNo; currentRecipeResult.RecipeStepType = step.Type.ToString(); currentRecipeResult.RecipeStepDescription = string.IsNullOrEmpty(step.Description) ? "" : step.Description; currentRecipeResult.RecipeStepSetTime = step.Time; currentRecipeResult.RecipeType = _currentRecipe.Header.Type.ToString(); currentRecipeResult.RecipeStepDuringTime = new System.TimeSpan(0, 0, System.Convert.ToInt32(_stepTime.ElapsedMilliseconds / 1000)); var result = step.Run(); if (result == RState.Failed) { RecipeDone("Fail"); UpdateWaferStatus(false); Runner.Stop($"Recipe:{CurrentRunningRecipe}, Step:{_currentStep + 1} Failed"); EV.PostAlarmLog(Module.ToString(), $"Recipe:{CurrentRunningRecipe}, Step:{_currentStep + 1} Failed"); return true; } else if (result == RState.End || isMaualEndStep == true) { if (_currentRecipe.Steps.Count > _currentStep + 1 && _currentRecipe.Steps[_currentStep + 1].Type != StepType.OverEtch) { _currentRecipe.Steps[_currentStep].End(); } if (_currentRecipe.Steps[_currentStep].CycleEnd) { if (_loopCounter > 0) { _loopCounter--; currentRecipeResult.RecipeCurrentCounter += 1; _currentStep = _loopStartStep; return StartNewStep() != RState.Running; } else { _bLoopMode = false; _loopStartStep = 0; //_processHelper.loopStep(false, 0, 0); _processHelper.isLoop = false; _processHelper.loopsteps = 0; _processHelper.currentStepIndex = 0; } } if (_currentStep < _currentRecipe.Steps.Count - 1 || isMaualEndStep == true) { result = RState.End; isMaualEndStep = false; _currentStep++; return StartNewStep() != RState.Running; } else { Notify($"Recipe:{CurrentRunningRecipe} finished"); //FaEvent.FaPostInfo(Module.ToString(), $"Recipe:{CurrentRunningRecipe} finished"); UpdateWaferStatus(); //if (_currentRecipe.Header.Type == RecipeType.Process) //{ // processRecipe = (Recipe)SerializeHelper.Instance.Clone(_currentRecipe); //} _chamber.EPDRecipeStop(); RecipeDone("Success"); return true; } } return false; } public void RecipeDone(string result) { Recipetime.Stop(); WaferInfo waferInfo = WaferManager.Instance.GetWafer(ModuleHelper.Converter(Module.ToString()), 0); if (!waferInfo.IsEmpty) { WaferId = waferInfo.InnerId.ToString(); SlotID = waferInfo.OriginSlot.ToString(); //LotID = waferInfo.ProcessJob == null || string.IsNullOrEmpty(waferInfo.ProcessJob.ControlJobName) ? "" : waferInfo.ProcessJob.ControlJobName; } RecipeEndTime = RecipeStartTime + Recipetime.Elapsed; switch (_currentRecipe.Header.Type) { case RecipeType.Clean: ProcessDataRecorder.RecordPrecess(RecipeId, RecipeStartTime, RecipeEndTime, _currentRecipe.Header.Name, result, "", _chamber.Name, "", "", _currentRecipe.Header.Type.ToString()); break; case RecipeType.Chuck: case RecipeType.DeChuck: case RecipeType.Process: ProcessDataRecorder.RecordPrecess(RecipeId, RecipeStartTime, RecipeEndTime, _currentRecipe.Header.Name, result, WaferId, _chamber.Name, LotID, SlotID, _currentRecipe.Header.Type.ToString()); break; } } private bool ProcessDone() { _currentRecipe.Steps[_currentStep].End(); RecipeFileManager.Instance.SaveAsRecipe2(Module.ToString(), _currentRecipe.Header.Type.ToString(), _currentRecipe.Header.Name, RecipeUnity.RecipeToString(_currentRecipe)); _stepTime.Stop(); WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Idle); WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Completed); CloseAllValves(500); _chamber.GeneratorSetpower(0); _chamber.GeneratorBiasSetpower(0); _chamber.GeneratorBiasPowerOn(false); _chamber.GeneratorPowerOn(false); _chamber.OpenValve(ValveType.TurboPumpPumping, true); _chamber.OpenValve(ValveType.TurboPumpPurge, true); _chamber.OpenValve(ValveType.Guage, true); _chamber.SetPVPostion(1000); if (_jetChamber == JetChamber.Venus && _chamber.IsHVOn == true) { _chamber.OnOffSetESCHV(false); } return true; } private void UpdateWaferStatus(bool bDone = true) { if (bDone == false) { WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Failed); if (_currentRecipe.Header.Type == RecipeType.Chuck) { // set wafer chucked flag even if the chuck recipe failed. WaferManager.Instance.UpdateWaferChuckStatus(Module, 0, EnumWaferChuckStatus.Chucked); } } switch (_currentRecipe.Header.Type) { case RecipeType.Process: break; case RecipeType.Chuck: WaferManager.Instance.UpdateWaferChuckStatus(Module, 0, EnumWaferChuckStatus.Chucked); break; case RecipeType.DeChuck: WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Completed); WaferManager.Instance.UpdateWaferChuckStatus(Module, 0, EnumWaferChuckStatus.Dechucked); break; } } public async void Abort() { if (_currentRecipe != null) { RecipeDone("Abort"); } _chamber.GeneratorBiasPowerOn(false); _chamber.GeneratorPowerOn(false); CloseAllValves(); _chamber.OpenValve(ValveType.TurboPumpPumping, true); _chamber.OpenValve(ValveType.TurboPumpPurge, true); if (_chamber.ChamberType == JetChamber.Venus) { await Task.Delay(3000); _chamber.OnOffSetESCHV(false); } } } }