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 内部变量 /// <summary> /// 设备对象 /// </summary> private RinseDevice _device; /// <summary> /// Recipe /// </summary> private QdrRecipe _recipe; /// <summary> /// Fill打开注水记录Water Level /// </summary> private double _fillingDetectStartWaterLevel; /// <summary> /// Concurrent Fill Time Seconds /// </summary> private int _concurrentFillTimeSeconds; /// <summary> /// 开始注水的偏差 /// </summary> private double _fillingStartedDelta; /// <summary> /// 注满数值 /// </summary> private int _sensorReadingFull; /// <summary> /// 液位为空数值 /// </summary> private int _sensorReadingEmpty; /// <summary> /// 开始流水时间 /// </summary> private DateTime _startFillTime; /// <summary> /// 从开始排水到检测是否排空的间隔时间 /// </summary> private int _normalDrainTimeSeconds; /// <summary> /// 表示当前正在执行第几次rinse /// </summary> private int _rinseCycleTwo_RinsesCompleted = 0; /// <summary> /// Resistivity取值样本列表 /// </summary> private List<double> _resistivitySample = new List<double>(); /// <summary> /// 开始注水后检测是否正常注水间隔 /// </summary> private int _checkIsFillingTimeSeconds; /// <summary> /// 开始注水后检测是否注满间隔 /// </summary> private int _checkIsFullTimeSeconds; /// <summary> /// 采样水阻值次数 /// </summary> private int _readResistivityCount = 0; /// <summary> /// 当前执行到哪一步的显示 /// </summary> private string _currentStateMachine; /// <summary> /// 开始采样时间 /// </summary> private DateTime _startSampleTime = DateTime.Now; /// <summary> /// 采样时间 /// </summary> private DateTime _sampeIntervalTime = DateTime.Now; /// <summary> /// 启动采样 /// </summary> private bool _startSample = false; #endregion /// <summary> /// 构造函数 /// </summary> /// <param name="module"></param> public RinseSecondStepRoutine(string module) : base(module) { } /// <summary> /// 启动 /// </summary> /// <param name="objects"></param> /// <returns></returns> public RState Start(params object[] objects) { _startSample = false; _device = DEVICE.GetDevice<RinseDevice>(Module); _resistivitySample.Clear(); _readResistivityCount = 0; _recipe = objects[0] as QdrRecipe; _concurrentFillTimeSeconds = SC.GetValue<int>("QDR.ConcurrentFillTimeSeconds"); _fillingStartedDelta = SC.GetValue<double>("QDR.FillingStartedDelta"); _sensorReadingFull = SC.GetValue<int>("QDR.SensorReadingFull"); _sensorReadingEmpty = SC.GetValue<int>("QDR.SensorReadingEmpty"); _normalDrainTimeSeconds = SC.GetValue<int>("QDR.NominalDrainTimeSeconds"); _checkIsFillingTimeSeconds = SC.GetValue<int>("QDR.NominalCheckFillWaterTimeSeconds"); _checkIsFullTimeSeconds = SC.GetValue<int>("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"); } /// <summary> /// 监控 /// </summary> /// <returns></returns> 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; } /// <summary> /// N2 Valve Off /// </summary> /// <returns></returns> private bool N2ValveOff() { if (_recipe.Step1N2BubbleOn) { return _device.N2ValveOff(); } else { return true; } } /// <summary> /// 检验N2Valve off /// </summary> /// <returns></returns> private bool CheckN2ValveOff() { if (_recipe.Step1N2BubbleOn) { return !_device.RinseData.N2Valve; } else { return true; } } /// <summary> /// 启动N2和关闭Dump Valve /// </summary> /// <returns></returns> 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; } /// <summary> /// 检验Fill Valve打开状态 /// </summary> /// <returns></returns> private bool CheckFillValveOn() { _startFillTime = DateTime.Now; _fillingDetectStartWaterLevel = _device.RinseData.WaterLevel; return true; } /// <summary> /// 检验是否正常注水 /// </summary> /// <returns></returns> private bool CheckFillNormalStatus() { double currentWaterLevel = _device.RinseData.WaterLevel; return (currentWaterLevel - _fillingDetectStartWaterLevel) > _fillingStartedDelta; } /// <summary> /// 检验是否注满 /// </summary> /// <returns></returns> private bool CheckFillFullStatus() { double currentWaterLevel = _device.RinseData.WaterLevel; bool result = currentWaterLevel > _sensorReadingFull; return result; } /// <summary> /// 增加已完成Rinses的次数 /// </summary> /// <returns></returns> private bool CountNumberofRinsesComplete() { _rinseCycleTwo_RinsesCompleted++; return true; } /// <summary> /// 开始Dwell /// </summary> /// <returns></returns> 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; } /// <summary> /// 打开Dump Valve /// </summary> /// <returns></returns> private bool StartOpenDumpValve() { CheckNeedCloseMetalDrain(); bool result = _device.DrainValveOn(); if (!result) { NotifyError(eEvent.ERR_RINSE, "Open Dump valve error", 0); return false; } return result; } /// <summary> /// 检验快排是否排空 /// </summary> /// <returns></returns> 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; } /// <summary> /// 检验是否需要关闭Metal Valve 加上第一步的number of rinse /// </summary> /// <returns></returns> 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); } } } /// <summary> /// ResistivityStart /// </summary> /// <returns></returns> 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; } /// <summary> /// ResistivityAveraging /// </summary> /// <returns></returns> private bool ResistivityAveraging() { if (_recipe.ResistivityDurationSeconds == 0) { _resistivitySample.Add(GetResitivity()); } else { _startSampleTime = DateTime.Now; _sampeIntervalTime = DateTime.Now; _startSample = true; } return true; } /// <summary> /// 采样Resitivity /// </summary> /// <returns></returns> 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<ResistivityController>(_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; } /// <summary> /// CheckResistivity /// </summary> /// <returns></returns> 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; } /// <summary> /// Abort /// </summary> public void Abort() { Runner.Stop("Manual abort"); } } }