Process.cs 34 KB

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