using Aitex.Core.Common; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.Fsm; using Aitex.Core.RT.Log; using Aitex.Core.RT.RecipeCenter; using Aitex.Core.RT.Routine; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using Aitex.Sorter.Common; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.Jobs; using MECF.Framework.Common.Schedulers; using MECF.Framework.Common.SubstrateTrackings; using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Robot; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using Venus_Core; using Venus_RT.Modules.Schedulers; using Venus_RT.Scheduler; using static Aitex.Core.Util.SubscriptionAttribute; namespace Venus_RT.Modules { public class SETMCycle : ModuleRoutineBase, IRoutine { enum TMCycleStep { Start, ReturnBack, Cycling, End, } private List _lstControlJobs = new List(); private List _lstProcessJobs = new List(); private Dictionary dictSchedulers = new Dictionary(); private List tmCycleRoutine = new List() { ModuleName.VCE1, ModuleName.PMA, ModuleName.PMB, ModuleName.PMC, ModuleName.VCE1 }; private int CycleNum = 0; private bool _isCycleMode; private ModuleName _sourceModule = ModuleName.VCE1; private ModuleName _destinationModule = ModuleName.VCE1; private int _sourceSlotNumber = 25; //private int _destinationSlotNumber = 25; private SchedulerSETMRobot _TMRobot = (SchedulerSETMRobot)Singleton.Instance.GetScheduler(ModuleName.SETM); private SchedulerFACallback _faCallback; private SchedulerDBCallback _dbCallback; //private readonly int INVALID_SLOT = -1; private Queue _runningItems = new Queue(); private Queue _CycleWafers = new Queue(); private RState _cycleState = RState.Init; private Stopwatch _cycleWatch = new Stopwatch(); private List _moveQueue = new List(); private List movingStatus = new List(); private double _throughput = 0; private DateTime _starttime; public int? CycleIndex; private int CycledWafer => _currentWafer + _pastWafer; private int _currentWafer; private int _pastWafer; private bool _IsFirstLot = true; private bool IsSequenceCycle = false; //判断是否跑货 #region 构造函数 public SETMCycle(ModuleName module) : base(module) { Name = "TM Cycle"; void _initMoudle(ModuleName name, SchedulerModule sche) { if (ModuleHelper.IsInstalled(name)) { dictSchedulers[name] = sche; } } _initMoudle(ModuleName.VCE1, new SchedulerVCE(ModuleName.VCE1)); _initMoudle(ModuleName.PMA, new SchedulerPM(ModuleName.PMA)); _initMoudle(ModuleName.PMB, new SchedulerPM(ModuleName.PMB)); _initMoudle(ModuleName.PMC, new SchedulerPM(ModuleName.PMC)); _initMoudle(ModuleName.PMD, new SchedulerPM(ModuleName.PMD)); _faCallback = new SchedulerFACallback(); _dbCallback = new SchedulerDBCallback(); _currentWafer = 0; _pastWafer = 0; DATA.Subscribe("SEScheduler.CycledWafer", ()=> CycledWafer ); DATA.Subscribe("SEScheduler.CycleSetPoint", ()=> CycleNum); DATA.Subscribe("SEScheduler.CycleCount", ()=> CycleIndex); DATA.Subscribe("SEScheduler.ThroughPut", ()=> _throughput); } #endregion #region Cycle //run货模式 public RState StartJob(string jobName) { if (_TMRobot.IsVCESlitDoorClosed) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"VCE SlitDoor is Close cannot run!"); return RState.Failed; } //sequenceCycle CycleIndex = 0; _pastWafer = 0; _currentWafer = 0; CycleNum = SC.GetValue("System.CycleCount"); _CycleWafers.Clear(); IsSequenceCycle = true; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"start job rejected, not found job with id {jobName}"); return RState.Failed; } if (cj.State == EnumControlJobState.WaitingForStart) { cj.SetState(EnumControlJobState.Executing); //PreJobClean(cj); cj.JetState = EnumJetCtrlJobState.Quequed; cj.StartTime = DateTime.Now; if (_IsFirstLot) { _dbCallback.LotCreated(cj); _IsFirstLot = false; } _faCallback.JobStarted(cj, GetFirstProcessJob(cj)); int maxslot = 0; cj.LotWafers.ForEach(x => maxslot = x.OriginSlot > maxslot ? x.OriginSlot : maxslot); //waiting status means no use movingStatus = new List(); movingStatus.AddRange(new MovingStatus[maxslot + 1]); for (int i = 0; i < movingStatus.Count; i++) movingStatus[i] = MovingStatus.Waiting; foreach (var wafer in cj.LotWafers) { WaferInfo currentWafer = WaferManager.Instance.GetWafer((ModuleName)wafer.OriginStation, wafer.OriginSlot); WaferManager.Instance.UpdateWaferProcessStatus((ModuleName)wafer.OriginStation, wafer.OriginSlot, EnumWaferProcessStatus.Idle); currentWafer.ProcessJob = _lstProcessJobs.Find(x => x.Sequence.Name == wafer.SequenceName); if (currentWafer.ProcessJob.Sequence.Steps[0].StepParameter.ContainsValue("VPA")) movingStatus[wafer.OriginSlot] = MovingStatus.WaitAlign; else movingStatus[wafer.OriginSlot] = MovingStatus.WaitProcess; } } if (!_cycleWatch.IsRunning) { _cycleWatch.Restart(); } _starttime = DateTime.Now; _cycleState = RState.Running; return _cycleState; } //processJob(sequence num) ControlJob(1) public void CreateJob(Dictionary param) { _isCycleMode = SC.GetValue("System.IsCycleMode"); CycleNum = _isCycleMode ? SC.GetValue("System.CycleCount") : 0; string[] slotSequence = (string[])param["SlotSequence"]; string jobId = (string)param["JobId"]; string module = (string)param["Module"]; string lotId = jobId; if (param.ContainsKey("LotId")) lotId = (string)param["LotId"]; if (!ModuleHelper.IsVCE(ModuleHelper.Converter(module))) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"{module} should be VCE"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } if (string.IsNullOrEmpty(jobId)) { jobId = "CJ_Local_" + module; } if (_lstControlJobs.Exists(x => x.Name == jobId)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"{jobId} already created"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } ControlJobInfo cj = new ControlJobInfo(); cj.Name = jobId; cj.Module = module; cj.LotName = lotId; cj.LotInnerId = Guid.NewGuid(); cj.LotWafers = new List(); cj.SetState(EnumControlJobState.WaitingForStart); cj.JetState = EnumJetCtrlJobState.Created; Dictionary seqSlot = new Dictionary(); Dictionary>> seqSlotWafers = new Dictionary>>(); Dictionary indexSequence = new Dictionary(); bool enableGroupBySequence = SC.GetValue("Scheduler.GroupWaferBySequence"); for (int i = 0; i < SC.GetValue("VCE1.SlotNumber"); i++) { if (string.IsNullOrEmpty(slotSequence[i]) || string.IsNullOrEmpty(slotSequence[i].Trim())) continue; string groupName = enableGroupBySequence ? slotSequence[i].Trim() : i.ToString(); indexSequence[groupName] = slotSequence[i]; if (!seqSlot.ContainsKey(groupName)) { seqSlot[groupName] = new bool[SC.GetValue("VCE1.SlotNumber")]; } if (!seqSlotWafers.ContainsKey(groupName)) { seqSlotWafers[groupName] = new List>(); } seqSlot[groupName][i] = true; if (!WaferManager.Instance.CheckHasWafer(module, i)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job wafer: {module} slot {i + 1} not in the carrier"); return; } if (!WaferManager.Instance.CheckWafer(ModuleHelper.Converter(module), i, WaferStatus.Normal)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job wafer: {module} slot {i + 1} status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Status}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } if (WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState != EnumWaferProcessStatus.Idle) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job wafer: {module} slot {i + 1} process status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } cj.LotWafers.Add(WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i)); WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).SequenceName = slotSequence[i]; seqSlotWafers[groupName].Add(Tuple.Create(ModuleHelper.Converter(module), i)); cj.JobWaferSize = WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Size; LOG.Write(eEvent.EV_ROUTER, ModuleName.System, $"Assigned wafer job, wafer {module}.{i + 1}, sequence: {slotSequence[i]}"); } if (seqSlotWafers.Count == 0) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job has not assign wafer"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } List pjs = new List(); string[] seqs = seqSlot.Keys.ToArray(); for (int i = 0; i < seqs.Length; i++) { ProcessJobInfo pj = new ProcessJobInfo(); pj.Name = jobId + "_" + (i + 1); pj.Sequence = SequenceInfoHelper.GetInfo(indexSequence[seqs[i]]); pj.ControlJobName = cj.Name; pj.LotName = lotId; pj.SlotWafers = seqSlotWafers[seqs[i]]; pj.SetState(EnumProcessJobState.Queued); if (!CheckSequencePmReady(pj.Sequence, null, out _, out string reason)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"no valid chamber for the {reason}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } if (!RouteManager.IsATMMode && !CheckSequenceRecipeFileValid(pj.Sequence, out reason)) { LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"recipe file not valid in the sequence, {reason}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } pjs.Add(pj); } _dbCallback.LotUpdate(cj); foreach (var pj in pjs) { cj.ProcessJobNameList.Add(pj.Name); _lstProcessJobs.Add(pj); } _lstControlJobs.Add(cj); } public void AbortJob(string jobName) { } public RState Start(params object[] objs) { if (_TMRobot.IsVCESlitDoorClosed) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"VCE SlitDoor is Close cannot run!"); return RState.Failed; } //普通Cycle if (objs.Length == 2) { var modules = ((string[])objs[0]).ToList(); if (modules.Count >= 2) tmCycleRoutine.Clear(); foreach (var mod in modules) { try { ModuleName module = ModuleHelper.Converter(mod); tmCycleRoutine.Add(module); } catch (Exception ex) { LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Invalid module string: {mod}, Exception:{ex}"); return RState.Failed; } } CycleNum = (int)objs[1]; CycleNum = SC.GetValue("System.CycleCount"); _CycleWafers.Clear(); CycleIndex = 0; for (int i = 0; i < _sourceSlotNumber; i++) { if (WaferManager.Instance.CheckHasWafer(_sourceModule, i)) _CycleWafers.Enqueue(new MoveItem(_sourceModule, i, _destinationModule, i, Hand.None)); } IsSequenceCycle = false; _cycleState = RState.Running; } else { LOG.Write(eEvent.ERR_TM,Module,"Invalid parameter for cycle!"); _cycleState = RState.Failed; } return _cycleState; } public RState Monitor() { if (_cycleState == RState.Running) { //run sequence if (IsSequenceCycle) { CheckCycleDone(); SETMRobotTask(); SERunTMRobotTask(); SEPMTask(); } //only TMCycle else { Cycling(); //PMProcess(); } } return _cycleState; } #endregion #region 推进|检查|计数 private void CheckCycleDone() { //how to calculate current wafer? => _currentWafer //1. wafer in vce //2. wafer is Idle _currentWafer = 0; for (int index = 0; index < movingStatus.Count;++index) { if(WaferManager.Instance.CheckHasWafer(ModuleName.VCE1,index) && movingStatus[index] == MovingStatus.Idle) _currentWafer ++; } //throughput = CycledWafer/time, time(h) _throughput = Math.Round(CycledWafer / (DateTime.Now - _starttime).TotalSeconds * 3600,2); //how to confirm a sequence is over? //3、wafer is all in vce //2、all status is over => idle //1、TMRobot and PM is available if (_TMRobot.IsAvailable && AllPMIsAvailable() && ALLStatusIsOver() && WaferAllInVCE()) { ++CycleIndex; if (CycleIndex >= CycleNum) { _lstControlJobs.Find(lcj => lcj.State == EnumControlJobState.Executing).SetState(EnumControlJobState.WaitingForStart); _cycleState = RState.End; } else { _pastWafer += _currentWafer; ControlJobInfo cj = _lstControlJobs.Find(lcj => lcj.State == EnumControlJobState.Executing); _currentWafer = 0; int maxslot = 0; cj.LotWafers.ForEach(x => maxslot = x.OriginSlot > maxslot ? x.OriginSlot : maxslot); //waiting status means no use movingStatus = new List(); movingStatus.AddRange(new MovingStatus[maxslot + 1]); for (int i = 0; i < movingStatus.Count; i++) movingStatus[i] = MovingStatus.Waiting; foreach (var wafer in cj.LotWafers) { WaferInfo currentWafer = WaferManager.Instance.GetWafer((ModuleName)wafer.OriginStation, wafer.OriginSlot); WaferManager.Instance.UpdateWaferProcessStatus((ModuleName)wafer.OriginStation, wafer.OriginSlot, EnumWaferProcessStatus.Idle); currentWafer.ProcessJob = _lstProcessJobs.Find(x => x.Sequence.Name == wafer.SequenceName); if (currentWafer.ProcessJob.Sequence.Steps[0].StepParameter.ContainsValue("VPA")) movingStatus[wafer.OriginSlot] = MovingStatus.WaitAlign; else movingStatus[wafer.OriginSlot] = MovingStatus.WaitProcess; } } } } //movingStatus is all Idle or no use private bool ALLStatusIsOver() { bool flag = true; movingStatus.ForEach(x => flag = (x == MovingStatus.Idle || x == MovingStatus.Waiting) && flag) ; return flag; } private bool WaferAllInVCE() { ControlJobInfo cj = _lstControlJobs.Find(lcj => lcj.State == EnumControlJobState.Executing); bool flag = true; cj.LotWafers.ForEach(wafer => flag = WaferManager.Instance.CheckHasWafer((ModuleName)wafer.OriginStation, wafer.OriginSlot) && flag); return flag; } private bool AllPMIsAvailable() { foreach (KeyValuePair pair in dictSchedulers) if (ModuleHelper.IsPm(pair.Key) && !pair.Value.IsAvailable) return false; return true; } private void SERunTMRobotTask() { if (_TMRobot.IsAvailable) { if (_moveQueue.Count > 0 &&_TMRobot.PostMoveItems(_moveQueue.ToArray())) { foreach (var item in _moveQueue) { var wafer = WaferManager.Instance.GetWafer(item.SourceModule, item.SourceSlot); if (wafer.IsEmpty) { // post alarm _cycleState = RState.Failed; LOG.Write(eEvent.ERR_ROUTER, ModuleName.System, $"Cannot run TM moving task as Get {item.SourceModule}{item.SourceModule} Wafer Info failed"); return; } } _moveQueue.Clear(); } } } private void SERunPMTask() { } private void SETMRobotTask() { //tm is free if (_TMRobot.IsAvailable) { CheckWaferFromVceNeedAlign(); PrepareWaferOnRBToPM(); GetBackWafer(); GetNextWaferPickFromVCE(); } } private void SEPMTask() { //foreach all pms and the wafer's receipe foreach (KeyValuePair dict in dictSchedulers) { //pm has wafer && pm is available => find the wafer's receipe if (ModuleHelper.IsPm(dict.Key)) { if (dict.Value.IsAvailable) { foreach (var ctrljob in _lstControlJobs) { //Port is run if (ctrljob.State == EnumControlJobState.Executing) { if (WaferManager.Instance.CheckHasWafer(dict.Key, 0)) { WaferInfo currentWafer = WaferManager.Instance.GetWafer(dict.Key, 0); switch (movingStatus[currentWafer.OriginSlot]) { case MovingStatus.WaitProcess: dict.Value.EventWaferArrived?.Invoke(this, new WaferMoveArgs(ModuleName.TMRobot, 0, dict.Key, 0)); movingStatus[currentWafer.OriginSlot] = MovingStatus.StartProcess; break; case MovingStatus.Processing: movingStatus[currentWafer.OriginSlot] = MovingStatus.Idle; break; case MovingStatus.StartProcess: movingStatus[currentWafer.OriginSlot] = MovingStatus.Processing; break; default: break; } } } } } } } } //get the wafer need back private void GetBackWafer() { //find foreach (KeyValuePair dict in dictSchedulers) { //pm has wafer && pm is available => find the wafer's receipe if (ModuleHelper.IsPm(dict.Key) && WaferManager.Instance.CheckHasWafer(dict.Key, 0) && WaferManager.Instance.CheckNoWafer(ModuleName.VPA, 0)) { WaferInfo currentWafer = WaferManager.Instance.GetWafer(dict.Key, 0); //if the wafer has been processed, we need to pickfrom it. if (dict.Value.IsAvailable && movingStatus[currentWafer.OriginSlot] == MovingStatus.Idle && _TMRobot.IsAvailable) { _moveQueue.Add(new MoveItem(dict.Key,0, (ModuleName)currentWafer.OriginStation, currentWafer.OriginSlot,0)); //one hand has wafer if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) && movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0).OriginSlot] == MovingStatus.WaitProcess && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1)) { WaferInfo wafer = WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0); if (wafer.ProcessJob.Sequence.PMs.Contains(dict.Key)) { _moveQueue.Add(new MoveItem(ModuleName.TMRobot,0,dict.Key,0,0)); } } else if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1) && movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1).OriginSlot] == MovingStatus.WaitProcess && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0)) { WaferInfo wafer = WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1); if (wafer.ProcessJob.Sequence.PMs.Contains(dict.Key)) { _moveQueue.Add(new MoveItem(ModuleName.TMRobot, 1, dict.Key, 0, (Hand)1)); } } return; } } } } //get the wafer need Pick From vce private void GetNextWaferPickFromVCE() { if (_TMRobot.IsAvailable) { Dictionary nextslot = new Dictionary(); foreach (var ctrljob in _lstControlJobs) { //Port is run if (ctrljob.State == EnumControlJobState.Executing) { bool nohastowaitpm = false; foreach (WaferInfo wafer in ctrljob.LotWafers) { //if need pm available =>pick 2 if (!wafer.IsEmpty && SequenceNeedPMsAvailable(_lstProcessJobs.Find(x => x.Sequence.Name == wafer.SequenceName).Sequence.PMs) && ModuleHelper.IsVCE((ModuleName)wafer.OriginStation) && (movingStatus[wafer.OriginSlot] == MovingStatus.WaitProcess)) { nohastowaitpm = true; ModuleName _destination = SearchPM(_lstProcessJobs.Find(x => x.Sequence.Name == wafer.SequenceName).Sequence.PMs, nextslot); nextslot.Add(wafer.OriginSlot, _destination); _moveQueue.Add(new MoveItem(ModuleName.VCE1, wafer.OriginSlot, _destination, 0, 0)); if (nextslot.Count >= 2 || _moveQueue.Count >= 4) { return; } } //if need align available => pick 2 if (!wafer.IsEmpty && WaferManager.Instance.CheckNoWafer(ModuleName.VPA, 0) && ModuleHelper.IsVCE((ModuleName)wafer.OriginStation) && SequenceNeedPMsAvailable(_lstProcessJobs.Find(x => x.Sequence.Name == wafer.SequenceName).Sequence.PMs) && (movingStatus[wafer.OriginSlot] == MovingStatus.WaitAlign)) { nohastowaitpm = true; ModuleName _destination = ModuleName.TMRobot; //double pick if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1)) { nextslot.Add(wafer.OriginSlot, _destination); _moveQueue.Add(new MoveItem(ModuleName.VCE1, wafer.OriginSlot, _destination, 0, 0)); } if (nextslot.Count >= 2 || _moveQueue.Count >= 4) { return; } } } //if all need PM unavailable =>pick 1 wait for swap //or 1 can input pm => pick 2, A in pm ,B wait for swap if (nextslot.Count == 0 || nohastowaitpm && nextslot.Count == 1 && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot,0) && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot,1) && WaferManager.Instance.CheckNoWafer(ModuleName.VPA, 0)) { foreach (WaferInfo wafer in ctrljob.LotWafers) { if (wafer.NextSequenceStep == 0 && !wafer.IsEmpty && ModuleHelper.IsVCE((ModuleName)wafer.SourceStation)) { ModuleName _destination = _lstProcessJobs.Find(x => x.Sequence.Name == wafer.SequenceName).Sequence.PMs[0]; nextslot.Add(wafer.SourceSlot, _destination); foreach (KeyValuePair slot in nextslot) { _moveQueue.Add(new MoveItem(ModuleName.VCE1, slot.Key, slot.Value, 0, 0)); } return; } if (movingStatus[wafer.OriginSlot] == MovingStatus.WaitAlign && !wafer.IsEmpty && WaferManager.Instance.CheckHasWafer((ModuleName)wafer.OriginStation, wafer.OriginSlot) && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1) && _moveQueue.Count == 0 ) { //ModuleName _destination = ModuleName.TMRobot; _moveQueue.Add(new MoveItem((ModuleName)wafer.OriginStation, wafer.OriginSlot, ModuleName.TMRobot, 0, 0)); return; } } } } } return; } } private void PrepareWaferOnRBToPM() { //tm robot has wafer and wait for process which means have to go to PM if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) && movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0).OriginSlot] == MovingStatus.WaitProcess ) { //find PM has no wafer WaferInfo wafer = WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0); if (SequenceNeedPMsAvailable(wafer.ProcessJob.Sequence.PMs)) { ModuleName _destination = SearchPM(wafer.ProcessJob.Sequence.PMs, new Dictionary()); if (ModuleHelper.IsPm(_destination) && _TMRobot.IsAvailable) { _moveQueue.Add(new MoveItem(ModuleName.TMRobot, 0, _destination, 0, 0)); SERunTMRobotTask(); return; } } } if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1) && movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1).OriginSlot] == MovingStatus.WaitProcess) { WaferInfo wafer = WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1); if (SequenceNeedPMsAvailable(wafer.ProcessJob.Sequence.PMs)) { ModuleName _destination = SearchPM(wafer.ProcessJob.Sequence.PMs, new Dictionary()); if (ModuleHelper.IsPm(_destination) && _TMRobot.IsAvailable) { _moveQueue.Add(new MoveItem(ModuleName.TMRobot, 1, _destination, 0, (Hand)1)); SERunTMRobotTask(); return; } } } } private void CheckWaferFromVceNeedAlign() { //if has wafer and need align and align is empty => goto PA if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot,0) && movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0).OriginSlot] == MovingStatus.WaitAlign && WaferManager.Instance.CheckNoWafer(ModuleName.VPA, 0) &&_TMRobot.IsAvailable) { _moveQueue.Add(new MoveItem(ModuleName.TMRobot, 0,ModuleName.VPA, 0, 0)); movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0).OriginSlot] = MovingStatus.StartAlign; SERunTMRobotTask(); return; } if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1) && movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1).OriginSlot] == MovingStatus.WaitAlign && WaferManager.Instance.CheckNoWafer(ModuleName.VPA, 0) && _TMRobot.IsAvailable) { _moveQueue.Add(new MoveItem(ModuleName.TMRobot, 1, ModuleName.VPA, 0, (Hand)1)); movingStatus[WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1).OriginSlot] = MovingStatus.StartAlign; SERunTMRobotTask(); return; } //PA has wafer need align => if (WaferManager.Instance.CheckHasWafer(ModuleName.VPA, 0) && _TMRobot.IsAvailable) { WaferInfo currentwafer = WaferManager.Instance.GetWafer(ModuleName.VPA, 0); switch (movingStatus[currentwafer.OriginSlot]) { case MovingStatus.StartAlign: if(_TMRobot.IsAvailable) { float angle = float.Parse(currentwafer.ProcessJob.Sequence.Steps[0].StepParameter["AlignAngle"].ToString()); _TMRobot.Align(angle); movingStatus[currentwafer.OriginSlot] = MovingStatus.Aligning; SERunTMRobotTask(); return; } break; case MovingStatus.Aligning: movingStatus[currentwafer.OriginSlot] = MovingStatus.WaitProcess; if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) && _TMRobot.IsAvailable) { _moveQueue.Add(new MoveItem(ModuleName.VPA, 0, ModuleName.TMRobot, 0, 0)); SERunTMRobotTask(); return; } if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1) && _TMRobot.IsAvailable) { _moveQueue.Add(new MoveItem(ModuleName.VPA, 0, ModuleName.TMRobot, 1, (Hand)1)); SERunTMRobotTask(); return; } break; } } } private void PrepareTMMoveitems() { } private void Cycling() { //所有的PM都Idle TM都空闲的情况 if (IsAllNeedPMsAvailabe() && IsModuleAvailable(_destinationModule) && IsModuleAvailable(_sourceModule) && _TMRobot.IsAvailable) { //如果PM没有wafer tm也闲置 但Cycle数量未达到目标 if (!PMsHasWafers() && _TMRobot.IsAvailable && _CycleWafers.Count == 0) { ++CycleIndex; if (CycleIndex < CycleNum) { for (int i = 0; i < _sourceSlotNumber; i++) { if (WaferManager.Instance.CheckHasWafer(_sourceModule, i)) _CycleWafers.Enqueue(new MoveItem(_sourceModule, i, _destinationModule, i, Hand.None)); } } else { _cycleState = RState.End; return; } } //wafer返回 if (PMsHasWafers()) { var pmSlots = GetReadyOutPMs(); //var inSlots = GetReadyInSlot(_destinationModule, _destinationSlotNumber); for (int i = 0; i < pmSlots.Count ; i++) { WaferInfo _endwafer = WaferManager.Instance.GetWafer(pmSlots[i], 0); int _endslot = _endwafer.OriginSlot; _runningItems.Enqueue(new MoveItem(pmSlots[i], 0, _destinationModule, _endslot, Hand.Both)); } } else { //进腔环节 可在此处根据PM的角度 增加进入角度 //三个腔体的进入方式 var InPMs = GetReadyInPMs(); if (_CycleWafers.Count > 0 && InPMs.Count >= 1) { var item = _CycleWafers.Dequeue(); _runningItems.Enqueue(new MoveItem(item.SourceModule, item.SourceSlot, InPMs[0], 0, Hand.Both)); } if (_CycleWafers.Count > 0 && InPMs.Count >= 2) { var item = _CycleWafers.Dequeue(); _runningItems.Enqueue(new MoveItem(item.SourceModule, item.SourceSlot, InPMs[1], 0, Hand.Both)); } if (_CycleWafers.Count > 0 && InPMs.Count >= 3) { var item = _CycleWafers.Dequeue(); _runningItems.Enqueue(new MoveItem(item.SourceModule, item.SourceSlot, InPMs[2], 0, Hand.Both)); } } if (_runningItems.Count > 0) { if (_TMRobot.PostMoveItems(_runningItems.ToArray())) _runningItems.Clear(); } } //存在PM可用 //else if (IsExistPMsAvailabe() && IsModuleAvailable(_destinationModule) && IsModuleAvailable(_sourceModule) && _TMRobot.IsAvailable) //{ // //获得可以用的chamber 向里面传片 //} } private void PMProcess() { //foreach (ModuleName chamber in tmCycleRoutine) //{ // if (ModuleHelper.IsPm(chamber) && WaferManager.Instance.CheckHasWafer(chamber, 0) && ModuleHelper.IsInstalled(chamber)) // { // WaferInfo info = WaferManager.Instance.GetWafer(chamber, 0); // // dictSchedulers[chamber].EventWaferArrived?.Invoke(this, new WaferMoveArgs(ModuleName.TMRobot, 0, chamber, 0)); // } //} } public void Abort() { CycleIndex = null; _runningItems.Clear(); _CycleWafers.Clear(); _moveQueue.Clear(); movingStatus.Clear(); foreach(var cj in _lstControlJobs.FindAll(x => x.State == EnumControlJobState.Executing)) cj.SetState(EnumControlJobState.WaitingForStart); _TMRobot._entityTaskToken = (int)FSM_MSG.NONE; _cycleState = RState.End; } #endregion #region 搜索函数 //回到的槽位确认 采用补进方式即自底向上填 private List GetReadyInSlot(ModuleName module, int slotCount) { List slots = new List(); for (int i = 0; i < slotCount; i++) { if (WaferManager.Instance.CheckNoWafer(module, i)) slots.Add(i); if (slots.Count >= 2) return slots; } return slots; } //确认 private List GetReadyOutSlot(ModuleName module, int slotCount) { List slots = new List(); for (int i = 0; i < slotCount; i++) { if (WaferManager.Instance.CheckHasWafer(module, i)) slots.Add(i); if (slots.Count >= 2) return slots; } return slots; } //确认PM是否可用 且没有wafer private List GetReadyInPMs() { List inpm = new List(); foreach (var module in tmCycleRoutine) { if (ModuleHelper.IsPm(module)) { if (IsModuleAvailable(module) && WaferManager.Instance.CheckNoWafer(module, 0)) { inpm.Add(module); if (inpm.Count >= 3) break; } } } return inpm; } private List GetReadyOutPMs() { List outpm = new List(); foreach (var module in tmCycleRoutine) { if (ModuleHelper.IsPm(module)) { if (IsModuleAvailable(module) && WaferManager.Instance.CheckHasWafer(module, 0)) { outpm.Add(module); if (outpm.Count >= 3) break; } } } return outpm; } //PM有wafer private bool PMsHasWafers() { foreach (var module in tmCycleRoutine) { if (ModuleHelper.IsPm(module) && ModuleHelper.IsInstalled(module)) { if (WaferManager.Instance.CheckHasWafer(module, 0)) return true; } } return false; } //PM全可用 private bool IsAllNeedPMsAvailabe() { foreach (var module in tmCycleRoutine) { if (ModuleHelper.IsPm(module) && ModuleHelper.IsInstalled(module)) { if (!IsModuleAvailable(module)) return false; } } return true; } //PM存在可用 private bool SequenceNeedPMsAvailable(List needPMs) { foreach (var module in needPMs) { if (ModuleHelper.IsPm(module) && ModuleHelper.IsInstalled(module)) { if (IsModuleAvailable(module) && WaferManager.Instance.CheckNoWafer(module,0)) return true; } } return false; } private ModuleName SearchPM(List needPMs,Dictionary PMsChoice) { foreach (var module in needPMs) { if (ModuleHelper.IsPm(module) && ModuleHelper.IsInstalled(module)) { if (IsModuleAvailable(module) && !PMsChoice.ContainsValue(module) && WaferManager.Instance.CheckNoWafer(module,0)) return module; } } return ModuleName.TMRobot; } private bool IsModuleAvailable(ModuleName module) { return dictSchedulers.Keys.Contains(module) && dictSchedulers[module].IsAvailable; } //检查需要使用的pm private bool CheckSequencePmReady(SequenceInfo seq, List pmOccupied, out List pmUsed, out string reason) { pmUsed = new List(); reason = ""; bool pmIsReady = true; for (int i = 0; i < seq.Steps.Count; i++) { SequenceStepInfo stepInfo = seq.Steps[i]; bool hasPm = false; foreach (var module in stepInfo.StepModules) { if (!ModuleHelper.IsPm(module)) { hasPm = true; break; } if (ModuleHelper.IsInstalled(module) && (pmOccupied == null || !pmOccupied.Contains(module))) { hasPm = true; } if (!pmUsed.Contains(module)) pmUsed.Add(module); } if (pmIsReady && !hasPm) { reason = $"Step {i + 1} no valid PM, " + string.Join("|", stepInfo.StepModules); pmIsReady = false; } } return pmIsReady; } //检查是否又对应PM的recipe private bool CheckSequenceRecipeFileValid(SequenceInfo seq, out string reason) { for (int i = 0; i < seq.Steps.Count; i++) { SequenceStepInfo stepInfo = seq.Steps[i]; foreach (var module in stepInfo.StepModules) { if (ModuleHelper.IsPm(module)) { string attr = $"{module}Recipe"; if (stepInfo.StepParameter.ContainsKey(attr) && !string.IsNullOrEmpty((string)stepInfo.StepParameter[attr])) { var recipeName = (string)stepInfo.StepParameter[attr]; if (!string.IsNullOrEmpty(recipeName)) { var recipeContent = RecipeFileManager.Instance.LoadRecipe($"{module}", recipeName, false, "Process"); if (string.IsNullOrEmpty(recipeContent)) { reason = $"Can not find recipe file{recipeName}"; return false; } } } } } } reason = ""; return true; } //找到对应sequence public ProcessJobInfo GetFirstProcessJob(ControlJobInfo cj) { foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) return pj; } return null; } #endregion } }