using System; using System.Collections.Generic; using System.Xml; using Aitex.Core.RT.Device; using Aitex.Core.RT.Event; using Aitex.Core.RT.Job; using Aitex.Core.RT.Log; using Aitex.Core.RT.RecipeCenter; using Aitex.Core.RT.Routine; using Aitex.Core.Util; using Aitex.Core.RT.SCCore; using Aitex.Platform; using Aitex.RT.Properties; using Aitex.Triton160.Common; using Aitex.Triton160.RT.Device; using Aitex.Triton160.RT.Module; namespace Aitex.Triton160.RT.Routine.Process { public class Process : CommonRoutine { /// /// 工艺程序运行引擎状态定义 /// enum RecipeEngineState { Error, RecipeCompleted, ExecStep, TimeWait, ConditionWait, StepCompleted, Paused, } enum CycleCountResetFlag { ResetByDay, ResetByRecipe, } /// /// 工艺程序数据 /// 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();//用于定时计算工艺程序估计的结束时间 private int _currentRecipeCycleCount = 0; private string _previousRecipeName; private string _processCompleteInfo; private bool _isProcessCompleted; 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; } public string CurrentJobName { 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; var ResetFlag = (int)SC.GetValue(SCName.ProcessConfig_RecipeCycleCouterFlag); lock (_lockerTotalCycle) { if (ResetFlag == (int)CycleCountResetFlag.ResetByDay) { if (_recipeTotalCycle.ContainsKey(DateTimeRecipeBaseName)) return _recipeTotalCycle[DateTimeRecipeBaseName]; } else { return _currentRecipeCycleCount; } } 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 Process(string module, string name) { Module = module; Name = name; Display = Resources.Process_Process_Process; RecipeStartTime = new DateTime(0); EstimatedTotalLeftTime = 0; } public bool Initialize() { InitCommon(); CalcEstimatedRecipeEndTime(); return true; } public Result Start(params object[] param) { RecipeStartTime = DateTime.Now; CurrentRecipeBaseName = (string)param[0]; if (String.Equals(_previousRecipeName,CurrentRecipeBaseName)) _currentRecipeCycleCount++; else { _currentRecipeCycleCount = 1; _previousRecipeName = CurrentRecipeBaseName; } CurrentRecipeRunningName = (string)param[1]; RecipeChangeNo = (int)param[2]; CurrentLotName = (string)param[3]; CurrentRecipeContent = (string)param[4]; CurrentRecipeHead = (RecipeHead)param[5]; CurrentRecipeStepList = (List)param[6]; CurrentJobName = (string)param[7]; 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); } } CurStepNum = CurStepTotalLoopCount = 0; _estimatedTimeCalcTimer.Start(1000); //TmService.NotifySusceptorStatusChanged(RecipeName, SusceptorStatus.InProcessing); //@AAA EV.PostSoundMessage(String.Format("Run Recipe {0}", CurrentRecipeRunningName)); EV.PostMessage(Module, EventEnum.RecipeStart, Module, CurrentRecipeRunningName); //更新当前的工艺状态到数据库中 Singleton.Instance.CurrentRunningJob.JobResult = JobStatus.InProcessing; Singleton.Instance.UpdateProcessStatus(Singleton.Instance.CurrentRunningJob.RecipeRunId, JobStatus.InProcessing); _state = RecipeEngineState.ExecStep; return Result.RUN; } public void PopProcessEndDialog() { if(_isProcessCompleted) { if (_trigExitMessage.Q) { EV.PostPopDialogMessage(EventLevel.InformationNoDelay, string.Format(Resources.Process_Exit_0RecipeCompletelyRun, Module), _processCompleteInfo); } } else { EV.PostPopDialogMessage(EventLevel.Warning, string.Format(Resources.Process_Exit_0RecipeWasAborted, Module), _processCompleteInfo); } } /// /// quiting current state /// /// public void Exit() { if (DeviceModel.PressureControl != null) { DeviceModel.PressureControl.EnableTolerance = false; } var ts = DateTime.Now - RecipeStartTime; var totalTime = string.Format("{0}:{1}:{2}", Convert.ToInt32(ts.TotalHours), ts.Minutes, ts.Seconds); JobInfo job = Singleton.Instance.CurrentRunningJob; _isProcessCompleted = _state == RecipeEngineState.RecipeCompleted; if (_isProcessCompleted) { //正常工艺运行结束 Singleton.Instance.AddRecord(job.RecipeRunId, CarrierDataType.ProcessNormalEnd, totalTime, "recipe finished"); _processCompleteInfo = 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.PostMessage(Module, EventEnum.ProcessSuccEnd, Module, _processCompleteInfo); _trigExitMessage.CLK = true; job.JobResult = JobStatus.Processed; } else { //人为终止工艺运行结束 Singleton.Instance.AddRecord(job.RecipeRunId, CarrierDataType.PostProcessErrorEnd, totalTime, "recipe aborted"); string _processCompleteInfo = 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.PostMessage(Module, EventEnum.RecipeAborted, Module, _processCompleteInfo); //TmProxy.NotifyProcessEnded(false); //Update susceptor information job.JobResult = JobStatus.Troubled; } if((int)SC.GetValue(SCName.ProcessConfig_ProcessFinishedReminderFlag) == 0) { PopProcessEndDialog(); } 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) || (DeviceModel.MfcGas4 != null && DeviceModel.MfcGas4.SetPoint > 0.001) || (DeviceModel.MfcGas5 != null && DeviceModel.MfcGas5.SetPoint > 0.001); } } /// /// 工艺程序运行引擎放于此处 /// public Result Monitor() { string reason = string.Empty; CalcEstimatedRecipeEndTime(); //检查软件互锁是否强制停止运行工艺程序 //if (!doRoutineRunning.Value) //{ // if (!doRoutineRunning.Check(true, out reason)) // { // EV.PostMessage(Module, EventEnum.RecipeAbortedByInterlock, Module, reason); // return Result.FAIL; // Reactor.PostEvent(new AlarmHandleCommand("软件互锁")); // } //} //工艺程序运行监控,自动打开阀门如果对应的MFC设定设置大于0 if (DeviceModel.ValveMfc1 != null && DeviceModel.MfcGas1 != null) DeviceModel.ValveMfc1.TurnValve(DeviceModel.MfcGas1.SetPoint > 0, out reason); if (DeviceModel.ValveMfc2 != null && DeviceModel.MfcGas2 != null) DeviceModel.ValveMfc2.TurnValve(DeviceModel.MfcGas2.SetPoint > 0, out reason); if (DeviceModel.ValveMfc3 != null && DeviceModel.MfcGas3 != null) DeviceModel.ValveMfc3.TurnValve(DeviceModel.MfcGas3.SetPoint > 0, out reason); if (DeviceModel.ValveMfc4 != null && DeviceModel.MfcGas4 != null) DeviceModel.ValveMfc4.TurnValve(DeviceModel.MfcGas4.SetPoint > 0, out reason); if (DeviceModel.ValveMfc5 != null && DeviceModel.MfcGas5 != null) DeviceModel.ValveMfc5.TurnValve(DeviceModel.MfcGas5.SetPoint > 0, out reason); if (DeviceModel.ValveProcessGasFinal != null) { DeviceModel.ValveProcessGasFinal.TurnValve( (DeviceModel.MfcGas1 != null && DeviceModel.MfcGas1.SetPoint > 0) || (DeviceModel.MfcGas2 != null && DeviceModel.MfcGas2.SetPoint > 0) || (DeviceModel.MfcGas3 != null && DeviceModel.MfcGas3.SetPoint > 0) || (DeviceModel.MfcGas4 != null && DeviceModel.MfcGas4.SetPoint > 0) || (DeviceModel.MfcGas5 != null && DeviceModel.MfcGas5.SetPoint > 0), out reason); } //工艺程序运行引擎 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.PostMessage(Module, EventEnum.RecipeStepStart, Module, CurrentRecipeRunningName, CurStepNum + 1, _recipeStepList[CurStepNum].StepName); //执行工艺程序命令 foreach (var recipeCmd in _recipeStepList[CurStepNum].RecipeCommands.Keys) { if (!DEVICE.CanDo(recipeCmd)) { EV.PostMessage(Module, EventEnum.RecipeItemUnknow, 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]); } } //if (string.IsNullOrEmpty(_recipeStepList[CurStepNum].EndBy) || String.Compare(_recipeStepList[CurStepNum].EndBy, "None", true) == 0) { _state = RecipeEngineState.TimeWait; } //else //{ // _state = RecipeEngineState.ConditionWait; //} } break; case RecipeEngineState.TimeWait: 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.PostMessage(Module, EventEnum.RecipeStepComplete, 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 { CurStepNum++; } if (CurStepNum >= _recipeStepList.Count) { //EV.PostMessage(Module, EventEnum.RecipeComplete, Module, RecipeName); CurStepNum = _recipeStepList.Count - 1; _state = RecipeEngineState.RecipeCompleted; return Result.DONE; } else { _state = RecipeEngineState.ExecStep; } } break; case RecipeEngineState.RecipeCompleted: { return Result.DONE; } case RecipeEngineState.Error: return Result.FAIL; default: break; } } catch (Exception ex) { EV.PostMessage(Module, EventEnum.RecipeProcessException, Module, CurStepNum + 1, "语法错误"); LOG.Write(ex); 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.PostMessage(Module, EventEnum.RecipePaused, 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 = string.Format("{0}:{1}:{2}", ((int)tspan.TotalHours).ToString("00"), tspan.Minutes.ToString("00"), tspan.Seconds.ToString("00")); curStepNode.SetAttribute("Time", timeString); LOG.Write(string.Format("执行Resume命令,将当前第{0}步时间修改为{1}", 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.PostMessage(Module, EventEnum.RecipeResumed, 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.PostMessage(Module, EventEnum.RecipeItemUnknow, 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.PostMessage(Module, EventEnum.RecipeAborted, Module, Resources.Process_AbortRecipe_UserAbortedRecipe); } /// /// 跳至工艺程序下一步 /// public void SkipCurrentRecipeStep() { if (_state == RecipeEngineState.Paused) { EV.PostMessage(Module, EventEnum.ReactorCmdReject, 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 = string.Format("{0}:{1}:{2}", ((int)tspan.TotalHours).ToString("00"), tspan.Minutes.ToString("00"), tspan.Seconds.ToString("00")); curStepNode.SetAttribute("Time", timeString); LOG.Write(string.Format("step skipped,step {0} time changed to {1}", currentStepNo, 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.RecipeStepSkipped, 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.ReadRecipeFail, Module, CurrentRecipeRunningName); return false; } else { string oldRecipeName = CurrentRecipeRunningName; CurrentRecipeRunningName = string.Format("{0}-{1}-({2})", DateTime.Now.ToString("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.PostMessage(Module, EventEnum.RecipeUpdated, 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); } } } }