using Aitex.Core.RT.Device; using Aitex.Core.RT.Event; using Aitex.Core.RT.Routine; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using MECF.Framework.Common.DBCore; using System; using System.Collections.Generic; using VirgoRT.Devices; using VirgoRT.Devices.IODevices; namespace VirgoRT.Modules.PMs { public enum VerifyMode { OnePoint, TenPoint, } class PMMfcVerificationRoutine : PMRoutineBase, IRoutine { enum RoutineStep { CheckNeedPumpDown1, CheckNeedPumpDown2, RunPumpRoutine1, RunPumpRoutine2, RunPumpRoutine3, CheckThrottleValveStatus, ClosePumpValve, GetBeginPressure, SetGasFlow, CalcMfcCalibration, Delay1, Delay2, StopGasFlow, CheckFinished, Loop, EndLoop, CheckFlowStable, } private readonly PumpDownRoutine _pumpdownRoutine; private int _paramContinuePumpTime; private double _beginPressure; private double _endPressure; private double _elapsedTime; private DeviceTimer _verificationDeviceTimer = new DeviceTimer(); private int _mfcIndex; private float _mfcFlow; private double _flowTime; private double _mfcActualFlow; private double _getBeginPressureDelayTime; private double _maxPressure; private MfcBase1 _mfcDevice; private VerifyMode _paramMode; private Dictionary _paramFlowSet = new Dictionary(); private Dictionary> _calibrationResult = new Dictionary>(); private bool _isPumpDownNeed; private float _pressureStableTolerance = 2;//2mTorr private float _flowStableTolerance = 0.02f;//2% private int _stableTime = 1;//1s private double _chamberVolume; private double _gasTemperature; private double _leakRate; private double _maxDeviation; public PMMfcVerificationRoutine(JetPM chamber, PumpDownRoutine pumpDownRoutine) : base(chamber) { Name = "MFC Verification"; bUINotify = true; _pumpdownRoutine = pumpDownRoutine; } internal void Init(string mfc, double flow, int flowCount) { int.TryParse(mfc.Replace("MFC", ""), out _mfcIndex); _mfcDevice = DEVICE.GetDevice($"{Module}.MfcGas{_mfcIndex}"); _mfcIndex -= 1;//start from 0 _mfcFlow = (float)flow; if (flowCount == 10) _paramMode = VerifyMode.TenPoint; else _paramMode = VerifyMode.OnePoint; } public Result Start(params object[] objs) { Reset(); if (CheckLid() != Result.RUN) { return Result.FAIL; } if (CheckSlitDoor() != Result.RUN) { return Result.FAIL; } if (CheckDryPump() != Result.RUN) { return Result.FAIL; } _calibrationResult.Clear(); _paramFlowSet.Clear(); _chamberVolume = SC.GetValue($"{Module}.MFCVerification.ChamberVolume"); _gasTemperature = SC.GetValue($"{Module}.MFCVerification.GasTemperature"); _flowTime = SC.GetValue($"{Module}.MFCVerification.GasFlowTime"); _maxDeviation = SC.GetValue($"{Module}.MFCVerification.MaxDeviation"); _paramContinuePumpTime = 20;//20s _getBeginPressureDelayTime = 2;//2s _pressureStableTolerance = (float)SC.GetValue($"{Module}.MFCVerification.PressureStableTolerance"); _flowStableTolerance = (float) (SC.GetValue($"{Module}.MFCVerification.FlowStableTolerance") / 100.0); _stableTime = 1;//1s _maxPressure = SC.GetValue($"{Module}.MFCVerification.TargetPressure"); if (_paramMode == VerifyMode.TenPoint) { for (int i = 0; i < 10; i++) { _paramFlowSet.Add(i, (float)_mfcDevice.Scale * (i + 1) / 10); } } else { if (_mfcFlow <= 0 || _mfcFlow > _mfcDevice.Scale) { EV.PostWarningLog(Module, $"MFC set value {_mfcFlow} not valid"); return Result.FAIL; } _paramFlowSet.Add(0, _mfcFlow); } _mfcDevice.ResetVerificationData(); _isPumpDownNeed = true; _leakRate = 0; var dbData = DataQuery.Query($"SELECT * FROM \"leak_check_data\" where \"module_name\" = '{Module}' order by \"operate_time\" DESC;"); if (dbData != null && dbData.Rows.Count > 0 && !dbData.Rows[0]["leak_rate"].Equals(DBNull.Value)) { _leakRate = Convert.ToDouble(dbData.Rows[0]["leak_rate"]); } return Result.RUN; } public Result Monitor() { try { if (_isPumpDownNeed) { ExecuteRoutine((int)RoutineStep.RunPumpRoutine1, _pumpdownRoutine); } CheckThrottleValveFullOpen((int)RoutineStep.CheckThrottleValveStatus); StartLoop((int)RoutineStep.Loop, "", _paramFlowSet.Count, Notify, Stop); //抽到底压 CheckNeedPumpDown((int)RoutineStep.CheckNeedPumpDown1); if (_isPumpDownNeed) { ExecuteRoutine((int)RoutineStep.RunPumpRoutine2, _pumpdownRoutine); } //流气 SetGasFlow((int)RoutineStep.SetGasFlow, _mfcIndex, _paramMode == VerifyMode.TenPoint ? _paramFlowSet[LoopCounter] : _mfcFlow); //等待,稳定一下 Delay((int)RoutineStep.Delay1, _paramContinuePumpTime); //检查Stable CheckFlowStable((int)RoutineStep.CheckFlowStable, _mfcDevice, _flowStableTolerance, _pressureStableTolerance, _stableTime); //关闭抽气阀 SetValve((int)RoutineStep.ClosePumpValve, ValveType.FAST_PUMP, false); //稳压 Delay((int)RoutineStep.Delay2, _getBeginPressureDelayTime); //得到初始压力值 GetBeginPressure((int)RoutineStep.GetBeginPressure); //等待,流气时间或者压力到设定值 CheckFinished((int)RoutineStep.CheckFinished, _flowTime, _maxPressure); //计算Flow CalcMfcCalibration((int)RoutineStep.CalcMfcCalibration, _paramMode == VerifyMode.TenPoint ? _paramFlowSet[LoopCounter] : _mfcFlow); StopGasFlow((int)RoutineStep.StopGasFlow, _mfcIndex); EndLoop((int)RoutineStep.EndLoop, Notify, Stop); CheckNeedPumpDown((int)RoutineStep.CheckNeedPumpDown2); if (_isPumpDownNeed) { ExecuteRoutine((int)RoutineStep.RunPumpRoutine3, _pumpdownRoutine); } } catch (RoutineBreakException) { return Result.RUN; } catch (RoutineFaildException) { _verificationDeviceTimer.Stop(); _chamber.StopAllGases(); _chamber.SetValveOnOff(ValveType.PROCESS, false); _mfcDevice.ResetVerificationData(); return Result.FAIL; } return Result.DONE; } public new void Abort() { _verificationDeviceTimer.Stop(); _chamber.StopAllGases(); _chamber.SetValveOnOff(ValveType.PROCESS, false); _mfcDevice.ResetVerificationData(); } private void CheckNeedPumpDown(int id) { string reason = string.Empty; Tuple ret = Execute(id, () => { Notify($"Check {Module} need pump down"); _isPumpDownNeed = _chamber.ChamberPressure > SC.GetValue($"{Module}.Pump.PumpBasePressure"); return true; }); if (ret.Item1) { if (ret.Item2 == Result.FAIL) { throw (new RoutineFaildException()); } else throw (new RoutineBreakException()); } } private void SetGasFlow(int id, int mfcId, double flow) { string reason = string.Empty; Tuple ret = Execute(id, () => { Notify($"Set gas {mfcId} flow to {flow} sccm"); _chamber.SetValveOnOff(ValveType.PROCESS, true); if (!_chamber.FlowGas(mfcId, flow)) { return false; } return true; }); if (ret.Item1) { if (ret.Item2 == Result.FAIL) { Stop($"Set gas {mfcId} flow to {flow} sccm failed, for {reason}"); throw (new RoutineFaildException()); } else throw (new RoutineBreakException()); } } private void StopGasFlow(int id, int mfcId) { string reason = string.Empty; Tuple ret = Execute(id, () => { Notify($"Stop gas {mfcId} flow"); _chamber.SetValveOnOff(ValveType.PROCESS, false); if (!_chamber.FlowGas(mfcId, 0)) { return false; } return true; }); if (ret.Item1) { if (ret.Item2 == Result.FAIL) { Stop($"Stop gas {mfcId} flow failed, for {reason}"); throw (new RoutineFaildException()); } else throw (new RoutineBreakException()); } } private void CalcMfcCalibration(int id, float flow) { Tuple ret = Execute(id, () => { _mfcActualFlow = 273.15 * _chamberVolume / ((273.15 + _gasTemperature) * 760000) * ((_endPressure - _beginPressure) / _elapsedTime - _leakRate); EV.PostInfoLog(Module, $"Calculate flow: calculate flow={_mfcActualFlow}, setpoint={flow}, begin pressure(torr)={_beginPressure:f3}, end pressure(torr)={_endPressure:f3}," + $"elapsed time(minute)={_elapsedTime:f3}"); double deviation = (Math.Abs(_mfcActualFlow) - Math.Abs(flow)) / Math.Abs(flow) * 100; bool isOk = Math.Abs(deviation) <= Math.Abs(_maxDeviation); if (!isOk) { EV.PostWarningLog(Module, $"MFC {_mfcDevice.DisplayName} verify failed, deviation is {deviation}%, exceed max tolerance {_maxDeviation}%"); } if (_paramMode == VerifyMode.TenPoint) { _calibrationResult[flow] = Tuple.Create((float)_mfcActualFlow, (float)_elapsedTime); _mfcDevice.SetVerificationResult((float)flow, (float)_mfcActualFlow, _calibrationResult.Count == 10, _elapsedTime*60, deviation, isOk); } else { _mfcDevice.SetVerificationResult((float)flow, (float)_mfcActualFlow, true, _elapsedTime*60, deviation, isOk); } return true; }); } private void GetBeginPressure(int id) { Tuple ret = Execute(id, () => { Notify($"Get begin pressure {_chamber.ChamberPressure.ToString("f1")}"); _beginPressure = _chamber.ChamberPressure; _verificationDeviceTimer.Start(0); return true; }); } private void CheckFlowStable(int id, MfcBase1 mfc, float flowStable, float pressureStable, int time) { Tuple ret = ExecuteAndWait(id, () => { Notify($"Check {mfc.Name} flow stable"); _verificationDeviceTimer.Start(0); _beginPressure = _chamber.ChamberPressure; return true; }, () => { if (_verificationDeviceTimer.GetElapseTime() > time * 1000 && Math.Abs(_chamber.ChamberPressure - _beginPressure) <= pressureStable && Math.Abs(mfc.SetPoint - mfc.FeedBack) / mfc.SetPoint < flowStable) { return true; } return false; }, time * 2 * 1000); if (ret.Item1) { if (ret.Item2 == Result.FAIL) { Stop($"Check {mfc.Name} flow {mfc.SetPoint} stable failed."); throw (new RoutineFaildException()); } else if (ret.Item2 == Result.TIMEOUT) //timeout { Stop($"Check {mfc.Name} flow stable timeout in {time * 2} seconds"); throw (new RoutineFaildException()); } else throw (new RoutineBreakException()); } } private void CheckFinished(int id, double flowTime, double maxPressure) { Tuple ret = ExecuteAndWait(id, () => { Notify($"Check finished one point"); return true; }, () => { if (_verificationDeviceTimer.GetElapseTime() > flowTime * 1000 || _chamber.ChamberPressure / 1000 > maxPressure) { _endPressure = _chamber.ChamberPressure;//mTorr _elapsedTime = _verificationDeviceTimer.GetElapseTime() / (1000 * 60);//unit minutes return true; } return false; }, flowTime * 2 * 1000); if (ret.Item1) { if (ret.Item2 == Result.FAIL) { throw (new RoutineFaildException()); } else if (ret.Item2 == Result.TIMEOUT) //timeout { Stop($"Check finished one point can not finished in {flowTime * 2} seconds"); throw (new RoutineFaildException()); } else throw (new RoutineBreakException()); } } } }