using System; using System.Collections.Generic; using System.Xml; using Aitex.Core.Equipment.SusceptorDefine; 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.Util; using MECF.Framework.Common.DBCore; using MECF.Framework.Common.Equipment; using Virgo_DRT.Devices; namespace Virgo_DRT.Modules.PMs { class ProcessRoutine : PMRoutineBase { /// /// 工艺程序运行引擎状态定义 /// private enum RecipeEngineState { Error, RecipeCompleted, ExecStep, TimeWait, ConditionWait, StepCompleted, Paused, } /// /// 工艺程序数据 /// private object _lockerTotalCycle = new object(); private Dictionary _recipeTotalCycle = new Dictionary(); //public bool IsAtmRecipeRun { private set; get; } 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; } } public ProcessRoutine(JetPM chamber) : base(chamber) { Name = "ProcessRoutine"; RecipeStartTime = new DateTime(0); EstimatedTotalLeftTime = 0; CalcEstimatedRecipeEndTime(); } public Result 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.IsRFGInterlockOn) { EV.PostAlarmLog(Module, "射频电源 Interlock条件不满足"); return Result.VERIFYFAIL; } 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); } } _chamber.OpenValve(ValveType.PROCESS, true); CurStepNum = CurStepTotalLoopCount = 0; _estimatedTimeCalcTimer.Start(1000); //EV.PostSoundMessage($"Run Recipe {CurrentRecipeRunningName}"); EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 开始运行"); //更新当前的工艺状态到数据库中 //Singleton.Instance.CurrentRunningJob.JobResult = JobStatus.InProcessing; //Singleton.Instance.UpdateProcessStatus(Singleton.Instance.CurrentRunningJob.RecipeRunId, JobStatus.InProcessing); ProcessDataRecorder.UpdateStatus(RecipeRunGuid.ToString(), SusceptorStatus.InProcessing.ToString()); _state = RecipeEngineState.ExecStep; return Result.RUN; } /// /// quiting current state /// /// public void Exit() { _chamber.IsPressureToleranceEnabled = false; var ts = DateTime.Now - RecipeStartTime; var totalTime = $"{Convert.ToInt32(ts.TotalHours)}:{ts.Minutes}:{ts.Seconds}"; //JobInfo job = Singleton.Instance.CurrentRunningJob; bool isProcessCompleted = _state == RecipeEngineState.RecipeCompleted; if (isProcessCompleted) { //正常工艺运行结束 //Singleton.Instance.AddRecord(job.RecipeRunId, CarrierDataType.ProcessNormalEnd, totalTime, "recipe finished"); //string info = string.Format(Resources.Process_Exit_Recipe0RNStartTime1RNEndTime2RNTotalTime3, // CurrentRecipeRunningName, RecipeStartTime.ToString("yyyy/MM/dd HH:mm:ss"), // DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), totalTime); EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 正常运行完毕"); //if (true) //{ // EV.PostPopDialogMessage(EventLevel.Information, string.Format(Resources.Process_Exit_0RecipeCompletelyRun, Module), info); //} //job.JobResult = JobStatus.Processed; } else { //人为终止工艺运行结束 //Singleton.Instance.AddRecord(job.RecipeRunId, CarrierDataType.PostProcessErrorEnd, totalTime, "recipe aborted"); 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); //Update susceptor information //job.JobResult = JobStatus.Troubled; } //job.ProcessEndTime = DateTime.Now; //重置工艺程序名 CurrentRecipeRunningName = string.Empty; CurrentLotName = string.Empty; //stop all ramping when quiting processing state //StopRamp(); } //private void CheckPressureStability() //{ // //如果有ThrottleValve 就检查Throttle Valve的tolerance // if (DeviceModel.PressureControl != null) // { // DeviceModel.PressureControl.EnableTolerance = // DeviceModel.MfcGas1 != null && DeviceModel.MfcGas1.SetPoint > 0.001 || // DeviceModel.MfcGas2 != null && DeviceModel.MfcGas2.SetPoint > 0.001 || // DeviceModel.MfcGas3 != null && DeviceModel.MfcGas3.SetPoint > 0.001; // } //} /// /// 工艺程序运行引擎放于此处 /// public Result Monitor() { string reason = string.Empty; CalcEstimatedRecipeEndTime(); //检查软件互锁是否强制停止运行工艺程序 //if (!doRoutineRunning.Value) //{ // if (!doRoutineRunning.Check(true, out reason)) // { // EV.PostMessage(Module.ToString(), EventEnum.RecipeAbortedByInterlock, Module, reason); // return Result.FAIL; // Reactor.PostEvent(new AlarmHandleCommand("软件互锁")); // } //} //工艺程序运行监控,自动打开阀门如果对应的MFC设定设置大于0 _chamber.Monitor(); if (_chamber.IsError) return Result.FAIL; if (_chamber.CheckFactoryScrubberSysAlarm) { _chamber.OpenValve(ValveType.FAST_PUMP, false); _chamber.OpenValve(ValveType.SOFT_PUMP, false); EV.PostAlarmLog(Module, $"{Module} {Name} Factory Scrubber System is alarm "); return Result.FAIL; } //工艺程序运行引擎 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); //Singleton.Instance.AddRecord(Singleton.Instance.CurrentRunningJob.RecipeRunId, // CarrierDataType.RecipeStepStart, (CurStepNum + 1).ToString(), // string.Format(Resources.Process_Monitor_RecipeRunningStartStep01, CurStepNum + 1, _recipeStepList[CurStepNum].StepName)); //发送信息到用户界面 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 (!OP.CanDoOperation($"{Module}." + recipeCmd, out reason, param)) { EV.PostAlarmLog(Module, $"不能执行 {recipeCmd}, {reason}"); return Result.FAIL; } else { var rampTime_ms = (int)(_recipeStepList[CurStepNum].StepTime * 1000); if (_recipeStepList[CurStepNum].IsJumpStep) rampTime_ms = 0; OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param); } // if (!DEVICE.CanDo(recipeCmd)) //{ // EV.PostMessage(Module.ToString(), EventEnum.DefaultWarning, Module, recipeCmd); //} //else //{ // var rampTime_ms = (int)(_recipeStepList[CurStepNum].StepTime * 1000); // if (_recipeStepList[CurStepNum].IsJumpStep) // rampTime_ms = 0; // string str = _recipeStepList[CurStepNum].RecipeCommands[cmdkey]; // if (!string.IsNullOrEmpty(str)) // DEVICE.Do(recipeCmd, rampTime_ms, false, str); //} } string endby = _recipeStepList[CurStepNum].EndBy; if (string.IsNullOrEmpty(endby) || string.Compare(endby, "EndByStepTime", StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(endby, "EndByRfTime", StringComparison.OrdinalIgnoreCase) == 0) { _state = RecipeEngineState.TimeWait; } else { _state = RecipeEngineState.ConditionWait; } } break; case RecipeEngineState.TimeWait: _chamber.CheckPressureStability(); if (StepTimer.IsTimeout()) { _state = RecipeEngineState.StepCompleted; } break; case RecipeEngineState.ConditionWait: { //var endbyCondition = (EndByCondition)Enum.Parse(typeof(EndByCondition), _recipeStepList[CurStepNum].EndBy, true); //var endbyValue = Convert.ToDouble(_recipeStepList[CurStepNum].EndByValue); //if (isStepEndby(endbyCondition, endbyValue)) { _state = RecipeEngineState.StepCompleted; } } break; case RecipeEngineState.Paused: break; case RecipeEngineState.StepCompleted: { //发送新的一步开始的信息到用户界面 EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 第{CurStepNum + 1}步结束"); //添加石墨盘信息 //Singleton.Instance.AddRecord(Singleton.Instance.CurrentRunningJob.RecipeRunId, CarrierDataType.RecipeStepNormalEnd, (CurStepNum + 1).ToString(), string.Format("recipe running:end step {0}", CurStepNum + 1)); //判断是否当前步循环终止 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 Result.DONE; } } } break; case RecipeEngineState.RecipeCompleted: return Result.DONE; case RecipeEngineState.Error: return Result.FAIL; default: break; } } catch (Exception ex) { EV.PostAlarmLog(Module, $"{CurStepNum + 1}, 语法错误, {ex.Message}"); return Result.FAIL; } } return Result.RUN; } /// /// 暂停工艺程序运行 /// public void PauseRecipe() { if (_state != RecipeEngineState.TimeWait && _state != RecipeEngineState.ConditionWait) return; if (!IsPaused) { string reason = string.Empty; IsPaused = true; _pausedState = _state; _state = RecipeEngineState.Paused; _beginPauseTimer.Start(0); //Singleton.Instance.AddRecord(Singleton.Instance.CurrentRunningJob.RecipeRunId, CarrierDataType.RecipeStepPaused, CurStepNum.ToString(), string.Format(Resources.Process_PauseRecipe_工艺运行中当前第0步暂停, CurStepNum + 1)); EV.PostInfoLog(Module, $"工艺程序'{CurrentRecipeRunningName}'第{CurStepNum + 1}步暂停"); //pause mfc //pause pc //StopRamp(); } } /// /// 恢复工艺程序运行 /// 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; //Singleton.Instance.AddRecord(Singleton.Instance.CurrentRunningJob.RecipeRunId, CarrierDataType.RecipeStepResume, CurStepNum.ToString(), string.Format("工艺运行中:当前第{0}步,取消暂停", CurStepNum + 1)); 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(); _state = RecipeEngineState.RecipeCompleted; CalcEstimatedRecipeEndTime(); //StopRamp(); //send informational event //Singleton.Instance.AddRecord(Singleton.Instance.CurrentRunningJob.RecipeRunId, CarrierDataType.RecipeStepResume, CurStepNum.ToString(), string.Format("recipe running:aborted in step {0}", CurStepNum + 1)); 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")}"; 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.ConditionWait || _state == RecipeEngineState.TimeWait) { _state = RecipeEngineState.StepCompleted; //send informational event EV.PostMessage(Module, EventEnum.GeneralInfo, Module, CurrentRecipeRunningName, CurStepNum + 1); //Singleton.Instance.AddRecord(Singleton.Instance.CurrentRunningJob.RecipeRunId, CarrierDataType.RecipeStepJump, CurStepNum.ToString(), string.Format("Recipe Running:current step {0},skip to next step", CurStepNum + 1)); } } public bool UpdateRecipe(string newRecipeContent) { lock (_recipeLocker) { RecipeHead head = null; List newRecipeData = null; if (!Recipe.Parse(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; //Singleton.Instance.UpdateProcessRecipeName(Singleton.Instance.CurrentRunningJob.RecipeRunId, CurrentRecipeRunningName); //update susceptor recipe name //Susceptor sus = SusceptorManager.Instance.GetSystemSusceptors()[ModuleName.System]; //if (sus != null) //{ // sus.RecipeName = RecipeName; // SusceptorManager.Instance.Persist(); //} //send informational event EV.PostInfoLog(Module, $"运行工艺程序'{oldRecipeName}'第{CurStepNum + 1}步时更新工艺程序为'{CurrentRecipeRunningName}"); //Singleton.Instance.AddRecord(Singleton.Instance.CurrentRunningJob.RecipeRunId, CarrierDataType.RecipeUpdated, CurStepNum.ToString(), string.Format("工艺运行中:当前第{0}步,更新程序:{1}", CurStepNum + 1, CurrentRecipeRunningName)); //notify recipe change event to TM RecipeFileManager.Instance.SaveRecipeHistory(ModuleName.System.ToString(), CurrentRecipeRunningName, CurrentRecipeContent); } } return true; } /// /// Status name /// /// public override string ToString() { return "recipe running"; } protected void sendRecipeStopToSmart() { ///工艺停止发送StreamOffCommand // NotifierSmart.SendCommand("StreamOffCommand", Reactor.ChamId); } /// /// 工艺程序估计结束时间计算 /// 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); } } } }