using System; using System.Collections.Generic; using System.Xml; using Aitex.Core.RT.Device; using Aitex.Core.RT.Event; using Aitex.Core.RT.Log; using Aitex.Core.RT.OperationCenter; using Aitex.Core.RT.RecipeCenter; using Aitex.Core.RT.Routine; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using JetVirgoPM.Devices; using JetVirgoPM.PMs.Routines; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.SubstrateTrackings; using MECF.Framework.RT.ModuleLibrary.PMModules; namespace JetVirgoPM.PMs.RecipeExecutors { class ProcessRoutine : PMRoutineBase { private enum RecipeEngineState { Error, RecipeCompleted, ExecStep, TimeWait, EndPointWait, StepCompleted, Paused, } private object _lockerTotalCycle = new object(); private Dictionary _recipeTotalCycle = new Dictionary(); private DeviceTimer _beginPauseTimer = new DeviceTimer(); private object _recipeLocker = new object(); private RecipeEngineState _state = RecipeEngineState.ExecStep; private RecipeEngineState _pausedState = RecipeEngineState.ExecStep; private DeviceTimer _estimatedTimeCalcTimer = new DeviceTimer();//用于定时计算工艺程序估计的结束时间 public DateTime RecipeStartTime { get; private set; } public int RecipeChangeNo { get; private set; } public string CurrentRecipeBaseName { get; private set; } public string CurrentRecipeRunningName { get; private set; } public string CurrentRecipeContent { get; private set; } public string CurrentLotName { get; set; } private List _recipeStepList = new List(); public List CurrentRecipeStepList { get { return _recipeStepList; } set { _recipeStepList = value; } } public RecipeHead CurrentRecipeHead { get; set; } public int CurStepNum { get; private set; } public int CurStepTotalLoopCount { get; private set; } public double CurStepTotalTime { get { if (_recipeStepList == null || _recipeStepList.Count == 0 || _state == RecipeEngineState.RecipeCompleted || _state == RecipeEngineState.Error) return 0; return _recipeStepList[CurStepNum].StepTime * 1000; } } public int CurrentLoopCount { get; private set; } private DeviceTimer StepTimer = new DeviceTimer(); public bool IsPaused { private set; get; } public string CurStepComment { get { if (_recipeStepList == null || _recipeStepList.Count == 0) return string.Empty; return _recipeStepList[CurStepNum].StepName; } } public double CurStepLeftTime { get { if (IsPaused) return StepTimer.GetTotalTime() - StepTimer.GetElapseTime() + _beginPauseTimer.GetElapseTime(); //return Math.Max(0,StepTimer.GetTotalTime() - StepTimer.GetElapseTime()); return StepTimer.GetTotalTime() - StepTimer.GetElapseTime(); } } public double CurStepElpasedTime { get { if (_recipeStepList == null || _recipeStepList.Count == 0 || _state == RecipeEngineState.RecipeCompleted || _state == RecipeEngineState.Error) return 0; return StepTimer.GetElapseTime(); } } public double CurStepTotalRfTime { get { if (_recipeStepList == null || _recipeStepList.Count == 0 || _state == RecipeEngineState.RecipeCompleted || _state == RecipeEngineState.Error) return 0; if (_recipeStepList[CurStepNum].RecipeCommands.ContainsKey("Rf.SetPower") && !string.IsNullOrEmpty(_recipeStepList[CurStepNum].RecipeCommands["Rf.SetPower"]) && Convert.ToDouble(_recipeStepList[CurStepNum].RecipeCommands["Rf.SetPower"]) > 0.1) return CurStepTotalTime; return 0; } } public int TotalCycle { get { if (string.IsNullOrEmpty(DateTimeRecipeBaseName)) return 0; lock (_lockerTotalCycle) { if (_recipeTotalCycle.ContainsKey(DateTimeRecipeBaseName)) return _recipeTotalCycle[DateTimeRecipeBaseName]; } return 0; } } public string DateTimeRecipeBaseName { get { if (string.IsNullOrEmpty(CurrentRecipeBaseName)) return ""; return DateTime.Now.ToString("yyyyMMdd") + CurrentRecipeBaseName; } } public double PausedTime { get { return IsPaused ? _beginPauseTimer.GetElapseTime() : 0; } } public double EstimatedTotalLeftTime { get; private set; } public int RecipeTotalStepNum { get { return _recipeStepList.Count; } } protected PMModule _chamberEntity; private RecipeFACallback _faCallback; private RecipeDBCallback _dbCallback; private Fdc _fdc; private bool _needcheckwafer; public ProcessRoutine(JetDualPM chamber, PMModule pmModule) : base(chamber) { Name = "ProcessRoutine"; RecipeStartTime = new DateTime(0); EstimatedTotalLeftTime = 0; CalcEstimatedRecipeEndTime(); _chamberEntity = pmModule; _faCallback = new RecipeFACallback(); _dbCallback = new RecipeDBCallback(); _fdc = new Fdc(Module); } public RState Start(params object[] param) { RecipeStartTime = DateTime.Now; CurrentRecipeBaseName = (string)param[0]; CurrentRecipeRunningName = (string)param[1]; RecipeChangeNo = (int)param[2]; CurrentLotName = (string)param[3]; CurrentRecipeContent = (string)param[4]; CurrentRecipeHead = (RecipeHead)param[5]; CurrentRecipeStepList = (List)param[6]; if (!_chamber.NotHasInterlock) { EV.PostAlarmLog(Module, "射频电源 Interlock条件不满足"); return RState.Failed; } lock (_lockerTotalCycle) { if (_recipeTotalCycle.ContainsKey(DateTimeRecipeBaseName)) _recipeTotalCycle[DateTimeRecipeBaseName] += 1; else { _recipeTotalCycle[DateTimeRecipeBaseName] = 1; } List keys = new List(); foreach (KeyValuePair item in _recipeTotalCycle) { if (!item.Key.StartsWith(DateTime.Now.ToString("yyyyMMdd"))) keys.Add(item.Key); } foreach (string key in keys) { _recipeTotalCycle.Remove(key); } } if (!_chamber.IsPlus) _chamber.OpenValve(ValveType.PURGE, true); _chamber.OpenValve(ValveType.PROCESS, true); CurStepNum = CurStepTotalLoopCount = 0; _estimatedTimeCalcTimer.Start(1000); EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 开始运行"); _chamberEntity.RecipeRunningInfo.InnerId = Guid.NewGuid(); _chamberEntity.RecipeRunningInfo.BeginTime = DateTime.Now; _chamberEntity.RecipeRunningInfo.RecipeName = CurrentRecipeBaseName; _chamberEntity.RecipeRunningInfo.RecipeStepList = CurrentRecipeStepList; //ProcessDataRecorder.UpdateStatus(RecipeRunGuid.ToString(), SusceptorStatus.InProcessing.ToString(), Module); _state = RecipeEngineState.ExecStep; double totalTime = 0; foreach (var step in CurrentRecipeStepList) { totalTime += step.StepTime; } _faCallback.RecipeStart(_chamber.Module.ToString(), CurrentRecipeBaseName); if (!WaferManager.Instance.GetWafer(ModuleHelper.Converter(Module), 0).IsEmpty) _dbCallback.RecipeStart(_chamber.Module.ToString(), 0, _chamberEntity.RecipeRunningInfo.InnerId.ToString(), _chamberEntity.RecipeRunningInfo.RecipeName); if (!WaferManager.Instance.GetWafer(ModuleHelper.Converter(Module), 1).IsEmpty) _dbCallback.RecipeStart(_chamber.Module.ToString(), 1, _chamberEntity.RecipeRunningInfo.InnerId.ToString(), _chamberEntity.RecipeRunningInfo.RecipeName); _dbCallback.RecipeUpdateStatus(_chamberEntity.RecipeRunningInfo.InnerId.ToString(), "InProcess", (float)totalTime); _fdc.Reset(); _chamberEntity.EndPointRecipeStart(CurrentRecipeBaseName); _needcheckwafer = true; if (CurrentRecipeBaseName.ToLower().Contains("prejob") || CurrentRecipeBaseName.ToLower().Contains("preclean") || CurrentRecipeBaseName.ToLower().Contains("postclean") || CurrentRecipeBaseName.ToLower().Contains("postjob")) _needcheckwafer = false; return Runner.Start(_chamber.Module.ToString(), Name); } /// /// quiting current state /// /// public void Exit() { _chamber.IsPressureToleranceEnabled = false; var ts = DateTime.Now - RecipeStartTime; var totalTime = $"{Convert.ToInt32(ts.TotalHours)}:{ts.Minutes}:{ts.Seconds}"; bool isProcessCompleted = _state == RecipeEngineState.RecipeCompleted; if (isProcessCompleted) { EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 正常运行完毕"); _faCallback.RecipeComplete(_chamber.Module.ToString(), CurrentRecipeBaseName); _dbCallback.RecipeComplete(_chamberEntity.RecipeRunningInfo.InnerId.ToString()); _fdc.Stop(); } else { _faCallback.RecipeFailed(_chamber.Module.ToString(), CurrentRecipeBaseName); _dbCallback.RecipeFailed(_chamberEntity.RecipeRunningInfo.InnerId.ToString()); _chamberEntity.StopEndPoint(); string info = string.Format("Recipe:{0}\r\nStart time:{1:yyyy/MM/dd HH:mm:ss}\r\nEnd time:{2:yyyy/MM/dd HH:mm:ss}\r\nTotal time:{3}", CurrentRecipeRunningName, RecipeStartTime, DateTime.Now, totalTime); EV.PostWarningLog(Module, info); EV.PostPopDialogMessage(EventLevel.Warning, $"{Module} recipe was aborted", info); } _chamberEntity.EndPointRecipeStop(); //重置工艺程序名 CurrentRecipeRunningName = string.Empty; CurrentLotName = string.Empty; var tolerance = _chamberEntity.RecipeRunningInfo.RecipeStepList[0].ToleranceCommands; foreach (var cmd in tolerance) { OP.DoOperation($"{Module}.{cmd.Key}", out string reason1, 0, new object[] { "0", "0", "0" }); } } public RState Monitor() { string reason = string.Empty; CalcEstimatedRecipeEndTime(); //工艺程序运行监控,自动打开阀门如果对应的MFC设定设置大于0 _chamber.Monitor(); if (_chamber.IsError) return RState.Failed; if (SC.GetValue($"System.ProcessEnableCheckLE")) { if ((!_chamber.LETemp1ON && !_chamber.Chamber1Disable) || (!_chamber.LETemp2ON && !_chamber.Chamber2Disable)) { EV.PostAlarmLog(Module, $"腔体工艺时底座温度不能被关闭"); return RState.Failed; } } //工艺程序运行引擎 lock (_recipeLocker) { try { switch (_state) { case RecipeEngineState.ExecStep: { //工艺程序循环设置 if (_recipeStepList[CurStepNum].IsLoopStartStep) { CurStepTotalLoopCount = _recipeStepList[CurStepNum].LoopCount; if (CurStepTotalLoopCount == 0) { CurrentLoopCount = 0; } else { CurrentLoopCount++; } } //当前工艺程序步的定时器设定 StepTimer.Start(_recipeStepList[CurStepNum].StepTime * 1000); //发送信息到用户界面 EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 第{CurStepNum + 1} 步开始:{_recipeStepList[CurStepNum].StepName}"); //执行工艺程序命令 foreach (var cmdkey in _recipeStepList[CurStepNum].RecipeCommands.Keys) { string recipeCmd = cmdkey; string param = _recipeStepList[CurStepNum].RecipeCommands[recipeCmd]; if (string.IsNullOrWhiteSpace(param)) continue; if (recipeCmd == "EPD.SetConfig" && !SC.GetValue($"{Module}.EPD.IsEnabled")) continue; if (!OP.CanDoOperation($"{Module}." + recipeCmd, out reason, param)) { EV.PostAlarmLog(Module, $"不能执行 {recipeCmd}, {reason}"); return RState.Failed; } else { var rampTime_ms = (int)(_recipeStepList[CurStepNum].StepTime * 1000); if (_recipeStepList[CurStepNum].IsJumpStep) rampTime_ms = 0; if (recipeCmd.StartsWith("Rf1")) { if (!SC.IsATMMode && !SC.GetValueOrDefault($"System.SetUp.{Module}Chamber1Disabled.IsInstalled") && checkwafer(0)) OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param); } else if (recipeCmd.StartsWith("BiasRf1")) { if (!SC.IsATMMode && !SC.GetValueOrDefault($"System.SetUp.{Module}Chamber1Disabled.IsInstalled") && WaferManager.Instance.CheckHasWafer(Module, 0)) OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param); } else if (recipeCmd.StartsWith("Rf2")) { if (!SC.IsATMMode && !SC.GetValueOrDefault($"System.SetUp.{Module}Chamber2Disabled.IsInstalled") && checkwafer(1)) OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param); } else if (recipeCmd.StartsWith("BiasRf2")) { if (!SC.IsATMMode && !SC.GetValueOrDefault($"System.SetUp.{Module}Chamber2Disabled.IsInstalled") && WaferManager.Instance.CheckHasWafer(Module, 1)) OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param); } else if (recipeCmd == "EPD.SetConfig") { if (SC.GetValue($"{Module}.EPD.IsEnabled")) OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, CurStepNum, _recipeStepList[CurStepNum].RecipeCommands[recipeCmd]); } else if (recipeCmd == "LiftPin1.SetState") { if (!SC.GetValue($"System.SetUp.{Module}Chamber1Disabled.IsInstalled")) { OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param); } } else if (recipeCmd == "LiftPin2.SetState") { if (!SC.GetValue($"System.SetUp.{Module}Chamber2Disabled.IsInstalled")) { OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param); } } else { OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param); } } } foreach (var toleranceCommand in _recipeStepList[CurStepNum].ToleranceCommands) { OP.DoOperation($"{Module}." + toleranceCommand.Key, out string reason1, 0, toleranceCommand.Value); } string endby = _recipeStepList[CurStepNum].EndBy; if (!string.IsNullOrEmpty(endby) && endby == "EndByEndPoint") { _state = RecipeEngineState.EndPointWait; } else { _state = RecipeEngineState.TimeWait; } _faCallback.RecipeStepStart(_chamber.Module.ToString(), CurrentRecipeBaseName, CurStepNum); _dbCallback.RecipeStepStart(_chamberEntity.RecipeRunningInfo.InnerId.ToString(), CurStepNum, _chamberEntity.RecipeRunningInfo.RecipeStepList[CurStepNum].StepName, (float)_chamberEntity.RecipeRunningInfo.RecipeStepList[CurStepNum].StepTime); _fdc.Start(_chamberEntity.RecipeRunningInfo.RecipeStepList[CurStepNum].RecipeCommands); } break; case RecipeEngineState.TimeWait: _chamber.CheckPressureStability(); if (StepTimer.IsTimeout()) { _state = RecipeEngineState.StepCompleted; } break; case RecipeEngineState.EndPointWait: { if (_chamberEntity.CheckEndPoint()) { _state = RecipeEngineState.StepCompleted; } if (StepTimer.IsTimeout()) { if (_recipeStepList[CurStepNum].FaultIfNoEPDTrigger) { EV.PostAlarmLog(Module, $"{_chamberEntity.Module} EPD step not triggered, timeout"); return RState.Failed; } else { EV.PostWarningLog(Module, $"{_chamberEntity.Module} EPD step not triggered, skipped"); } _state = RecipeEngineState.StepCompleted; } } break; case RecipeEngineState.Paused: break; case RecipeEngineState.StepCompleted: { _chamber.CheckPressureDisable(); EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 第{CurStepNum + 1}步结束"); _chamberEntity.StopEndPoint(); _faCallback.RecipeStepEnd(_chamber.Module.ToString(), CurrentRecipeBaseName, CurStepNum); _dbCallback.RecipeStepEnd(_chamberEntity.RecipeRunningInfo.InnerId.ToString(), CurStepNum, _fdc.DataList); _fdc.Stop(); //判断是否当前步循环终止 if (_recipeStepList[CurStepNum].IsLoopEndStep) { //重新读取循环的设定次数 for (int nn = CurStepNum; nn >= 0; nn--) { if (_recipeStepList[nn].IsLoopStartStep) { CurStepTotalLoopCount = _recipeStepList[nn].LoopCount; break; } } if (CurrentLoopCount >= CurStepTotalLoopCount) { CurrentLoopCount = CurStepTotalLoopCount = 0; CurStepNum++; } else { int n = CurStepNum - 1; int next = -1; while (n >= 0) { if (_recipeStepList[n].IsLoopStartStep) { next = n; break; } n--; } if (next == -1) throw new Exception("Loop End control error"); CurStepNum = next; } } else { if (CurStepNum < _recipeStepList.Count - 1) { CurStepNum++; _state = RecipeEngineState.ExecStep; } else { EV.PostInfoLog(Module, $"工艺 {CurrentRecipeRunningName} 完毕"); CurStepNum = _recipeStepList.Count - 1; _state = RecipeEngineState.RecipeCompleted; return RState.End; } } } break; case RecipeEngineState.RecipeCompleted: return RState.End; case RecipeEngineState.Error: return RState.Failed; default: break; } } catch (Exception ex) { EV.PostAlarmLog(Module, $"{CurStepNum + 1}, 语法错误, {ex.Message}"); return RState.Failed; } } return RState.Running; } /// /// 暂停工艺程序运行 /// public void PauseRecipe() { if (_state != RecipeEngineState.TimeWait && _state != RecipeEngineState.EndPointWait) return; if (!IsPaused) { string reason = string.Empty; IsPaused = true; _pausedState = _state; _state = RecipeEngineState.Paused; _beginPauseTimer.Start(0); EV.PostInfoLog(Module, $"工艺程序'{CurrentRecipeRunningName}'第{CurStepNum + 1}步暂停"); } } /// /// 恢复工艺程序运行 /// public void ResumeRecipe() { if (IsPaused) { //update current recipe step time string recipeXml = CurrentRecipeContent; int currentStepNo = CurStepNum; XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(recipeXml); var stepNodes = xmlDoc.SelectNodes("/TableRecipeData/Step"); if (currentStepNo >= 0 && currentStepNo < stepNodes.Count) { var curStepNode = stepNodes[currentStepNo] as XmlElement; var curStepNewTime = CurStepTotalTime + PausedTime; if (curStepNewTime < 0) curStepNewTime = 0; TimeSpan tspan = new TimeSpan(0, 0, 0, 0, (int)curStepNewTime); var timeString = $"{((int)tspan.TotalHours).ToString("00")}:{tspan.Minutes.ToString("00")}:{tspan.Seconds.ToString("00")}"; curStepNode.SetAttribute("Time", timeString); LOG.Write($"执行Resume命令,将当前第{currentStepNo}步时间修改为{timeString}"); UpdateRecipe(xmlDoc.OuterXml); } //Resume recipe IsPaused = false; _state = _pausedState; EV.PostInfoLog(Module, $"工艺程序'{CurrentRecipeRunningName}'第{CurStepNum + 1}步继续"); //resume recipe double stepElapsedTime = StepTimer.GetElapseTime() - _beginPauseTimer.GetElapseTime(); double stepLeftTime = StepTimer.GetTotalTime() - stepElapsedTime; if (stepLeftTime < 0) stepLeftTime = 0; //更新当前步的定时器时间 StepTimer.Restart(StepTimer.GetTotalTime() + _beginPauseTimer.GetElapseTime()); //重新执行当前工艺程序步 foreach (var recipeCmd in _recipeStepList[CurStepNum].RecipeCommands.Keys) { if (!DEVICE.CanDo(recipeCmd)) { EV.PostInfoLog(Module, $"不可识别的工艺程序控制命令{recipeCmd}"); } else { var rampTime_ms = (int)(_recipeStepList[CurStepNum].StepTime * 1000); if (_recipeStepList[CurStepNum].IsJumpStep) rampTime_ms = 0; DEVICE.Do(recipeCmd, rampTime_ms, false, _recipeStepList[CurStepNum].RecipeCommands[recipeCmd]); } } } } /// /// 终止工艺程序运行 /// public void AbortRecipe() { ResumeRecipe(); _chamberEntity.StopEndPoint(); _state = RecipeEngineState.Error; CalcEstimatedRecipeEndTime(); _faCallback.RecipeFailed(_chamber.Module.ToString(), CurrentRecipeBaseName); _dbCallback.RecipeFailed(_chamberEntity.RecipeRunningInfo.InnerId.ToString()); _fdc.Stop(); EV.PostInfoLog(Module, "工艺程序被终止运行"); } /// /// 跳至工艺程序下一步 /// public void SkipCurrentRecipeStep() { if (_state == RecipeEngineState.Paused) { EV.PostInfoLog(Module, "在 Pause Recipe 状态下不能执行 Skip to next step 命令"); return; } try { //update current recipe step time string recipeXml = CurrentRecipeContent; int currentStepNo = CurStepNum; XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(recipeXml); var stepNodes = xmlDoc.SelectNodes("/TableRecipeData/Step"); if (currentStepNo >= 0 && currentStepNo < stepNodes.Count) { var curStepNode = stepNodes[currentStepNo] as XmlElement; var curStepElapsedTime = CurStepTotalTime - CurStepLeftTime;//ms if (curStepElapsedTime < 0) curStepElapsedTime = 0; //TimeSpan tspan = new TimeSpan(0, 0, 0, 0, (int)curStepElapsedTime); //var timeString = // $"{((int)tspan.TotalHours).ToString("00")}:{tspan.Minutes.ToString("00")}:{tspan.Seconds.ToString("00")}"; string timeString = ((int)(curStepElapsedTime / 1000)).ToString(); curStepNode.SetAttribute("Time", timeString); LOG.Write($"step skipped,step {currentStepNo} time changed to {timeString}"); //recipe update command UpdateRecipe(xmlDoc.OuterXml); } } catch (Exception ex) { LOG.Write(ex); } if (_state == RecipeEngineState.EndPointWait || _state == RecipeEngineState.TimeWait) { _state = RecipeEngineState.StepCompleted; //send informational event EV.PostMessage(Module, EventEnum.GeneralInfo, Module, CurrentRecipeRunningName, CurStepNum + 1); } } public bool UpdateRecipe(string newRecipeContent) { lock (_recipeLocker) { RecipeHead head = null; List newRecipeData = null; if (!Recipe.Parse(Module, newRecipeContent, out head, out newRecipeData)) { EV.PostMessage(Module, EventEnum.DefaultAlarm, Module, CurrentRecipeRunningName); return false; } else { string oldRecipeName = CurrentRecipeRunningName; CurrentRecipeRunningName = $"{DateTime.Now:yyyyMMddHHmmss}-{CurrentRecipeBaseName}-({RecipeChangeNo++})"; //update local recipe data _recipeStepList = newRecipeData; CurrentRecipeContent = newRecipeContent; //send informational event EV.PostInfoLog(Module, $"运行工艺程序'{oldRecipeName}'第{CurStepNum + 1}步时更新工艺程序为'{CurrentRecipeRunningName}"); RecipeFileManager.Instance.SaveRecipeHistory(ModuleName.System.ToString(), CurrentRecipeRunningName, CurrentRecipeContent); } } return true; } private bool checkwafer(int slot) { if (_needcheckwafer) return WaferManager.Instance.CheckHasWafer(Module, slot); else return true; } public override void Abort() { _dbCallback.RecipeFailed(_chamberEntity.RecipeRunningInfo.InnerId.ToString()); base.Abort(); } public override string ToString() { return "recipe running"; } /// /// 工艺程序估计结束时间计算 /// protected void CalcEstimatedRecipeEndTime() { try { //(*计算当前工艺程序预计所需的总时间,从当前步开始计算剩余步的估算时间+已经既成事实的时间 => 总的估计时间,采用该种方式进行总工艺时间理论上最为精确*) if (!_estimatedTimeCalcTimer.IsTimeout()) return; _estimatedTimeCalcTimer.Start(1000); EstimatedTotalLeftTime = 0; if (_state == RecipeEngineState.RecipeCompleted) return; if (!(CurStepNum >= 0 && CurStepNum <= _recipeStepList.Count - 1)) return; if (CurStepLeftTime > 0) { EstimatedTotalLeftTime = CurStepLeftTime; } int nextBegin = CurStepNum; //(*判断当前是否处于循环之中*) bool IsInLoop = false; int iNum1 = 0; int iNum2 = 0; //int j=i; for (int j = CurStepNum; j < _recipeStepList.Count; j++) { if (_recipeStepList[j].IsLoopEndStep) { iNum2 = j; IsInLoop = true; break; } else if (j > CurStepNum && _recipeStepList[j].IsLoopStartStep) { IsInLoop = false; break; } } if (IsInLoop) { //(*当前步处于循环中*) iNum1 = CurStepNum; for (int j = CurStepNum; j >= 0; j--) { if (_recipeStepList[j].IsLoopStartStep) { iNum1 = j; break; } } for (int j = CurStepNum + 1; j <= iNum2; j++) { EstimatedTotalLeftTime += _recipeStepList[j].StepTime * 1000 * (_recipeStepList[iNum1].LoopCount - CurrentLoopCount + 1); } for (int j = iNum1; j <= CurStepNum; j++) { EstimatedTotalLeftTime += _recipeStepList[j].StepTime * 1000 * (_recipeStepList[iNum1].LoopCount - CurrentLoopCount); } nextBegin = iNum2 + 1; } else { nextBegin++; } //(*当前步处于循环外*) for (int j = nextBegin; j < _recipeStepList.Count; j++) { if (_recipeStepList[j].IsLoopStartStep) { //j=i; iNum1 = j; iNum2 = j + 1; double lr1 = 0; for (int m = j; m < _recipeStepList.Count; m++) { lr1 += _recipeStepList[m].StepTime * 1000; if (_recipeStepList[m].IsLoopEndStep) { iNum2 = m; break; } } EstimatedTotalLeftTime = EstimatedTotalLeftTime + lr1 * _recipeStepList[iNum1].LoopCount; j = iNum2; } else { EstimatedTotalLeftTime += _recipeStepList[j].StepTime * 1000; } //END_WHILE } } catch (Exception ex) { LOG.Write(ex); } } } }