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; using Aitex.Core.RT.Log; 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 Recipelist = new List(); //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 Fdc _fdc; private double BaseTemperature { get { if (ProcessRecipeHead != null) { if (!string.IsNullOrEmpty(ProcessRecipeHead.Temperature)) { double setpoint = Convert.ToDouble(ProcessRecipeHead.Temperature); 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) { 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); if (recipeName != recipe.Header.Name) { Stop($"Recipe内部名{recipe.Header.Name}与外部名{recipeName}不一致,请确认Recipe来源"); return RState.Failed; } if (_jetChamber != recipe.Header.ChamberType) { Stop($"不可以在{_jetChamber}腔体跑{recipe.Header.ChamberType}类型的Recipe,请确认Recipe来源"); return RState.Failed; } 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()); 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($"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.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() { //if (_chamber.ProcessPressure < BasePressure && RtInstance.ConfigType==ConfigType.Kepler2200) //{ // _needPumpDown = false; // return true; //} //_needPumpDown = true; return _pumpDownRoutine.Start(BasePressure) == RState.Running; } private bool IsPressureReady() { //if (_needPumpDown == false && RtInstance.ConfigType == ConfigType.Kepler2200) //{ // 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; //} return true; } private bool IsTempReady() { //if (_jetChamber == JetChamber.Venus) //{ // return CheckCoolantTemp(BaseTemperature, _tolerance); //} //else //{ // return true; //} 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(); LOG.Write(eEvent.INFO_PROCESS, Module, $"Recipe:{CurrentRunningRecipe} Step{_currentStep + 1} Start"); _faCallback.RecipeStepStart(Module.ToString(), CurrentRunningRecipe, _currentStep); if (state != RState.Running) { Runner.Stop($"Start Step {_currentStep + 1} failed."); 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 = (_currentStep+1); currentRecipeResult.RecipeStepType = step.Type.ToString(); currentRecipeResult.RecipeStepDescription = string.IsNullOrEmpty(step.Description) ? "" : step.Description; switch (step.Type) { case StepType.Time: currentRecipeResult.RecipeStepSetTime = step.Time; break; case StepType.OverEtch: currentRecipeResult.RecipeStepSetTime = (int)_processHelper.lastEPDStepTime / 1000; break; default: currentRecipeResult.RecipeStepSetTime = null; break; } //currentRecipeResult.RecipeStepSetTime = step.Type==StepType.OverEtch? (int)_processHelper.lastEPDStepTime/1000: step.Time; currentRecipeResult.RecipeType = _currentRecipe.Header.Type.ToString(); currentRecipeResult.RecipeStepDuringTime = (int)_stepTime.ElapsedMilliseconds / 1000; var result = step.Run(); if (result == RState.Failed) { RecipeDone("Fail"); UpdateWaferStatus(false); Runner.Stop($"Recipe:{CurrentRunningRecipe}, Step:{_currentStep + 1} Failed in {_stepTime.ElapsedMilliseconds / 1000}s"); EV.PostAlarmLog(Module.ToString(), $"Recipe:{CurrentRunningRecipe}, Step:{_currentStep + 1} Failed"); _faCallback.RecipeFailed(Module.ToString(), CurrentRunningRecipe); return true; } else if (result == RState.End || isMaualEndStep == true) { if (_currentRecipe.Steps.Count > _currentStep + 1) { _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++; LOG.Write(eEvent.INFO_PROCESS, Module, $"Recipe:{CurrentRunningRecipe} Step{_currentStep} End"); ProcessDataRecorder.StepStart(RecipeId, _currentRecipe.Steps[_currentStep].StepNo, $"{Module}:{_currentRecipe.Header.Name}:{_currentRecipe.Steps[_currentStep].Description}", _currentRecipe.Steps[_currentStep].Time); _faCallback.RecipeStepEnd(Module.ToString(), CurrentRunningRecipe, _currentStep); return StartNewStep() != RState.Running; } else { LOG.Write(eEvent.INFO_PROCESS, Module, $"Recipe:{CurrentRunningRecipe} Step{_currentStep + 1} End"); 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(); _faCallback.RecipeComplete(Module.ToString(), CurrentRunningRecipe); RecipeFileManager.Instance.SaveRecipe(Module.ToString(), _currentRecipe.Header.Type.ToString(), _currentRecipe.Header.Name, RecipeUnity.RecipeToString(_currentRecipe), false, false); _stepTime.Stop(); WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Idle); WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Completed); 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 ((_chamber.ChamberType == JetChamber.VenusSE || _chamber.ChamberType == JetChamber.VenusDE) && _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"); } WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Failed); _chamber.GeneratorBiasPowerOn(false); _chamber.GeneratorPowerOn(false); CloseAllValves(); _chamber.OpenValve(ValveType.TurboPumpPumping, true); _chamber.OpenValve(ValveType.TurboPumpPurge, true); if (_chamber.ChamberType == JetChamber.VenusSE || _chamber.ChamberType == JetChamber.VenusDE) { _chamber.SetBacksideHeThreshold(0, 0); _chamber.MagnetSetpower(0); await Task.Delay(2000); _chamber.OnOffSetESCHV(false); } } } }