ProcessRoutine.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  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 _needcheckwafer;
  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. if (!_chamber.NotHasInterlock)
  189. {
  190. EV.PostAlarmLog(Module, "射频电源 Interlock条件不满足");
  191. return RState.Failed;
  192. }
  193. lock (_lockerTotalCycle)
  194. {
  195. if (_recipeTotalCycle.ContainsKey(DateTimeRecipeBaseName))
  196. _recipeTotalCycle[DateTimeRecipeBaseName] += 1;
  197. else
  198. {
  199. _recipeTotalCycle[DateTimeRecipeBaseName] = 1;
  200. }
  201. List<string> keys = new List<string>();
  202. foreach (KeyValuePair<string, int> item in _recipeTotalCycle)
  203. {
  204. if (!item.Key.StartsWith(DateTime.Now.ToString("yyyyMMdd")))
  205. keys.Add(item.Key);
  206. }
  207. foreach (string key in keys)
  208. {
  209. _recipeTotalCycle.Remove(key);
  210. }
  211. }
  212. if (!_chamber.IsPlus)
  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. _needcheckwafer = true;
  238. if (CurrentRecipeBaseName.ToLower().Contains("prejob") || CurrentRecipeBaseName.ToLower().Contains("preclean") || CurrentRecipeBaseName.ToLower().Contains("postclean") || CurrentRecipeBaseName.ToLower().Contains("postjob"))
  239. _needcheckwafer = false;
  240. return Runner.Start(_chamber.Module.ToString(), Name);
  241. }
  242. /// <summary>
  243. /// quiting current state
  244. /// </summary>
  245. /// <param name="nextState"></param>
  246. public void Exit()
  247. {
  248. _chamber.IsPressureToleranceEnabled = false;
  249. var ts = DateTime.Now - RecipeStartTime;
  250. var totalTime = $"{Convert.ToInt32(ts.TotalHours)}:{ts.Minutes}:{ts.Seconds}";
  251. bool isProcessCompleted = _state == RecipeEngineState.RecipeCompleted;
  252. if (isProcessCompleted)
  253. {
  254. EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 正常运行完毕");
  255. _faCallback.RecipeComplete(_chamber.Module.ToString(), CurrentRecipeBaseName);
  256. _dbCallback.RecipeComplete(_chamberEntity.RecipeRunningInfo.InnerId.ToString());
  257. _fdc.Stop();
  258. }
  259. else
  260. {
  261. _faCallback.RecipeFailed(_chamber.Module.ToString(), CurrentRecipeBaseName);
  262. _dbCallback.RecipeFailed(_chamberEntity.RecipeRunningInfo.InnerId.ToString());
  263. _chamberEntity.StopEndPoint();
  264. 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}",
  265. CurrentRecipeRunningName, RecipeStartTime, DateTime.Now, totalTime);
  266. EV.PostWarningLog(Module, info);
  267. EV.PostPopDialogMessage(EventLevel.Warning, $"{Module} recipe was aborted", info);
  268. }
  269. _chamberEntity.EndPointRecipeStop();
  270. //重置工艺程序名
  271. CurrentRecipeRunningName = string.Empty;
  272. CurrentLotName = string.Empty;
  273. var tolerance = _chamberEntity.RecipeRunningInfo.RecipeStepList[0].ToleranceCommands;
  274. foreach (var cmd in tolerance)
  275. {
  276. OP.DoOperation($"{Module}.{cmd.Key}", out string reason1, 0, new object[] { "0", "0", "0" });
  277. }
  278. }
  279. public RState Monitor()
  280. {
  281. string reason = string.Empty;
  282. CalcEstimatedRecipeEndTime();
  283. //工艺程序运行监控,自动打开阀门如果对应的MFC设定设置大于0
  284. _chamber.Monitor();
  285. if (_chamber.IsError)
  286. return RState.Failed;
  287. if (SC.GetValue<bool>($"System.ProcessEnableCheckLE"))
  288. {
  289. if ((!_chamber.LETemp1ON && !_chamber.Chamber1Disable) || (!_chamber.LETemp2ON && !_chamber.Chamber2Disable))
  290. {
  291. EV.PostAlarmLog(Module, $"腔体工艺时底座温度不能被关闭");
  292. return RState.Failed;
  293. }
  294. }
  295. //工艺程序运行引擎
  296. lock (_recipeLocker)
  297. {
  298. try
  299. {
  300. switch (_state)
  301. {
  302. case RecipeEngineState.ExecStep:
  303. {
  304. //工艺程序循环设置
  305. if (_recipeStepList[CurStepNum].IsLoopStartStep)
  306. {
  307. CurStepTotalLoopCount = _recipeStepList[CurStepNum].LoopCount;
  308. if (CurStepTotalLoopCount == 0)
  309. {
  310. CurrentLoopCount = 0;
  311. }
  312. else
  313. {
  314. CurrentLoopCount++;
  315. }
  316. }
  317. //当前工艺程序步的定时器设定
  318. StepTimer.Start(_recipeStepList[CurStepNum].StepTime * 1000);
  319. //发送信息到用户界面
  320. EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 第{CurStepNum + 1} 步开始:{_recipeStepList[CurStepNum].StepName}");
  321. //执行工艺程序命令
  322. foreach (var cmdkey in _recipeStepList[CurStepNum].RecipeCommands.Keys)
  323. {
  324. string recipeCmd = cmdkey;
  325. string param = _recipeStepList[CurStepNum].RecipeCommands[recipeCmd];
  326. if (string.IsNullOrWhiteSpace(param)) continue;
  327. if (recipeCmd == "EPD.SetConfig" && !SC.GetValue<bool>($"{Module}.EPD.IsEnabled"))
  328. continue;
  329. if (!OP.CanDoOperation($"{Module}." + recipeCmd, out reason, param))
  330. {
  331. EV.PostAlarmLog(Module, $"不能执行 {recipeCmd}, {reason}");
  332. return RState.Failed;
  333. }
  334. else
  335. {
  336. var rampTime_ms = (int)(_recipeStepList[CurStepNum].StepTime * 1000);
  337. if (_recipeStepList[CurStepNum].IsJumpStep)
  338. rampTime_ms = 0;
  339. if (recipeCmd.StartsWith("Rf1"))
  340. {
  341. if (!SC.IsATMMode && !SC.GetValueOrDefault<bool>($"System.SetUp.{Module}Chamber1Disabled.IsInstalled") && checkwafer(0))
  342. OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param);
  343. }
  344. else if (recipeCmd.StartsWith("BiasRf1"))
  345. {
  346. if (!SC.IsATMMode && !SC.GetValueOrDefault<bool>($"System.SetUp.{Module}Chamber1Disabled.IsInstalled") && WaferManager.Instance.CheckHasWafer(Module, 0))
  347. OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param);
  348. }
  349. else if (recipeCmd.StartsWith("Rf2"))
  350. {
  351. if (!SC.IsATMMode && !SC.GetValueOrDefault<bool>($"System.SetUp.{Module}Chamber2Disabled.IsInstalled") && checkwafer(1))
  352. OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param);
  353. }
  354. else if (recipeCmd.StartsWith("BiasRf2"))
  355. {
  356. if (!SC.IsATMMode && !SC.GetValueOrDefault<bool>($"System.SetUp.{Module}Chamber2Disabled.IsInstalled") && WaferManager.Instance.CheckHasWafer(Module, 1))
  357. OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param);
  358. }
  359. else if (recipeCmd == "EPD.SetConfig")
  360. {
  361. if (SC.GetValue<bool>($"{Module}.EPD.IsEnabled"))
  362. OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, CurStepNum, _recipeStepList[CurStepNum].RecipeCommands[recipeCmd]);
  363. }
  364. else if (recipeCmd == "LiftPin1.SetState")
  365. {
  366. if (!SC.GetValue<bool>($"System.SetUp.{Module}Chamber1Disabled.IsInstalled"))
  367. {
  368. OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param);
  369. }
  370. }
  371. else if (recipeCmd == "LiftPin2.SetState")
  372. {
  373. if (!SC.GetValue<bool>($"System.SetUp.{Module}Chamber2Disabled.IsInstalled"))
  374. {
  375. OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param);
  376. }
  377. }
  378. else
  379. {
  380. OP.DoOperation($"{Module}." + recipeCmd, out string reason1, rampTime_ms, param);
  381. }
  382. }
  383. }
  384. foreach (var toleranceCommand in _recipeStepList[CurStepNum].ToleranceCommands)
  385. {
  386. OP.DoOperation($"{Module}." + toleranceCommand.Key, out string reason1, 0, toleranceCommand.Value);
  387. }
  388. string endby = _recipeStepList[CurStepNum].EndBy;
  389. if (!string.IsNullOrEmpty(endby) && endby == "EndByEndPoint")
  390. {
  391. _state = RecipeEngineState.EndPointWait;
  392. }
  393. else
  394. {
  395. _state = RecipeEngineState.TimeWait;
  396. }
  397. _faCallback.RecipeStepStart(_chamber.Module.ToString(), CurrentRecipeBaseName, CurStepNum);
  398. _dbCallback.RecipeStepStart(_chamberEntity.RecipeRunningInfo.InnerId.ToString(), CurStepNum, _chamberEntity.RecipeRunningInfo.RecipeStepList[CurStepNum].StepName, (float)_chamberEntity.RecipeRunningInfo.RecipeStepList[CurStepNum].StepTime);
  399. _fdc.Start(_chamberEntity.RecipeRunningInfo.RecipeStepList[CurStepNum].RecipeCommands);
  400. }
  401. break;
  402. case RecipeEngineState.TimeWait:
  403. _chamber.CheckPressureStability();
  404. if (StepTimer.IsTimeout())
  405. {
  406. _state = RecipeEngineState.StepCompleted;
  407. }
  408. break;
  409. case RecipeEngineState.EndPointWait:
  410. {
  411. if (_chamberEntity.CheckEndPoint())
  412. {
  413. _state = RecipeEngineState.StepCompleted;
  414. }
  415. if (StepTimer.IsTimeout())
  416. {
  417. if (_recipeStepList[CurStepNum].FaultIfNoEPDTrigger)
  418. {
  419. EV.PostAlarmLog(Module, $"{_chamberEntity.Module} EPD step not triggered, timeout");
  420. return RState.Failed;
  421. }
  422. else
  423. {
  424. EV.PostWarningLog(Module, $"{_chamberEntity.Module} EPD step not triggered, skipped");
  425. }
  426. _state = RecipeEngineState.StepCompleted;
  427. }
  428. }
  429. break;
  430. case RecipeEngineState.Paused:
  431. break;
  432. case RecipeEngineState.StepCompleted:
  433. {
  434. _chamber.CheckPressureDisable();
  435. EV.PostInfoLog(Module, $"工艺程序 {CurrentRecipeRunningName} 第{CurStepNum + 1}步结束");
  436. _chamberEntity.StopEndPoint();
  437. _faCallback.RecipeStepEnd(_chamber.Module.ToString(), CurrentRecipeBaseName, CurStepNum);
  438. _dbCallback.RecipeStepEnd(_chamberEntity.RecipeRunningInfo.InnerId.ToString(), CurStepNum, _fdc.DataList);
  439. _fdc.Stop();
  440. //判断是否当前步循环终止
  441. if (_recipeStepList[CurStepNum].IsLoopEndStep)
  442. {
  443. //重新读取循环的设定次数
  444. for (int nn = CurStepNum; nn >= 0; nn--)
  445. {
  446. if (_recipeStepList[nn].IsLoopStartStep)
  447. {
  448. CurStepTotalLoopCount = _recipeStepList[nn].LoopCount;
  449. break;
  450. }
  451. }
  452. if (CurrentLoopCount >= CurStepTotalLoopCount)
  453. {
  454. CurrentLoopCount = CurStepTotalLoopCount = 0;
  455. CurStepNum++;
  456. }
  457. else
  458. {
  459. int n = CurStepNum - 1;
  460. int next = -1;
  461. while (n >= 0)
  462. {
  463. if (_recipeStepList[n].IsLoopStartStep)
  464. {
  465. next = n;
  466. break;
  467. }
  468. n--;
  469. }
  470. if (next == -1)
  471. throw new Exception("Loop End control error");
  472. CurStepNum = next;
  473. }
  474. }
  475. else
  476. {
  477. if (CurStepNum < _recipeStepList.Count - 1)
  478. {
  479. CurStepNum++;
  480. _state = RecipeEngineState.ExecStep;
  481. }
  482. else
  483. {
  484. EV.PostInfoLog(Module, $"工艺 {CurrentRecipeRunningName} 完毕");
  485. CurStepNum = _recipeStepList.Count - 1;
  486. _state = RecipeEngineState.RecipeCompleted;
  487. return RState.End;
  488. }
  489. }
  490. }
  491. break;
  492. case RecipeEngineState.RecipeCompleted:
  493. return RState.End;
  494. case RecipeEngineState.Error:
  495. return RState.Failed;
  496. default:
  497. break;
  498. }
  499. }
  500. catch (Exception ex)
  501. {
  502. EV.PostAlarmLog(Module, $"{CurStepNum + 1}, 语法错误, {ex.Message}");
  503. return RState.Failed;
  504. }
  505. }
  506. return RState.Running;
  507. }
  508. /// <summary>
  509. /// 暂停工艺程序运行
  510. /// </summary>
  511. public void PauseRecipe()
  512. {
  513. if (_state != RecipeEngineState.TimeWait && _state != RecipeEngineState.EndPointWait)
  514. return;
  515. if (!IsPaused)
  516. {
  517. string reason = string.Empty;
  518. IsPaused = true;
  519. _pausedState = _state;
  520. _state = RecipeEngineState.Paused;
  521. _beginPauseTimer.Start(0);
  522. EV.PostInfoLog(Module, $"工艺程序'{CurrentRecipeRunningName}'第{CurStepNum + 1}步暂停");
  523. }
  524. }
  525. /// <summary>
  526. /// 恢复工艺程序运行
  527. /// </summary>
  528. public void ResumeRecipe()
  529. {
  530. if (IsPaused)
  531. {
  532. //update current recipe step time
  533. string recipeXml = CurrentRecipeContent;
  534. int currentStepNo = CurStepNum;
  535. XmlDocument xmlDoc = new XmlDocument();
  536. xmlDoc.LoadXml(recipeXml);
  537. var stepNodes = xmlDoc.SelectNodes("/TableRecipeData/Step");
  538. if (currentStepNo >= 0 && currentStepNo < stepNodes.Count)
  539. {
  540. var curStepNode = stepNodes[currentStepNo] as XmlElement;
  541. var curStepNewTime = CurStepTotalTime + PausedTime;
  542. if (curStepNewTime < 0)
  543. curStepNewTime = 0;
  544. TimeSpan tspan = new TimeSpan(0, 0, 0, 0, (int)curStepNewTime);
  545. var timeString =
  546. $"{((int)tspan.TotalHours).ToString("00")}:{tspan.Minutes.ToString("00")}:{tspan.Seconds.ToString("00")}";
  547. curStepNode.SetAttribute("Time", timeString);
  548. LOG.Write($"执行Resume命令,将当前第{currentStepNo}步时间修改为{timeString}");
  549. UpdateRecipe(xmlDoc.OuterXml);
  550. }
  551. //Resume recipe
  552. IsPaused = false;
  553. _state = _pausedState;
  554. EV.PostInfoLog(Module, $"工艺程序'{CurrentRecipeRunningName}'第{CurStepNum + 1}步继续");
  555. //resume recipe
  556. double stepElapsedTime = StepTimer.GetElapseTime() - _beginPauseTimer.GetElapseTime();
  557. double stepLeftTime = StepTimer.GetTotalTime() - stepElapsedTime;
  558. if (stepLeftTime < 0)
  559. stepLeftTime = 0;
  560. //更新当前步的定时器时间
  561. StepTimer.Restart(StepTimer.GetTotalTime() + _beginPauseTimer.GetElapseTime());
  562. //重新执行当前工艺程序步
  563. foreach (var recipeCmd in _recipeStepList[CurStepNum].RecipeCommands.Keys)
  564. {
  565. if (!DEVICE.CanDo(recipeCmd))
  566. {
  567. EV.PostInfoLog(Module, $"不可识别的工艺程序控制命令{recipeCmd}");
  568. }
  569. else
  570. {
  571. var rampTime_ms = (int)(_recipeStepList[CurStepNum].StepTime * 1000);
  572. if (_recipeStepList[CurStepNum].IsJumpStep)
  573. rampTime_ms = 0;
  574. DEVICE.Do(recipeCmd, rampTime_ms, false, _recipeStepList[CurStepNum].RecipeCommands[recipeCmd]);
  575. }
  576. }
  577. }
  578. }
  579. /// <summary>
  580. /// 终止工艺程序运行
  581. /// </summary>
  582. public void AbortRecipe()
  583. {
  584. ResumeRecipe();
  585. _chamberEntity.StopEndPoint();
  586. _state = RecipeEngineState.Error;
  587. CalcEstimatedRecipeEndTime();
  588. _faCallback.RecipeFailed(_chamber.Module.ToString(), CurrentRecipeBaseName);
  589. _dbCallback.RecipeFailed(_chamberEntity.RecipeRunningInfo.InnerId.ToString());
  590. _fdc.Stop();
  591. EV.PostInfoLog(Module, "工艺程序被终止运行");
  592. }
  593. /// <summary>
  594. /// 跳至工艺程序下一步
  595. /// </summary>
  596. public void SkipCurrentRecipeStep()
  597. {
  598. if (_state == RecipeEngineState.Paused)
  599. {
  600. EV.PostInfoLog(Module, "在 Pause Recipe 状态下不能执行 Skip to next step 命令");
  601. return;
  602. }
  603. try
  604. {
  605. //update current recipe step time
  606. string recipeXml = CurrentRecipeContent;
  607. int currentStepNo = CurStepNum;
  608. XmlDocument xmlDoc = new XmlDocument();
  609. xmlDoc.LoadXml(recipeXml);
  610. var stepNodes = xmlDoc.SelectNodes("/TableRecipeData/Step");
  611. if (currentStepNo >= 0 && currentStepNo < stepNodes.Count)
  612. {
  613. var curStepNode = stepNodes[currentStepNo] as XmlElement;
  614. var curStepElapsedTime = CurStepTotalTime - CurStepLeftTime;//ms
  615. if (curStepElapsedTime < 0)
  616. curStepElapsedTime = 0;
  617. //TimeSpan tspan = new TimeSpan(0, 0, 0, 0, (int)curStepElapsedTime);
  618. //var timeString =
  619. // $"{((int)tspan.TotalHours).ToString("00")}:{tspan.Minutes.ToString("00")}:{tspan.Seconds.ToString("00")}";
  620. string timeString = ((int)(curStepElapsedTime / 1000)).ToString();
  621. curStepNode.SetAttribute("Time", timeString);
  622. LOG.Write($"step skipped,step {currentStepNo} time changed to {timeString}");
  623. //recipe update command
  624. UpdateRecipe(xmlDoc.OuterXml);
  625. }
  626. }
  627. catch (Exception ex)
  628. {
  629. LOG.Write(ex);
  630. }
  631. if (_state == RecipeEngineState.EndPointWait || _state == RecipeEngineState.TimeWait)
  632. {
  633. _state = RecipeEngineState.StepCompleted;
  634. //send informational event
  635. EV.PostMessage(Module, EventEnum.GeneralInfo, Module, CurrentRecipeRunningName, CurStepNum + 1);
  636. }
  637. }
  638. public bool UpdateRecipe(string newRecipeContent)
  639. {
  640. lock (_recipeLocker)
  641. {
  642. RecipeHead head = null;
  643. List<RecipeStep> newRecipeData = null;
  644. if (!Recipe.Parse(Module, newRecipeContent, out head, out newRecipeData))
  645. {
  646. EV.PostMessage(Module, EventEnum.DefaultAlarm, Module, CurrentRecipeRunningName);
  647. return false;
  648. }
  649. else
  650. {
  651. string oldRecipeName = CurrentRecipeRunningName;
  652. CurrentRecipeRunningName =
  653. $"{DateTime.Now:yyyyMMddHHmmss}-{CurrentRecipeBaseName}-({RecipeChangeNo++})";
  654. //update local recipe data
  655. _recipeStepList = newRecipeData;
  656. CurrentRecipeContent = newRecipeContent;
  657. //send informational event
  658. EV.PostInfoLog(Module, $"运行工艺程序'{oldRecipeName}'第{CurStepNum + 1}步时更新工艺程序为'{CurrentRecipeRunningName}");
  659. RecipeFileManager.Instance.SaveRecipeHistory(ModuleName.System.ToString(), CurrentRecipeRunningName, CurrentRecipeContent);
  660. }
  661. }
  662. return true;
  663. }
  664. private bool checkwafer(int slot)
  665. {
  666. if (_needcheckwafer)
  667. return WaferManager.Instance.CheckHasWafer(Module, slot);
  668. else
  669. return true;
  670. }
  671. public override void Abort()
  672. {
  673. _dbCallback.RecipeFailed(_chamberEntity.RecipeRunningInfo.InnerId.ToString());
  674. base.Abort();
  675. }
  676. public override string ToString()
  677. {
  678. return "recipe running";
  679. }
  680. /// <summary>
  681. /// 工艺程序估计结束时间计算
  682. /// </summary>
  683. protected void CalcEstimatedRecipeEndTime()
  684. {
  685. try
  686. {
  687. //(*计算当前工艺程序预计所需的总时间,从当前步开始计算剩余步的估算时间+已经既成事实的时间 => 总的估计时间,采用该种方式进行总工艺时间理论上最为精确*)
  688. if (!_estimatedTimeCalcTimer.IsTimeout())
  689. return;
  690. _estimatedTimeCalcTimer.Start(1000);
  691. EstimatedTotalLeftTime = 0;
  692. if (_state == RecipeEngineState.RecipeCompleted)
  693. return;
  694. if (!(CurStepNum >= 0 && CurStepNum <= _recipeStepList.Count - 1))
  695. return;
  696. if (CurStepLeftTime > 0)
  697. {
  698. EstimatedTotalLeftTime = CurStepLeftTime;
  699. }
  700. int nextBegin = CurStepNum;
  701. //(*判断当前是否处于循环之中*)
  702. bool IsInLoop = false;
  703. int iNum1 = 0;
  704. int iNum2 = 0;
  705. //int j=i;
  706. for (int j = CurStepNum; j < _recipeStepList.Count; j++)
  707. {
  708. if (_recipeStepList[j].IsLoopEndStep)
  709. {
  710. iNum2 = j;
  711. IsInLoop = true;
  712. break;
  713. }
  714. else if (j > CurStepNum && _recipeStepList[j].IsLoopStartStep)
  715. {
  716. IsInLoop = false;
  717. break;
  718. }
  719. }
  720. if (IsInLoop)
  721. {
  722. //(*当前步处于循环中*)
  723. iNum1 = CurStepNum;
  724. for (int j = CurStepNum; j >= 0; j--)
  725. {
  726. if (_recipeStepList[j].IsLoopStartStep)
  727. {
  728. iNum1 = j;
  729. break;
  730. }
  731. }
  732. for (int j = CurStepNum + 1; j <= iNum2; j++)
  733. {
  734. EstimatedTotalLeftTime += _recipeStepList[j].StepTime * 1000 * (_recipeStepList[iNum1].LoopCount - CurrentLoopCount + 1);
  735. }
  736. for (int j = iNum1; j <= CurStepNum; j++)
  737. {
  738. EstimatedTotalLeftTime += _recipeStepList[j].StepTime * 1000 * (_recipeStepList[iNum1].LoopCount - CurrentLoopCount);
  739. }
  740. nextBegin = iNum2 + 1;
  741. }
  742. else
  743. {
  744. nextBegin++;
  745. }
  746. //(*当前步处于循环外*)
  747. for (int j = nextBegin; j < _recipeStepList.Count; j++)
  748. {
  749. if (_recipeStepList[j].IsLoopStartStep)
  750. {
  751. //j=i;
  752. iNum1 = j;
  753. iNum2 = j + 1;
  754. double lr1 = 0;
  755. for (int m = j; m < _recipeStepList.Count; m++)
  756. {
  757. lr1 += _recipeStepList[m].StepTime * 1000;
  758. if (_recipeStepList[m].IsLoopEndStep)
  759. {
  760. iNum2 = m;
  761. break;
  762. }
  763. }
  764. EstimatedTotalLeftTime = EstimatedTotalLeftTime + lr1 * _recipeStepList[iNum1].LoopCount;
  765. j = iNum2;
  766. }
  767. else
  768. {
  769. EstimatedTotalLeftTime += _recipeStepList[j].StepTime * 1000;
  770. }
  771. //END_WHILE
  772. }
  773. }
  774. catch (Exception ex)
  775. {
  776. LOG.Write(ex);
  777. }
  778. }
  779. }
  780. }