SETMCycle.cs 46 KB

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