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 Venus_Unity; using System.Threading.Tasks; using Aitex.Core.RT.Event; namespace Venus_RT.Modules.PMs { class PMProcessRoutine : PMRoutineBase, IRoutine { private enum ProcessStep { kPreparePressure, kPrepareTemperature, kRunRecipes, kEnd, } 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 FullRecipeName { get; set; } string Guidall; //int Timeall; private Stopwatch Processtime = 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 Queue _qeRecipes = new Queue(); 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 double ChillerTemp { get { if (ProcessRecipeHead != null) { if (!string.IsNullOrEmpty(ProcessRecipeHead.ChillerTemp)) { double setpoint = Convert.ToDouble(ProcessRecipeHead.ChillerTemp); 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($"{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($"{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) { 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 >= 2) //{ // _withWafer = (bool)objs[2]; //} 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; } RecipeStartTime = DateTime.Now; // Load/Validate Recipe _qeRecipes.Clear(); 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($"{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()); var chuckRecipe = Recipe.Load(chuckcontent); if (chuckRecipe != null) { ChuckRecipeName = recipe.Header.ChuckRecipe; _qeRecipes.Enqueue(chuckRecipe); } } ProcessRecipeHead = recipe.Header; ProcessRecipeName = recipeName; _qeRecipes.Enqueue(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()); var dechuckRecipe = Recipe.Load(dechuckcontent); if (dechuckRecipe != null) { DechuckRecipeName = recipe.Header.DechuckRecipe; _qeRecipes.Enqueue(dechuckRecipe); } } break; case RecipeType.Chuck: ChuckRecipeName = recipeName; _qeRecipes.Enqueue(recipe); break; case RecipeType.DeChuck: DechuckRecipeName = recipeName; _qeRecipes.Enqueue(recipe); break; case RecipeType.Clean: CleanRecipeName = recipeName; _qeRecipes.Enqueue(recipe); break; } foreach (Recipe rcp in _qeRecipes) { 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; _tolerance = SC.GetValue($"System.MaxTemperatureToleranceToTarget"); _OffsetTemp = SC.GetValue($"{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.kPreparePressure, PreparePressure, IsPressureReady) .Run(ProcessStep.kPrepareTemperature, PrepareTemp, IsTempReady) .Run(ProcessStep.kRunRecipes, StartNewRecipe, RunRecipes, 5 * 60 * 60 * 1000) .End(ProcessStep.kEnd, ProcessDone, _delay_1s); return Runner.Status; } private bool PreparePressure() { //2023/09/02 tps remove //if(_chamber.ProcessHighPressure < 5 && _chamber.ProcessLowPressure 0) { _currentStep = 0; _currentRecipe = _qeRecipes.Dequeue(); CurrentRunningRecipe = _currentRecipe.Header.Name; if (Guidall == null) { Guidall = Guid.NewGuid().ToString(); RecipeStartTime = DateTime.Now; Processtime.Restart(); } Notify($"Recipe:{CurrentRunningRecipe} start"); //FaEvent.FaPostInfo(Module.ToString(), $"Recipe:{CurrentRunningRecipe} start"); _chamber.EPDRecipeStart(CurrentRunningRecipe); return StartNewStep() == RState.Running; } return false; } private bool RunRecipes() { 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 = (int)_stepTime.ElapsedMilliseconds/1000; currentRecipeResult.RecipeStepDuringTime = new System.TimeSpan(0, 0, System.Convert.ToInt32(_stepTime.ElapsedMilliseconds / 1000)); var result = step.Run(); if (result == RState.Failed) { WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Completed); 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; FullRecipeName = string.Format(@"{0} {1} {2}", ChuckRecipeName, ProcessRecipeName, DechuckRecipeName); } Processtime.Stop(); RecipeEndTime = RecipeStartTime + Processtime.Elapsed; ProcessDataRecorder.RecordPrecess(Guidall, RecipeStartTime, DateTime.Now, FullRecipeName, "Fail", WaferId, _chamber.Name, LotID, SlotID); Guidall = null; 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(); return !StartNewRecipe(); } } return false; } 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); CloseAllValves(); _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); } WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Completed); 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; FullRecipeName = string.Format(@"{0} {1} {2}", ChuckRecipeName, ProcessRecipeName, DechuckRecipeName); } Processtime.Stop(); RecipeEndTime = RecipeStartTime + Processtime.Elapsed; ProcessDataRecorder.RecordPrecess(Guidall, RecipeStartTime, RecipeEndTime, FullRecipeName, "Success", WaferId, _chamber.Name, LotID, SlotID); Guidall = null; 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() { 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; FullRecipeName = string.Format(@"{0} {1} {2}", ChuckRecipeName, ProcessRecipeName, DechuckRecipeName); } Processtime.Stop(); RecipeEndTime = RecipeStartTime + Processtime.Elapsed; //ProcessDataRecorder.RecordPrecess(Guidall, RecipeStartTime, RecipeEndTime, FullRecipeName, "Fail", WaferId, _chamber.Name, LotID, SlotID); if (Guidall != null) { ProcessDataRecorder.RecordPrecess(Guidall, RecipeStartTime, RecipeEndTime, FullRecipeName, "Abort", WaferId, _chamber.Name, LotID, SlotID); } Guidall = null; _chamber.GeneratorBiasPowerOn(false); _chamber.GeneratorPowerOn(false); _chamber.TurnPendulumValve(false); CloseAllValves(); _chamber.OpenValve(ValveType.TurboPumpPumping, true); _chamber.OpenValve(ValveType.TurboPumpPurge, true); if (_chamber.ChamberType == JetChamber.Venus) { await Task.Delay(3000); _chamber.OnOffSetESCHV(false); } } } }