Process.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Xml;
  4. using Aitex.Core.Equipment.SusceptorDefine;
  5. using Aitex.Core.RT.Device;
  6. using Aitex.Core.RT.Event;
  7. using Aitex.Core.RT.Log;
  8. using Aitex.Core.RT.OperationCenter;
  9. using Aitex.Core.RT.RecipeCenter;
  10. using Aitex.Core.RT.Routine;
  11. using Aitex.Core.Util;
  12. using MECF.Framework.Common.DBCore;
  13. using MECF.Framework.Common.Equipment;
  14. using Virgo_DRT.Devices;
  15. namespace Virgo_DRT.Modules.PMs
  16. {
  17. class ProcessRoutine : PMRoutineBase
  18. {
  19. /// <summary>
  20. /// 工艺程序运行引擎状态定义
  21. /// </summary>
  22. private enum RecipeEngineState
  23. {
  24. Error,
  25. RecipeCompleted,
  26. ExecStep,
  27. TimeWait,
  28. ConditionWait,
  29. StepCompleted,
  30. Paused,
  31. }
  32. /// <summary>
  33. /// 工艺程序数据
  34. /// </summary>
  35. private object _lockerTotalCycle = new object();
  36. private Dictionary<string, int> _recipeTotalCycle = new Dictionary<string, int>();
  37. //public bool IsAtmRecipeRun { private set; get; }
  38. private DeviceTimer _beginPauseTimer = new DeviceTimer();
  39. /// <summary>
  40. /// 防止工艺程序运行线程和外部更新工艺程序数据的线程并发进行
  41. /// </summary>
  42. private object _recipeLocker = new object();
  43. /// <summary>
  44. /// 当前工艺执行引擎的状态
  45. /// </summary>
  46. private RecipeEngineState _state = RecipeEngineState.ExecStep;
  47. private RecipeEngineState _pausedState = RecipeEngineState.ExecStep;
  48. private DeviceTimer _estimatedTimeCalcTimer = new DeviceTimer();//用于定时计算工艺程序估计的结束时间
  49. public DateTime RecipeStartTime { get; private set; }
  50. public int RecipeChangeNo { get; private set; }
  51. public string CurrentRecipeBaseName { get; private set; }
  52. public string CurrentRecipeRunningName { get; private set; }
  53. public string CurrentRecipeContent { get; private set; }
  54. public string CurrentLotName { get; set; }
  55. private List<RecipeStep> _recipeStepList = new List<RecipeStep>();
  56. public List<RecipeStep> CurrentRecipeStepList
  57. {
  58. get
  59. {
  60. return _recipeStepList;
  61. }
  62. set
  63. {
  64. _recipeStepList = value;
  65. }
  66. }
  67. public RecipeHead CurrentRecipeHead { get; set; }
  68. public int CurStepNum { get; private set; }
  69. public int CurStepTotalLoopCount { get; private set; }
  70. public double CurStepTotalTime
  71. {
  72. get
  73. {
  74. if (_recipeStepList == null || _recipeStepList.Count == 0 || _state == RecipeEngineState.RecipeCompleted || _state == RecipeEngineState.Error)
  75. return 0;
  76. return _recipeStepList[CurStepNum].StepTime * 1000;
  77. }
  78. }
  79. public int CurrentLoopCount
  80. {
  81. get;
  82. private set;
  83. }
  84. private DeviceTimer StepTimer = new DeviceTimer();
  85. public bool IsPaused
  86. {
  87. private set;
  88. get;
  89. }
  90. public string CurStepComment
  91. {
  92. get
  93. {
  94. if (_recipeStepList == null || _recipeStepList.Count == 0)
  95. return string.Empty;
  96. return _recipeStepList[CurStepNum].StepName;
  97. }
  98. }
  99. public double CurStepLeftTime
  100. {
  101. get
  102. {
  103. if (IsPaused)
  104. return StepTimer.GetTotalTime() - StepTimer.GetElapseTime() + _beginPauseTimer.GetElapseTime();
  105. //return Math.Max(0,StepTimer.GetTotalTime() - StepTimer.GetElapseTime());
  106. return StepTimer.GetTotalTime() - StepTimer.GetElapseTime();
  107. }
  108. }
  109. public double CurStepElpasedTime
  110. {
  111. get
  112. {
  113. if (_recipeStepList == null || _recipeStepList.Count == 0 || _state == RecipeEngineState.RecipeCompleted || _state == RecipeEngineState.Error)
  114. return 0;
  115. return StepTimer.GetElapseTime();
  116. }
  117. }
  118. public double CurStepTotalRfTime
  119. {
  120. get
  121. {
  122. if (_recipeStepList == null || _recipeStepList.Count == 0 || _state == RecipeEngineState.RecipeCompleted || _state == RecipeEngineState.Error)
  123. return 0;
  124. if (_recipeStepList[CurStepNum].RecipeCommands.ContainsKey("Rf.SetPower") &&
  125. !string.IsNullOrEmpty(_recipeStepList[CurStepNum].RecipeCommands["Rf.SetPower"]) &&
  126. Convert.ToDouble(_recipeStepList[CurStepNum].RecipeCommands["Rf.SetPower"]) > 0.1)
  127. return CurStepTotalTime;
  128. return 0;
  129. }
  130. }
  131. public int TotalCycle
  132. {
  133. get
  134. {
  135. if (string.IsNullOrEmpty(DateTimeRecipeBaseName))
  136. return 0;
  137. lock (_lockerTotalCycle)
  138. {
  139. if (_recipeTotalCycle.ContainsKey(DateTimeRecipeBaseName))
  140. return _recipeTotalCycle[DateTimeRecipeBaseName];
  141. }
  142. return 0;
  143. }
  144. }
  145. public string DateTimeRecipeBaseName
  146. {
  147. get
  148. {
  149. if (string.IsNullOrEmpty(CurrentRecipeBaseName))
  150. return "";
  151. return DateTime.Now.ToString("yyyyMMdd") + CurrentRecipeBaseName;
  152. }
  153. }
  154. public double PausedTime
  155. {
  156. get
  157. {
  158. return IsPaused ? _beginPauseTimer.GetElapseTime() : 0;
  159. }
  160. }
  161. public double EstimatedTotalLeftTime
  162. {
  163. get;
  164. private set;
  165. }
  166. public int RecipeTotalStepNum
  167. {
  168. get
  169. {
  170. return _recipeStepList.Count;
  171. }
  172. }
  173. public ProcessRoutine(JetPM chamber) : base(chamber)
  174. {
  175. Name = "ProcessRoutine";
  176. RecipeStartTime = new DateTime(0);
  177. EstimatedTotalLeftTime = 0;
  178. CalcEstimatedRecipeEndTime();
  179. }
  180. public Result Start(params object[] param)
  181. {
  182. RecipeStartTime = DateTime.Now;
  183. CurrentRecipeBaseName = (string)param[0];
  184. CurrentRecipeRunningName = (string)param[1];
  185. RecipeChangeNo = (int)param[2];
  186. CurrentLotName = (string)param[3];
  187. CurrentRecipeContent = (string)param[4];
  188. CurrentRecipeHead = (RecipeHead)param[5];
  189. CurrentRecipeStepList = (List<RecipeStep>)param[6];
  190. if (!_chamber.IsRFGInterlockOn)
  191. {
  192. EV.PostAlarmLog(Module, "射频电源 Interlock条件不满足");
  193. return Result.VERIFYFAIL;
  194. }
  195. lock (_lockerTotalCycle)
  196. {
  197. if (_recipeTotalCycle.ContainsKey(DateTimeRecipeBaseName))
  198. _recipeTotalCycle[DateTimeRecipeBaseName] += 1;
  199. else
  200. {
  201. _recipeTotalCycle[DateTimeRecipeBaseName] = 1;
  202. }
  203. List<string> keys = new List<string>();
  204. foreach (KeyValuePair<string, int> item in _recipeTotalCycle)
  205. {
  206. if (!item.Key.StartsWith(DateTime.Now.ToString("yyyyMMdd")))
  207. keys.Add(item.Key);
  208. }
  209. foreach (string key in keys)
  210. {
  211. _recipeTotalCycle.Remove(key);
  212. }
  213. }
  214. _chamber.OpenValve(ValveType.PROCESS, true);
  215. CurStepNum = CurStepTotalLoopCount = 0;
  216. _estimatedTimeCalcTimer.Start(1000);
  217. //EV.PostSoundMessage($"Run Recipe {CurrentRecipeRunningName}");
  218. EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 开始运行");
  219. //更新当前的工艺状态到数据库中
  220. //Singleton<PMEntity>.Instance.CurrentRunningJob.JobResult = JobStatus.InProcessing;
  221. //Singleton<ProcessRecorder>.Instance.UpdateProcessStatus(Singleton<PMEntity>.Instance.CurrentRunningJob.RecipeRunId, JobStatus.InProcessing);
  222. ProcessDataRecorder.UpdateStatus(RecipeRunGuid.ToString(), SusceptorStatus.InProcessing.ToString());
  223. _state = RecipeEngineState.ExecStep;
  224. return Result.RUN;
  225. }
  226. /// <summary>
  227. /// quiting current state
  228. /// </summary>
  229. /// <param name="nextState"></param>
  230. public void Exit()
  231. {
  232. _chamber.IsPressureToleranceEnabled = false;
  233. var ts = DateTime.Now - RecipeStartTime;
  234. var totalTime = $"{Convert.ToInt32(ts.TotalHours)}:{ts.Minutes}:{ts.Seconds}";
  235. //JobInfo job = Singleton<PMEntity>.Instance.CurrentRunningJob;
  236. bool isProcessCompleted = _state == RecipeEngineState.RecipeCompleted;
  237. if (isProcessCompleted)
  238. {
  239. //正常工艺运行结束
  240. //Singleton<ProcessRecorder>.Instance.AddRecord(job.RecipeRunId, CarrierDataType.ProcessNormalEnd, totalTime, "recipe finished");
  241. //string info = string.Format(Resources.Process_Exit_Recipe0RNStartTime1RNEndTime2RNTotalTime3,
  242. // CurrentRecipeRunningName, RecipeStartTime.ToString("yyyy/MM/dd HH:mm:ss"),
  243. // DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), totalTime);
  244. EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 正常运行完毕");
  245. //if (true)
  246. //{
  247. // EV.PostPopDialogMessage(EventLevel.Information, string.Format(Resources.Process_Exit_0RecipeCompletelyRun, Module), info);
  248. //}
  249. //job.JobResult = JobStatus.Processed;
  250. }
  251. else
  252. {
  253. //人为终止工艺运行结束
  254. //Singleton<ProcessRecorder>.Instance.AddRecord(job.RecipeRunId, CarrierDataType.PostProcessErrorEnd, totalTime, "recipe aborted");
  255. 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}",
  256. CurrentRecipeRunningName, RecipeStartTime, DateTime.Now, totalTime);
  257. EV.PostWarningLog(Module, info);
  258. EV.PostPopDialogMessage(EventLevel.Warning, $"{Module} recipe was aborted", info);
  259. //Update susceptor information
  260. //job.JobResult = JobStatus.Troubled;
  261. }
  262. //job.ProcessEndTime = DateTime.Now;
  263. //重置工艺程序名
  264. CurrentRecipeRunningName = string.Empty;
  265. CurrentLotName = string.Empty;
  266. //stop all ramping when quiting processing state
  267. //StopRamp();
  268. }
  269. //private void CheckPressureStability()
  270. //{
  271. // //如果有ThrottleValve 就检查Throttle Valve的tolerance
  272. // if (DeviceModel.PressureControl != null)
  273. // {
  274. // DeviceModel.PressureControl.EnableTolerance =
  275. // DeviceModel.MfcGas1 != null && DeviceModel.MfcGas1.SetPoint > 0.001 ||
  276. // DeviceModel.MfcGas2 != null && DeviceModel.MfcGas2.SetPoint > 0.001 ||
  277. // DeviceModel.MfcGas3 != null && DeviceModel.MfcGas3.SetPoint > 0.001;
  278. // }
  279. //}
  280. /// <summary>
  281. /// 工艺程序运行引擎放于此处
  282. /// </summary>
  283. public Result Monitor()
  284. {
  285. string reason = string.Empty;
  286. CalcEstimatedRecipeEndTime();
  287. //检查软件互锁是否强制停止运行工艺程序
  288. //if (!doRoutineRunning.Value)
  289. //{
  290. // if (!doRoutineRunning.Check(true, out reason))
  291. // {
  292. // EV.PostMessage(Module.ToString(), EventEnum.RecipeAbortedByInterlock, Module, reason);
  293. // return Result.FAIL; // Reactor.PostEvent(new AlarmHandleCommand("软件互锁"));
  294. // }
  295. //}
  296. //工艺程序运行监控,自动打开阀门如果对应的MFC设定设置大于0
  297. _chamber.Monitor();
  298. if (_chamber.IsError)
  299. return Result.FAIL;
  300. if (_chamber.CheckFactoryScrubberSysAlarm)
  301. {
  302. _chamber.OpenValve(ValveType.FAST_PUMP, false);
  303. _chamber.OpenValve(ValveType.SOFT_PUMP, false);
  304. EV.PostAlarmLog(Module, $"{Module} {Name} Factory Scrubber System is alarm ");
  305. return Result.FAIL;
  306. }
  307. //工艺程序运行引擎
  308. lock (_recipeLocker)
  309. {
  310. try
  311. {
  312. switch (_state)
  313. {
  314. case RecipeEngineState.ExecStep:
  315. {
  316. //工艺程序循环设置
  317. if (_recipeStepList[CurStepNum].IsLoopStartStep)
  318. {
  319. CurStepTotalLoopCount = _recipeStepList[CurStepNum].LoopCount;
  320. if (CurStepTotalLoopCount == 0)
  321. {
  322. CurrentLoopCount = 0;
  323. }
  324. else
  325. {
  326. CurrentLoopCount++;
  327. }
  328. }
  329. //当前工艺程序步的定时器设定
  330. StepTimer.Start(_recipeStepList[CurStepNum].StepTime * 1000);
  331. //Singleton<ProcessRecorder>.Instance.AddRecord(Singleton<PMEntity>.Instance.CurrentRunningJob.RecipeRunId,
  332. // CarrierDataType.RecipeStepStart, (CurStepNum + 1).ToString(),
  333. // string.Format(Resources.Process_Monitor_RecipeRunningStartStep01, CurStepNum + 1, _recipeStepList[CurStepNum].StepName));
  334. //发送信息到用户界面
  335. EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 第{CurStepNum + 1} 步开始:{_recipeStepList[CurStepNum].StepName}");
  336. //执行工艺程序命令
  337. foreach (var cmdkey in _recipeStepList[CurStepNum].RecipeCommands.Keys)
  338. {
  339. string recipeCmd = cmdkey;
  340. string param = _recipeStepList[CurStepNum].RecipeCommands[recipeCmd];
  341. if (string.IsNullOrWhiteSpace(param)) continue;
  342. if (!OP.CanDoOperation($"{Module}." + recipeCmd, out reason, param))
  343. {
  344. EV.PostAlarmLog(Module, $"不能执行 {recipeCmd}, {reason}");
  345. return Result.FAIL;
  346. }
  347. else
  348. {
  349. var rampTime_ms = (int)(_recipeStepList[CurStepNum].StepTime * 1000);
  350. if (_recipeStepList[CurStepNum].IsJumpStep)
  351. rampTime_ms = 0;
  352. OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param);
  353. }
  354. // if (!DEVICE.CanDo(recipeCmd))
  355. //{
  356. // EV.PostMessage(Module.ToString(), EventEnum.DefaultWarning, Module, recipeCmd);
  357. //}
  358. //else
  359. //{
  360. // var rampTime_ms = (int)(_recipeStepList[CurStepNum].StepTime * 1000);
  361. // if (_recipeStepList[CurStepNum].IsJumpStep)
  362. // rampTime_ms = 0;
  363. // string str = _recipeStepList[CurStepNum].RecipeCommands[cmdkey];
  364. // if (!string.IsNullOrEmpty(str))
  365. // DEVICE.Do(recipeCmd, rampTime_ms, false, str);
  366. //}
  367. }
  368. string endby = _recipeStepList[CurStepNum].EndBy;
  369. if (string.IsNullOrEmpty(endby) ||
  370. string.Compare(endby, "EndByStepTime", StringComparison.OrdinalIgnoreCase) == 0 ||
  371. string.Compare(endby, "EndByRfTime", StringComparison.OrdinalIgnoreCase) == 0)
  372. {
  373. _state = RecipeEngineState.TimeWait;
  374. }
  375. else
  376. {
  377. _state = RecipeEngineState.ConditionWait;
  378. }
  379. }
  380. break;
  381. case RecipeEngineState.TimeWait:
  382. _chamber.CheckPressureStability();
  383. if (StepTimer.IsTimeout())
  384. {
  385. _state = RecipeEngineState.StepCompleted;
  386. }
  387. break;
  388. case RecipeEngineState.ConditionWait:
  389. {
  390. //var endbyCondition = (EndByCondition)Enum.Parse(typeof(EndByCondition), _recipeStepList[CurStepNum].EndBy, true);
  391. //var endbyValue = Convert.ToDouble(_recipeStepList[CurStepNum].EndByValue);
  392. //if (isStepEndby(endbyCondition, endbyValue))
  393. {
  394. _state = RecipeEngineState.StepCompleted;
  395. }
  396. }
  397. break;
  398. case RecipeEngineState.Paused:
  399. break;
  400. case RecipeEngineState.StepCompleted:
  401. {
  402. //发送新的一步开始的信息到用户界面
  403. EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 第{CurStepNum + 1}步结束");
  404. //添加石墨盘信息
  405. //Singleton<ProcessRecorder>.Instance.AddRecord(Singleton<PMEntity>.Instance.CurrentRunningJob.RecipeRunId, CarrierDataType.RecipeStepNormalEnd, (CurStepNum + 1).ToString(), string.Format("recipe running:end step {0}", CurStepNum + 1));
  406. //判断是否当前步循环终止
  407. if (_recipeStepList[CurStepNum].IsLoopEndStep)
  408. {
  409. //重新读取循环的设定次数
  410. for (int nn = CurStepNum; nn >= 0; nn--)
  411. {
  412. if (_recipeStepList[nn].IsLoopStartStep)
  413. {
  414. CurStepTotalLoopCount = _recipeStepList[nn].LoopCount;
  415. break;
  416. }
  417. }
  418. if (CurrentLoopCount >= CurStepTotalLoopCount)
  419. {
  420. CurrentLoopCount = CurStepTotalLoopCount = 0;
  421. CurStepNum++;
  422. }
  423. else
  424. {
  425. int n = CurStepNum - 1;
  426. int next = -1;
  427. while (n >= 0)
  428. {
  429. if (_recipeStepList[n].IsLoopStartStep)
  430. {
  431. next = n;
  432. break;
  433. }
  434. n--;
  435. }
  436. if (next == -1)
  437. throw new Exception("Loop End control error");
  438. CurStepNum = next;
  439. }
  440. }
  441. else
  442. {
  443. if (CurStepNum < _recipeStepList.Count - 1)
  444. {
  445. CurStepNum++;
  446. _state = RecipeEngineState.ExecStep;
  447. }
  448. else
  449. {
  450. EV.PostInfoLog(Module, $"工艺 {CurrentRecipeRunningName} 完毕");
  451. CurStepNum = _recipeStepList.Count - 1;
  452. _state = RecipeEngineState.RecipeCompleted;
  453. return Result.DONE;
  454. }
  455. }
  456. }
  457. break;
  458. case RecipeEngineState.RecipeCompleted:
  459. return Result.DONE;
  460. case RecipeEngineState.Error:
  461. return Result.FAIL;
  462. default:
  463. break;
  464. }
  465. }
  466. catch (Exception ex)
  467. {
  468. EV.PostAlarmLog(Module, $"{CurStepNum + 1}, 语法错误, {ex.Message}");
  469. return Result.FAIL;
  470. }
  471. }
  472. return Result.RUN;
  473. }
  474. /// <summary>
  475. /// 暂停工艺程序运行
  476. /// </summary>
  477. public void PauseRecipe()
  478. {
  479. if (_state != RecipeEngineState.TimeWait && _state != RecipeEngineState.ConditionWait)
  480. return;
  481. if (!IsPaused)
  482. {
  483. string reason = string.Empty;
  484. IsPaused = true;
  485. _pausedState = _state;
  486. _state = RecipeEngineState.Paused;
  487. _beginPauseTimer.Start(0);
  488. //Singleton<ProcessRecorder>.Instance.AddRecord(Singleton<PMEntity>.Instance.CurrentRunningJob.RecipeRunId, CarrierDataType.RecipeStepPaused, CurStepNum.ToString(), string.Format(Resources.Process_PauseRecipe_工艺运行中当前第0步暂停, CurStepNum + 1));
  489. EV.PostInfoLog(Module, $"工艺程序'{CurrentRecipeRunningName}'第{CurStepNum + 1}步暂停");
  490. //pause mfc
  491. //pause pc
  492. //StopRamp();
  493. }
  494. }
  495. /// <summary>
  496. /// 恢复工艺程序运行
  497. /// </summary>
  498. public void ResumeRecipe()
  499. {
  500. if (IsPaused)
  501. {
  502. //update current recipe step time
  503. string recipeXml = CurrentRecipeContent;
  504. int currentStepNo = CurStepNum;
  505. XmlDocument xmlDoc = new XmlDocument();
  506. xmlDoc.LoadXml(recipeXml);
  507. var stepNodes = xmlDoc.SelectNodes("/TableRecipeData/Step");
  508. if (currentStepNo >= 0 && currentStepNo < stepNodes.Count)
  509. {
  510. var curStepNode = stepNodes[currentStepNo] as XmlElement;
  511. var curStepNewTime = CurStepTotalTime + PausedTime;
  512. if (curStepNewTime < 0)
  513. curStepNewTime = 0;
  514. TimeSpan tspan = new TimeSpan(0, 0, 0, 0, (int)curStepNewTime);
  515. var timeString =
  516. $"{((int)tspan.TotalHours).ToString("00")}:{tspan.Minutes.ToString("00")}:{tspan.Seconds.ToString("00")}";
  517. curStepNode.SetAttribute("Time", timeString);
  518. LOG.Write($"执行Resume命令,将当前第{currentStepNo}步时间修改为{timeString}");
  519. UpdateRecipe(xmlDoc.OuterXml);
  520. }
  521. //Resume recipe
  522. IsPaused = false;
  523. _state = _pausedState;
  524. //Singleton<ProcessRecorder>.Instance.AddRecord(Singleton<PMEntity>.Instance.CurrentRunningJob.RecipeRunId, CarrierDataType.RecipeStepResume, CurStepNum.ToString(), string.Format("工艺运行中:当前第{0}步,取消暂停", CurStepNum + 1));
  525. EV.PostInfoLog(Module, $"工艺程序'{CurrentRecipeRunningName}'第{CurStepNum + 1}步继续");
  526. //resume recipe
  527. double stepElapsedTime = StepTimer.GetElapseTime() - _beginPauseTimer.GetElapseTime();
  528. double stepLeftTime = StepTimer.GetTotalTime() - stepElapsedTime;
  529. if (stepLeftTime < 0)
  530. stepLeftTime = 0;
  531. //更新当前步的定时器时间
  532. StepTimer.Restart(StepTimer.GetTotalTime() + _beginPauseTimer.GetElapseTime());
  533. //重新执行当前工艺程序步
  534. foreach (var recipeCmd in _recipeStepList[CurStepNum].RecipeCommands.Keys)
  535. {
  536. if (!DEVICE.CanDo(recipeCmd))
  537. {
  538. EV.PostInfoLog(Module, $"不可识别的工艺程序控制命令{recipeCmd}");
  539. }
  540. else
  541. {
  542. var rampTime_ms = (int)(_recipeStepList[CurStepNum].StepTime * 1000);
  543. if (_recipeStepList[CurStepNum].IsJumpStep)
  544. rampTime_ms = 0;
  545. DEVICE.Do(recipeCmd, rampTime_ms, false, _recipeStepList[CurStepNum].RecipeCommands[recipeCmd]);
  546. }
  547. }
  548. }
  549. }
  550. /// <summary>
  551. /// 终止工艺程序运行
  552. /// </summary>
  553. public void AbortRecipe()
  554. {
  555. ResumeRecipe();
  556. _state = RecipeEngineState.RecipeCompleted;
  557. CalcEstimatedRecipeEndTime();
  558. //StopRamp();
  559. //send informational event
  560. //Singleton<ProcessRecorder>.Instance.AddRecord(Singleton<PMEntity>.Instance.CurrentRunningJob.RecipeRunId, CarrierDataType.RecipeStepResume, CurStepNum.ToString(), string.Format("recipe running:aborted in step {0}", CurStepNum + 1));
  561. EV.PostInfoLog(Module, "工艺程序被终止运行");
  562. }
  563. /// <summary>
  564. /// 跳至工艺程序下一步
  565. /// </summary>
  566. public void SkipCurrentRecipeStep()
  567. {
  568. if (_state == RecipeEngineState.Paused)
  569. {
  570. EV.PostInfoLog(Module, "在 Pause Recipe 状态下不能执行 Skip to next step 命令");
  571. return;
  572. }
  573. try
  574. {
  575. //update current recipe step time
  576. string recipeXml = CurrentRecipeContent;
  577. int currentStepNo = CurStepNum;
  578. XmlDocument xmlDoc = new XmlDocument();
  579. xmlDoc.LoadXml(recipeXml);
  580. var stepNodes = xmlDoc.SelectNodes("/TableRecipeData/Step");
  581. if (currentStepNo >= 0 && currentStepNo < stepNodes.Count)
  582. {
  583. var curStepNode = stepNodes[currentStepNo] as XmlElement;
  584. var curStepElapsedTime = CurStepTotalTime - CurStepLeftTime;//ms
  585. if (curStepElapsedTime < 0)
  586. curStepElapsedTime = 0;
  587. TimeSpan tspan = new TimeSpan(0, 0, 0, 0, (int)curStepElapsedTime);
  588. var timeString =
  589. $"{((int)tspan.TotalHours).ToString("00")}:{tspan.Minutes.ToString("00")}:{tspan.Seconds.ToString("00")}";
  590. curStepNode.SetAttribute("Time", timeString);
  591. LOG.Write($"step skipped,step {currentStepNo} time changed to {timeString}");
  592. //recipe update command
  593. UpdateRecipe(xmlDoc.OuterXml);
  594. }
  595. }
  596. catch (Exception ex)
  597. {
  598. LOG.Write(ex);
  599. }
  600. if (_state == RecipeEngineState.ConditionWait || _state == RecipeEngineState.TimeWait)
  601. {
  602. _state = RecipeEngineState.StepCompleted;
  603. //send informational event
  604. EV.PostMessage(Module, EventEnum.GeneralInfo, Module, CurrentRecipeRunningName, CurStepNum + 1);
  605. //Singleton<ProcessRecorder>.Instance.AddRecord(Singleton<PMEntity>.Instance.CurrentRunningJob.RecipeRunId, CarrierDataType.RecipeStepJump, CurStepNum.ToString(), string.Format("Recipe Running:current step {0},skip to next step", CurStepNum + 1));
  606. }
  607. }
  608. public bool UpdateRecipe(string newRecipeContent)
  609. {
  610. lock (_recipeLocker)
  611. {
  612. RecipeHead head = null;
  613. List<RecipeStep> newRecipeData = null;
  614. if (!Recipe.Parse(newRecipeContent, out head, out newRecipeData))
  615. {
  616. EV.PostMessage(Module, EventEnum.DefaultAlarm, Module, CurrentRecipeRunningName);
  617. return false;
  618. }
  619. else
  620. {
  621. string oldRecipeName = CurrentRecipeRunningName;
  622. CurrentRecipeRunningName =
  623. $"{DateTime.Now:yyyyMMddHHmmss}-{CurrentRecipeBaseName}-({RecipeChangeNo++})";
  624. //update local recipe data
  625. _recipeStepList = newRecipeData;
  626. CurrentRecipeContent = newRecipeContent;
  627. //Singleton<ProcessRecorder>.Instance.UpdateProcessRecipeName(Singleton<PMEntity>.Instance.CurrentRunningJob.RecipeRunId, CurrentRecipeRunningName);
  628. //update susceptor recipe name
  629. //Susceptor sus = SusceptorManager.Instance.GetSystemSusceptors()[ModuleName.System];
  630. //if (sus != null)
  631. //{
  632. // sus.RecipeName = RecipeName;
  633. // SusceptorManager.Instance.Persist();
  634. //}
  635. //send informational event
  636. EV.PostInfoLog(Module, $"运行工艺程序'{oldRecipeName}'第{CurStepNum + 1}步时更新工艺程序为'{CurrentRecipeRunningName}");
  637. //Singleton<ProcessRecorder>.Instance.AddRecord(Singleton<PMEntity>.Instance.CurrentRunningJob.RecipeRunId, CarrierDataType.RecipeUpdated, CurStepNum.ToString(), string.Format("工艺运行中:当前第{0}步,更新程序:{1}", CurStepNum + 1, CurrentRecipeRunningName));
  638. //notify recipe change event to TM
  639. RecipeFileManager.Instance.SaveRecipeHistory(ModuleName.System.ToString(), CurrentRecipeRunningName, CurrentRecipeContent);
  640. }
  641. }
  642. return true;
  643. }
  644. /// <summary>
  645. /// Status name
  646. /// </summary>
  647. /// <returns></returns>
  648. public override string ToString()
  649. {
  650. return "recipe running";
  651. }
  652. protected void sendRecipeStopToSmart()
  653. {
  654. ///工艺停止发送StreamOffCommand
  655. // NotifierSmart.SendCommand("StreamOffCommand", Reactor.ChamId);
  656. }
  657. /// <summary>
  658. /// 工艺程序估计结束时间计算
  659. /// </summary>
  660. protected void CalcEstimatedRecipeEndTime()
  661. {
  662. try
  663. {
  664. //(*计算当前工艺程序预计所需的总时间,从当前步开始计算剩余步的估算时间+已经既成事实的时间 => 总的估计时间,采用该种方式进行总工艺时间理论上最为精确*)
  665. if (!_estimatedTimeCalcTimer.IsTimeout())
  666. return;
  667. _estimatedTimeCalcTimer.Start(1000);
  668. EstimatedTotalLeftTime = 0;
  669. if (_state == RecipeEngineState.RecipeCompleted)
  670. return;
  671. if (!(CurStepNum >= 0 && CurStepNum <= _recipeStepList.Count - 1))
  672. return;
  673. if (CurStepLeftTime > 0)
  674. {
  675. EstimatedTotalLeftTime = CurStepLeftTime;
  676. }
  677. int nextBegin = CurStepNum;
  678. //(*判断当前是否处于循环之中*)
  679. bool IsInLoop = false;
  680. int iNum1 = 0;
  681. int iNum2 = 0;
  682. //int j=i;
  683. for (int j = CurStepNum; j < _recipeStepList.Count; j++)
  684. {
  685. if (_recipeStepList[j].IsLoopEndStep)
  686. {
  687. iNum2 = j;
  688. IsInLoop = true;
  689. break;
  690. }
  691. else if (j > CurStepNum && _recipeStepList[j].IsLoopStartStep)
  692. {
  693. IsInLoop = false;
  694. break;
  695. }
  696. }
  697. if (IsInLoop)
  698. {
  699. //(*当前步处于循环中*)
  700. iNum1 = CurStepNum;
  701. for (int j = CurStepNum; j >= 0; j--)
  702. {
  703. if (_recipeStepList[j].IsLoopStartStep)
  704. {
  705. iNum1 = j;
  706. break;
  707. }
  708. }
  709. for (int j = CurStepNum + 1; j <= iNum2; j++)
  710. {
  711. EstimatedTotalLeftTime += _recipeStepList[j].StepTime * 1000 * (_recipeStepList[iNum1].LoopCount - CurrentLoopCount + 1);
  712. }
  713. for (int j = iNum1; j <= CurStepNum; j++)
  714. {
  715. EstimatedTotalLeftTime += _recipeStepList[j].StepTime * 1000 * (_recipeStepList[iNum1].LoopCount - CurrentLoopCount);
  716. }
  717. nextBegin = iNum2 + 1;
  718. }
  719. else
  720. {
  721. nextBegin++;
  722. }
  723. //(*当前步处于循环外*)
  724. for (int j = nextBegin; j < _recipeStepList.Count; j++)
  725. {
  726. if (_recipeStepList[j].IsLoopStartStep)
  727. {
  728. //j=i;
  729. iNum1 = j;
  730. iNum2 = j + 1;
  731. double lr1 = 0;
  732. for (int m = j; m < _recipeStepList.Count; m++)
  733. {
  734. lr1 += _recipeStepList[m].StepTime * 1000;
  735. if (_recipeStepList[m].IsLoopEndStep)
  736. {
  737. iNum2 = m;
  738. break;
  739. }
  740. }
  741. EstimatedTotalLeftTime = EstimatedTotalLeftTime + lr1 * _recipeStepList[iNum1].LoopCount;
  742. j = iNum2;
  743. }
  744. else
  745. {
  746. EstimatedTotalLeftTime += _recipeStepList[j].StepTime * 1000;
  747. }
  748. //END_WHILE
  749. }
  750. }
  751. catch (Exception ex)
  752. {
  753. LOG.Write(ex);
  754. }
  755. }
  756. }
  757. }