SETMCycle.cs 49 KB

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