SETMCycle.cs 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133
  1. using Aitex.Core.Common;
  2. using Aitex.Core.RT.DataCenter;
  3. using Aitex.Core.RT.Fsm;
  4. using Aitex.Core.RT.Log;
  5. using Aitex.Core.RT.RecipeCenter;
  6. using Aitex.Core.RT.Routine;
  7. using Aitex.Core.RT.SCCore;
  8. using Aitex.Core.Util;
  9. using Aitex.Sorter.Common;
  10. using MECF.Framework.Common.Equipment;
  11. using MECF.Framework.Common.Jobs;
  12. using MECF.Framework.Common.Schedulers;
  13. using MECF.Framework.Common.SubstrateTrackings;
  14. using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Robot;
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using System.Linq;
  19. using System.Text;
  20. using System.Threading.Tasks;
  21. using Venus_Core;
  22. using Venus_RT.Modules.Schedulers;
  23. using Venus_RT.Scheduler;
  24. using static Aitex.Core.Util.SubscriptionAttribute;
  25. namespace Venus_RT.Modules
  26. {
  27. public class SETMCycle : ModuleRoutineBase, IRoutine
  28. {
  29. enum TMCycleStep
  30. {
  31. Start,
  32. ReturnBack,
  33. Cycling,
  34. End,
  35. }
  36. private List<ControlJobInfo> _lstControlJobs = new List<ControlJobInfo>();
  37. private List<ProcessJobInfo> _lstProcessJobs = new List<ProcessJobInfo>();
  38. private Dictionary<ModuleName, SchedulerModule> dictSchedulers = new Dictionary<ModuleName, SchedulerModule>();
  39. private List<ModuleName> tmCycleRoutine = new List<ModuleName>()
  40. {
  41. ModuleName.VCE1,
  42. ModuleName.PMA,
  43. ModuleName.PMB,
  44. ModuleName.PMC,
  45. ModuleName.VCE1
  46. };
  47. private int CycleNum = 0;
  48. private bool _isCycleMode;
  49. private ModuleName _sourceModule = ModuleName.VCE1;
  50. private ModuleName _destinationModule = ModuleName.VCE1;
  51. private int _sourceSlotNumber = 25;
  52. //private int _destinationSlotNumber = 25;
  53. private SchedulerSETMRobot _TMRobot = (SchedulerSETMRobot)Singleton<TransferModule>.Instance.GetScheduler(ModuleName.SETM);
  54. private SchedulerFACallback _faCallback;
  55. private SchedulerDBCallback _dbCallback;
  56. //private readonly int INVALID_SLOT = -1;
  57. private Queue<MoveItem> _runningItems = new Queue<MoveItem>();
  58. private Queue<MoveItem> _CycleWafers = new Queue<MoveItem>();
  59. private RState _cycleState = RState.Init;
  60. private Stopwatch _cycleWatch = new Stopwatch();
  61. private List<MoveItem> _moveQueue = new List<MoveItem>();
  62. private List<MovingStatus> movingStatus = new List<MovingStatus>();
  63. private double _throughput = 0;
  64. private DateTime _starttime;
  65. public int? CycleIndex;
  66. private int CycledWafer => _currentWafer + _pastWafer;
  67. private int _currentWafer;
  68. private int _pastWafer;
  69. private bool IsSequenceCycle = false; //判断是否跑货
  70. #region 构造函数
  71. public SETMCycle(ModuleName module) : base(module)
  72. {
  73. Name = "TM Cycle";
  74. void _initMoudle(ModuleName name, SchedulerModule sche)
  75. {
  76. if (ModuleHelper.IsInstalled(name))
  77. {
  78. dictSchedulers[name] = sche;
  79. }
  80. }
  81. _initMoudle(ModuleName.VCE1, new SchedulerVCE(ModuleName.VCE1));
  82. _initMoudle(ModuleName.PMA, new SchedulerPM(ModuleName.PMA));
  83. _initMoudle(ModuleName.PMB, new SchedulerPM(ModuleName.PMB));
  84. _initMoudle(ModuleName.PMC, new SchedulerPM(ModuleName.PMC));
  85. _initMoudle(ModuleName.PMD, new SchedulerPM(ModuleName.PMD));
  86. _faCallback = new SchedulerFACallback();
  87. _dbCallback = new SchedulerDBCallback();
  88. _currentWafer = 0;
  89. _pastWafer = 0;
  90. DATA.Subscribe("SEScheduler.CycledWafer", ()=> CycledWafer );
  91. DATA.Subscribe("SEScheduler.CycleSetPoint", ()=> CycleNum);
  92. DATA.Subscribe("SEScheduler.CycleCount", ()=> CycleIndex);
  93. DATA.Subscribe("SEScheduler.ThroughPut", ()=> _throughput);
  94. }
  95. #endregion
  96. #region Cycle
  97. //run货模式
  98. public RState StartJob(string jobName)
  99. {
  100. if (_TMRobot.IsVCESlitDoorClosed)
  101. {
  102. LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"VCE SlitDoor is Close cannot run!");
  103. return RState.Failed;
  104. }
  105. //sequenceCycle
  106. CycleIndex = 0;
  107. _pastWafer = 0;
  108. _currentWafer = 0;
  109. CycleNum = SC.GetValue<int>("System.CycleCount");
  110. _CycleWafers.Clear();
  111. IsSequenceCycle = true;
  112. ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName);
  113. if (cj == null)
  114. {
  115. LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"start job rejected, not found job with id {jobName}");
  116. return RState.Failed;
  117. }
  118. if (cj.State == EnumControlJobState.WaitingForStart)
  119. {
  120. cj.SetState(EnumControlJobState.Executing);
  121. //PreJobClean(cj);
  122. cj.JetState = EnumJetCtrlJobState.Quequed;
  123. cj.StartTime = DateTime.Now;
  124. _dbCallback.LotCreated(cj);
  125. _faCallback.JobStarted(cj, GetFirstProcessJob(cj));
  126. int maxslot = 0;
  127. cj.LotWafers.ForEach(x => maxslot = x.OriginSlot > maxslot ? x.OriginSlot : maxslot);
  128. //waiting status means no use
  129. movingStatus = new List<MovingStatus>();
  130. movingStatus.AddRange(new MovingStatus[maxslot + 1]);
  131. for (int i = 0; i < movingStatus.Count; i++)
  132. movingStatus[i] = MovingStatus.Waiting;
  133. foreach (var wafer in cj.LotWafers)
  134. {
  135. WaferInfo currentWafer = WaferManager.Instance.GetWafer((ModuleName)wafer.OriginStation, wafer.OriginSlot);
  136. WaferManager.Instance.UpdateWaferProcessStatus((ModuleName)wafer.OriginStation, wafer.OriginSlot, EnumWaferProcessStatus.Idle);
  137. currentWafer.ProcessJob = _lstProcessJobs.Find(x => x.Sequence.Name == wafer.SequenceName);
  138. if (currentWafer.ProcessJob.Sequence.Steps[0].StepParameter.ContainsValue("VPA"))
  139. movingStatus[wafer.OriginSlot] = MovingStatus.WaitAlign;
  140. else
  141. movingStatus[wafer.OriginSlot] = MovingStatus.WaitProcess;
  142. }
  143. }
  144. if (!_cycleWatch.IsRunning)
  145. {
  146. _cycleWatch.Restart();
  147. }
  148. _starttime = DateTime.Now;
  149. _cycleState = RState.Running;
  150. return _cycleState;
  151. }
  152. //processJob(sequence num) ControlJob(1)
  153. public void CreateJob(Dictionary<string, object> param)
  154. {
  155. _isCycleMode = SC.GetValue<bool>("System.IsCycleMode");
  156. CycleNum = _isCycleMode ? SC.GetValue<int>("System.CycleCount") : 0;
  157. string[] slotSequence = (string[])param["SlotSequence"];
  158. string jobId = (string)param["JobId"];
  159. string module = (string)param["Module"];
  160. string lotId = jobId;
  161. if (param.ContainsKey("LotId"))
  162. lotId = (string)param["LotId"];
  163. if (!ModuleHelper.IsVCE(ModuleHelper.Converter(module)))
  164. {
  165. LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"{module} should be VCE");
  166. _faCallback.JobCreateFailed(module, lotId, jobId, "");
  167. return;
  168. }
  169. if (string.IsNullOrEmpty(jobId))
  170. {
  171. jobId = "CJ_Local_" + module;
  172. }
  173. if (_lstControlJobs.Exists(x => x.Name == jobId))
  174. {
  175. LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"{jobId} already created");
  176. _faCallback.JobCreateFailed(module, lotId, jobId, "");
  177. return;
  178. }
  179. ControlJobInfo cj = new ControlJobInfo();
  180. cj.Name = jobId;
  181. cj.Module = module;
  182. cj.LotName = lotId;
  183. cj.LotInnerId = Guid.NewGuid();
  184. cj.LotWafers = new List<WaferInfo>();
  185. cj.SetState(EnumControlJobState.WaitingForStart);
  186. cj.JetState = EnumJetCtrlJobState.Created;
  187. Dictionary<string, bool[]> seqSlot = new Dictionary<string, bool[]>();
  188. Dictionary<string, List<Tuple<ModuleName, int>>> seqSlotWafers = new Dictionary<string, List<Tuple<ModuleName, int>>>();
  189. Dictionary<string, string> indexSequence = new Dictionary<string, string>();
  190. bool enableGroupBySequence = SC.GetValue<bool>("Scheduler.GroupWaferBySequence");
  191. for (int i = 0; i < SC.GetValue<int>("VCE1.SlotNumber"); i++)
  192. {
  193. if (string.IsNullOrEmpty(slotSequence[i]) || string.IsNullOrEmpty(slotSequence[i].Trim()))
  194. continue;
  195. string groupName = enableGroupBySequence ? slotSequence[i].Trim() : i.ToString();
  196. indexSequence[groupName] = slotSequence[i];
  197. if (!seqSlot.ContainsKey(groupName))
  198. {
  199. seqSlot[groupName] = new bool[SC.GetValue<int>("VCE1.SlotNumber")];
  200. }
  201. if (!seqSlotWafers.ContainsKey(groupName))
  202. {
  203. seqSlotWafers[groupName] = new List<Tuple<ModuleName, int>>();
  204. }
  205. seqSlot[groupName][i] = true;
  206. if (!WaferManager.Instance.CheckHasWafer(module, i))
  207. {
  208. LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job wafer: {module} slot {i + 1} not in the carrier");
  209. return;
  210. }
  211. if (!WaferManager.Instance.CheckWafer(ModuleHelper.Converter(module), i, WaferStatus.Normal))
  212. {
  213. LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job wafer: {module} slot {i + 1} status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Status}");
  214. _faCallback.JobCreateFailed(module, lotId, jobId, "");
  215. return;
  216. }
  217. if (WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState != EnumWaferProcessStatus.Idle)
  218. {
  219. LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job wafer: {module} slot {i + 1} process status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState}");
  220. _faCallback.JobCreateFailed(module, lotId, jobId, "");
  221. return;
  222. }
  223. cj.LotWafers.Add(WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i));
  224. WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).SequenceName = slotSequence[i];
  225. seqSlotWafers[groupName].Add(Tuple.Create(ModuleHelper.Converter(module), i));
  226. cj.JobWaferSize = WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Size;
  227. LOG.Write(eEvent.EV_ROUTER, ModuleName.System, $"Assigned wafer job, wafer {module}.{i + 1}, sequence: {slotSequence[i]}");
  228. }
  229. if (seqSlotWafers.Count == 0)
  230. {
  231. LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job has not assign wafer");
  232. _faCallback.JobCreateFailed(module, lotId, jobId, "");
  233. return;
  234. }
  235. List<ProcessJobInfo> pjs = new List<ProcessJobInfo>();
  236. string[] seqs = seqSlot.Keys.ToArray();
  237. for (int i = 0; i < seqs.Length; i++)
  238. {
  239. ProcessJobInfo pj = new ProcessJobInfo();
  240. pj.Name = jobId + "_" + (i + 1);
  241. pj.Sequence = SequenceInfoHelper.GetInfo(indexSequence[seqs[i]]);
  242. pj.ControlJobName = cj.Name;
  243. pj.LotName = lotId;
  244. pj.SlotWafers = seqSlotWafers[seqs[i]];
  245. pj.SetState(EnumProcessJobState.Queued);
  246. if (!CheckSequencePmReady(pj.Sequence, null, out _, out string reason))
  247. {
  248. LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"no valid chamber for the {reason}");
  249. _faCallback.JobCreateFailed(module, lotId, jobId, "");
  250. return;
  251. }
  252. if (!RouteManager.IsATMMode && !CheckSequenceRecipeFileValid(pj.Sequence, out reason))
  253. {
  254. LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"recipe file not valid in the sequence, {reason}");
  255. _faCallback.JobCreateFailed(module, lotId, jobId, "");
  256. return;
  257. }
  258. pjs.Add(pj);
  259. }
  260. _dbCallback.LotUpdate(cj);
  261. foreach (var pj in pjs)
  262. {
  263. cj.ProcessJobNameList.Add(pj.Name);
  264. _lstProcessJobs.Add(pj);
  265. }
  266. _lstControlJobs.Add(cj);
  267. }
  268. public void AbortJob(string jobName)
  269. {
  270. }
  271. public RState Start(params object[] objs)
  272. {
  273. if (_TMRobot.IsVCESlitDoorClosed)
  274. {
  275. LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"VCE SlitDoor is Close cannot run!");
  276. return RState.Failed;
  277. }
  278. //普通Cycle
  279. if (objs.Length == 2)
  280. {
  281. var modules = ((string[])objs[0]).ToList();
  282. if (modules.Count >= 2)
  283. tmCycleRoutine.Clear();
  284. foreach (var mod in modules)
  285. {
  286. try
  287. {
  288. ModuleName module = ModuleHelper.Converter(mod);
  289. tmCycleRoutine.Add(module);
  290. }
  291. catch (Exception _)
  292. {
  293. LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Invalid module string: {mod}");
  294. return RState.Failed;
  295. }
  296. }
  297. CycleNum = (int)objs[1];
  298. CycleNum = SC.GetValue<int>("System.CycleCount");
  299. _CycleWafers.Clear();
  300. CycleIndex = 0;
  301. for (int i = 0; i < _sourceSlotNumber; i++)
  302. {
  303. if (WaferManager.Instance.CheckHasWafer(_sourceModule, i))
  304. _CycleWafers.Enqueue(new MoveItem(_sourceModule, i, _destinationModule, i, Hand.None));
  305. }
  306. IsSequenceCycle = false;
  307. _cycleState = RState.Running;
  308. }
  309. else
  310. {
  311. LOG.Write(eEvent.ERR_TM,Module,"Invalid parameter for cycle!");
  312. _cycleState = RState.Failed;
  313. }
  314. return _cycleState;
  315. }
  316. public RState Monitor()
  317. {
  318. if (_cycleState == RState.Running)
  319. {
  320. //run sequence
  321. if (IsSequenceCycle)
  322. {
  323. CheckCycleDone();
  324. SETMRobotTask();
  325. SERunTMRobotTask();
  326. SEPMTask();
  327. }
  328. //only TMCycle
  329. else
  330. {
  331. Cycling();
  332. PMProcess();
  333. }
  334. }
  335. return _cycleState;
  336. }
  337. #endregion
  338. #region 推进|检查|计数
  339. private void CheckCycleDone()
  340. {
  341. //how to calculate current wafer? => _currentWafer
  342. //1. wafer in vce
  343. //2. wafer is Idle
  344. _currentWafer = 0;
  345. for (int index = 0; index < movingStatus.Count;++index)
  346. {
  347. if(WaferManager.Instance.CheckHasWafer(ModuleName.VCE1,index) && movingStatus[index] == MovingStatus.Idle)
  348. _currentWafer ++;
  349. }
  350. //throughput = CycledWafer/time, time(h)
  351. _throughput = Math.Round(CycledWafer / (DateTime.Now - _starttime).TotalSeconds * 3600,2);
  352. //how to confirm a sequence is over?
  353. //3、wafer is all in vce
  354. //2、all status is over => idle
  355. //1、TMRobot and PM is available
  356. if (_TMRobot.IsAvailable && AllPMIsAvailable() && ALLStatusIsOver() && WaferAllInVCE())
  357. {
  358. ++CycleIndex;
  359. if (CycleIndex >= CycleNum)
  360. {
  361. _lstControlJobs.Find(lcj => lcj.State == EnumControlJobState.Executing).SetState(EnumControlJobState.WaitingForStart);
  362. _cycleState = RState.End;
  363. }
  364. else
  365. {
  366. _pastWafer += _currentWafer;
  367. ControlJobInfo cj = _lstControlJobs.Find(lcj => lcj.State == EnumControlJobState.Executing);
  368. _currentWafer = 0;
  369. int maxslot = 0;
  370. cj.LotWafers.ForEach(x => maxslot = x.OriginSlot > maxslot ? x.OriginSlot : maxslot);
  371. //waiting status means no use
  372. movingStatus = new List<MovingStatus>();
  373. movingStatus.AddRange(new MovingStatus[maxslot + 1]);
  374. for (int i = 0; i < movingStatus.Count; i++)
  375. movingStatus[i] = MovingStatus.Waiting;
  376. foreach (var wafer in cj.LotWafers)
  377. {
  378. WaferInfo currentWafer = WaferManager.Instance.GetWafer((ModuleName)wafer.OriginStation, wafer.OriginSlot);
  379. WaferManager.Instance.UpdateWaferProcessStatus((ModuleName)wafer.OriginStation, wafer.OriginSlot, EnumWaferProcessStatus.Idle);
  380. currentWafer.ProcessJob = _lstProcessJobs.Find(x => x.Sequence.Name == wafer.SequenceName);
  381. if (currentWafer.ProcessJob.Sequence.Steps[0].StepParameter.ContainsValue("VPA"))
  382. movingStatus[wafer.OriginSlot] = MovingStatus.WaitAlign;
  383. else
  384. movingStatus[wafer.OriginSlot] = MovingStatus.WaitProcess;
  385. }
  386. }
  387. }
  388. }
  389. //movingStatus is all Idle or no use
  390. private bool ALLStatusIsOver()
  391. {
  392. bool flag = true;
  393. movingStatus.ForEach(x => flag = (x == MovingStatus.Idle || x == MovingStatus.Waiting) && flag) ;
  394. return flag;
  395. }
  396. private bool WaferAllInVCE()
  397. {
  398. ControlJobInfo cj = _lstControlJobs.Find(lcj => lcj.State == EnumControlJobState.Executing);
  399. bool flag = true;
  400. cj.LotWafers.ForEach(wafer => flag = WaferManager.Instance.CheckHasWafer((ModuleName)wafer.OriginStation, wafer.OriginSlot) && flag);
  401. return flag;
  402. }
  403. private bool AllPMIsAvailable()
  404. {
  405. foreach (KeyValuePair<ModuleName,SchedulerModule> pair in dictSchedulers)
  406. if (ModuleHelper.IsPm(pair.Key) && !pair.Value.IsAvailable)
  407. return false;
  408. return true;
  409. }
  410. private void SERunTMRobotTask()
  411. {
  412. if (_TMRobot.IsAvailable)
  413. {
  414. if (_moveQueue.Count > 0 &&_TMRobot.PostMoveItems(_moveQueue.ToArray()))
  415. {
  416. foreach (var item in _moveQueue)
  417. {
  418. var wafer = WaferManager.Instance.GetWafer(item.SourceModule, item.SourceSlot);
  419. if (wafer.IsEmpty)
  420. {
  421. // post alarm
  422. _cycleState = RState.Failed;
  423. LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Cannot run TM moving task as Get {item.SourceModule}{item.SourceModule} Wafer Info failed");
  424. return;
  425. }
  426. }
  427. _moveQueue.Clear();
  428. }
  429. }
  430. }
  431. private void SERunPMTask()
  432. {
  433. }
  434. private void SETMRobotTask()
  435. {
  436. //tm is free
  437. if (_TMRobot.IsAvailable)
  438. {
  439. CheckWaferFromVceNeedAlign();
  440. PrepareWaferOnRBToPM();
  441. GetBackWafer();
  442. GetNextWaferPickFromVCE();
  443. }
  444. }
  445. private void SEPMTask()
  446. {
  447. //foreach all pms and the wafer's receipe
  448. foreach (KeyValuePair<ModuleName,SchedulerModule> dict in dictSchedulers)
  449. {
  450. //pm has wafer && pm is available => find the wafer's receipe
  451. if (ModuleHelper.IsPm(dict.Key))
  452. {
  453. if (dict.Value.IsAvailable)
  454. {
  455. foreach (var ctrljob in _lstControlJobs)
  456. {
  457. //Port is run
  458. if (ctrljob.State == EnumControlJobState.Executing)
  459. {
  460. if (WaferManager.Instance.CheckHasWafer(dict.Key, 0))
  461. {
  462. WaferInfo currentWafer = WaferManager.Instance.GetWafer(dict.Key, 0);
  463. switch (movingStatus[currentWafer.OriginSlot])
  464. {
  465. case MovingStatus.WaitProcess:
  466. dict.Value.EventWaferArrived?.Invoke(this, new WaferMoveArgs(ModuleName.TMRobot, 0, dict.Key, 0));
  467. movingStatus[currentWafer.OriginSlot] = MovingStatus.StartProcess;
  468. break;
  469. case MovingStatus.Processing:
  470. movingStatus[currentWafer.OriginSlot] = MovingStatus.Idle;
  471. break;
  472. case MovingStatus.StartProcess:
  473. movingStatus[currentWafer.OriginSlot] = MovingStatus.Processing;
  474. break;
  475. default:
  476. break;
  477. }
  478. }
  479. }
  480. }
  481. }
  482. }
  483. }
  484. }
  485. //get the wafer need back
  486. private void GetBackWafer()
  487. {
  488. //find
  489. foreach (KeyValuePair<ModuleName, SchedulerModule> dict in dictSchedulers)
  490. {
  491. //pm has wafer && pm is available => find the wafer's receipe
  492. if (ModuleHelper.IsPm(dict.Key) && WaferManager.Instance.CheckHasWafer(dict.Key, 0) && WaferManager.Instance.CheckNoWafer(ModuleName.VPA, 0))
  493. {
  494. WaferInfo currentWafer = WaferManager.Instance.GetWafer(dict.Key, 0);
  495. //if the wafer has been processed, we need to pickfrom it.
  496. if (dict.Value.IsAvailable && movingStatus[currentWafer.OriginSlot] == MovingStatus.Idle && _TMRobot.IsAvailable)
  497. {
  498. _moveQueue.Add(new MoveItem(dict.Key,0, (ModuleName)currentWafer.OriginStation, currentWafer.OriginSlot,0));
  499. //one hand has wafer
  500. if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0)
  501. && movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0).OriginSlot] == MovingStatus.WaitProcess
  502. && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1))
  503. {
  504. WaferInfo wafer = WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0);
  505. if (wafer.ProcessJob.Sequence.PMs.Contains(dict.Key))
  506. {
  507. _moveQueue.Add(new MoveItem(ModuleName.TMRobot,0,dict.Key,0,0));
  508. }
  509. }
  510. else if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1)
  511. && movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1).OriginSlot] == MovingStatus.WaitProcess
  512. && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0))
  513. {
  514. WaferInfo wafer = WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1);
  515. if (wafer.ProcessJob.Sequence.PMs.Contains(dict.Key))
  516. {
  517. _moveQueue.Add(new MoveItem(ModuleName.TMRobot, 1, dict.Key, 0, (Hand)1));
  518. }
  519. }
  520. return;
  521. }
  522. }
  523. }
  524. }
  525. //get the wafer need Pick From vce
  526. private void GetNextWaferPickFromVCE()
  527. {
  528. if (_TMRobot.IsAvailable)
  529. {
  530. Dictionary<int, ModuleName> nextslot = new Dictionary<int, ModuleName>();
  531. foreach (var ctrljob in _lstControlJobs)
  532. {
  533. //Port is run
  534. if (ctrljob.State == EnumControlJobState.Executing)
  535. {
  536. bool nohastowaitpm = false;
  537. foreach (WaferInfo wafer in ctrljob.LotWafers)
  538. {
  539. //if need pm available =>pick 2
  540. if (!wafer.IsEmpty
  541. && SequenceNeedPMsAvailable(_lstProcessJobs.Find(x => x.Sequence.Name == wafer.SequenceName).Sequence.PMs)
  542. && ModuleHelper.IsVCE((ModuleName)wafer.OriginStation)
  543. && (movingStatus[wafer.OriginSlot] == MovingStatus.WaitProcess))
  544. {
  545. nohastowaitpm = true;
  546. ModuleName _destination = SearchPM(_lstProcessJobs.Find(x => x.Sequence.Name == wafer.SequenceName).Sequence.PMs, nextslot);
  547. nextslot.Add(wafer.OriginSlot, _destination);
  548. _moveQueue.Add(new MoveItem(ModuleName.VCE1, wafer.OriginSlot, _destination, 0, 0));
  549. if (nextslot.Count >= 2 || _moveQueue.Count >= 4)
  550. {
  551. return;
  552. }
  553. }
  554. //if need align available => pick 2
  555. if (!wafer.IsEmpty
  556. && WaferManager.Instance.CheckNoWafer(ModuleName.VPA, 0)
  557. && ModuleHelper.IsVCE((ModuleName)wafer.OriginStation)
  558. && SequenceNeedPMsAvailable(_lstProcessJobs.Find(x => x.Sequence.Name == wafer.SequenceName).Sequence.PMs)
  559. && (movingStatus[wafer.OriginSlot] == MovingStatus.WaitAlign))
  560. {
  561. nohastowaitpm = true;
  562. ModuleName _destination = ModuleName.TMRobot;
  563. //double pick
  564. if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1))
  565. {
  566. nextslot.Add(wafer.OriginSlot, _destination);
  567. _moveQueue.Add(new MoveItem(ModuleName.VCE1, wafer.OriginSlot, _destination, 0, 0));
  568. }
  569. if (nextslot.Count >= 2 || _moveQueue.Count >= 4)
  570. {
  571. return;
  572. }
  573. }
  574. }
  575. //if all need PM unavailable =>pick 1 wait for swap
  576. //or 1 can input pm => pick 2, A in pm ,B wait for swap
  577. if (nextslot.Count == 0 || nohastowaitpm && nextslot.Count == 1
  578. && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot,0)
  579. && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot,1)
  580. && WaferManager.Instance.CheckNoWafer(ModuleName.VPA, 0))
  581. {
  582. foreach (WaferInfo wafer in ctrljob.LotWafers)
  583. {
  584. if (wafer.NextSequenceStep == 0 && !wafer.IsEmpty
  585. && ModuleHelper.IsVCE((ModuleName)wafer.SourceStation))
  586. {
  587. ModuleName _destination = _lstProcessJobs.Find(x => x.Sequence.Name == wafer.SequenceName).Sequence.PMs[0];
  588. nextslot.Add(wafer.SourceSlot, _destination);
  589. foreach (KeyValuePair<int, ModuleName> slot in nextslot)
  590. {
  591. _moveQueue.Add(new MoveItem(ModuleName.VCE1, slot.Key, slot.Value, 0, 0));
  592. }
  593. return;
  594. }
  595. if (movingStatus[wafer.OriginSlot] == MovingStatus.WaitAlign && !wafer.IsEmpty
  596. && WaferManager.Instance.CheckHasWafer((ModuleName)wafer.OriginStation, wafer.OriginSlot)
  597. && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0)
  598. && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1)
  599. && _moveQueue.Count == 0
  600. )
  601. {
  602. ModuleName _destination = ModuleName.TMRobot;
  603. _moveQueue.Add(new MoveItem((ModuleName)wafer.OriginStation, wafer.OriginSlot, ModuleName.TMRobot, 0, 0));
  604. return;
  605. }
  606. }
  607. }
  608. }
  609. }
  610. return;
  611. }
  612. }
  613. private void PrepareWaferOnRBToPM()
  614. {
  615. //tm robot has wafer and wait for process which means have to go to PM
  616. if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0)
  617. && movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0).OriginSlot] == MovingStatus.WaitProcess
  618. )
  619. {
  620. //find PM has no wafer
  621. WaferInfo wafer = WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0);
  622. if (SequenceNeedPMsAvailable(wafer.ProcessJob.Sequence.PMs))
  623. {
  624. ModuleName _destination = SearchPM(wafer.ProcessJob.Sequence.PMs, new Dictionary<int, ModuleName>());
  625. if (ModuleHelper.IsPm(_destination) && _TMRobot.IsAvailable)
  626. {
  627. _moveQueue.Add(new MoveItem(ModuleName.TMRobot, 0, _destination, 0, 0));
  628. SERunTMRobotTask();
  629. return;
  630. }
  631. }
  632. }
  633. if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1)
  634. && movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1).OriginSlot] == MovingStatus.WaitProcess)
  635. {
  636. WaferInfo wafer = WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1);
  637. if (SequenceNeedPMsAvailable(wafer.ProcessJob.Sequence.PMs))
  638. {
  639. ModuleName _destination = SearchPM(wafer.ProcessJob.Sequence.PMs, new Dictionary<int, ModuleName>());
  640. if (ModuleHelper.IsPm(_destination) && _TMRobot.IsAvailable)
  641. {
  642. _moveQueue.Add(new MoveItem(ModuleName.TMRobot, 1, _destination, 0, (Hand)1));
  643. SERunTMRobotTask();
  644. return;
  645. }
  646. }
  647. }
  648. }
  649. private void CheckWaferFromVceNeedAlign()
  650. {
  651. //if has wafer and need align and align is empty => goto PA
  652. if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot,0)
  653. && movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0).OriginSlot] == MovingStatus.WaitAlign
  654. && WaferManager.Instance.CheckNoWafer(ModuleName.VPA, 0)
  655. &&_TMRobot.IsAvailable)
  656. {
  657. _moveQueue.Add(new MoveItem(ModuleName.TMRobot, 0,ModuleName.VPA, 0, 0));
  658. movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0).OriginSlot] = MovingStatus.StartAlign;
  659. SERunTMRobotTask();
  660. return;
  661. }
  662. if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1)
  663. && movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1).OriginSlot] == MovingStatus.WaitAlign
  664. && WaferManager.Instance.CheckNoWafer(ModuleName.VPA, 0)
  665. && _TMRobot.IsAvailable)
  666. {
  667. _moveQueue.Add(new MoveItem(ModuleName.TMRobot, 1, ModuleName.VPA, 0, (Hand)1));
  668. movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1).OriginSlot] = MovingStatus.StartAlign;
  669. SERunTMRobotTask();
  670. return;
  671. }
  672. //PA has wafer need align =>
  673. if (WaferManager.Instance.CheckHasWafer(ModuleName.VPA, 0) && _TMRobot.IsAvailable)
  674. {
  675. WaferInfo currentwafer = WaferManager.Instance.GetWafer(ModuleName.VPA, 0);
  676. switch (movingStatus[currentwafer.OriginSlot])
  677. {
  678. case MovingStatus.StartAlign:
  679. if(_TMRobot.IsAvailable)
  680. {
  681. float angle = float.Parse(currentwafer.ProcessJob.Sequence.Steps[0].StepParameter["AlignAngle"].ToString());
  682. _TMRobot.Align(angle);
  683. movingStatus[currentwafer.OriginSlot] = MovingStatus.Aligning;
  684. SERunTMRobotTask();
  685. return;
  686. }
  687. break;
  688. case MovingStatus.Aligning:
  689. movingStatus[currentwafer.OriginSlot] = MovingStatus.WaitProcess;
  690. if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) && _TMRobot.IsAvailable)
  691. {
  692. _moveQueue.Add(new MoveItem(ModuleName.VPA, 0, ModuleName.TMRobot, 0, 0));
  693. SERunTMRobotTask();
  694. return;
  695. }
  696. if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1) && _TMRobot.IsAvailable)
  697. {
  698. _moveQueue.Add(new MoveItem(ModuleName.VPA, 0, ModuleName.TMRobot, 1, (Hand)1));
  699. SERunTMRobotTask();
  700. return;
  701. }
  702. break;
  703. }
  704. }
  705. }
  706. private void PrepareTMMoveitems()
  707. {
  708. }
  709. private void Cycling()
  710. {
  711. //所有的PM都Idle TM都空闲的情况
  712. if (IsAllNeedPMsAvailabe() && IsModuleAvailable(_destinationModule) && IsModuleAvailable(_sourceModule) && _TMRobot.IsAvailable)
  713. {
  714. //如果PM没有wafer tm也闲置 但Cycle数量未达到目标
  715. if (!PMsHasWafers() && _TMRobot.IsAvailable && _CycleWafers.Count == 0)
  716. {
  717. ++CycleIndex;
  718. if (CycleIndex < CycleNum)
  719. {
  720. for (int i = 0; i < _sourceSlotNumber; i++)
  721. {
  722. if (WaferManager.Instance.CheckHasWafer(_sourceModule, i))
  723. _CycleWafers.Enqueue(new MoveItem(_sourceModule, i, _destinationModule, i, Hand.None));
  724. }
  725. }
  726. else
  727. {
  728. _cycleState = RState.End;
  729. return;
  730. }
  731. }
  732. //wafer返回
  733. if (PMsHasWafers())
  734. {
  735. var pmSlots = GetReadyOutPMs();
  736. //var inSlots = GetReadyInSlot(_destinationModule, _destinationSlotNumber);
  737. for (int i = 0; i < pmSlots.Count ; i++)
  738. {
  739. WaferInfo _endwafer = WaferManager.Instance.GetWafer(pmSlots[i], 0);
  740. int _endslot = _endwafer.OriginSlot;
  741. _runningItems.Enqueue(new MoveItem(pmSlots[i], 0, _destinationModule, _endslot, Hand.Both));
  742. }
  743. }
  744. else
  745. {
  746. //进腔环节 可在此处根据PM的角度 增加进入角度
  747. //三个腔体的进入方式
  748. var InPMs = GetReadyInPMs();
  749. if (_CycleWafers.Count > 0 && InPMs.Count >= 1)
  750. {
  751. var item = _CycleWafers.Dequeue();
  752. _runningItems.Enqueue(new MoveItem(item.SourceModule, item.SourceSlot, InPMs[0], 0, Hand.Both));
  753. }
  754. if (_CycleWafers.Count > 0 && InPMs.Count >= 2)
  755. {
  756. var item = _CycleWafers.Dequeue();
  757. _runningItems.Enqueue(new MoveItem(item.SourceModule, item.SourceSlot, InPMs[1], 0, Hand.Both));
  758. }
  759. if (_CycleWafers.Count > 0 && InPMs.Count >= 3)
  760. {
  761. var item = _CycleWafers.Dequeue();
  762. _runningItems.Enqueue(new MoveItem(item.SourceModule, item.SourceSlot, InPMs[2], 0, Hand.Both));
  763. }
  764. }
  765. if (_runningItems.Count > 0)
  766. {
  767. if (_TMRobot.PostMoveItems(_runningItems.ToArray()))
  768. _runningItems.Clear();
  769. }
  770. }
  771. //存在PM可用
  772. //else if (IsExistPMsAvailabe() && IsModuleAvailable(_destinationModule) && IsModuleAvailable(_sourceModule) && _TMRobot.IsAvailable)
  773. //{
  774. // //获得可以用的chamber 向里面传片
  775. //}
  776. }
  777. private void PMProcess()
  778. {
  779. //foreach (ModuleName chamber in tmCycleRoutine)
  780. //{
  781. // if (ModuleHelper.IsPm(chamber) && WaferManager.Instance.CheckHasWafer(chamber, 0) && ModuleHelper.IsInstalled(chamber))
  782. // {
  783. // WaferInfo info = WaferManager.Instance.GetWafer(chamber, 0);
  784. //
  785. // dictSchedulers[chamber].EventWaferArrived?.Invoke(this, new WaferMoveArgs(ModuleName.TMRobot, 0, chamber, 0));
  786. // }
  787. //}
  788. }
  789. public void Abort()
  790. {
  791. CycleIndex = null;
  792. _TMRobot._entityTaskToken = (int)FSM_MSG.NONE;
  793. }
  794. #endregion
  795. #region 搜索函数
  796. //回到的槽位确认 采用补进方式即自底向上填
  797. private List<int> GetReadyInSlot(ModuleName module, int slotCount)
  798. {
  799. List<int> slots = new List<int>();
  800. for (int i = 0; i < slotCount; i++)
  801. {
  802. if (WaferManager.Instance.CheckNoWafer(module, i))
  803. slots.Add(i);
  804. if (slots.Count >= 2)
  805. return slots;
  806. }
  807. return slots;
  808. }
  809. //确认
  810. private List<int> GetReadyOutSlot(ModuleName module, int slotCount)
  811. {
  812. List<int> slots = new List<int>();
  813. for (int i = 0; i < slotCount; i++)
  814. {
  815. if (WaferManager.Instance.CheckHasWafer(module, i))
  816. slots.Add(i);
  817. if (slots.Count >= 2)
  818. return slots;
  819. }
  820. return slots;
  821. }
  822. //确认PM是否可用 且没有wafer
  823. private List<ModuleName> GetReadyInPMs()
  824. {
  825. List<ModuleName> inpm = new List<ModuleName>();
  826. foreach (var module in tmCycleRoutine)
  827. {
  828. if (ModuleHelper.IsPm(module))
  829. {
  830. if (IsModuleAvailable(module) && WaferManager.Instance.CheckNoWafer(module, 0))
  831. {
  832. inpm.Add(module);
  833. if (inpm.Count >= 3)
  834. break;
  835. }
  836. }
  837. }
  838. return inpm;
  839. }
  840. private List<ModuleName> GetReadyOutPMs()
  841. {
  842. List<ModuleName> outpm = new List<ModuleName>();
  843. foreach (var module in tmCycleRoutine)
  844. {
  845. if (ModuleHelper.IsPm(module))
  846. {
  847. if (IsModuleAvailable(module) && WaferManager.Instance.CheckHasWafer(module, 0))
  848. {
  849. outpm.Add(module);
  850. if (outpm.Count >= 3)
  851. break;
  852. }
  853. }
  854. }
  855. return outpm;
  856. }
  857. //PM有wafer
  858. private bool PMsHasWafers()
  859. {
  860. foreach (var module in tmCycleRoutine)
  861. {
  862. if (ModuleHelper.IsPm(module) && ModuleHelper.IsInstalled(module))
  863. {
  864. if (WaferManager.Instance.CheckHasWafer(module, 0))
  865. return true;
  866. }
  867. }
  868. return false;
  869. }
  870. //PM全可用
  871. private bool IsAllNeedPMsAvailabe()
  872. {
  873. foreach (var module in tmCycleRoutine)
  874. {
  875. if (ModuleHelper.IsPm(module) && ModuleHelper.IsInstalled(module))
  876. {
  877. if (!IsModuleAvailable(module))
  878. return false;
  879. }
  880. }
  881. return true;
  882. }
  883. //PM存在可用
  884. private bool SequenceNeedPMsAvailable(List<ModuleName> needPMs)
  885. {
  886. foreach (var module in needPMs)
  887. {
  888. if (ModuleHelper.IsPm(module) && ModuleHelper.IsInstalled(module))
  889. {
  890. if (IsModuleAvailable(module) && WaferManager.Instance.CheckNoWafer(module,0))
  891. return true;
  892. }
  893. }
  894. return false;
  895. }
  896. private ModuleName SearchPM(List<ModuleName> needPMs,Dictionary<int,ModuleName> PMsChoice)
  897. {
  898. foreach (var module in needPMs)
  899. {
  900. if (ModuleHelper.IsPm(module) && ModuleHelper.IsInstalled(module))
  901. {
  902. if (IsModuleAvailable(module) && !PMsChoice.ContainsValue(module) && WaferManager.Instance.CheckNoWafer(module,0))
  903. return module;
  904. }
  905. }
  906. return ModuleName.TMRobot;
  907. }
  908. private bool IsModuleAvailable(ModuleName module)
  909. {
  910. return dictSchedulers.Keys.Contains(module) && dictSchedulers[module].IsAvailable;
  911. }
  912. //检查需要使用的pm
  913. private bool CheckSequencePmReady(SequenceInfo seq, List<ModuleName> pmOccupied, out List<ModuleName> pmUsed, out string reason)
  914. {
  915. pmUsed = new List<ModuleName>();
  916. reason = "";
  917. bool pmIsReady = true;
  918. for (int i = 0; i < seq.Steps.Count; i++)
  919. {
  920. SequenceStepInfo stepInfo = seq.Steps[i];
  921. bool hasPm = false;
  922. foreach (var module in stepInfo.StepModules)
  923. {
  924. if (!ModuleHelper.IsPm(module))
  925. {
  926. hasPm = true;
  927. break;
  928. }
  929. if (ModuleHelper.IsInstalled(module) && (pmOccupied == null || !pmOccupied.Contains(module)))
  930. {
  931. hasPm = true;
  932. }
  933. if (!pmUsed.Contains(module))
  934. pmUsed.Add(module);
  935. }
  936. if (pmIsReady && !hasPm)
  937. {
  938. reason = $"Step {i + 1} no valid PM, " + string.Join("|", stepInfo.StepModules);
  939. pmIsReady = false;
  940. }
  941. }
  942. return pmIsReady;
  943. }
  944. //检查是否又对应PM的recipe
  945. private bool CheckSequenceRecipeFileValid(SequenceInfo seq, out string reason)
  946. {
  947. for (int i = 0; i < seq.Steps.Count; i++)
  948. {
  949. SequenceStepInfo stepInfo = seq.Steps[i];
  950. foreach (var module in stepInfo.StepModules)
  951. {
  952. if (ModuleHelper.IsPm(module))
  953. {
  954. string attr = $"{module}Recipe";
  955. if (stepInfo.StepParameter.ContainsKey(attr)
  956. && !string.IsNullOrEmpty((string)stepInfo.StepParameter[attr]))
  957. {
  958. var recipeName = (string)stepInfo.StepParameter[attr];
  959. if (!string.IsNullOrEmpty(recipeName))
  960. {
  961. var recipeContent =
  962. RecipeFileManager.Instance.LoadRecipe($"{module}", recipeName, false, "Process");
  963. if (string.IsNullOrEmpty(recipeContent))
  964. {
  965. reason = $"Can not find recipe file{recipeName}";
  966. return false;
  967. }
  968. }
  969. }
  970. }
  971. }
  972. }
  973. reason = "";
  974. return true;
  975. }
  976. //找到对应sequence
  977. public ProcessJobInfo GetFirstProcessJob(ControlJobInfo cj)
  978. {
  979. foreach (var pj in _lstProcessJobs)
  980. {
  981. if (pj.ControlJobName == cj.Name)
  982. return pj;
  983. }
  984. return null;
  985. }
  986. #endregion
  987. }
  988. }