ProcessRoutine.cs 37 KB

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