using System; using System.Collections.Generic; using Aitex.Core.RT.Event; using Aitex.Core.RT.Log; using Aitex.Core.RT.RecipeCenter; using Aitex.Core.RT.Routine; using Aitex.Core.RT.SCCore; using MECF.Framework.Common.DBCore; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.SubstrateTrackings; using VirgoRT.Devices; using VirgoCommon; namespace VirgoRT.Modules.PMs { class PreProcessRoutine : PMRoutineBase { public enum Routine { SetSlitDoor, DelayForTest, CheckDoor, CheckDryPump, PrePumpDown, PostPumpDown, PumpDown, SetRfMatchMode, SetElectrodeTemp, SetHeaterTemp, SetLiftPin, PinDownAndPump, End, } //---------------------------------Fields---------------------------------------- // private readonly PumpDownRoutine _pumpRoutine; //private readonly TemperatureControlRoutine _TempRoutine; private int _checkSubstrateTempTimeout = 60; private double _tolerance; private double _OffsetTemp = 0.0f; private bool _bPinDownExecuted = false; private readonly int _PumpingAndPinDownTimeout = 60 * 1000; //---------------------------------Properties------------------------------------ // public string CurrentRecipeBaseName { get; private set; } public string CurrentRecipeRunningName { get; private set; } public string CurrentRecipeContent { get; private set; } public string CurrentLotName { get; set; } public List CurrentRecipeStepList { get; set; } public RecipeHead CurrentRecipeHead { get; set; } public string CurrentRecipeGuid { get; set; } private bool _withWafer; private double BasePressure { get { if (CurrentRecipeHead != null) { if (!string.IsNullOrEmpty(CurrentRecipeHead.BasePressure)) { double setpoint = Convert.ToDouble(CurrentRecipeHead.BasePressure); if (setpoint > 0 && setpoint < 750000) return setpoint; } } return SC.GetValue($"{Module}.Pump.PumpBasePressure"); } } private double PumpDownLimit { get { if (CurrentRecipeHead != null) { if (!string.IsNullOrEmpty(CurrentRecipeHead.PumpDownLimit)) { double setpoint = Convert.ToDouble(CurrentRecipeHead.PumpDownLimit); if (setpoint > 0 && setpoint < 600) return setpoint; } } return SC.GetValue($"{Module}.Pump.PumpTimeLimit"); } } public bool PumpingPinIsUp { get { if (CurrentRecipeHead != null) { if (!string.IsNullOrEmpty(CurrentRecipeHead.PumpingPinState)) { return CurrentRecipeHead.PumpingPinState == "Up" ? true : false; } } return false; } } public double SubstrateTemp { get { if (CurrentRecipeHead != null) { if (!string.IsNullOrEmpty(CurrentRecipeHead.SubstrateTemp)) { double setpoint = Convert.ToDouble(CurrentRecipeHead.SubstrateTemp); if (setpoint > 0 && setpoint < 600) return setpoint; } } return 0; } } private int PinDownPressure { get { if (CurrentRecipeHead != null) { if (!string.IsNullOrEmpty(CurrentRecipeHead.PinDownPressure)) { int setpoint = Convert.ToInt32(CurrentRecipeHead.PinDownPressure); if (setpoint > 0) return setpoint; } } return 1000; } } private string alarmRecipeFileInvalid = "RecipeFileInvalid"; private int _timeout; public PreProcessRoutine(JetPM chamber, PumpDownRoutine pdr) : base(chamber) { Name = "Pre Process"; _pumpRoutine = pdr; EV.Subscribe(new EventItem("Event", alarmRecipeFileInvalid, "Recipe File Invalid", EventLevel.Alarm, EventType.HostNotification)); } public void Terminate() { } public Result LoadRecipe(params object[] param) { CurrentRecipeBaseName = (string)param[0]; if (param.Length > 1) WaferID = (string)param[1]; else WaferID = null; if (param.Length > 2) _withWafer = (bool)param[2]; else _withWafer = true; CurrentRecipeContent = RecipeFileManager.Instance.LoadRecipe("", CurrentRecipeBaseName, true); if (string.IsNullOrEmpty(CurrentRecipeContent)) { EV.PostInfoLog(ModuleName.System.ToString(), $"error during read recipe file {CurrentRecipeBaseName}"); return Result.FAIL; } if (!RecipeFileManager.Instance.CheckRecipe(Module, CurrentRecipeContent, out var reasons)) { LOG.Write($"recipe file content not valid {CurrentRecipeBaseName}"); EV.Notify(alarmRecipeFileInvalid); string info = ""; foreach (var item in reasons) info += item; EV.PostPopDialogMessage(EventLevel.Alarm, "recipe file not valid", info); } CurrentRecipeRunningName = $"{CurrentRecipeBaseName}"; if (!Recipe.Parse(Module, CurrentRecipeContent, out RecipeHead recipeHead, out var recipeSteps)) { return Result.FAIL; } CurrentRecipeStepList = recipeSteps; CurrentRecipeHead = recipeHead; return Result.DONE; } public Result Start(params object[] param) { if (_withWafer && WaferManager.Instance.CheckNoWafer(Module.ToString(), 0)) { EV.PostWarningLog(Module.ToString(), $"can not run process, no wafer at {Module}"); return Result.FAIL; } if ( CheckDryPump() != Result.RUN) { return Result.FAIL; } _chamber.SetGeneratorCommunicationMode(1); // RS232 mode _tolerance = SC.GetValue($"System.MaxTemperatureToleranceToTarget"); _OffsetTemp = SC.GetValue($"{Module}.Chiller.ChillerTemperatureOffset"); try { _timeout = SC.GetValue($"{Module}.PrepareTransferTimeout"); if (BasePressure <= 0 || BasePressure >= 760000) { return Result.FAIL; } if (PumpDownLimit <= 0.01 || PumpDownLimit >= 600) { return Result.FAIL; } _checkSubstrateTempTimeout = SC.GetValue($"{Module}.CheckSubstrateTempTimeout"); Reset(); CurrentRecipeGuid = Guid.NewGuid().ToString(); //ProcessDataRecorder.Start(RecipeRunGuid.ToString(), CurrentRecipeRunningName.Split('\\')[1], WaferID, CurrentRecipeRunningName.Split('\\')[0]); //RecipeFileManager.Instance.SaveRecipeHistory(ModuleName.System.ToString(), CurrentRecipeRunningName, CurrentRecipeContent); } catch (Exception ex) { LOG.Write(ex, "Preprocess Start has exception"); throw ex; } Notify("开始"); _bPinDownExecuted = false; return Result.RUN; } public Result Monitor() { try { CloseSlitDoor((int)Routine.SetSlitDoor, _timeout); ParallellyPumpAndPinDown((int)Routine.PinDownAndPump); if (SC.GetValue($"{Module}.Chiller.EnableChiller")) { CheckCoolantTemp((int)Routine.CheckDoor, SubstrateTemp, _OffsetTemp, _checkSubstrateTempTimeout, _tolerance); } else { CheckSubstrateTemp((int)Routine.CheckDoor, SubstrateTemp, _checkSubstrateTempTimeout, _tolerance); } //设置RF Match Mode //SetRfMatchMode((int)Routine.SetRfMatchMode, string.Format(Resources.PreProcess_Monitor_SetRFMatchMode, _rfMatchModeDuringProcess==(int)VirgoRfMatchMode.Manual ? "Manual" : "Auto"), _rfMatchModeDuringProcess); End((int)Routine.End); } catch (RoutineBreakException) { return Result.RUN; } catch (RoutineFaildException) { return Result.FAIL; } catch (Exception ex) { Stop(ex.Message); return Result.FAIL; } Notify("结束"); return Result.DONE; } public void Exit() { } protected void CloseSlitDoor(int id, int timeout) { Tuple ret = ExecuteAndWait(id, () => { Notify($"设置传送门 {_chamber.Name} " + (false ? "开" : "关")); if (!_chamber.CheckSlitDoorClose()) { _chamber.SetSlitDoor(false, out _); } return true; }, () => _chamber.CheckSlitDoorClose(), timeout * 1000); if (ret.Item1) { if (ret.Item2 == Result.FAIL) { throw new RoutineFaildException(); } else if (ret.Item2 == Result.TIMEOUT) //timeout { Stop($"无法关传送门 in {timeout} seconds"); throw new RoutineFaildException(); } else throw new RoutineBreakException(); } } private void ParallellyPumpAndPinDown(int id) { bool Func() { _pumpRoutine.Start(BasePressure); return true; } bool? Check1() { if (!_bPinDownExecuted) { if ((PumpingPinIsUp == false) && ((_chamber.ChamberPressure <= PinDownPressure) || (PinDownPressure <= BasePressure))) { _bPinDownExecuted = true; Notify($"设置 lift pin {_chamber.Name} " + "降"); if (!_chamber.SetLiftPin(MovementPosition.Down, out string reason)) { Stop(reason); return false; } } } bool res1 = _chamber.CheckLiftDown(); var result = _pumpRoutine.Monitor(); return (res1 || PumpingPinIsUp) && (result == Result.DONE); } Tuple ret = ExecuteAndWait(id, Func, Check1, _PumpingAndPinDownTimeout); if (ret.Item1) { if (ret.Item2 == Result.FAIL) { throw new RoutineFaildException(); } if (ret.Item2 == Result.TIMEOUT) { Stop(!_chamber.CheckLiftDown() ? $"Set Pin Down timeout in {_PumpingAndPinDownTimeout / 1000} seconds" : $"Pumping timeout in {_PumpingAndPinDownTimeout / 1000} seconds"); throw new RoutineFaildException(); } throw new RoutineBreakException(); } } } }