Process.cs 32 KB

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