WaferHolderTask.cs 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056
  1. using Aitex.Core.RT.Log;
  2. using Aitex.Core.Util;
  3. using MECF.Framework.Common.Equipment;
  4. using MECF.Framework.Common.SubstrateTrackings;
  5. using MECF.Framework.Common.WaferHolder;
  6. using CyberX8_Core;
  7. using CyberX8_RT.Modules.Loader;
  8. using CyberX8_RT.Modules.PUF;
  9. using CyberX8_RT.Modules;
  10. using CyberX8_RT.Schedulers;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.ComponentModel;
  14. using System.Linq;
  15. using System.Text;
  16. using System.Threading.Tasks;
  17. using System.Windows.Documents;
  18. using Aitex.Core.Common;
  19. using MECF.Framework.Common.CommonData;
  20. using MECF.Framework.Common.Jobs;
  21. using Aitex.Core.RT.Fsm;
  22. using MECF.Framework.Common.RecipeCenter;
  23. using MECF.Framework.Common.ToolLayout;
  24. using Aitex.Core.RT.Routine;
  25. using CyberX8_RT.Schedulers.Transporter;
  26. namespace CyberX8_RT.Dispatch
  27. {
  28. public enum WaferHolderTaskState
  29. {
  30. Created,
  31. Processing,
  32. End,
  33. Error,
  34. Canceled
  35. }
  36. /// <summary>
  37. /// WaferHolder
  38. /// </summary>
  39. /// <param name="info"></param>
  40. /// <param name="schedulerSequence"></param>
  41. public delegate void WaferHolderSchedulerComplete(WaferHolderInfo info, SchedulerSequence schedulerSequence);
  42. /// <summary>
  43. /// WaferHolderTask完成
  44. /// </summary>
  45. /// <param name="id"></param>
  46. public delegate void WaferHolderTaskComplete(string id);
  47. public class WaferHolderTask
  48. {
  49. #region 内部变量
  50. /// <summary>
  51. /// 调度步骤
  52. /// </summary>
  53. private List<SchedulerSequence> _schedulerSequences;
  54. /// <summary>
  55. /// 当前步骤索引
  56. /// </summary>
  57. private int _currentSequenceIndex = 0;
  58. /// <summary>
  59. /// PUF实例
  60. /// </summary>
  61. private PUFEntity _puf1Entity;
  62. /// <summary>
  63. /// PUF实例
  64. /// </summary>
  65. private PUFEntity _puf2Entity;
  66. /// <summary>
  67. /// 是否开始执行
  68. /// </summary>
  69. private bool _isStart = false;
  70. /// <summary>
  71. /// 暂停时的索引
  72. /// </summary>
  73. private int _pausedIndex = -1;
  74. /// <summary>
  75. /// 错误上升触发
  76. /// </summary>
  77. private R_TRIG _sequenceErrorTigger = new R_TRIG();
  78. /// <summary>
  79. /// Sequence类型
  80. /// </summary>
  81. private string _sequenceType = "";
  82. /// <summary>
  83. /// FA回复
  84. /// </summary>
  85. private SchedulerFACallback _schedulerFACallback = new SchedulerFACallback();
  86. #endregion
  87. #region 属性
  88. /// <summary>
  89. /// ID
  90. /// </summary>
  91. public string ID { get;private set; }
  92. /// <summary>
  93. /// WaferHolder属性
  94. /// </summary>
  95. public WaferHolderInfo WaferHolderInfo { get;private set; }
  96. /// <summary>
  97. /// Process Job信息
  98. /// </summary>
  99. public ProcessJobInfo ProcessJobInfo { get;private set; }
  100. /// <summary>
  101. /// 状态
  102. /// </summary>
  103. public WaferHolderTaskState State { get; private set; }
  104. /// <summary>
  105. /// 步骤完成事件
  106. /// </summary>
  107. public event WaferHolderSchedulerComplete OnSchedulerComplete;
  108. /// <summary>
  109. /// 任务完成事件
  110. /// </summary>
  111. public event WaferHolderTaskComplete OnTaskComplete;
  112. /// <summary>
  113. /// 调度集合
  114. /// </summary>
  115. public List<SchedulerSequence> SchedulerSequences { get { return _schedulerSequences; } }
  116. #endregion
  117. /// <summary>
  118. /// 构造函数
  119. /// </summary>
  120. /// <param name="waferHolderInfo"></param>
  121. /// <param name="schedulerSequences"></param>
  122. public WaferHolderTask(WaferHolderInfo waferHolderInfo,ProcessJobInfo processJobInfo)
  123. {
  124. ID=Guid.NewGuid().ToString();
  125. WaferHolderInfo=waferHolderInfo;
  126. WaferHolderInfo.Status = WaferHolderStatus.Normal;
  127. if (processJobInfo != null)
  128. {
  129. WaferHolderInfo.SequenceId = processJobInfo.SequenceRecipe.Ppid;
  130. WaferHolderInfo.SequenceRecipe = processJobInfo.SequenceRecipe;
  131. _sequenceType = processJobInfo.SequenceRecipe.SequenceType;
  132. WaferHolderInfo.CurrentControlJobId = processJobInfo.ControlJobName;
  133. WaferHolderInfo.LotId = processJobInfo.LotName;
  134. }
  135. else
  136. {
  137. AnalyseProductionProcessJobInfo();
  138. }
  139. ProcessJobInfo=processJobInfo;
  140. State = WaferHolderTaskState.Created;
  141. _puf1Entity = Singleton<RouteManager>.Instance.GetModule<PUFEntity>(ModuleName.PUF1.ToString());
  142. _puf2Entity = Singleton<RouteManager>.Instance.GetModule<PUFEntity>(ModuleName.PUF2.ToString());
  143. }
  144. /// <summary>
  145. /// 初始化调度步骤
  146. /// </summary>
  147. /// <param name="schedulers"></param>
  148. public void InitialSchedulers(List<SchedulerSequence> schedulers)
  149. {
  150. _schedulerSequences = schedulers;
  151. }
  152. /// <summary>
  153. /// 分析生产片的ProcessJob信息
  154. /// </summary>
  155. private void AnalyseProductionProcessJobInfo()
  156. {
  157. if (!string.IsNullOrEmpty(WaferHolderInfo.WaferAId))
  158. {
  159. WaferInfo waferAInfo = WaferManager.Instance.GetWaferByWaferId(WaferHolderInfo.WaferAId);
  160. if (waferAInfo != null && waferAInfo.WaferType == WaferType.Production)
  161. {
  162. ProcessJobInfo = waferAInfo.ProcessJob;
  163. _sequenceType = ProcessJobInfo.SequenceRecipe.SequenceType;
  164. WaferHolderInfo.CurrentControlJobId = ProcessJobInfo.ControlJobName;
  165. WaferHolderInfo.LotId = ProcessJobInfo.LotName;
  166. WaferHolderInfo.SequenceRecipe = ProcessJobInfo.SequenceRecipe;
  167. return;
  168. }
  169. }
  170. if (!string.IsNullOrEmpty(WaferHolderInfo.WaferBId))
  171. {
  172. WaferInfo waferBInfo = WaferManager.Instance.GetWaferByWaferId(WaferHolderInfo.WaferBId);
  173. if (waferBInfo != null && waferBInfo.WaferType == WaferType.Production)
  174. {
  175. ProcessJobInfo = waferBInfo.ProcessJob;
  176. _sequenceType = ProcessJobInfo.SequenceRecipe.SequenceType;
  177. WaferHolderInfo.CurrentControlJobId = ProcessJobInfo.ControlJobName;
  178. WaferHolderInfo.LotId = ProcessJobInfo.LotName;
  179. WaferHolderInfo.SequenceRecipe = ProcessJobInfo.SequenceRecipe;
  180. }
  181. }
  182. }
  183. /// <summary>
  184. /// 执行
  185. /// </summary>
  186. public void Run()
  187. {
  188. if(_currentSequenceIndex>=_schedulerSequences.Count)
  189. {
  190. State = WaferHolderTaskState.End;
  191. if(OnTaskComplete!=null)
  192. {
  193. OnTaskComplete(ID);
  194. }
  195. FaModuleNotifier.Instance.NotifyWaferShuttleJobEnd(WaferHolderInfo);
  196. LOG.WriteLog(eEvent.EV_SEQUENCE, "Sequence", $"WaferHolder {WaferHolderInfo.Id} task complete");
  197. return;
  198. }
  199. if (!_isStart)
  200. {
  201. bool preCondition = CheckStartCondition();
  202. if (!preCondition)
  203. {
  204. return;
  205. }
  206. LOG.WriteLog(eEvent.EV_SEQUENCE, "Sequence", $"{WaferHolderInfo.Id} prepare Start {_currentSequenceIndex + 1} sequence");
  207. _isStart = true;
  208. if (WaferHolderInfo.SchedulerModules == null)
  209. {
  210. WaferHolderInfo.SchedulerModules = new List<string>();
  211. }
  212. WaferHolderInfo.SchedulerModules.Clear();
  213. }
  214. SchedulerSequence sequence = _schedulerSequences[_currentSequenceIndex];
  215. //暂停中
  216. if (sequence.State == RState.Init && _currentSequenceIndex >= _pausedIndex && _pausedIndex != -1)
  217. {
  218. return;
  219. }
  220. if (_currentSequenceIndex == _schedulerSequences.Count - 1 && sequence.State == RState.End)
  221. {
  222. State=WaferHolderTaskState.End;
  223. if (OnTaskComplete != null)
  224. {
  225. OnTaskComplete(ID);
  226. }
  227. FaModuleNotifier.Instance.NotifyWaferShuttleJobEnd(WaferHolderInfo);
  228. LOG.WriteLog(eEvent.EV_SEQUENCE, "Sequence", $"WaferHolder {WaferHolderInfo.Id} task complete");
  229. return;
  230. }
  231. if(_currentSequenceIndex<_schedulerSequences.Count)
  232. {
  233. if(sequence.State==RState.Init)
  234. {
  235. if(sequence.SchedulerModule==null)
  236. {
  237. return;
  238. }
  239. if(!BufferWaferHolderJudgeResource(sequence))
  240. {
  241. return;
  242. }
  243. string reason = "";
  244. bool sequeceCondition = sequence.SchedulerModule.CheckPrecondition(_schedulerSequences, _currentSequenceIndex, sequence.Parameters,WaferHolderInfo.Id,ref reason);
  245. if (sequeceCondition)
  246. {
  247. bool result = sequence.SchedulerModule.RunProcess(sequence.Recipe,sequence.Parameters, sequence.SynchronousModuleMessages);
  248. if (result)
  249. {
  250. if (State == WaferHolderTaskState.Created)
  251. {
  252. State = WaferHolderTaskState.Processing;
  253. FaModuleNotifier.Instance.NotifyWaferShuttleJobStart(WaferHolderInfo);
  254. }
  255. sequence.State = RState.Running;
  256. sequence.StartTime = DateTime.Now;
  257. LOG.WriteLog(eEvent.EV_SEQUENCE, "Sequence", $"{WaferHolderInfo.Id} Start {_currentSequenceIndex + 1} sequence {sequence.SchedulerModule.Module}");
  258. }
  259. else
  260. {
  261. if (sequence.ModuleName == ModuleName.Transporter1&&sequence.NextModuleType==ModuleType.Metal)
  262. {
  263. if (!CheckAfterMetalAvaible())
  264. {
  265. LOG.WriteLog(eEvent.EV_SEQUENCE, "Sequence", $"{WaferHolderInfo.Id} {_currentSequenceIndex + 1} sequence {sequence.SchedulerModule.Module} has no avaible metal");
  266. TransporterAction action = sequence.Parameters as TransporterAction;
  267. if (action.ActionMsg == Modules.Transporter.TransporterMSG.Transfer)
  268. {
  269. WaferHolderMoveItem waferHolderMoveItem = action.Parameter as WaferHolderMoveItem;
  270. waferHolderMoveItem.DestModuleType = ModuleType.Dryer;
  271. waferHolderMoveItem.DestModule = ModuleName.Unknown;
  272. LOG.WriteLog(eEvent.EV_SEQUENCE, "Sequence", $"{WaferHolderInfo.Id} {_currentSequenceIndex + 1} sequence {sequence.SchedulerModule.Module} next sequence changed to dryer");
  273. }
  274. }
  275. }
  276. }
  277. }
  278. else
  279. {
  280. _sequenceErrorTigger.CLK = !sequeceCondition;
  281. if (_sequenceErrorTigger.Q)
  282. {
  283. LOG.WriteLog(eEvent.EV_SEQUENCE, "System", $"{WaferHolderInfo.Id} Start {_currentSequenceIndex + 1} sequence {sequence.SchedulerModule.Module} failed,{reason}");
  284. }
  285. }
  286. }
  287. else if(sequence.State==RState.Running)
  288. {
  289. sequence.SchedulerModule.MonitorProcess(sequence,false);
  290. _sequenceErrorTigger.CLK = sequence.SchedulerModule.IsError;
  291. if (sequence.SchedulerModule.IsError)
  292. {
  293. //用于LoadTransporter取的WaferHolder Id不一致,则取消当前WaferHolderTask
  294. if (sequence.ModuleName == ModuleName.Transporter2)
  295. {
  296. LOG.WriteLog(eEvent.EV_SEQUENCE, "Sequence", $"{WaferHolderInfo.Id} {_currentSequenceIndex + 1} sequence {sequence.SchedulerModule.Module} failed,task canceled");
  297. State = WaferHolderTaskState.Canceled;
  298. return;
  299. }
  300. if (_sequenceErrorTigger.Q)
  301. {
  302. AnalyseSchedulerErrorState(sequence);
  303. }
  304. }
  305. else if(sequence.SchedulerModule.IsIdle)
  306. {
  307. sequence.EndTime = DateTime.Now;
  308. sequence.State = RState.End;
  309. sequence.ProcessMilliSeconds=sequence.EndTime.Subtract(sequence.StartTime).TotalMilliseconds;
  310. if(sequence.IsProcessSequece)
  311. {
  312. UpdateWaferHolderProcessingStatus();
  313. }
  314. if(sequence.IsLastProcessSequence)
  315. {
  316. UpdateWaferHolderProcessComplete();
  317. }
  318. if (OnSchedulerComplete != null)
  319. {
  320. OnSchedulerComplete(WaferHolderInfo, sequence);
  321. }
  322. UpdateLoaderToBufferWaferHolderToloaderStatus(sequence);
  323. LOG.WriteLog(eEvent.EV_SEQUENCE, "System", $"{WaferHolderInfo.Id} {_currentSequenceIndex + 1} sequence {sequence.ModuleName} complete, time length {sequence.ProcessMilliSeconds}");
  324. _currentSequenceIndex++;
  325. AutoChangeLoaderTransporterScheduler();
  326. //检验rinse后面是否存在可用的Metal
  327. if (sequence.ModuleType == ModuleType.Rinse)
  328. {
  329. AnalyseRinseAfterMetalAvaible(sequence);
  330. }
  331. else if (sequence.ModuleType == ModuleType.Prewet)
  332. {
  333. AnalysePrewetAfterMetalAvaible(sequence);
  334. }
  335. }
  336. }
  337. else if (sequence.State == RState.End)
  338. {
  339. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} {_currentSequenceIndex + 1} sequence is End");
  340. _currentSequenceIndex++;
  341. }
  342. }
  343. }
  344. /// <summary>
  345. /// 更新任务结束
  346. /// </summary>
  347. public void UpdateDryerLastSchedulerComplete()
  348. {
  349. SchedulerSequence sequence = _schedulerSequences[_currentSequenceIndex];
  350. sequence.EndTime = DateTime.Now;
  351. sequence.State = RState.End;
  352. sequence.ProcessMilliSeconds = sequence.EndTime.Subtract(sequence.StartTime).TotalMilliseconds;
  353. LOG.WriteLog(eEvent.EV_SEQUENCE, "System", $"{WaferHolderInfo.Id} {_currentSequenceIndex+1} sequence {sequence.ModuleName} complete, time length {sequence.ProcessMilliSeconds}");
  354. State = WaferHolderTaskState.End;
  355. if (OnTaskComplete != null)
  356. {
  357. OnTaskComplete(ID);
  358. }
  359. LOG.WriteLog(eEvent.EV_SEQUENCE, "Sequence", $"WaferHolder {WaferHolderInfo.Id} task complete");
  360. }
  361. /// <summary>
  362. /// 更新从Loader到Buffer的WaferHolder更新为不可以拉回至Loader
  363. /// </summary>
  364. /// <param name="sequence"></param>
  365. private void UpdateLoaderToBufferWaferHolderToloaderStatus(SchedulerSequence sequence)
  366. {
  367. string currentLocation = WaferHolderInfo.CurrentLocation;
  368. if(!Enum.TryParse(currentLocation,out ModuleName locationName))
  369. {
  370. return;
  371. }
  372. if (!ModuleHelper.IsBuffer(locationName))
  373. {
  374. return;
  375. }
  376. if (sequence.ModuleName == ModuleName.Transporter2)
  377. {
  378. TransporterAction transporterAction = sequence.Parameters as TransporterAction;
  379. if (transporterAction.ActionMsg == Modules.Transporter.TransporterMSG.Transfer)
  380. {
  381. WaferHolderMoveItem waferHolderMoveItem = transporterAction.Parameter as WaferHolderMoveItem;
  382. if (waferHolderMoveItem.SourceModule == ModuleName.Loader1 && waferHolderMoveItem.DestModuleType == ModuleType.Buffer)
  383. {
  384. WaferHolderInfo.IsToLoader = false;
  385. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} IsToLoader Changed to false;");
  386. }
  387. else if (waferHolderMoveItem.DestModuleType == ModuleType.Buffer && waferHolderMoveItem.SourceModuleType != ModuleType.Loader)
  388. {
  389. WaferHolderInfo.IsToLoader = true;
  390. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} IsToLoader Changed to true;");
  391. }
  392. }
  393. }
  394. }
  395. /// <summary>
  396. /// 分析错误状态下的调度
  397. /// </summary>
  398. private void AnalyseSchedulerErrorState(SchedulerSequence sequence)
  399. {
  400. if (sequence.ModuleType == ModuleType.Prewet)
  401. {
  402. AnalyseSchedulerPrewetErrorState(sequence);
  403. }
  404. else if (sequence.ModuleType == ModuleType.Dryer)
  405. {
  406. AnalyseSchedulerDryerErrorState(sequence);
  407. }
  408. else if (sequence.ModuleType == ModuleType.Rinse)
  409. {
  410. AnalyseSchedulerRinseErrorState(sequence);
  411. }
  412. else if (sequence.ModuleType == ModuleType.Metal)
  413. {
  414. AnalyseSchedulerMetalErrorState(sequence);
  415. }
  416. }
  417. /// <summary>
  418. /// 判定Buffer中WaferHolder的后面工序是否存在可用资源,不存在的话,则Task直接结束
  419. /// </summary>
  420. /// <param name="sequence"></param>
  421. private bool BufferWaferHolderJudgeResource(SchedulerSequence sequence)
  422. {
  423. string currentLocation = WaferHolderInfo.CurrentLocation;
  424. if (Enum.TryParse(currentLocation, out ModuleName moduleName))
  425. {
  426. //当前WaferHolder处于Buffer中,当前步骤不是第一步,当前调度模块为transporter2
  427. if (ModuleHelper.IsBuffer(moduleName)&&_currentSequenceIndex!=0&&sequence.ModuleName==ModuleName.Transporter2)
  428. {
  429. if (!SchedulerSequenceRecipeManager.Instance.ExistAvaibleProcessCell(WaferHolderInfo.SequenceRecipe, false))
  430. {
  431. for (int i = _currentSequenceIndex; i < _schedulerSequences.Count; i++)
  432. {
  433. SchedulerSequence item = _schedulerSequences[i];
  434. item.State = RState.End;
  435. }
  436. _currentSequenceIndex = _schedulerSequences.Count - 1;
  437. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} exist no avaible cell");
  438. WaferHolderInfo.IsToLoader = true;
  439. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} IsToLoader Changed to true");
  440. RemoveWaferHolderSRDScheduler();
  441. UpdateWaferHolderMisProcessedStatus();
  442. return false;
  443. }
  444. }
  445. }
  446. return true;
  447. }
  448. /// <summary>
  449. /// 检验Prewet后面的Metal是否存在可用
  450. /// </summary>
  451. private void AnalysePrewetAfterMetalAvaible(SchedulerSequence schedulerSequence)
  452. {
  453. //获取可用的Metal以及Rinse
  454. if (!CheckAfterMetalAvaible())
  455. {
  456. LOG.WriteLog(eEvent.WARN_SCHEDULER, "System", $"{WaferHolderInfo.Id} has no avaible metal after prewet");
  457. SkipAfterMetal(schedulerSequence,true);
  458. RemoveWaferHolderSRDScheduler();
  459. UpdateWaferHolderMisProcessedStatus();
  460. //SchedulerSequence nextSchedulerSequence = _schedulerSequences[_currentSequenceIndex];
  461. //if (nextSchedulerSequence.ModuleName != ModuleName.Transporter1)
  462. //{
  463. // return;
  464. //}
  465. //WaferHolderMoveItem waferHolderMoveItem = nextSchedulerSequence.Parameters as WaferHolderMoveItem;
  466. //waferHolderMoveItem.DestModuleType = ModuleType.Dryer;
  467. //waferHolderMoveItem.DestModule = ModuleName.Unknown;
  468. }
  469. }
  470. /// <summary>
  471. /// 检验Rinse后面的Metal是否存在可用
  472. /// </summary>
  473. /// <returns></returns>
  474. private void AnalyseRinseAfterMetalAvaible(SchedulerSequence schedulerSequence)
  475. {
  476. //获取可用的Metal以及Rinse
  477. if(!CheckAfterMetalAvaible())
  478. {
  479. LOG.WriteLog(eEvent.WARN_SCHEDULER, "System", $"{WaferHolderInfo.Id} has no avaible metal after rinse");
  480. SkipAfterMetal(schedulerSequence,true);
  481. RemoveWaferHolderSRDScheduler();
  482. UpdateWaferHolderMisProcessedStatus();
  483. }
  484. }
  485. /// <summary>
  486. /// 检验后续Metal可用性
  487. /// </summary>
  488. /// <returns></returns>
  489. private bool CheckAfterMetalAvaible()
  490. {
  491. for (int i = _currentSequenceIndex + 1; i < _schedulerSequences.Count; i++)
  492. {
  493. SchedulerSequence sequence = _schedulerSequences[i];
  494. if (sequence.ModuleType != ModuleType.Metal)
  495. {
  496. continue ;
  497. }
  498. if (sequence.Recipe == null || !(sequence.Recipe is DepRecipe))
  499. {
  500. continue;
  501. }
  502. DepRecipe depRecipe = (DepRecipe)sequence.Recipe;
  503. bool result= SchedulerSequenceManager.Instance.CalculateAvaibleMetalCellByChemistry(depRecipe.Chemistry, sequence.SequenceType,sequence.WaferSize);
  504. if (!result)
  505. {
  506. return false;
  507. }
  508. }
  509. return true;
  510. }
  511. #region Analyse ErrorState Scheduler
  512. /// <summary>
  513. /// Prewet出错重新调度
  514. /// </summary>
  515. /// <param name="sequence"></param>
  516. private void AnalyseSchedulerPrewetErrorState(SchedulerSequence sequence)
  517. {
  518. SkipAfterSchedulerToDry(ModuleName.Transporter2);
  519. sequence.State = RState.End;
  520. sequence.EndTime = DateTime.Now;
  521. sequence.ProcessMilliSeconds= sequence.EndTime.Subtract(sequence.StartTime).TotalMilliseconds;
  522. RemoveWaferHolderSRDScheduler();
  523. UpdateWaferHolderMisProcessedStatus();
  524. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} Prewet meet error,scheduler changed to transporter2");
  525. }
  526. /// <summary>
  527. /// 路过后面调度直接至Dryer
  528. /// </summary>
  529. private void SkipAfterSchedulerToDry(ModuleName transporterName)
  530. {
  531. for (int i = _currentSequenceIndex=1; i < _schedulerSequences.Count; i++)
  532. {
  533. SchedulerSequence item = _schedulerSequences[i];
  534. if (item.ModuleName != ModuleName.Transporter1)
  535. {
  536. item.State = RState.End;
  537. item.ProcessMilliSeconds = 0;
  538. }
  539. else
  540. {
  541. if (item.Parameters is TransporterAction)
  542. {
  543. TransporterAction action= (TransporterAction)item.Parameters;
  544. if (action.ActionMsg == Modules.Transporter.TransporterMSG.Transfer)
  545. {
  546. WaferHolderMoveItem waferHolderMoveItem = action.Parameter as WaferHolderMoveItem;
  547. if (waferHolderMoveItem.DestModuleType == ModuleType.Dryer)
  548. {
  549. _currentSequenceIndex = i;
  550. item.SchedulerModule = SchedulerManager.Instance.GetScheduler(transporterName);
  551. waferHolderMoveItem.SourceModule = ModuleName.Prewet1;
  552. waferHolderMoveItem.SourceModuleType = ModuleType.Prewet;
  553. break;
  554. }
  555. else
  556. {
  557. item.State = RState.End;
  558. item.ProcessMilliSeconds = 0;
  559. }
  560. }
  561. }
  562. }
  563. }
  564. }
  565. /// <summary>
  566. /// Dryer出错重新调度,上一步调度修改为transporter2,同时tranporter2搬运源目标为当前Module,目标类型也是Dryer
  567. /// </summary>
  568. /// <param name="sequence"></param>
  569. private void AnalyseSchedulerDryerErrorState(SchedulerSequence sequence)
  570. {
  571. if (SchedulerSequenceManager.Instance.GetAvaibleModuleCell(_sequenceType,ModuleType.Dryer)!=ModuleName.Unknown)
  572. {
  573. sequence.State = RState.Init;
  574. SchedulerSequence preSchedulerSequence = GetPreSchedulerSequence();
  575. preSchedulerSequence.State = RState.Init;
  576. preSchedulerSequence.SchedulerModule = SchedulerManager.Instance.GetScheduler(ModuleName.Transporter2);
  577. TransporterAction action = preSchedulerSequence.Parameters as TransporterAction;
  578. if (action.ActionMsg != Modules.Transporter.TransporterMSG.Transfer)
  579. {
  580. return;
  581. }
  582. WaferHolderMoveItem waferHolderMoveItem = action.Parameter as WaferHolderMoveItem;
  583. waferHolderMoveItem.SourceModule = waferHolderMoveItem.DestModule;
  584. waferHolderMoveItem.SourceModuleType = waferHolderMoveItem.DestModuleType;
  585. waferHolderMoveItem.DestModuleType = waferHolderMoveItem.DestModuleType;
  586. waferHolderMoveItem.DestModule = ModuleName.Unknown;
  587. sequence.SchedulerModule = null;
  588. _currentSequenceIndex--;
  589. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} Dryer meet error,rescheduler changed to transporter2");
  590. }
  591. else
  592. {
  593. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} Dryer meet error,rescheduler has no avaible dryer");
  594. }
  595. }
  596. /// <summary>
  597. /// Rinse出错重新调度,回退至上一步调度
  598. /// </summary>
  599. /// <param name="sequence"></param>
  600. private void AnalyseSchedulerRinseErrorState(SchedulerSequence sequence)
  601. {
  602. ModuleName metalName = SchedulerSequenceManager.Instance.GetPreMetalModuleName(_currentSequenceIndex, _schedulerSequences);
  603. if (metalName == ModuleName.Unknown)
  604. {
  605. return;
  606. }
  607. if (SchedulerSequenceManager.Instance.GetAvaibleModuleCell(_sequenceType, ModuleType.Rinse,metalName) != ModuleName.Unknown)
  608. {
  609. sequence.State = RState.Init;
  610. SchedulerSequence preSchedulerSequence = GetPreSchedulerSequence();
  611. preSchedulerSequence.State = RState.Init;
  612. TransporterAction action = preSchedulerSequence.Parameters as TransporterAction;
  613. if (action.ActionMsg != Modules.Transporter.TransporterMSG.Transfer)
  614. {
  615. return;
  616. }
  617. WaferHolderMoveItem waferHolderMoveItem = action.Parameter as WaferHolderMoveItem;
  618. waferHolderMoveItem.SourceModule = waferHolderMoveItem.DestModule;
  619. waferHolderMoveItem.SourceModuleType = waferHolderMoveItem.DestModuleType;
  620. waferHolderMoveItem.DestModuleType = waferHolderMoveItem.DestModuleType;
  621. waferHolderMoveItem.DestModule = ModuleName.Unknown;
  622. sequence.SchedulerModule = null;
  623. _currentSequenceIndex--;
  624. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} Rinse meet error,scheduler changed to transporter1");
  625. }
  626. else
  627. {
  628. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} Rinse meet error,rescheduler has no avaible rinse");
  629. }
  630. }
  631. /// <summary>
  632. /// Metal出错重新调度,回退至上一步调度
  633. /// </summary>
  634. /// <param name="sequence"></param>
  635. private void AnalyseSchedulerMetalErrorState(SchedulerSequence sequence)
  636. {
  637. //当前步骤结束,WaferHolder状态
  638. UpdateWaferHolderErrorStatus();
  639. sequence.EndTime = DateTime.Now;
  640. sequence.ProcessMilliSeconds= sequence.EndTime.Subtract(sequence.StartTime).TotalMilliseconds;
  641. sequence.State = RState.End;
  642. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} Metal meet error,rescheduler changed to next module");
  643. //跳过后面的metal
  644. SkipAfterMetal(sequence,false);
  645. //移除SRD调度
  646. RemoveWaferHolderSRDScheduler();
  647. _currentSequenceIndex++;
  648. }
  649. /// <summary>
  650. /// 跳过后面的Metal
  651. /// </summary>
  652. private void SkipAfterMetal(SchedulerSequence schedulerSequence,bool isSetLastSequenceSourceModule)
  653. {
  654. int startIndex = -1;
  655. int endIndex = -1;
  656. //从下一个Metal直到碰到Dryer
  657. for (int i = _currentSequenceIndex + 1; i < _schedulerSequences.Count; i++)
  658. {
  659. SchedulerSequence item = _schedulerSequences[i];
  660. if (item.ModuleType == ModuleType.Metal && startIndex == -1)
  661. {
  662. startIndex = i;
  663. }
  664. if (item.ModuleType == ModuleType.Dryer)
  665. {
  666. endIndex = i - 1;//Dryer前一步骤为Transporter,保留Dryer前面的transporter
  667. break;
  668. }
  669. }
  670. //将后面的Metal的步骤直接跳过,包含gh g 后面的transporter
  671. if (startIndex != -1)
  672. {
  673. for (int i = startIndex - 1; i < endIndex; i++)
  674. {
  675. SchedulerSequence item = _schedulerSequences[i];
  676. item.State = RState.End;
  677. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} skip {i+1} sequence {item.ModuleType}");
  678. }
  679. }
  680. //是否设置最后调度的源模块
  681. if (isSetLastSequenceSourceModule)
  682. {
  683. SchedulerSequence lastSchedulerSequence = _schedulerSequences[endIndex];
  684. if (lastSchedulerSequence.ModuleType == ModuleType.Transporter)
  685. {
  686. TransporterAction action = lastSchedulerSequence.Parameters as TransporterAction;
  687. if (action.ActionMsg != Modules.Transporter.TransporterMSG.Transfer)
  688. {
  689. return;
  690. }
  691. WaferHolderMoveItem waferHolderMoveItem = action.Parameter as WaferHolderMoveItem;
  692. if (schedulerSequence.SchedulerModule != null)
  693. {
  694. waferHolderMoveItem.SourceModule = schedulerSequence.ModuleName;
  695. waferHolderMoveItem.SourceModuleType = schedulerSequence.ModuleType;
  696. }
  697. }
  698. }
  699. }
  700. /// <summary>
  701. /// 移除SRD调度
  702. /// </summary>
  703. private void RemoveWaferHolderSRDScheduler()
  704. {
  705. WaferTask waferATask = WaferTaskManager.Instance.GetWaferTask(WaferHolderInfo.WaferAId);
  706. if (waferATask != null)
  707. {
  708. waferATask.RemoveSrdScheduler();
  709. }
  710. WaferTask waferBTask = WaferTaskManager.Instance.GetWaferTask(WaferHolderInfo.WaferBId);
  711. if (waferBTask != null)
  712. {
  713. waferBTask.RemoveSrdScheduler();
  714. }
  715. }
  716. #endregion
  717. /// <summary>
  718. /// 自动更新LoaderTransporter的工序
  719. /// </summary>
  720. private void AutoChangeLoaderTransporterScheduler()
  721. {
  722. if(_currentSequenceIndex>=_schedulerSequences.Count)
  723. {
  724. return;
  725. }
  726. if(_currentSequenceIndex+2>=_schedulerSequences.Count)
  727. {
  728. return;
  729. }
  730. SchedulerSequence schedulerSequence = _schedulerSequences[_currentSequenceIndex];
  731. if (schedulerSequence == null)
  732. {
  733. return;
  734. }
  735. if(schedulerSequence.ModuleName==ModuleName.Transporter2)
  736. {
  737. if (schedulerSequence.Parameters == null || !(schedulerSequence.Parameters is TransporterAction))
  738. {
  739. return;
  740. }
  741. TransporterAction action = (TransporterAction)schedulerSequence.Parameters;
  742. if (action.ActionMsg != Modules.Transporter.TransporterMSG.Transfer)
  743. {
  744. return;
  745. }
  746. WaferHolderMoveItem waferHolderMoveItem = (WaferHolderMoveItem)action.Parameter;
  747. if(waferHolderMoveItem.DestModuleType!=ModuleType.Buffer)
  748. {
  749. return;
  750. }
  751. SchedulerSequence nextSchedulerSequence = _schedulerSequences[_currentSequenceIndex + 1];
  752. if(nextSchedulerSequence.ModuleName!=ModuleName.Transporter2)
  753. {
  754. return;
  755. }
  756. if(nextSchedulerSequence.Parameters==null||!(nextSchedulerSequence.Parameters is TransporterAction))
  757. {
  758. return;
  759. }
  760. TransporterAction nextAction = nextSchedulerSequence.Parameters as TransporterAction;
  761. if (nextAction.ActionMsg != Modules.Transporter.TransporterMSG.Transfer)
  762. {
  763. return;
  764. }
  765. WaferHolderMoveItem nextWaferHolderMoveItem = (WaferHolderMoveItem)nextAction.Parameter;
  766. if(nextWaferHolderMoveItem.DestModule==ModuleName.Unknown)
  767. {
  768. if (ProcessJobInfo != null && ProcessJobInfo.SequenceRecipe != null)
  769. {
  770. //后面不存在资源,搬运至Buffer中,而不是进后面cell
  771. if (!SchedulerSequenceRecipeManager.Instance.ExistAvaibleProcessCell(ProcessJobInfo.SequenceRecipe, false))
  772. {
  773. return;
  774. }
  775. }
  776. if (CheckBufferHasUnProcessedWaferHolder())
  777. {
  778. return;
  779. }
  780. //若Buffer后面的cell存在资源,则直接搬运至Cell中
  781. ModuleName avaibleModuleName = SchedulerSequenceManager.Instance.GetAvaibleEmptyModuleCell(nextWaferHolderMoveItem.DestModuleType,_sequenceType);
  782. if(avaibleModuleName==ModuleName.Unknown)
  783. {
  784. return;
  785. }
  786. else
  787. {
  788. schedulerSequence.State = RState.End;
  789. _currentSequenceIndex++;
  790. nextWaferHolderMoveItem.DestModule = avaibleModuleName;
  791. nextWaferHolderMoveItem.SourceModule = ModuleName.Loader1;
  792. nextWaferHolderMoveItem.SourceModuleType = ModuleType.Loader;
  793. UpdateNextSequenceModule(_currentSequenceIndex + 1,avaibleModuleName);
  794. }
  795. }
  796. else if(nextWaferHolderMoveItem.DestModule==ModuleName.Loader1)
  797. {
  798. LoaderEntity loaderEntity = Singleton<RouteManager>.Instance.GetModule<LoaderEntity>(ModuleName.Loader1.ToString());
  799. if(loaderEntity==null)
  800. {
  801. return;
  802. }
  803. if(loaderEntity.WaferHolderInfo!=null)
  804. {
  805. return;
  806. }
  807. if(loaderEntity.IsBusy)
  808. {
  809. return;
  810. }
  811. schedulerSequence.State = RState.End;
  812. _currentSequenceIndex++;
  813. nextWaferHolderMoveItem.SourceModule = waferHolderMoveItem.SourceModule;
  814. nextWaferHolderMoveItem.SourceModuleType = waferHolderMoveItem.SourceModuleType;
  815. }
  816. }
  817. }
  818. /// <summary>
  819. /// 检验Buffer中是否存在未处理的WaferHolder
  820. /// </summary>
  821. /// <returns></returns>
  822. private bool CheckBufferHasUnProcessedWaferHolder()
  823. {
  824. List<string> bufferModules = BufferItemManager.Instance.InstalledModules;
  825. foreach (string module in bufferModules)
  826. {
  827. WaferHolderInfo item = WaferHolderManager.Instance.GetWaferHolder(module);
  828. if (item == null)
  829. {
  830. continue;
  831. }
  832. if (string.IsNullOrEmpty(item.WaferAId))
  833. {
  834. continue;
  835. }
  836. if (string.IsNullOrEmpty(item.WaferBId))
  837. {
  838. continue;
  839. }
  840. WaferInfo waferA=WaferManager.Instance.GetWaferByWaferId(item.WaferAId);
  841. if (waferA == null)
  842. {
  843. continue;
  844. }
  845. if (waferA.ProcessState == EnumWaferProcessStatus.Idle&&waferA.WaferType==WaferType.Production)
  846. {
  847. return true;
  848. }
  849. WaferInfo waferB = WaferManager.Instance.GetWaferByWaferId(item.WaferBId);
  850. if(waferB == null)
  851. {
  852. continue;
  853. }
  854. if (waferB.ProcessState == EnumWaferProcessStatus.Idle && waferB.WaferType == WaferType.Production)
  855. {
  856. return true;
  857. }
  858. }
  859. return false;
  860. }
  861. /// <summary>
  862. /// 更新下一工序的调度模块
  863. /// </summary>
  864. /// <param name="sequenceIndex"></param>
  865. /// <param name="moduleName"></param>
  866. private void UpdateNextSequenceModule(int sequenceIndex,ModuleName moduleName)
  867. {
  868. if(sequenceIndex<_schedulerSequences.Count)
  869. {
  870. SchedulerSequence schedulerSequence = _schedulerSequences[sequenceIndex];
  871. if(schedulerSequence!=null&&schedulerSequence.SchedulerModule==null)
  872. {
  873. schedulerSequence.SchedulerModule = SchedulerManager.Instance.GetScheduler(moduleName);
  874. schedulerSequence.ModuleName = moduleName;
  875. }
  876. }
  877. }
  878. /// <summary>
  879. /// 更新WaferHolder工序完成Wafer状态
  880. /// </summary>
  881. private void UpdateWaferHolderProcessComplete()
  882. {
  883. if (WaferHolderInfo.Status != WaferHolderStatus.Completed)
  884. {
  885. UpdateWaferHolderWaferProcessStatus(EnumWaferProcessStatus.Completed);
  886. WaferHolderInfo.Status = WaferHolderStatus.Completed;
  887. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} Status changed to {WaferHolderInfo.Status}");
  888. }
  889. MaterialTrackerManager.Instance.UpdateModuleMaterial(WaferHolderInfo.CurrentLocation);
  890. }
  891. /// <summary>
  892. /// 更新WaferHolder工序正在加工
  893. /// </summary>
  894. private void UpdateWaferHolderProcessingStatus()
  895. {
  896. if (WaferHolderInfo.Status == WaferHolderStatus.Normal)
  897. {
  898. UpdateWaferHolderWaferProcessStatus(EnumWaferProcessStatus.InProcess);
  899. WaferHolderInfo.Status = WaferHolderStatus.Processing;
  900. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{WaferHolderInfo.Id} Status changed to {WaferHolderInfo.Status}");
  901. MaterialTrackerManager.Instance.UpdateModuleMaterial(WaferHolderInfo.CurrentLocation);
  902. }
  903. }
  904. /// <summary>
  905. /// 更新WaferHolder工序错误状态
  906. /// </summary>
  907. /// <param name="sequence"></param>
  908. private void UpdateWaferHolderErrorStatus()
  909. {
  910. UpdateWaferHolderWaferProcessStatus(EnumWaferProcessStatus.Failed);
  911. WaferHolderInfo.Status = WaferHolderStatus.Failed;
  912. MaterialTrackerManager.Instance.UpdateModuleMaterial(WaferHolderInfo.CurrentLocation);
  913. }
  914. /// <summary>
  915. /// 更新WaferHolder工序MisProcessed状态
  916. /// </summary>
  917. /// <param name="sequence"></param>
  918. private void UpdateWaferHolderMisProcessedStatus()
  919. {
  920. UpdateWaferHolderWaferProcessStatus(EnumWaferProcessStatus.MisProcessed);
  921. WaferHolderInfo.Status = WaferHolderStatus.MisProcessed;
  922. MaterialTrackerManager.Instance.UpdateModuleMaterial(WaferHolderInfo.CurrentLocation);
  923. }
  924. /// <summary>
  925. /// 更新WaferHolder处理状态
  926. /// </summary>
  927. /// <param name="sequence"></param>
  928. /// <param name="processStatus"></param>
  929. private void UpdateWaferHolderWaferProcessStatus(EnumWaferProcessStatus processStatus)
  930. {
  931. if (Enum.TryParse(WaferHolderInfo.CurrentLocation, out ModuleName moduleName))
  932. {
  933. if (!string.IsNullOrEmpty(WaferHolderInfo.WaferAId))
  934. {
  935. WaferInfo waferInfo = WaferManager.Instance.GetWaferByWaferId(WaferHolderInfo.WaferAId);
  936. if (waferInfo != null && waferInfo.WaferType == WaferType.Production)
  937. {
  938. WaferManager.Instance.UpdateWaferProcessStatus(moduleName, 0, processStatus);
  939. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{waferInfo.WaferID} status changed to {processStatus}");
  940. }
  941. }
  942. if (!string.IsNullOrEmpty(WaferHolderInfo.WaferBId))
  943. {
  944. WaferInfo waferInfo = WaferManager.Instance.GetWaferByWaferId(WaferHolderInfo.WaferBId);
  945. if (waferInfo != null && waferInfo.WaferType == WaferType.Production)
  946. {
  947. WaferManager.Instance.UpdateWaferProcessStatus(moduleName, 1, processStatus);
  948. LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{waferInfo.WaferID} status changed to {processStatus}");
  949. }
  950. }
  951. }
  952. }
  953. /// <summary>
  954. /// 检验前置条件
  955. /// </summary>
  956. /// <returns></returns>
  957. private bool CheckStartCondition()
  958. {
  959. LoaderEntity loaderEntity = Singleton<RouteManager>.Instance.GetModule<LoaderEntity>(ModuleName.Loader1.ToString());
  960. if (loaderEntity.WaferHolderInfo != null)
  961. {
  962. return false;
  963. }
  964. //if (ProcessJobInfo != null)
  965. //{
  966. // if (!SchedulerSequenceRecipeManager.Instance.ExistAvaibleProcessCell(ProcessJobInfo.SequenceRecipe, false))
  967. // {
  968. // _currentSequenceIndex = _schedulerSequences.Count;
  969. // LOG.WriteLog(eEvent.WARN_SCHEDULER, "System", $"WaferHolder {WaferHolderInfo.Id} start scheduler meet no avaible cell");
  970. // return false;
  971. // }
  972. //}
  973. return true;
  974. }
  975. /// <summary>
  976. /// 释放资源
  977. /// </summary>
  978. public void Dispose()
  979. {
  980. _schedulerSequences.Clear();
  981. }
  982. /// <summary>
  983. /// 暂停
  984. /// </summary>
  985. public void Pause()
  986. {
  987. _pausedIndex = _currentSequenceIndex;
  988. }
  989. /// <summary>
  990. /// 恢复
  991. /// </summary>
  992. public void Resume()
  993. {
  994. _pausedIndex = -1;
  995. }
  996. /// <summary>
  997. /// 获取当前调度阶段
  998. /// </summary>
  999. /// <returns></returns>
  1000. public SchedulerSequence GetCurrentSchedulerSequence()
  1001. {
  1002. return GetSchedulerSequenceByIndex(_currentSequenceIndex);
  1003. }
  1004. /// <summary>
  1005. /// 获取前一个调度阶段
  1006. /// </summary>
  1007. /// <returns></returns>
  1008. public SchedulerSequence GetPreSchedulerSequence()
  1009. {
  1010. return GetSchedulerSequenceByIndex(_currentSequenceIndex-1);
  1011. }
  1012. /// <summary>
  1013. /// 根据索引获取调度阶段对象
  1014. /// </summary>
  1015. /// <param name="index"></param>
  1016. /// <returns></returns>
  1017. private SchedulerSequence GetSchedulerSequenceByIndex(int index)
  1018. {
  1019. if (index == -1 || index >= _schedulerSequences.Count)
  1020. {
  1021. return null;
  1022. }
  1023. return _schedulerSequences[index];
  1024. }
  1025. /// <summary>
  1026. /// 是否调度完成Loader调度
  1027. /// </summary>
  1028. /// <returns></returns>
  1029. public bool IsNotPassLoader()
  1030. {
  1031. return _currentSequenceIndex <= 1;
  1032. }
  1033. }
  1034. }