using System; using System.Collections.Generic; using System.Linq; using System.Xml; using System.Diagnostics; using System.Text; using System.Threading.Tasks; using VirgoRT.Modules.Schedulers; using VirgoRT.Scheduler; using MECF.Framework.Common.DBCore; 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.PMs; using Aitex.Core.RT.SCCore; using Aitex.Sorter.Common; using Aitex.Core.Common; using Aitex.Core.RT.Routine; using Aitex.Core.RT.Event; using Aitex.Core.RT.Device; using Aitex.Core.RT.Log; using Aitex.Core.RT.RecipeCenter; using Aitex.Core.RT.DataCenter; namespace VirgoRT.Modules { enum RobotFreeHand { Blade1 = 0, //Lower arm Blade2 = 1, //Upper arm Both = 2, None = 3, } enum ActionState { Init = 0, Prepare, Run, Post, Done, } struct MoveAction { public EnumTransferType eType; public ModuleName targetModule; public int targetSlot; public int robotSlot1; public int robotSlot2; public ActionState state; public MoveAction(EnumTransferType type, ModuleName module, int tSlot = 0, int Slot1 = 0, int Slot2 = 0) { eType = type; targetModule = module; targetSlot = tSlot; robotSlot1 = Slot1; robotSlot2 = Slot2; state = ActionState.Init; } } enum ModulePriority { High, Middle, Low, Stop, } class ModuleFlag { public ModulePriority priority; public ModuleFlag(ModulePriority _priority) { priority = _priority; } } enum RtTraceState { Init, Pumping, WaitEfemRobotReady, WaitPenddingActionsFinish, WaitModuleReady, WaitPMReadyForPlace, WaitPMReadyForPick, WaitModuleReadyForPlace, WaitModuleReadyForPick, WaitPMReadyForTemperature, VerifyTransferAction, } public class AutoTransfer_T : TransferModule, IAutoTransfer { private List _lstControlJobs = new List(); private List _lstProcessJobs = new List(); Dictionary dictSchedulers = new Dictionary(); Dictionary dictModules = new Dictionary(); Dictionary dictWaferProcessPM = new Dictionary(); Dictionary dictLPAssociatedPM = new Dictionary(); Queue queMoveAction = new Queue(); private List _mapTarget = new List(); private const string LogSource = "Scheduler"; private bool _isCycleMode; private int _cycleSetPoint = 0; private int _cycledCount = 0; private int _cycledWafer = 0; private bool _isInited; private Stopwatch _blockingWatcher = new Stopwatch(); private RtTraceState _lastTraceState; private RtTraceState _currentTraceState; public bool HasJobRunning => _lstControlJobs.Count > 0; public bool IsEfemRobotAvavilable => _efemRobot.IsAvailable; public static int SystemInternalWaferCount => SC.GetValue($"System.MaxInternalWaferCount"); private static int _maxBufferWaferCount = 1; private int _bufferSlotNumber = 0; private bool IsModuleAvailable(ModuleName module) => dictSchedulers.Keys.Contains(module) && dictSchedulers[module].IsAvailable; private SchedulerModule GetScheduler(ModuleName module) => dictSchedulers.Keys.Contains(module) ? dictSchedulers[module] : null; private SchedulerFACallback _faCallback; private SchedulerDBCallback _dbCallback; MoveAction _curAction = new MoveAction(); public AutoTransfer_T() { _faCallback = new SchedulerFACallback(); _dbCallback = new SchedulerDBCallback(); InitModules(); _curAction.state = ActionState.Done; DATA.Subscribe("Scheduler.CycledCount", () => _cycledCount); DATA.Subscribe("Scheduler.CycledWafer", () => _cycledWafer); DATA.Subscribe("Scheduler.CycleSetPoint", () => _cycleSetPoint); DATA.Subscribe("Scheduler.PjIdList", () => Array.ConvertAll(_lstProcessJobs.ToArray(), x => x.InnerId.ToString()).ToList()); foreach (var lp in dictSchedulers) { if (ModuleHelper.IsLoadPort(lp.Key)) { DATA.Subscribe($"{lp.Key}.LocalJobName", () => { var cj = _lstControlJobs.FirstOrDefault(x => x.Module == lp.Key.ToString()); if (cj != null) return cj.Name; return ""; }); DATA.Subscribe($"{lp.Key}.JobLotName", () => { var cj = _lstControlJobs.FirstOrDefault(x => x.Module == lp.Key.ToString()); if (cj != null) return cj.LotName; return ""; }); DATA.Subscribe($"{lp.Key}.LocalJobStatus", () => { var cj = _lstControlJobs.FirstOrDefault(x => x.Module == lp.Key.ToString()); if (cj != null) return cj.State.ToString(); return ""; }); DATA.Subscribe($"{lp.Key}.CurrentRecipeList", () => { var local = lp; List result = Enumerable.Repeat("", SC.GetValue("EFEM.LoadPort.SlotNumber")).ToList(); for (int i = 0; i < _lstProcessJobs.Count; i++) { foreach (var wafers in _lstProcessJobs[i].SlotWafers) { if (wafers.Item1 == local.Key) { result[wafers.Item2] = _lstProcessJobs[i].Sequence.Name; } } } return result; }); } } _maxBufferWaferCount = SC.GetValue("EFEM.Buffer.SlotNumber") / 2; } private void InitModules() { void _initMoudle(ModuleName name, SchedulerModule sche, ModulePriority prio) { if (ModuleHelper.IsInstalled(name)) { dictSchedulers[name] = sche; dictModules[name] = new ModuleFlag(prio); } } _initMoudle(ModuleName.PMA, _pma, ModulePriority.Middle); _initMoudle(ModuleName.PMB, _pmb, ModulePriority.Middle); _initMoudle(ModuleName.LP1, _lp1, ModulePriority.Middle); _initMoudle(ModuleName.LP2, _lp2, ModulePriority.Middle); _initMoudle(ModuleName.Aligner1, _aligner1, ModulePriority.Middle); _initMoudle(ModuleName.Aligner2, _aligner2, ModulePriority.Middle); _initMoudle(ModuleName.Cooling1, _cooling1, ModulePriority.Middle); _initMoudle(ModuleName.Cooling2, _cooling2, ModulePriority.Middle); _initMoudle(ModuleName.Flipper, _flipper, ModulePriority.Middle); _initMoudle(ModuleName.Buffer, _buffer, ModulePriority.Middle); } #region transfer routine private bool HasCrossSequence() { List lstStepModules = new List(); foreach (var pj in _lstProcessJobs) { lstStepModules.Clear(); foreach (var step in pj.Sequence.Steps) { foreach (var module in step.StepModules) { if (lstStepModules.Contains(module)) return true; else lstStepModules.Add(module); } } } return false; } private void dumpMoveAction(string prefix, MoveAction action) { EV.PostInfoLog("System", $"{prefix} : {action.state}, {action.eType}, {action.targetModule}, {action.targetSlot}"); } private void blockingChecking() { if (CheckAllJobDone()) return; if (_currentTraceState != _lastTraceState) { _lastTraceState = _currentTraceState; _blockingWatcher.Restart(); } if (_blockingWatcher.ElapsedMilliseconds >= 120 * 1000) { EV.PostInfoLog("", $"Router Blocking State: {_currentTraceState}"); dumpMoveAction("Current Action", _curAction); for (int i = 0; i < queMoveAction.Count; i++) { dumpMoveAction(i.ToString(), queMoveAction.ElementAt(i)); } foreach (var module in dictSchedulers) { string moduleStatus = module.Value.IsAvailable ? "Idle" : (module.Value.IsError ? "Error" : "Busy"); EV.PostInfoLog("", $"Module State: {module.Key} is {moduleStatus}"); } if (_curAction.state != ActionState.Done && dictSchedulers[_curAction.targetModule].IsError) { _curAction.state = ActionState.Done; queMoveAction.Clear(); } _blockingWatcher.Restart(); ResetTraceFlag(); } } private bool prelude() { MonitorEfemRobotMapTask(); return true; } private bool epilogue() { UpdateProcessJobStatus(); UpdateControlJobStatus(); StartNewJob(); MonitorCleanTasks(); blockingChecking(); return true; } private bool Pump() { if (queMoveAction.Count > 0 || _curAction.state != ActionState.Done) return false; // move remaining wafers in robot arms for(int slot = 0; slot < 2; slot++) { if (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, slot)) { var nextSlot = GetWaferNextReadySlot(new SlotItem(ModuleName.EfemRobot, slot)); if (nextSlot.Module != ModuleName.System) { queMoveAction.Enqueue(new MoveAction(EnumTransferType.Place, nextSlot.Module, nextSlot.Slot, slot)); PreparePMTemp(ModuleName.EfemRobot, slot, nextSlot.Module, EnumTransferType.Place); return true; } var swapSlot = GetWaferNextReadySlot(new SlotItem(ModuleName.EfemRobot, slot), true); if (swapSlot.Module != ModuleName.System) { int peerSlot = slot == 0 ? 1 : 0; if (WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, peerSlot)) { queMoveAction.Enqueue(new MoveAction(EnumTransferType.Swap, swapSlot.Module, swapSlot.Slot, peerSlot /* Pick Blade */, slot /* Place Blade */)); PreparePMTemp(ModuleName.EfemRobot, slot, swapSlot.Module, EnumTransferType.Pick); return true; } } } } ModulePriority[] prioritys = new ModulePriority[] { ModulePriority.High, ModulePriority.Middle, ModulePriority.Low }; foreach (var prio in prioritys) { foreach (var module in dictModules) { if (module.Value.priority != prio) continue; var outSlot = GetReadyOutSlot(module.Key); if (outSlot.Module != ModuleName.System && IsWaferCtrlJobRuning(outSlot)) { var nextSlot = GetWaferNextReadySlot(outSlot); if (nextSlot.Module != ModuleName.System) { var waitInSlot = GetWaitInWaferSlot(outSlot); if (waitInSlot.Module != ModuleName.System && GetRobotFreeHand() == RobotFreeHand.Both) { queMoveAction.Enqueue(new MoveAction(EnumTransferType.Pick, waitInSlot.Module, waitInSlot.Slot, (int)Hand.Blade1)); queMoveAction.Enqueue(new MoveAction(EnumTransferType.Swap, module.Key, outSlot.Slot, (int)Hand.Blade2 /* Pick Blade */, (int)Hand.Blade1 /* Place Blade */)); queMoveAction.Enqueue(new MoveAction(EnumTransferType.Place, nextSlot.Module, nextSlot.Slot, (int)Hand.Blade2)); PreparePMTemp(waitInSlot.Module, waitInSlot.Slot, module.Key, EnumTransferType.Pick); PreparePMTemp(module.Key, outSlot.Slot, nextSlot.Module, EnumTransferType.Place); return true; } else { int nFreeHand = (int)SelectRobotFreeHand(); if (nFreeHand != (int)RobotFreeHand.None) { queMoveAction.Enqueue(new MoveAction(EnumTransferType.Pick, module.Key, outSlot.Slot, nFreeHand)); queMoveAction.Enqueue(new MoveAction(EnumTransferType.Place, nextSlot.Module, nextSlot.Slot, nFreeHand)); PreparePMTemp(module.Key, outSlot.Slot, nextSlot.Module, EnumTransferType.Place); return true; } } } else { var swapSlot = GetCanSwapSlot(outSlot); if (swapSlot.Module != ModuleName.System && GetRobotFreeHand() == RobotFreeHand.Both) { queMoveAction.Enqueue(new MoveAction(EnumTransferType.Pick, swapSlot.Module, swapSlot.Slot, (int)Hand.Blade1)); queMoveAction.Enqueue(new MoveAction(EnumTransferType.Swap, module.Key, outSlot.Slot, (int)Hand.Blade2 /* Pick Blade */, (int)Hand.Blade1 /* Place Blade */)); queMoveAction.Enqueue(new MoveAction(EnumTransferType.Place, swapSlot.Module, swapSlot.Slot, (int)Hand.Blade2)); PreparePMTemp(swapSlot.Module, swapSlot.Slot, module.Key, EnumTransferType.Pick); PreparePMTemp(module.Key, outSlot.Slot, swapSlot.Module, EnumTransferType.Pick); return true; } } } } } // pick the wafer to robot arm while the next module is not ready if (HasCrossSequence() == false && GetRobotFreeHand() == RobotFreeHand.Both) { foreach (var module in dictModules) { var outSlot = GetReadyOutSlot(module.Key); if (outSlot.Module != ModuleName.System && IsWaferCtrlJobRuning(outSlot)) { var swapSlot = GetWaferNextReadySlot(outSlot, true); if (swapSlot.Module != ModuleName.System) { queMoveAction.Enqueue(new MoveAction(EnumTransferType.Pick, outSlot.Module, outSlot.Slot, (int)Hand.Blade1)); queMoveAction.Enqueue(new MoveAction(EnumTransferType.Swap, swapSlot.Module, swapSlot.Slot, (int)Hand.Blade2 /* Pick Blade */, (int)Hand.Blade1 /* Place Blade */)); PreparePMTemp(outSlot.Module, outSlot.Slot, swapSlot.Module, EnumTransferType.Pick); return true; } } } } _currentTraceState = RtTraceState.Pumping; return false; } private void Pop() { if (!_efemRobot.IsAvailable) { _currentTraceState = RtTraceState.WaitEfemRobotReady; return; } if (queMoveAction.Count == 0 && _curAction.state == ActionState.Done) return; switch (_curAction.state) { case ActionState.Init: { if (IsModuleAvailable(_curAction.targetModule)) { if (PrepareCurrentAction()) { _curAction.state = ActionState.Prepare; if (RunCurrentAction()) { _curAction.state = ActionState.Run; } } } else { _currentTraceState = RtTraceState.WaitModuleReady; } } break; case ActionState.Prepare: { if (RunCurrentAction()) { _curAction.state = ActionState.Run; } } break; case ActionState.Run: { if (dictSchedulers[_curAction.targetModule].IsWaitTransfer(ModuleName.EfemRobot)) { NotifyFaWaferEnd(); dictSchedulers[_curAction.targetModule].PostTransfer(ModuleName.EfemRobot); _curAction.state = ActionState.Post; } else { _currentTraceState = RtTraceState.VerifyTransferAction; } } break; case ActionState.Post: { if (IsModuleAvailable(_curAction.targetModule)) { PostProcessCurrentAction(); _curAction.state = ActionState.Done; } else { _currentTraceState = RtTraceState.WaitModuleReady; } } break; case ActionState.Done: { _curAction = queMoveAction.Dequeue(); } break; default: break; } } private bool PrepareCurrentAction() { if (_curAction.targetModule == ModuleName.PMA || _curAction.targetModule == ModuleName.PMB) { if ((_curAction.eType == EnumTransferType.Place && !dictSchedulers[_curAction.targetModule].IsReadyForPlace(ModuleName.EfemRobot, _curAction.targetSlot, (Hand)_curAction.robotSlot1)) || (_curAction.eType != EnumTransferType.Place && !dictSchedulers[_curAction.targetModule].IsReadyForPick(ModuleName.EfemRobot, _curAction.targetSlot, (Hand)_curAction.robotSlot1))) { dictSchedulers[_curAction.targetModule].PrepareTransfer(ModuleName.EfemRobot, _curAction.eType == EnumTransferType.Swap ? EnumTransferType.Pick : _curAction.eType, _curAction.targetSlot); _currentTraceState = _curAction.eType == EnumTransferType.Swap ? RtTraceState.WaitPMReadyForPick : RtTraceState.WaitPMReadyForPlace; return false; } else { if (_curAction.eType == EnumTransferType.Pick) return true; GetWaferTemperatureSetInRecipe(ModuleName.EfemRobot, _curAction.eType == EnumTransferType.Place ? _curAction.robotSlot1 : _curAction.robotSlot2, _curAction.targetModule, out float temp); if (temp < 0.1) return true; if (!((SchedulerPM)dictSchedulers[_curAction.targetModule]).IsTemperatureReady(temp)) { _currentTraceState = RtTraceState.WaitPMReadyForTemperature; return false; } } } return true; } private void PreparePMTemp(ModuleName waferModule, int waferSlot, ModuleName pmModule, EnumTransferType type) { if (pmModule == ModuleName.PMA || pmModule == ModuleName.PMB) { GetWaferTemperatureSetInRecipe(waferModule, waferSlot, pmModule, out float temp); if (Math.Abs(temp) > 0.1 && !((SchedulerPM)dictSchedulers[pmModule]).IsTemperatureReady(temp)) { ((SchedulerPM)dictSchedulers[pmModule]).PrepareTransfer(ModuleName.EfemRobot, type, 0, temp, Hand.Blade1, WaferSize.WS12); } } } private bool RunCurrentAction() { if (!IsModuleAvailable(_curAction.targetModule)) { return false; } switch (_curAction.eType) { case EnumTransferType.Pick: { if (dictSchedulers[_curAction.targetModule].IsReadyForPick(ModuleName.EfemRobot, _curAction.targetSlot, (Hand)_curAction.robotSlot1)) { _efemRobot.Pick(_curAction.targetModule, _curAction.targetSlot, (Hand)_curAction.robotSlot1); dictSchedulers[_curAction.targetModule].WaitTransfer(ModuleName.EfemRobot, EnumTransferType.Pick, _curAction.targetSlot); // Notify the FA for Port1JobWaferStart if (ModuleHelper.IsLoadPort(_curAction.targetModule)) { ProcessJobInfo pj = WaferManager.Instance.GetWafer(_curAction.targetModule, _curAction.targetSlot).ProcessJob; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == pj.ControlJobName); _faCallback.JobWaferStart(cj, pj, _curAction.targetModule.ToString(), _curAction.targetSlot); } return true; } else { _currentTraceState = RtTraceState.WaitModuleReadyForPick; } } break; case EnumTransferType.Place: { if (dictSchedulers[_curAction.targetModule].IsReadyForPlace(ModuleName.EfemRobot, _curAction.targetSlot, (Hand)_curAction.robotSlot1)) { _efemRobot.Place(_curAction.targetModule, _curAction.targetSlot, (Hand)_curAction.robotSlot1); dictSchedulers[_curAction.targetModule].WaitTransfer(ModuleName.EfemRobot, EnumTransferType.Place, _curAction.targetSlot); return true; } else { _currentTraceState = RtTraceState.WaitModuleReadyForPlace; } } break; case EnumTransferType.Swap: { if (dictSchedulers[_curAction.targetModule].IsReadyForPick(ModuleName.EfemRobot, _curAction.targetSlot, (Hand)_curAction.robotSlot1)) { _efemRobot.PickAndPlace(_curAction.targetModule, _curAction.targetSlot, _curAction.targetSlot, (Hand)_curAction.robotSlot1, (Hand)_curAction.robotSlot2); dictSchedulers[_curAction.targetModule].WaitTransfer(ModuleName.EfemRobot, EnumTransferType.Swap, _curAction.targetSlot); return true; } else { _currentTraceState = RtTraceState.WaitModuleReadyForPick; } } break; } return false; } private void PostProcessCurrentAction() { if (_curAction.eType != EnumTransferType.Pick) { var wafer = WaferManager.Instance.GetWafer(_curAction.targetModule, _curAction.targetSlot); if (wafer.IsEmpty) return; if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) return; if (_curAction.eType == EnumTransferType.Swap) { dictSchedulers[_curAction.targetModule].EventWaferLeaved?.Invoke(this, new WaferMoveArgs(_curAction.targetModule, _curAction.robotSlot2, ModuleName.EfemRobot, _curAction.targetSlot)); if (ModuleHelper.IsLoadPort(_curAction.targetModule) && IsFixedPMMode()) { AdjustLPPriority(_curAction.targetModule); } } dictSchedulers[_curAction.targetModule].EventWaferArrived?.Invoke(this, new WaferMoveArgs(ModuleName.EfemRobot, _curAction.robotSlot1, _curAction.targetModule, _curAction.targetSlot)); if(ModuleHelper.IsLoadPort(_curAction.targetModule)) RemoveWaferPMMark(new SlotItem(_curAction.targetModule, _curAction.targetSlot)); else if (ModuleHelper.IsPm(_curAction.targetModule)) AddWaferPMMark(_curAction.targetModule); else if (ModuleHelper.IsAligner(_curAction.targetModule)) { GetWaferSequenceAlignTime(_curAction.targetModule, 0, out float time); _efemRobot.Align(_curAction.targetModule, time); } else if (ModuleHelper.IsCooling(_curAction.targetModule)) { GetWaferSequenceAlignTime(_curAction.targetModule, 0, out float time); dictSchedulers[_curAction.targetModule].Cooling((int)time); } if (!ModuleHelper.IsLoadPort(_curAction.targetModule)) wafer.NextSequenceStep++; } else { dictSchedulers[_curAction.targetModule].EventWaferLeaved?.Invoke(this, new WaferMoveArgs(_curAction.targetModule, _curAction.targetSlot, ModuleName.EfemRobot, _curAction.robotSlot1)); if (IsFixedPMMode()) { if (ModuleHelper.IsLoadPort(_curAction.targetModule)) { AdjustLPPriority(_curAction.targetModule); } else if (ModuleHelper.IsAligner(_curAction.targetModule)) { AdjustAlignerPriority(_curAction.targetModule); } } } } private bool GetWaferSequenceAlignTime(ModuleName module, int slot, out float time) { time = 0; if (!WaferManager.Instance.CheckHasWafer(module, slot)) return false; WaferInfo wafer = WaferManager.Instance.GetWafer(module, slot); if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) return false; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) return false; if (!wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(ModuleName.Aligner1) && !wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(ModuleName.Aligner2) && !wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(ModuleName.Cooling1) && !wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(ModuleName.Cooling2)) return false; if (!wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepParameter.ContainsKey("CoolingTime")) return false; if (!float.TryParse((string)wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepParameter["CoolingTime"], out time)) return false; return true; } private bool GetWaferTemperatureSetInRecipe(ModuleName waferModule, int waferSlot, ModuleName pmModule, out float temp) { WaferInfo wafer = WaferManager.Instance.GetWafer(waferModule, waferSlot); var seq = wafer.ProcessJob.Sequence; for (int i = wafer.NextSequenceStep; i < seq.Steps.Count; i++) { SequenceStepInfo stepInfo = seq.Steps[i]; foreach (var module in stepInfo.StepModules) { if (module == pmModule) { temp = (float)seq.Steps[i].StepParameter[module == ModuleName.PMA ? "PMATemp" : "PMBTemp"]; return true; } } } temp = 0; LOG.Error($"Can not find wafer {waferModule} {waferSlot + 1} temperature set in recipe"); return false; } private SlotItem GetWaferNextReadySlot(SlotItem slot, bool bSwap = false) { var nextSlot = new SlotItem(ModuleName.System, -1); var inWafer = WaferManager.Instance.GetWafer(slot.Module, slot.Slot); if (inWafer.IsEmpty) return nextSlot; if (inWafer.ProcessJob == null || inWafer.ProcessJob.Sequence == null) return nextSlot; if (inWafer.NextSequenceStep >= inWafer.ProcessJob.Sequence.Steps.Count) // need return { return new SlotItem((ModuleName)inWafer.OriginStation, inWafer.OriginSlot); } foreach (var module in inWafer.ProcessJob.Sequence.Steps[inWafer.NextSequenceStep].StepModules) { if (IsModuleAvailable(module)) { // need enhancement int nMaxSlot = (module == ModuleName.LP1 || module == ModuleName.LP2 || module == ModuleName.LP3) ? SC.GetValue("EFEM.LoadPort.SlotNumber") : (module == ModuleName.Buffer ? SC.GetValue("EFEM.Buffer.SlotNumber") : 1); for (int i = 0; i < nMaxSlot; i++) { if (WaferManager.Instance.IsWaferSlotLocationValid(module, i)) { // the processed wafer must enter same pm if (ModuleHelper.IsPm(module) && ModuleHelper.IsInstalled(ModuleName.Flipper) && dictWaferProcessPM.ContainsKey(inWafer.InnerId) && dictWaferProcessPM[inWafer.InnerId] != module) continue; if (bSwap == false && WaferManager.Instance.CheckNoWafer(module, i)) { return new SlotItem(module, i); } if (bSwap && WaferManager.Instance.CheckHasWafer(module, i)) { return new SlotItem(module, i); } } } } } return nextSlot; } private SlotItem GetCanSwapSlot(SlotItem slot) { var swapSlot = new SlotItem(ModuleName.System, -1); var inWafer = WaferManager.Instance.GetWafer(slot.Module, slot.Slot); if (inWafer.IsEmpty) return swapSlot; if (inWafer.ProcessJob == null || inWafer.ProcessJob.Sequence == null) return swapSlot; if (inWafer.NextSequenceStep >= inWafer.ProcessJob.Sequence.Steps.Count) // need return return swapSlot; foreach (var module in inWafer.ProcessJob.Sequence.Steps[inWafer.NextSequenceStep].StepModules) { if (IsModuleAvailable(module)) { // need enhancement int nMaxSlot = (module == ModuleName.LP1 || module == ModuleName.LP2 || module == ModuleName.LP3) ? SC.GetValue("EFEM.LoadPort.SlotNumber") : (module == ModuleName.Buffer ? SC.GetValue("EFEM.Buffer.SlotNumber") : 1); for (int i = 0; i < nMaxSlot; i++) { if (WaferManager.Instance.IsWaferSlotLocationValid(module, i)) { if (WaferManager.Instance.CheckHasWafer(module, i)) { var swapWafer = WaferManager.Instance.GetWafer(module, i); if (!swapWafer.IsEmpty && swapWafer.ProcessJob != null && swapWafer.ProcessJob.Sequence != null && swapWafer.NextSequenceStep < swapWafer.ProcessJob.Sequence.Steps.Count && swapWafer.ProcessJob.Sequence.Steps[swapWafer.NextSequenceStep].StepModules.Contains(slot.Module)) { return new SlotItem(module, i); } } } } } } return swapSlot; } private SlotItem GetWaitInWaferSlot(SlotItem slot) { ModulePriority[] prioritys = new ModulePriority[] { ModulePriority.High, ModulePriority.Middle, ModulePriority.Low }; foreach (var prio in prioritys) { foreach (var module in dictModules) { if (module.Value.priority != prio || module.Key == slot.Module) continue; var inSlot = GetReadyOutSlot(module.Key); if (inSlot.Module != ModuleName.System) { var inWafer = WaferManager.Instance.GetWafer(inSlot.Module, inSlot.Slot); if (inWafer.IsEmpty) continue; if (inWafer.ProcessJob == null || inWafer.ProcessJob.Sequence == null) continue; if (inWafer.NextSequenceStep >= inWafer.ProcessJob.Sequence.Steps.Count) continue; if (inWafer.ProcessJob.Sequence.Steps[inWafer.NextSequenceStep].StepModules .Contains(slot.Module)) return inSlot; } } } return new SlotItem(ModuleName.System, -1); } private SlotItem GetReadyOutSlot(ModuleName module) { if (module == ModuleName.LP1 || module == ModuleName.LP2 || module == ModuleName.LP3) return GetLPReadyOutSlot(module); else if (module == ModuleName.Buffer) return GetBufferReadyOutSlot(module); else return GetModuleReadyOutSlot(module); } private bool IsWaferCtrlJobRuning(SlotItem slotItem) { return IsWaferCtrlJobRuning(slotItem.Module, slotItem.Slot); } private bool IsWaferCtrlJobRuning(ModuleName mod, int slot) { var wafer = WaferManager.Instance.GetWafer(mod, slot); if (wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) return false; var waferCtrlJob = _lstControlJobs.Find(item => (int)ModuleHelper.Converter(item.Module) == wafer.OriginStation); if (waferCtrlJob != null) { return waferCtrlJob.State == EnumControlJobState.Executing; } return false; } private SlotItem GetLPReadyOutSlot(ModuleName lp) { var slot = GetNextWaferInJobQueue(lp); if (slot == null) return new SlotItem(ModuleName.System, -1); return slot; } private SlotItem GetModuleReadyOutSlot(ModuleName module) { var slot = new SlotItem(ModuleName.System, -1); if (!IsModuleAvailable(module)) return slot; WaferInfo wafer = WaferManager.Instance.GetWafer(module, 0); if (wafer.IsEmpty) return slot; if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) return slot; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) // return to loadport return new SlotItem(module, 0); if (wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules .Contains(module)) return slot; return new SlotItem(module, 0); } private SlotItem GetBufferReadyOutSlot(ModuleName buffer) { return dictSchedulers[buffer].GetReadyOutSlot(); } private RobotFreeHand GetRobotFreeHand() { var blade1Enable = SC.GetValue("EFEM.EfemRobot.LowerBladeEnable"); var blade2Enable = SC.GetValue("EFEM.EfemRobot.UpperBladeEnable"); var blade1HasWafer = WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 0); var blade2HasWafer = WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 1); if (blade1HasWafer && blade2HasWafer) return RobotFreeHand.None; else if (!blade1HasWafer && !blade2HasWafer && blade1Enable && blade2Enable) return RobotFreeHand.Both; else if (!blade2HasWafer && blade2Enable) return RobotFreeHand.Blade2; else if (!blade1HasWafer && blade1Enable) return RobotFreeHand.Blade1; else return RobotFreeHand.None; } private RobotFreeHand SelectRobotFreeHand() { var freeHand = GetRobotFreeHand(); return freeHand == RobotFreeHand.Both ? RobotFreeHand.Blade1 : freeHand; } private void AddWaferPMMark(ModuleName pm) { var wafer = WaferManager.Instance.GetWafer(pm, 0); if (wafer.IsEmpty) return; dictWaferProcessPM[wafer.InnerId] = pm; } private void RemoveWaferPMMark(SlotItem slot) { var wafer = WaferManager.Instance.GetWafer(slot.Module, slot.Slot); if (wafer.IsEmpty) return; dictWaferProcessPM.Remove(wafer.InnerId); } private void ResetTraceFlag() { _lastTraceState = _currentTraceState = RtTraceState.Init; } public Result Monitor() { prelude(); Pump(); Pop(); epilogue(); return Result.RUN; } private List GetPmUsedInRunningPj() { var pmUsed = new List(); foreach (var cj in _lstControlJobs) { if (cj.State != EnumControlJobState.Executing && cj.State != EnumControlJobState.Paused) continue; foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name && (pj.State == EnumProcessJobState.Processing || pj.State == EnumProcessJobState.Paused)) { for (int i = 0; i < pj.Sequence.Steps.Count; i++) { SequenceStepInfo stepInfo = pj.Sequence.Steps[i]; foreach (var module in stepInfo.StepModules) { if (ModuleHelper.IsPm(module) && !pmUsed.Contains(module)) pmUsed.Add(module); } } } } } return pmUsed; } private List GetPmNeeded(ControlJobInfo cj) { List result = new List(); foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName != cj.Name) continue; var seq = pj.Sequence; for (int i = 0; i < seq.Steps.Count; i++) { SequenceStepInfo stepInfo = seq.Steps[i]; foreach (var module in stepInfo.StepModules) { if (ModuleHelper.IsPm(module)) { var pm = dictSchedulers[module] as SchedulerPM; if (pm != null && !result.Contains(pm)) { result.Add(pm); } } } } } return result; } private void InitClean() { foreach (var pm in dictSchedulers) { if (ModuleHelper.IsPm(pm.Key)) { var scheduler = pm.Value as SchedulerPM; scheduler.InitClean(); } } } public Result MonitorCleanTasks() { if (!_isInited) { _isInited = true; InitClean(); } foreach (var pm in dictSchedulers) { if (ModuleHelper.IsPm(pm.Key)) { var scheduler = pm.Value as SchedulerPM; scheduler.MonitorCleanTasks(); } } return Result.RUN; } private void PreJobClean(ControlJobInfo cj) { if (cj.IsPreJobCleanDone) return; cj.IsPreJobCleanDone = true; List pms = GetPmNeeded(cj); foreach (var pm in pms) { pm.PreJobClean(); } } private bool ActiveProcessJob(ProcessJobInfo pj) { foreach (var pjSlotWafer in pj.SlotWafers) { WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); wafer.ProcessJob = pj; wafer.NextSequenceStep = 0; WaferDataRecorder.SetPjInfo(wafer.InnerId.ToString(), pj.InnerId.ToString()); } ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == pj.ControlJobName); CarrierInfo carrier = CarrierManager.Instance.GetCarrier(cj.Module); JobDataRecorder.StartPJ(pj.InnerId.ToString(), carrier.InnerId.ToString(), cj.InnerId.ToString(), pj.Name, cj.Module, cj.Module, pj.SlotWafers.Count); pj.SetState(EnumProcessJobState.Processing); PreJobClean(cj); return true; } private void StartNewJob() { ControlJobInfo cjActived = null; List pmOccupied = GetPmUsedInRunningPj(); foreach (var cj in _lstControlJobs) { if (cj.State != EnumControlJobState.Executing) continue; cjActived = cj; foreach (var pjName in cjActived.ProcessJobNameList) { var pj = _lstProcessJobs.Find(x => x.Name == pjName); if (pj == null) { LOG.Error($"Not find pj named {pjName} in {cjActived.Name}"); continue; } if (pj.State == EnumProcessJobState.Queued) { if (CheckSequencePmReady(pj.Sequence, pmOccupied, out var pmUsed, out string reason)) { ActiveProcessJob(pj); foreach (var moduleName in pmUsed) { if (!pmOccupied.Contains(moduleName)) pmOccupied.Add(moduleName); } } break; } } } } private bool CheckWaferNeedProcessAndPMAvailable(ModuleName waferModule, int waferSlot, ModuleName processIn = ModuleName.System) { WaferInfo wafer = WaferManager.Instance.GetWafer(waferModule, waferSlot); if (wafer.IsEmpty) return false; if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) return false; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) return false; for (int i = wafer.NextSequenceStep; i < wafer.ProcessJob.Sequence.Steps.Count; i++) { if (wafer.ProcessJob.Sequence.Steps[i].StepModules .Contains(ModuleName.PMA) || wafer.ProcessJob.Sequence.Steps[i].StepModules .Contains(ModuleName.PMB)) { var hasPmAvailable = false; foreach (var stepModule in wafer.ProcessJob.Sequence.Steps[i].StepModules) { var pm = GetScheduler(stepModule) as SchedulerPM; if (pm != null) { if (pm.IsAvailable || (pm.IsOnline && pm.Task != SchedulerModule.TaskType.PreJobProcess)) { hasPmAvailable = true; break; } } } if (!hasPmAvailable) return false; if (processIn == ModuleName.System) return true; if (wafer.ProcessJob.Sequence.Steps[i].StepModules .Contains(processIn)) return true; } } return false; } private bool IsFixedPMMode() { List associatedPMs = new List(); foreach (var module in dictSchedulers.Keys) { if (ModuleHelper.IsLoadPort(module)) { if (dictLPAssociatedPM.ContainsKey(module)) { if (dictLPAssociatedPM[module] == ModuleName.System) return false; if (!associatedPMs.Contains(dictLPAssociatedPM[module])) associatedPMs.Add(dictLPAssociatedPM[module]); } } } return associatedPMs.Count >= 2; } void AdjustLPPriority(ModuleName lp) { foreach (var module in dictModules.Keys) { if (ModuleHelper.IsLoadPort(module)) { dictModules[module].priority = module == lp ? ModulePriority.Low : ModulePriority.Middle; } } } void AdjustAlignerPriority(ModuleName aligner) { foreach (var module in dictModules.Keys) { if (ModuleHelper.IsAligner(module)) { dictModules[module].priority = module == aligner ? ModulePriority.Low : ModulePriority.Middle; } } } private SlotItem GetNextWaferInJobQueue(ModuleName lpModule) { if (GetSystemInnerWaferCount() >= SystemInternalWaferCount || GetBufferWaferCount() > _maxBufferWaferCount) return new SlotItem(ModuleName.System, -1); foreach (var cj in _lstControlJobs) { if (lpModule != ModuleName.System && (cj.Module != lpModule.ToString())) continue; if (cj.State == EnumControlJobState.Executing) { foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name && pj.State == EnumProcessJobState.Processing) { foreach (var pjSlotWafer in pj.SlotWafers) { if (CheckWaferNeedProcessAndPMAvailable(pjSlotWafer.Item1, pjSlotWafer.Item2)) { return new SlotItem(pjSlotWafer.Item1, pjSlotWafer.Item2); } } } } } } return new SlotItem(ModuleName.System, -1); } static public int GetSystemInnerWaferCount() { int count = 0; var lstPosition = new List() { ModuleName.EfemRobot, ModuleName.Aligner1, ModuleName.Aligner2, ModuleName.Cooling1, ModuleName.Cooling2, ModuleName.Flipper, ModuleName.PMA, ModuleName.PMB, }; foreach (var moduleName in lstPosition) { if (ModuleHelper.IsInstalled(moduleName) && WaferManager.Instance.CheckHasWafer(moduleName, 0)) count++; } if (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 1)) count++; return count; } private int GetBufferWaferCount() { int nCount = 0; for (int i = 0; i < SC.GetValue("EFEM.Buffer.SlotNumber"); i++) { if (WaferManager.Instance.CheckHasWafer(ModuleName.Buffer, i)) nCount++; } return nCount; } #endregion transfer routine private void MonitorEfemRobotMapTask() { if (!_efemRobot.IsAvailable) return; if (_mapTarget.Count > 0) { ModuleName first = _mapTarget[0]; //避免重复map,job信息被清除 foreach (var cj in _lstControlJobs) { if (cj.Module != first.ToString()) continue; if (cj.State == EnumControlJobState.Executing || cj.State == EnumControlJobState.Paused) { EV.PostWarningLog("System", $"{first} is running, can not map again"); _mapTarget.Remove(first); } } SchedulerLoadPort lp = GetScheduler(first) as SchedulerLoadPort; if (lp != null && lp.IsAvailable) { _efemRobot.Map(first); _mapTarget.Remove(first); } } } 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; } PM pm = DEVICE.GetDevice(module.ToString()); if (pm != null && pm.IsInstalled && (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; } private bool CheckSequenceOrderOk(SequenceInfo seq, out string reason) { reason = ""; bool foundPm = false; bool isAlignerAfterPm = false; for (int i = 0; i < seq.Steps.Count; i++) { SequenceStepInfo stepInfo = seq.Steps[i]; foreach (var module in stepInfo.StepModules) { if (ModuleHelper.IsPm(module)) { foundPm = true; } if (ModuleHelper.IsAligner(module) && foundPm) { isAlignerAfterPm = true; } } } if (!foundPm) { reason = $"not found PM in the sequence file;"; } if (isAlignerAfterPm) { reason += "Aligner after PM not support"; } return true; } 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 == ModuleName.PMA ? "PMARecipe" : "PMBRecipe"; 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($"", recipeName, false); if (string.IsNullOrEmpty(recipeContent)) { reason = $"Can not find recipe file{recipeName}"; return false; } } } } } } reason = ""; return true; } private bool SetSequenceTemperatureInfo(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 == ModuleName.PMA ? "PMARecipe" : "PMBRecipe"; 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($"", recipeName, false); if (string.IsNullOrEmpty(recipeContent)) { reason = $"Can not find recipe file{recipeName}"; return false; } try { XmlDocument xml = new XmlDocument(); xml.LoadXml(recipeContent); var substrateTemp = xml.DocumentElement.HasAttribute("SubstrateTemp") ? xml.DocumentElement.Attributes["SubstrateTemp"].Value : ""; if (!float.TryParse(substrateTemp, out float temp)) { reason = $"Invalid substrate temperature in recipe head {recipeName}"; return false; } seq.Steps[i].StepParameter[module == ModuleName.PMA ? "PMATemp" : "PMBTemp"] = temp; } catch (Exception ex) { LOG.Write(ex); reason = $"recipe not valid, {recipeName}"; return false; } } } } } } reason = ""; return true; } private void AssociatedPMWithLP(ControlJobInfo ctrlInfo) { List lstPMs = new List(); foreach (var process in _lstProcessJobs) { if (process.ControlJobName == ctrlInfo.Name) { foreach (var step in process.Sequence.Steps) { foreach (var module in step.StepModules) { if (ModuleHelper.IsPm(module)) { if (!lstPMs.Contains(module)) lstPMs.Add(module); } } } } } var lp = ModuleHelper.Converter(ctrlInfo.Module); dictLPAssociatedPM[lp] = lstPMs.Count == 1 ? lstPMs[0] : ModuleName.System; } private static bool IsWaferNeedGotoModule(WaferInfo wafer, ModuleName module) { if (wafer.IsEmpty) return false; if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) return false; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) return false; for (int i = wafer.NextSequenceStep; i < wafer.ProcessJob.Sequence.Steps.Count; i++) { if (wafer.ProcessJob.Sequence.Steps[i].StepModules .Contains(module)) return true; } return false; } private bool CheckAllWaferReturned(ProcessJobInfo pj, bool checkAllProcessed) { for (int i = 0; i < pj.SlotWafers.Count; ++i) { WaferInfo wafer = WaferManager.Instance.GetWafer(pj.SlotWafers[i].Item1, pj.SlotWafers[i].Item2); if (wafer.IsEmpty) return false; if (checkAllProcessed && IsWaferNeedGotoModule(wafer, ModuleName.PMA) || IsWaferNeedGotoModule(wafer, ModuleName.PMB)) return false; } return true; } private void UpdateProcessJobStatus() { foreach (var pj in _lstProcessJobs) { if (pj.State == EnumProcessJobState.Processing) { if (CheckAllWaferReturned(pj, true)) { pj.SetState(EnumProcessJobState.ProcessingComplete); JobDataRecorder.EndPJ(pj.InnerId.ToString(), 0, 0); } } else if (pj.State == EnumProcessJobState.Stopping) { if (CheckAllWaferReturned(pj, false)) { pj.SetState(EnumProcessJobState.ProcessingComplete); JobDataRecorder.EndPJ(pj.InnerId.ToString(), 0, 0); } } } } private void CompleteJobClean(ControlJobInfo cj) { List pms = GetPmNeeded(cj); foreach (var pm in pms) { pm.CompleteJobClean(); } } private void UpdateControlJobStatus() { if (_lstControlJobs.Count == 0) return; if (_curAction.state != ActionState.Done) return; bool allControlJobComplete = true; List cjRemoveList = new List(); foreach (var cj in _lstControlJobs) { if (!IsModuleAvailable(ModuleHelper.Converter(cj.Module))) { allControlJobComplete = false; continue; } if (cj.State == EnumControlJobState.Executing) { bool allPjCompleted = true; foreach (var pjName in cj.ProcessJobNameList) { var pj = _lstProcessJobs.Find(x => x.Name == pjName); if (pj == null) { LOG.Error($"Not find pj named {pjName} in {cj.Name}"); continue; } if (pj.State != EnumProcessJobState.Complete && pj.State != EnumProcessJobState.ProcessingComplete) { allPjCompleted = false; break; } if (allPjCompleted) { pj.SlotWafers.ForEach(p => { if (ModuleHelper.IsLoadPort(p.Item1)) { allPjCompleted = allPjCompleted && WaferManager.Instance.CheckHasWafer(ModuleHelper.Converter(p.Item1.ToString()), p.Item2); } }); } } if (allPjCompleted) { cj.SetState(EnumControlJobState.Completed); cj.EndTime = DateTime.Now; if (!(_isCycleMode && _cycledCount + 1 < _cycleSetPoint)) { CompleteJobClean(cj); } _faCallback.JobFinished(cj, GetFirstProcessJob(cj)); _dbCallback.LotFinished(cj); if (!(_isCycleMode&& _cycledCount < _cycleSetPoint)) (GetScheduler( ModuleHelper.Converter(cj.Module)) as SchedulerLoadPort).NoteJobComplete(); } } LoadportCassetteState state = (LoadportCassetteState)DATA.Poll($"{cj.Module}.CassetteState"); if (cj.State == EnumControlJobState.Completed && state != LoadportCassetteState.Normal) { cjRemoveList.Add(cj); } allControlJobComplete = allControlJobComplete && cj.State == EnumControlJobState.Completed; } if (_isCycleMode && _cycledCount < (_isCycleMode ? _cycleSetPoint : 0)) { int countPerCycle = 0; int countProcessed = 0; foreach (var pj in _lstProcessJobs) { foreach (var pjSlotWafer in pj.SlotWafers) { countPerCycle++; WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); if (!wafer.IsEmpty && !IsWaferNeedGotoModule(wafer, ModuleName.PMA) && !IsWaferNeedGotoModule(wafer, ModuleName.PMB)) countProcessed++; } } _cycledWafer = _cycledCount * countPerCycle + countProcessed; if (allControlJobComplete) { _cycledCount++; if (_cycledCount < _cycleSetPoint) { foreach (var cj in _lstControlJobs) { cj.SetState(EnumControlJobState.Executing); cj.LotInnerId = Guid.NewGuid(); _dbCallback.LotCreated(cj); } foreach (var pj in _lstProcessJobs) { pj.SetState(EnumProcessJobState.Queued); pj.InnerId = Guid.NewGuid(); foreach (var pjSlotWafer in pj.SlotWafers) { WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); wafer.ProcessJob = null; wafer.NextSequenceStep = 0; wafer.ProcessState = EnumWaferProcessStatus.Idle; } } } } } foreach (var cj in cjRemoveList) { List pjRemoveList = new List(); foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) pjRemoveList.Add(pj); } foreach (var pj in pjRemoveList) { _lstProcessJobs.Remove(pj); } _lstControlJobs.Remove(cj); } } #region public interface public bool CheckAllJobDone() { foreach (var cj in _lstControlJobs) { if (cj.State == EnumControlJobState.Executing || cj.State == EnumControlJobState.Paused) return false; } return true; } public bool CheckJobJustDone(out string sJobName) { foreach (var cj in _lstControlJobs) { if (cj.State == EnumControlJobState.Completed && !cj.BeenPosted) { //LP;WaferSize;Lot;Number;Start;End; sJobName = $"{cj.Module};{cj.JobWaferSize};{cj.LotName};{cj.LotWafers.Count};{cj.StartTime:T};{cj.EndTime:T}"; cj.BeenPosted = true; return true; } } sJobName = "NULL"; return false; } public Result Start(params object[] objs) { _isCycleMode = SC.GetValue("System.IsCycleMode"); _cycleSetPoint = _isCycleMode ? SC.GetValue("System.CycleCount") : 0; _cycledWafer = 0; _cycledCount = 0; queMoveAction.Clear(); _curAction.state = ActionState.Done; bool hasPmOnline = false; ModuleName[] pms = { ModuleName.PMA, ModuleName.PMB }; foreach (var pm in pms) { if (IsModuleAvailable(pm)) { var pmSchedule = dictSchedulers[pm] as SchedulerPM; pmSchedule.SetPMAuto(); hasPmOnline = true; continue; } } if (!hasPmOnline) { EV.PostWarningLog("Scheduler", "can not change to auto mode, at least one process chamber be online"); return Result.FAIL; } return Result.RUN; } public void Map(string moduleName) { ModuleName target = ModuleHelper.Converter(moduleName); if (!ModuleHelper.IsLoadPort(target)) { EV.PostWarningLog(LogSource, $"Invalid map target {target}"); return; } if (!_mapTarget.Contains(target)) { _mapTarget.Add(target); } } public bool CheckRecipeUsedInJob(string pathName) { foreach (var pj in _lstProcessJobs) { var seq = pj.Sequence; 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 == ModuleName.PMA ? "PMARecipe" : "PMBRecipe"; if (stepInfo.StepParameter.ContainsKey(attr) && !string.IsNullOrEmpty((string)stepInfo.StepParameter[attr])) { var recipeName = (string)stepInfo.StepParameter[attr]; if (!string.IsNullOrEmpty(recipeName)) { if (pathName.EndsWith(recipeName + ".rcp")) return true; } } } } } } return false; } public bool CheckSequenceUsedInJob(string pathName) { foreach (var pj in _lstProcessJobs) { if (pathName.EndsWith(pj.Sequence.Name + ".seq")) return true; } return false; } public void CreateJob(Dictionary param) { _isCycleMode = SC.GetValue("System.IsCycleMode"); _cycleSetPoint = _isCycleMode ? SC.GetValue("System.CycleCount") : 0; string[] slotSequence = (string[])param["SlotSequence"]; string jobId = (string)param["JobId"]; string module = (string)param["Module"]; //bool autoStart = (bool)param["AutoStart"]; string lotId = jobId; if (param.ContainsKey("LotId")) lotId = (string)param["LotId"]; if (slotSequence.Length != SC.GetValue("EFEM.LoadPort.SlotNumber")) { EV.PostWarningLog(LogSource, $"slot sequence parameter not valid, length is {slotSequence.Length}, should be {SC.GetValue("EFEM.LoadPort.SlotNumber")}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } if (!ModuleHelper.IsLoadPort(ModuleHelper.Converter(module))) { EV.PostWarningLog(LogSource, $"{module} should be LoadPort"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } if (string.IsNullOrEmpty(jobId)) { jobId = "CJ_Local_" + module; } if (_lstControlJobs.Exists(x => x.Name == jobId)) { EV.PostWarningLog(LogSource, $"{jobId} already created"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } SchedulerLoadPort lp = GetScheduler(ModuleHelper.Converter(module)) as SchedulerLoadPort; if (!lp.CheckReadyRunJob()) { EV.PostWarningLog(LogSource, $"{module} not ready"); _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.CarrierID = lp.GetCarrierID(); cj.SetState(EnumControlJobState.WaitingForStart); 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("EFEM.LoadPort.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("EFEM.LoadPort.SlotNumber")]; } if (!seqSlotWafers.ContainsKey(groupName)) { seqSlotWafers[groupName] = new List>(); } seqSlot[groupName][i] = true; if (!WaferManager.Instance.CheckHasWafer(module, i)) { EV.PostWarningLog(LogSource, $"job wafer: {module} slot {i + 1} not in the carrier"); //return; } if (!WaferManager.Instance.CheckWafer(ModuleHelper.Converter(module), i, WaferStatus.Normal)) { EV.PostWarningLog(LogSource, $"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) { EV.PostWarningLog(LogSource, $"job wafer: {module} slot {i + 1} process status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } var smallWafer = SC.GetValue($"System.SmallWafer"); //特殊场景,3寸片第一片不能取放 if (i == 0 && (WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Size == WaferSize.WS3) && (int)smallWafer == 3) { EV.PostWarningLog(LogSource, $"job wafer: {module} 3 inch slot {i + 1} is not supported."); _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.Info($"Assigned wafer job, wafer {module}.{i + 1}, sequence: {slotSequence[i]}"); } if (seqSlotWafers.Count == 0) { EV.PostWarningLog(LogSource, $"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)) { EV.PostWarningLog(LogSource, $"no valid chamber for the {reason}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } if (!CheckSequenceOrderOk(pj.Sequence, out reason)) { EV.PostWarningLog(LogSource, $"sequence path not valid, {reason}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } if (!SC.GetValue("System.IsATMMode") && !CheckSequenceRecipeFileValid(pj.Sequence, out reason)) { EV.PostWarningLog(LogSource, $"recipe file not valid in the sequence, {reason}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return; } if (!SetSequenceTemperatureInfo(pj.Sequence, out reason)) { EV.PostWarningLog(LogSource, $"sequence recipe temperature not valid, {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); AssociatedPMWithLP(cj); _faCallback.JobCreated(cj, GetFirstProcessJob(cj)); } public ProcessJobInfo GetFirstProcessJob(ControlJobInfo cj) { foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) return pj; } return null; } public void AbortJob(string jobName) { ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { EV.PostWarningLog(LogSource, $"abort job rejected, not found job with id {jobName}"); return; } List pjAbortList = new List(); foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) { pj.SetState(EnumProcessJobState.Aborting); pjAbortList.Add(pj); int unprocessed = 0; int aborted = 0; WaferInfo[] wafers = WaferManager.Instance.GetWaferByProcessJob(pj.Name); foreach (var waferInfo in wafers) { waferInfo.ProcessJob = null; waferInfo.NextSequenceStep = 0; if (waferInfo.ProcessState != EnumWaferProcessStatus.Completed) unprocessed++; } JobDataRecorder.EndPJ(pj.InnerId.ToString(), aborted, unprocessed); } } _faCallback.JobAborted(cj, GetFirstProcessJob(cj)); _dbCallback.LotFinished(cj); foreach (var pj in pjAbortList) { _lstProcessJobs.Remove(pj); } _lstControlJobs.Remove(cj); } public void StopJob(string jobName) { ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { EV.PostWarningLog(LogSource, $"stop job rejected, not found job with id {jobName}"); return; } foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) { pj.SetState(EnumProcessJobState.Stopping); } } _faCallback.JobStopped(cj, GetFirstProcessJob(cj)); _dbCallback.LotFinished(cj); } public void ResumeJob(string jobName) { ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { EV.PostWarningLog(LogSource, $"resume job rejected, not found job with id {jobName}"); return; } if (cj.State == EnumControlJobState.Paused) { cj.SetState(EnumControlJobState.Executing); } _faCallback.JobResumed(cj, GetFirstProcessJob(cj)); } public void PauseJob(string jobName) { ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { EV.PostWarningLog(LogSource, $"pause job rejected, not found job with id {jobName}"); return; } if (cj.State == EnumControlJobState.Executing) { cj.SetState(EnumControlJobState.Paused); } _faCallback.JobPaused(cj, GetFirstProcessJob(cj)); } public void StartJob(string jobName) { ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { EV.PostWarningLog(LogSource, $"start job rejected, not found job with id {jobName}"); return; } if (cj.State == EnumControlJobState.WaitingForStart) { cj.SetState(EnumControlJobState.Executing); //PreJobClean(cj); (GetScheduler(ModuleHelper.Converter(cj.Module)) as SchedulerLoadPort).NoteJobStart(); cj.StartTime = DateTime.Now; _dbCallback.LotCreated(cj); _faCallback.JobStarted(cj, GetFirstProcessJob(cj)); if (!_blockingWatcher.IsRunning) _blockingWatcher.Restart(); ResetTraceFlag(); } } private void NotifyFaWaferEnd() { if (ModuleHelper.IsLoadPort(_curAction.targetModule)) { // Notify FA for Port1JobWaferEnd if (_curAction.eType == EnumTransferType.Place) { ProcessJobInfo pj = WaferManager.Instance.GetWafer(_curAction.targetModule, _curAction.targetSlot) .ProcessJob; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == pj.ControlJobName); _faCallback.JobWaferEnd(cj, pj, _curAction.targetModule.ToString(), _curAction.targetSlot); } } } public void ResetIdleCleanTime(string module) { if (string.IsNullOrEmpty(module)) { ModuleName[] pms = { ModuleName.PMA, ModuleName.PMB }; foreach (var schedulerPm in pms) { var pmSchedule = dictSchedulers[schedulerPm] as SchedulerPM; pmSchedule.ResetIdleCleanTime(); } } else { var pm = ModuleHelper.Converter(module); if (ModuleHelper.IsPm(pm)) { var pmSchedule = dictSchedulers[pm] as SchedulerPM; pmSchedule.ResetIdleCleanTime(); } } } public void ResetIdlePurgeTime(string module) { if (string.IsNullOrEmpty(module)) { ModuleName[] pms = { ModuleName.PMA, ModuleName.PMB }; foreach (var schedulerPm in pms) { var pmSchedule = dictSchedulers[schedulerPm] as SchedulerPM; pmSchedule.ResetIdlePurgeTime(); } } else { var pm = ModuleHelper.Converter(module); if (ModuleHelper.IsPm(pm)) { var pmSchedule = dictSchedulers[pm] as SchedulerPM; pmSchedule.ResetIdlePurgeTime(); } } } public void Clear() { _efemRobot.ResetTask(); foreach (var module in dictSchedulers) { module.Value.ResetTask(); } _lstControlJobs.Clear(); _lstProcessJobs.Clear(); _mapTarget.Clear(); queMoveAction.Clear(); _curAction.state = ActionState.Done; } #endregion } }