using Aitex.Core.RT.Device; using Aitex.Core.RT.Log; using Aitex.Core.RT.RecipeCenter; using Aitex.Core.RT.Routine; using Aitex.Core.RT.SCCore; using MECF.Framework.Common.DataCenter; using MECF.Framework.Common.RecipeCenter; using MECF.Framework.Common.Routine; using MECF.Framework.Common.ToolLayout; using MECF.Framework.Common.Utilities; using CyberX8_Core; using CyberX8_RT.Devices.Resistivity; using CyberX8_RT.Devices.Rinse; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Documents; namespace CyberX8_RT.Modules.Rinse { public class RinseSecondStepRoutine:RoutineBase,IRoutine { private enum RinseSecondStep { PrepareStart, Recipe_StartSecondRinseCycle, Recipe_PerformSecondRinseCycle, Recipe_SecondRinseCycleStartFilling, Recipe_SecondRinseCycleRecordStartWaterLevel, Recipe_SecondRinseCycleStartFillingWait, Recipe_SecondRinseCycleWaitFillingDetected, Recipe_SecondRinseCycleWaitFull, Recipe_SecondRinseCycleWaitEndFillTime, Recipe_SecondRinseCycleWaitDwell, Recipe_SecondRinseCycleWaitDrain, Recipe_SecondRinseCycleCheckDrain, Recipe_SecondRinseCycleWaitDrainExtraTime, Recipe_SecondRinseCycleWaitResistivityStart, Recipe_SecondRinseCycleResistivityAveraging, Recipe_SecondRinseCycleCheckResistivity, Recipe_SecondRinseCycleWaitDrain2, Recipe_SecondRinseCycleCheckDrain2, Recipe_SecondRinseCycleWaitDrain2ExtraTime, Recipe_SecondRinseCycleEnd, Recipe_SecondRinseCycleComplete } #region 内部变量 /// /// 设备对象 /// private RinseDevice _device; /// /// Recipe /// private QdrRecipe _recipe; /// /// Fill打开注水记录Water Level /// private double _fillingDetectStartWaterLevel; /// /// Concurrent Fill Time Seconds /// private int _concurrentFillTimeSeconds; /// /// 开始注水的偏差 /// private double _fillingStartedDelta; /// /// 注满数值 /// private int _sensorReadingFull; /// /// 液位为空数值 /// private int _sensorReadingEmpty; /// /// 开始流水时间 /// private DateTime _startFillTime; /// /// 从开始排水到检测是否排空的间隔时间 /// private int _normalDrainTimeSeconds; /// /// 表示当前正在执行第几次rinse /// private int _rinseCycleTwo_RinsesCompleted = 0; /// /// Resistivity取值样本列表 /// private List _resistivitySample = new List(); /// /// 开始注水后检测是否正常注水间隔 /// private int _checkIsFillingTimeSeconds; /// /// 开始注水后检测是否注满间隔 /// private int _checkIsFullTimeSeconds; /// /// 采样水阻值次数 /// private int _readResistivityCount = 0; /// /// 当前执行到哪一步的显示 /// private string _currentStateMachine; /// /// 开始采样时间 /// private DateTime _startSampleTime = DateTime.Now; /// /// 采样时间 /// private DateTime _sampeIntervalTime = DateTime.Now; /// /// 启动采样 /// private bool _startSample = false; #endregion /// /// 构造函数 /// /// public RinseSecondStepRoutine(string module) : base(module) { } /// /// 启动 /// /// /// public RState Start(params object[] objects) { _startSample = false; _device = DEVICE.GetDevice(Module); _resistivitySample.Clear(); _readResistivityCount = 0; _recipe = objects[0] as QdrRecipe; _concurrentFillTimeSeconds = SC.GetValue("QDR.ConcurrentFillTimeSeconds"); _fillingStartedDelta = SC.GetValue("QDR.FillingStartedDelta"); _sensorReadingFull = SC.GetValue("QDR.SensorReadingFull"); _sensorReadingEmpty = SC.GetValue("QDR.SensorReadingEmpty"); _normalDrainTimeSeconds = SC.GetValue("QDR.NominalDrainTimeSeconds"); _checkIsFillingTimeSeconds = SC.GetValue("QDR.NominalCheckFillWaterTimeSeconds"); _checkIsFullTimeSeconds = SC.GetValue("QDR.NominalCheckFillFullTimeSeconds"); //配置项规范性验证 if (_checkIsFillingTimeSeconds + _checkIsFullTimeSeconds > _concurrentFillTimeSeconds) { NotifyError(eEvent.ERR_RINSE, $"configuration item 'QDR.NominalCheckFillWaterTimeSeconds' plus 'QDR.NominalCheckFillFullTimeSeconds' is large than QDR.ConcurrentFillTimeSeconds", 0); return RState.Failed; } if (_normalDrainTimeSeconds > _recipe.DumpTimeSeconds) { NotifyError(eEvent.ERR_RINSE, $"configuration item 'QDR.NominalDrainTimeSeconds' is large than 'recipe.DumpTimeSeconds",0); return RState.Failed; } return Runner.Start(Module, "Start Rinse Second Step"); } /// /// 监控 /// /// public RState Monitor() { _currentStateMachine = Runner.CurrentStep.ToString(); _device.UpdateStateMachine(_currentStateMachine); Runner.RunIf(RinseSecondStep.PrepareStart, _recipe.Step2N2BubbleOn, N2ValveOff, _delay_1ms) //2.6-StartSecondRinseCycle .LoopStart(RinseSecondStep.Recipe_StartSecondRinseCycle, "Rinse Second step", _recipe.Step2NumberOfRinse, NullFun, _delay_1ms) //2.7-PerformSecondRinseCycle .LoopRun(RinseSecondStep.Recipe_PerformSecondRinseCycle, OpenN2ValveAndCloseDumpValve, _delay_1ms) //2.8-SecondRinseCycleStartFilling .LoopRun(RinseSecondStep.Recipe_SecondRinseCycleStartFilling, () => _device.FillValveOn(), _delay_1ms) .LoopRun(RinseSecondStep.Recipe_SecondRinseCycleRecordStartWaterLevel, CheckFillValveOn, _delay_1ms) .LoopRunOnlyTimeOutFault(RinseSecondStep.Recipe_SecondRinseCycleStartFillingWait, CheckFillNormalStatus, _checkIsFillingTimeSeconds * 1000) //2.9-SecondRinseCycleWaitFillingDetected //3.0-SecondRinseCycleWaitFull .LoopRunOnlyTimeOutFault(RinseSecondStep.Recipe_SecondRinseCycleWaitFillingDetected, CheckFillFullStatus, _checkIsFullTimeSeconds * 1000) //.LoopRun(RinseSecondStep.Recipe_SecondRinseCycleWaitFull, CheckFillFullStatus, _delay_1s) //3.1-SecondRinseCycleWaitEndFillTime .LoopDelay(RinseSecondStep.Recipe_SecondRinseCycleWaitEndFillTime, (_concurrentFillTimeSeconds - _checkIsFillingTimeSeconds - _checkIsFullTimeSeconds) * 1000) //3.2-SecondRinseCycleWaitDwell .LoopRunIf(RinseSecondStep.Recipe_SecondRinseCycleWaitDwell, ((_rinseCycleTwo_RinsesCompleted < _recipe.Step2NumberOfRinse - 1) && !_recipe.FinalRinseDry) || ((_rinseCycleTwo_RinsesCompleted < _recipe.Step2NumberOfRinse) && _recipe.FinalRinseDry), StartDwell, _recipe.Step2DwellTimeSeconds * 1000) //3.3-SecondRinseCycleWaitDrain .LoopRunIf(RinseSecondStep.Recipe_SecondRinseCycleWaitDrain, ((_rinseCycleTwo_RinsesCompleted < _recipe.Step2NumberOfRinse - 1) && !_recipe.FinalRinseDry) || ((_rinseCycleTwo_RinsesCompleted < _recipe.Step2NumberOfRinse) && _recipe.FinalRinseDry), StartOpenDumpValve, _delay_1ms) .LoopRunIfOnlyTimeOutFault(RinseSecondStep.Recipe_SecondRinseCycleCheckDrain, ((_rinseCycleTwo_RinsesCompleted < _recipe.Step2NumberOfRinse - 1) && !_recipe.FinalRinseDry) || ((_rinseCycleTwo_RinsesCompleted < _recipe.Step2NumberOfRinse) && _recipe.FinalRinseDry), CheckWaterLevelEmpty, _normalDrainTimeSeconds * 1000) //3.4-SecondRinseCycleWaitDrainExtraTime .LoopRunIf(RinseSecondStep.Recipe_SecondRinseCycleWaitDrainExtraTime, ((_rinseCycleTwo_RinsesCompleted < _recipe.Step2NumberOfRinse - 1) && !_recipe.FinalRinseDry && _recipe.DumpTimeSeconds - _normalDrainTimeSeconds > 0) || ((_rinseCycleTwo_RinsesCompleted < _recipe.Step2NumberOfRinse) && _recipe.FinalRinseDry && _recipe.DumpTimeSeconds - _normalDrainTimeSeconds > 0), NullFun, (_recipe.DumpTimeSeconds - _normalDrainTimeSeconds) * 1000) //3.7-SecondRinseCycleWaitResistivityStart 条件:(Final Rinse == false && RinseComplete == _recipe.Step2NumberOfRinse - 1) .LoopRunIf(RinseSecondStep.Recipe_SecondRinseCycleWaitResistivityStart, (_rinseCycleTwo_RinsesCompleted == _recipe.Step2NumberOfRinse - 1) && !_recipe.FinalRinseDry, ResistivityStart, _recipe.ResistivityStartTimeSeconds*1000) //3.8-SecondRinseCycleResistivityAveraging 条件:(Final Rinse == false && RinseComplete == _recipe.Step2NumberOfRinse - 1) .LoopRunIf(RinseSecondStep.Recipe_SecondRinseCycleResistivityAveraging, (_rinseCycleTwo_RinsesCompleted == _recipe.Step2NumberOfRinse - 1) && !_recipe.FinalRinseDry, ResistivityAveraging, _delay_1ms) //3.9-SecondRinseCycleCheckResistivity 条件:(Final Rinse == false && RinseComplete == _recipe.Step2NumberOfRinse - 1) .LoopWaitIf(RinseSecondStep.Recipe_SecondRinseCycleCheckResistivity, (_rinseCycleTwo_RinsesCompleted == _recipe.Step2NumberOfRinse - 1) && !_recipe.FinalRinseDry, ResitivitySampleResitivity,_recipe.ResistivityDurationSeconds*1000) //4.0-SecondRinseCycleWaitDrain 条件:(Final Rinse == false && RinseComplete == _recipe.Step2NumberOfRinse - 1) .LoopRunIf(RinseSecondStep.Recipe_SecondRinseCycleWaitDrain2, (_rinseCycleTwo_RinsesCompleted == _recipe.Step2NumberOfRinse - 1) && !_recipe.FinalRinseDry, StartOpenDumpValve, _delay_1ms) .LoopRunIfOnlyTimeOutFault(RinseSecondStep.Recipe_SecondRinseCycleCheckDrain2, (_rinseCycleTwo_RinsesCompleted == _recipe.Step2NumberOfRinse - 1) && !_recipe.FinalRinseDry, CheckWaterLevelEmpty, _normalDrainTimeSeconds * 1000) //4.1-Recipe_SecondRinseCycleWaitDrain2ExtraTime 条件:(Final Rinse == false && RinseComplete == _recipe.Step2NumberOfRinse - 1) .LoopRunIf(RinseSecondStep.Recipe_SecondRinseCycleWaitDrain2ExtraTime, (_rinseCycleTwo_RinsesCompleted == _recipe.Step2NumberOfRinse - 1) && !_recipe.FinalRinseDry && _recipe.DumpTimeSeconds - _normalDrainTimeSeconds > 0, NullFun, (_recipe.DumpTimeSeconds - _normalDrainTimeSeconds) * 1000) .LoopEnd(RinseSecondStep.Recipe_SecondRinseCycleEnd, CountNumberofRinsesComplete, _delay_1ms) .End(RinseSecondStep.Recipe_SecondRinseCycleComplete, ResetRinseComplete, _delay_1ms); return Runner.Status; } /// /// N2 Valve Off /// /// private bool N2ValveOff() { if (_recipe.Step1N2BubbleOn) { return _device.N2ValveOff(); } else { return true; } } /// /// 检验N2Valve off /// /// private bool CheckN2ValveOff() { if (_recipe.Step1N2BubbleOn) { return !_device.RinseData.N2Valve; } else { return true; } } /// /// 启动N2和关闭Dump Valve /// /// private bool OpenN2ValveAndCloseDumpValve() { bool result = true; if (_recipe.Step2N2BubbleOn) { result = _device.N2ValveOn(); if (!result) { NotifyError(eEvent.ERR_RINSE, "Open N2 Valve error", 0); return false; } } result = _device.DrainValveOff(); if (!result) { NotifyError(eEvent.ERR_RINSE, "Close Dump Valve error",0); } return result; } /// /// 检验Fill Valve打开状态 /// /// private bool CheckFillValveOn() { _startFillTime = DateTime.Now; _fillingDetectStartWaterLevel = _device.RinseData.WaterLevel; return true; } /// /// 检验是否正常注水 /// /// private bool CheckFillNormalStatus() { double currentWaterLevel = _device.RinseData.WaterLevel; return (currentWaterLevel - _fillingDetectStartWaterLevel) > _fillingStartedDelta; } /// /// 检验是否注满 /// /// private bool CheckFillFullStatus() { double currentWaterLevel = _device.RinseData.WaterLevel; bool result = currentWaterLevel > _sensorReadingFull; return result; } /// /// 增加已完成Rinses的次数 /// /// private bool CountNumberofRinsesComplete() { _rinseCycleTwo_RinsesCompleted++; return true; } /// /// 开始Dwell /// /// private bool StartDwell() { bool result = _device.FillValveOff(); if (!result) { NotifyError(eEvent.ERR_RINSE, "Fill Valve off error", 0); return false; } if (_recipe.Step2N2BubbleOn) { result = _device.N2ValveOff(); if (!result) { NotifyError(eEvent.ERR_RINSE, "N2 Valve off error",0); return false; } } return true; } /// /// 打开Dump Valve /// /// private bool StartOpenDumpValve() { CheckNeedCloseMetalDrain(); bool result = _device.DrainValveOn(); if (!result) { NotifyError(eEvent.ERR_RINSE, "Open Dump valve error", 0); return false; } return result; } /// /// 检验快排是否排空 /// /// private bool CheckWaterLevelEmpty() { double currentWaterLevel = _device.RinseData.WaterLevel; bool reseult = currentWaterLevel < _sensorReadingEmpty; if (!reseult) { NotifyError(eEvent.ERR_RINSE, $"current water level: {currentWaterLevel} is large than sensorReadingEmpty: {_sensorReadingEmpty}",0); } return reseult; } /// /// 检验是否需要关闭Metal Valve 加上第一步的number of rinse /// /// private void CheckNeedCloseMetalDrain() { if (_recipe.Step1NumberOfRinse + _rinseCycleTwo_RinsesCompleted + 1 > _recipe.Step1NumberOfDumpToMetalDrain) { bool result = _device.WasteValveOn(); if (!result) { NotifyError(eEvent.ERR_RINSE, "Close Metal Drain Valve error", 0); } } } /// /// ResistivityStart /// /// private bool ResistivityStart() { bool result = true; result = _device.FillValveOff(); if (!result) { NotifyError(eEvent.ERR_RINSE, "Fill Valve Off error",0); return false; } if (_recipe.Step2N2BubbleOn) { result = _device.N2ValveOff(); if (!result) { NotifyError(eEvent.ERR_RINSE, "N2 Valve Off error",0); return false; } } return result; } /// /// ResistivityAveraging /// /// private bool ResistivityAveraging() { if (_recipe.ResistivityDurationSeconds == 0) { _resistivitySample.Add(GetResitivity()); } else { _startSampleTime = DateTime.Now; _sampeIntervalTime = DateTime.Now; _startSample = true; } return true; } /// /// 采样Resitivity /// /// private bool ResitivitySampleResitivity() { if (!_startSample) { return true; } int sampleTotalCount = (int)Math.Ceiling(_recipe.ResistivityDurationSeconds / 0.1); //每隔100毫秒采样 if(DateTime.Now.Subtract(_sampeIntervalTime).TotalMilliseconds>=100) { _sampeIntervalTime= DateTime.Now; _readResistivityCount++; _resistivitySample.Add(GetResitivity()); } //ResistivityDurationSeconds时长减去0.1s,避免超时 if (DateTime.Now.Subtract(_startSampleTime).TotalMilliseconds>=_recipe.ResistivityDurationSeconds*1000-100) { bool result= CheckResistivity(); //if (!result) //{ // Runner.Stop("Sample Resitiviy abormal", true); //} return result; } return false; } //读取Resitivity的值 private double GetResitivity() { double resistivity = 0; ResistivityController resistivityController = DEVICE.GetDevice(_device.RinseItem.ResistivityID); if (resistivityController != null) { try { resistivity = double.Parse(resistivityController.ResisitivityValue.Trim()); } catch { resistivity = 0; //LOG.WriteLog(eEvent.ERR_RINSE, Module, "Resitivity value abnormal"); } } return resistivity; } /// /// CheckResistivity /// /// private bool CheckResistivity() { if(_recipe.ResistivityMegaOhms == 0) { _resistivitySample.Clear(); return true; } double resistivityAverageValue = _resistivitySample.Average(); if((_resistivitySample.Count == 1 && _resistivitySample[0]<_recipe.ResistivityMegaOhms)|| resistivityAverageValue < _recipe.ResistivityMegaOhms) { NotifyError(eEvent.ERR_RINSE, $"The detected resistivity value is abnormal,:{resistivityAverageValue}",0); } _resistivitySample.Clear(); return true; } private bool ResetRinseComplete() { _rinseCycleTwo_RinsesCompleted = 0; return true; } /// /// Abort /// public void Abort() { Runner.Stop("Manual abort"); } } }