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_RT.FAs; 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 DechuckRecipeNamae { get; set; } public string CleanRecipeName { get; set; } public RecipeHead ProcessRecipeHead { get; set; } public DateTime RecipeStartTime { get; private set; } 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 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) { if (_withWafer && WaferManager.Instance.CheckNoWafer(Module.ToString(), 0)) { Stop($"can not run process, no wafer at {Module}"); FaEvent.FaPostAlarm(Module.ToString(), $"can not run process, no wafer at {Module}"); return RState.Failed; } if (!_chamber.IsRFGInterlockOn) { Stop("射频电源 Interlock条件不满足"); FaEvent.FaPostAlarm(Module.ToString(), "射频电源 Interlock条件不满足"); return RState.Failed; } if (!CheckSlitDoor() || !CheckDryPump() || !CheckTurboPump()) { return RState.Failed; } RecipeStartTime = DateTime.Now; // Load/Validate Recipe _qeRecipes.Clear(); string recipeName = (string)objs[0]; currentRecipeResult = new RecipeResult(); currentRecipeResult.RecipeName = recipeName; string recipeContent = RecipeFileManager.Instance.LoadRecipe(_chamber.Name, recipeName, false); Recipe recipe = Recipe.Load(recipeContent); currentRecipeResult.RecipeStepCount=recipe.Steps.Count; if (recipe == null) { Stop($"Load Recipe:{recipeName} failed"); FaEvent.FaPostAlarm(Module.ToString(), $"Load Recipe:{recipeName} failed"); return RState.Failed; } _recipeRunningMode = SC.GetValue($"{Module}.RecipeRunningMode"); 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); 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); var dechuckRecipe = Recipe.Load(dechuckcontent); if(dechuckRecipe != null) { DechuckRecipeNamae = recipe.Header.DechuckRecipe; _qeRecipes.Enqueue(dechuckRecipe); } } break; case RecipeType.Chuck: ChuckRecipeName = recipeName; _qeRecipes.Enqueue(recipe); break; case RecipeType.DeChuck: DechuckRecipeNamae = 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"); FaEvent.FaPostAlarm(Module.ToString(), $"Associate process alogrithm with recipe:{rcp.Header.Name} failed"); return RState.Failed; } } _bLoopMode = false; _loopStartStep = 0; _loopCounter = 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((int)ProcessStep.kPreparePressure, PreparePressure, IsPressureReady) .Run((int)ProcessStep.kPrepareTemperature, PrepareTemp, IsTempReady) .Run((int)ProcessStep.kRunRecipes, StartNewRecipe, RunRecipes,5*60*60*1000) .End((int)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; 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) { ProcessDataRecorder.RecordPrecess(Guid.NewGuid().ToString(), RecipeStartTime, DateTime.Now, CurrentRunningRecipe, "Fail", "", _chamber.Name, "", ""); UpdateWaferStatus(false); Runner.Stop($"Recipe:{CurrentRunningRecipe}, Step:{_currentStep + 1} Failed"); FaEvent.FaPostAlarm(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; } } 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(); _chamber.EPDRecipeStop(); return !StartNewRecipe(); } } return false; } private bool ProcessDone() { _stepTime.Stop(); WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Idle); CloseAllValves(); _chamber.GeneratorBiasPowerOn(false); _chamber.GeneratorPowerOn(false); _chamber.OpenValve(ValveType.TurboPumpPumping, true); _chamber.OpenValve(ValveType.TurboPumpPurge, true); _chamber.OpenValve(ValveType.Guage, true); _chamber.SetPVPostion(1000); WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Completed); ProcessDataRecorder.RecordPrecess(Guid.NewGuid().ToString(), RecipeStartTime, DateTime.Now, CurrentRunningRecipe,"Success", "", _chamber.Name, "", ""); return true; } private void UpdateWaferStatus(bool bDone = true) { if(bDone == false) { WaferManager.Instance.UpdateWaferProcessStatus(ModuleName.PMA, 0, EnumWaferProcessStatus.Failed); if(_currentRecipe.Header.Type == RecipeType.Chuck) { // set wafer chucked flag even if the chuck recipe failed. WaferManager.Instance.UpdateWaferChuckStatus(ModuleName.PMA, 0, EnumWaferChuckStatus.Chucked); } } switch(_currentRecipe.Header.Type) { case RecipeType.Process: break; case RecipeType.Chuck: WaferManager.Instance.UpdateWaferChuckStatus(ModuleName.PMA, 0, EnumWaferChuckStatus.Chucked); break; case RecipeType.DeChuck: WaferManager.Instance.UpdateWaferProcessStatus(ModuleName.PMA, 0, EnumWaferProcessStatus.Completed); WaferManager.Instance.UpdateWaferChuckStatus(ModuleName.PMA, 0, EnumWaferChuckStatus.Dechucked); break; } } public void Abort() { ProcessDataRecorder.RecordPrecess(Guid.NewGuid().ToString(), RecipeStartTime, DateTime.Now, CurrentRunningRecipe, "Fail", "", _chamber.Name, "", ""); _chamber.GeneratorBiasPowerOn(false); _chamber.GeneratorPowerOn(false); _chamber.TurnPendulumValve(false); CloseAllValves(); _chamber.OpenValve(ValveType.TurboPumpPumping, true); _chamber.OpenValve(ValveType.TurboPumpPurge, true); } } }