using System; using System.Collections.Generic; using System.Linq; using System.Xml; using Aitex.Core.Common; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.Device; using Aitex.Core.RT.Event; using Aitex.Core.RT.Log; using Aitex.Core.RT.RecipeCenter; using Aitex.Core.RT.Routine; using Aitex.Core.RT.SCCore; using Aitex.Sorter.Common; 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.LoadPorts; using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.PMs; using Virgo_DRT.Modules.Schedulers; using Virgo_DRT.Scheduler; namespace Virgo_DRT.Modules { public class AutoTransfer : TransferModule { private List _lstControlJobs = new List(); private List _lstProcessJobs = new List(); //private Object _locker = new Object(); private List _lstPms = new List(); private List _lstBuffers = new List(); private List _lstLps = new List(); private const string LogSource = "Scheduler"; private bool _isCycleMode; private int _cycleSetPoint = 0; private int _cycledCount = 0; private int _cycledWafer = 0; private bool _isInParallelMode; private Dictionary _mapLpPm = new Dictionary(); private Dictionary _mapLpAligner = new Dictionary(); private List _mapTarget = new List(); public bool HasJobRunning { get { return _lstControlJobs.Count > 0; } } public AutoTransfer() { if (SC.GetValue("System.PMAIsInstalled")) _lstPms.Add(_pma); if (SC.GetValue("System.PMBIsInstalled")) _lstPms.Add(_pmb); _lstBuffers.AddRange(new[] { _buffer1, _buffer2 }); _lstLps.AddRange(new[] { _lp1, _lp2 }); 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()); DATA.Subscribe("LP1.LocalJobName", () => { if (_lstControlJobs.Exists(x => x.Name == "CJ_Local_LP1")) return "CJ_Local_LP1"; return ""; }); DATA.Subscribe("LP1.LocalJobStatus", () => { var jb = _lstControlJobs.Find(x => x.Name == "CJ_Local_LP1"); if (jb != null) return jb.State.ToString(); return ""; }); DATA.Subscribe("LP2.LocalJobName", () => { if (_lstControlJobs.Exists(x => x.Name == "CJ_Local_LP2")) return "CJ_Local_LP2"; return ""; }); DATA.Subscribe("LP2.LocalJobStatus", () => { var jb = _lstControlJobs.Find(x => x.Name == "CJ_Local_LP2"); if (jb != null) return jb.State.ToString(); return ""; }); } public void Clear() { _efemRobot.ResetTask(); foreach (var pm in _lstPms) { pm.ResetTask(); } foreach (var buffer in _lstBuffers) { buffer.ResetTask(); } foreach (var lp in _lstLps) { lp.ResetTask(); } _lstControlJobs.Clear(); _lstProcessJobs.Clear(); _mapLpAligner.Clear(); _mapLpPm.Clear(); _isInParallelMode = false; _mapTarget.Clear(); } #region Job Management public void CreateJob(Dictionary param) { string[] slotSequence = (string[])param["SlotSequence"]; string jobId = (string)param["JobId"]; string module = (string)param["Module"]; bool autoStart = (bool)param["AutoStart"]; if (slotSequence.Length != 25) { EV.PostWarningLog(LogSource, $"slot sequence parameter not valid, length is {slotSequence.Length}, should be 25"); return; } if (!ModuleHelper.IsLoadPort(ModuleHelper.Converter(module))) { EV.PostWarningLog(LogSource, $"{module} should be LoadPort"); return; } if (string.IsNullOrEmpty(jobId)) { jobId = "CJ_Local_" + module; } if (_lstControlJobs.Exists(x => x.Name == jobId)) { EV.PostWarningLog(LogSource, $"{jobId} already created"); return; } SchedulerLoadPort lp = GetModule(module) as SchedulerLoadPort; if (!lp.CheckReadyRunJob()) { EV.PostWarningLog(LogSource, $"{module} not ready"); return; } ControlJobInfo cj = new ControlJobInfo(); cj.Name = jobId; cj.Module = module; 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 < 25; 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[25]; } 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}"); 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}"); return; } //特殊场景,3寸片第一片不能取放 if (i ==0 && (WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Size==WaferSize.WS3)) { EV.PostWarningLog(LogSource, $"job wafer: {module} 3 inch slot {i + 1} is not supported."); return; } seqSlotWafers[groupName].Add(Tuple.Create(ModuleHelper.Converter(module), i)); } if (seqSlotWafers.Count == 0) { EV.PostWarningLog(LogSource, $"job has not assign wafer"); 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.SlotWafers = seqSlotWafers[seqs[i]]; pj.SetState(EnumProcessJobState.Queued); if (!CheckSequencePmReady(pj.Sequence, out string reason)) { EV.PostWarningLog(LogSource, $"no valid chamber for the {reason}"); return; } if (!SC.GetValue("System.IsATMMode") && !CheckSequenceRecipeFileValid(pj.Sequence, out reason)) { EV.PostWarningLog(LogSource, $"recipe file not valid in the sequence, {reason}"); return; } if (!SetSequenceTemperatureInfo(pj.Sequence, out reason)) { EV.PostWarningLog(LogSource, $"sequence recipe temperature not valid, {reason}"); return; } pjs.Add(pj); } foreach (var pj in pjs) { cj.ProcessJobNameList.Add(pj.Name); _lstProcessJobs.Add(pj); } _lstControlJobs.Add(cj); UpdateParallelMode(); } internal 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); } } } internal 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); } } foreach (var pj in pjAbortList) { _lstProcessJobs.Remove(pj); } _lstControlJobs.Remove(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); } } internal 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); } } internal 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); } } internal 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); } } #endregion Job Management public Result Start(params object[] objs) { _isCycleMode = SC.GetValue("System.IsCycleMode"); _cycleSetPoint = _isCycleMode ? SC.GetValue("System.CycleCount") : 0; _cycledWafer = 0; _cycledCount = 0; bool hasPmOnline = false; foreach (var schedulerPm in _lstPms) { if (schedulerPm.IsAvailable) { schedulerPm.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 Result Monitor() { ControlJobInfo cjActive = _lstControlJobs.Find(x => x.State == EnumControlJobState.Executing); if (cjActive != null) { MonitorModuleTasks(); } MonitorJobTasks(); return Result.RUN; } #region Job task public Result MonitorJobTasks() { UpdateParallelMode(); UpdateProcessJobStatus(); UpdateControlJobStatus(); StartNewJob(); return Result.RUN; } private void UpdateParallelMode() { bool isParallel = SC.GetValue("Scheduler.IsRunInParallelMode"); bool bothLpHasJob = true; Dictionary> lpNeededPmTemperature = new Dictionary>(); foreach (var lp in _lstLps) { var cj = _lstControlJobs.Find(x => x.Module == lp.Module.ToString()); bothLpHasJob = bothLpHasJob && (cj != null); if (cj != null) { lpNeededPmTemperature[lp.Module] = new Dictionary(); var pj = _lstProcessJobs.FirstOrDefault(x => x.ControlJobName == cj.Name); if (pj != null) { 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) && !lpNeededPmTemperature[lp.Module].ContainsKey(module)) { lpNeededPmTemperature[lp.Module][module] = (float) seq.Steps[i] .StepParameter[module == ModuleName.PMA ? "PMATemp" : "PMBTemp"]; } } } } } } bool needSamePm = false; ModuleName pmSource = ModuleName.System; foreach (var pmNeeded in lpNeededPmTemperature) { if (pmNeeded.Value.Count == 1) { var source = pmNeeded.Value.Keys.First(); if (source != pmSource) { pmSource = source; } else { needSamePm = true; break; } } } var needParallel = isParallel && bothLpHasJob && !needSamePm; if (needParallel != _isInParallelMode) { _isInParallelMode = needParallel; if (needParallel) { //aligner固定不做逻辑 _mapLpAligner[ModuleName.LP1] = ModuleName.Aligner1; _mapLpAligner[ModuleName.LP2] = ModuleName.Aligner2; bool assigned =false; foreach (var pmNeeded in lpNeededPmTemperature) { if (pmNeeded.Value.Count == 1) { _mapLpPm[pmNeeded.Key] = pmNeeded.Value.Keys.First(); var anotherLp = pmNeeded.Key == ModuleName.LP1 ? ModuleName.LP2 : ModuleName.LP1; var anotherPm = _mapLpPm[pmNeeded.Key]== ModuleName.PMA ? ModuleName.PMB : ModuleName.PMA; _mapLpPm[anotherLp] = anotherPm; assigned = true; break; } } if (!assigned) { _mapLpPm[ModuleName.LP1] = ModuleName.PMA; _mapLpPm[ModuleName.LP2] = ModuleName.PMB; float pmaTemp = ((SchedulerPM) GetModule(ModuleName.PMA.ToString())).Temperature; float pmbTemp = ((SchedulerPM)GetModule(ModuleName.PMB.ToString())).Temperature; float tempCurrent = Math.Abs(lpNeededPmTemperature[ModuleName.LP1][ModuleName.PMA] - pmaTemp) + Math.Abs(lpNeededPmTemperature[ModuleName.LP2][ModuleName.PMB] - pmbTemp); float tempAnother = Math.Abs(lpNeededPmTemperature[ModuleName.LP1][ModuleName.PMB] - pmbTemp) + Math.Abs(lpNeededPmTemperature[ModuleName.LP2][ModuleName.PMA] - pmaTemp); if (tempAnother < tempCurrent) { _mapLpPm[ModuleName.LP1] = ModuleName.PMB; _mapLpPm[ModuleName.LP2] = ModuleName.PMA; } } } } } 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 UpdateControlJobStatus() { if (_lstControlJobs.Count == 0) return; bool allControlJobComplete = true; List cjRemoveList = new List(); foreach (var cj in _lstControlJobs) { 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) { cj.SetState(EnumControlJobState.Completed); } } 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 < _cycleSetPoint) { 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 && !CheckWaferNeedProcess(pjSlotWafer.Item1, pjSlotWafer.Item2)) countProcessed++; } } _cycledWafer = _cycledCount * countPerCycle + countProcessed; if (allControlJobComplete) { _cycledCount++; if (_cycledCount < _cycleSetPoint) { foreach (var cj in _lstControlJobs) { cj.SetState(EnumControlJobState.Executing); } 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); } if (cjRemoveList.Count > 0) { UpdateParallelMode(); } } private void StartNewJob() { ControlJobInfo cjActived = null; foreach (var cj in _lstControlJobs) { if (cj.State == EnumControlJobState.Executing) { 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) { ActiveProcessJob(pj); break; } } if (!_isInParallelMode) break; } } } private bool ActiveProcessJob(ProcessJobInfo pj) { //pm is ok if (!CheckSequencePmReady( pj.Sequence, out string reason)) { EV.PostWarningLog(LogSource, $"can not active {pj.Name}, {reason}"); return false; } 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); return true; } #endregion Job task #region Module task public Result MonitorModuleTasks() { MonitorPMTask(); MonitorEfemRobotTask(); MonitorBufferTask(); return Result.RUN; } private void MonitorBufferTask() { foreach (var schedulerBuffer in _lstBuffers) { if (!schedulerBuffer.IsAvailable || WaferManager.Instance.CheckNoWafer(schedulerBuffer.Module, 0) ) continue; if (CheckWaferNextStepIsAlign(schedulerBuffer.Module, 0)) { WaferInfo wafer = WaferManager.Instance.GetWafers(schedulerBuffer.Module)[0]; var job = wafer.ProcessJob; GetWaferSequenceAlignTime(schedulerBuffer.Module, 0, out float time); if (schedulerBuffer.Align(time)) { wafer.NextSequenceStep++; } } } } private void MonitorEfemRobotTask() { if (!_efemRobot.IsAvailable) return; //efem robot is idle, release all the target foreach (var pm in _lstPms) { if (pm.IsWaitTransfer(ModuleName.EfemRobot)) { pm.PostTransfer(ModuleName.EfemRobot); } } foreach (var buffer in _lstBuffers) { if (buffer.IsWaitTransfer(ModuleName.EfemRobot)) { buffer.PostTransfer(ModuleName.EfemRobot); } } foreach (var lp in _lstLps) { if (lp.IsWaitTransfer(ModuleName.EfemRobot)) { lp.PostTransfer(ModuleName.EfemRobot); } } MonitorEfemRobotMapTask(); if (!_efemRobot.IsAvailable) return; MonitorEfemRobotPMTask(); if (!_efemRobot.IsAvailable) return; MonitorEfemRobotBufferTask(); if (!_efemRobot.IsAvailable) return; MonitorEfemRobotLoadPortTask(); } private void MonitorEfemRobotMapTask() { if (!_efemRobot.IsAvailable) return; if (_mapTarget.Count > 0) { ModuleName first = _mapTarget[0]; SchedulerLoadPort lp = GetModule(first.ToString()) as SchedulerLoadPort; if (lp != null && lp.IsAvailable) { _efemRobot.Map(first); _mapTarget.Remove(first); } } } private void MonitorEfemRobotPMTask() { if (!_efemRobot.IsAvailable) return; //place to pm if (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 0) && CheckWaferNeedProcess(ModuleName.EfemRobot, 0) && !CheckWaferNeedAlign(ModuleName.EfemRobot,0)) { foreach (var pm in _lstPms) { if (!pm.IsAvailable || !WaferManager.Instance.CheckNoWafer(pm.Module, 0) ) continue; if (_isInParallelMode && GetWaferCountOutofLP(ModuleName.LP1) <= 1 && GetWaferCountOutofLP(ModuleName.LP2) <= 1) { int origin = WaferManager.Instance.GetWafer(ModuleName.EfemRobot, 0).OriginStation; if (origin == (int)ModuleName.LP1 && _mapLpPm[ModuleName.LP1] != pm.Module) continue; if (origin == (int)ModuleName.LP2 && _mapLpPm[ModuleName.LP2] != pm.Module) continue; } if ( !pm.IsReadyForPlace(ModuleName.EfemRobot, 0)) continue; if (_efemRobot.Place(pm.Module, 0, Hand.Blade1)) { pm.WaitTransfer(ModuleName.EfemRobot, EnumTransferType.Place, 0); return; } } } if (!_efemRobot.IsAvailable) return; //pick from pm if (WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 0) ) { ModuleName pickPm = ModuleName.System; int waferOriginSlot = int.MaxValue; foreach (var schedulerPm in _lstPms) { if (!schedulerPm.IsAvailable || WaferManager.Instance.CheckNoWafer(schedulerPm.Module, 0) || CheckWaferNeedProcess(schedulerPm.Module, 0, schedulerPm.Module) ) continue; WaferInfo wafer = WaferManager.Instance.GetWafer(schedulerPm.Module, 0); bool pickAllowed = CheckVacuumWaferHasAvailableTarget(schedulerPm.Module, 0); if (pickAllowed) { int slot = wafer.OriginSlot; if (slot < waferOriginSlot) { waferOriginSlot = slot; pickPm = schedulerPm.Module; } } } if (pickPm != ModuleName.System) { SchedulerModule pm = GetModule(pickPm.ToString()); if (pm.IsReadyForPick(ModuleName.EfemRobot, 0)) { if (_efemRobot.Pick(pm.Module, 0, Hand.Blade1)) { pm.WaitTransfer(ModuleName.EfemRobot, EnumTransferType.Pick, 0); return; } } } } } private void MonitorEfemRobotLoadPortTask() { if (!_efemRobot.IsAvailable) return; //pick if (_isInParallelMode) { int outWaferLp1 = GetWaferCountOutofLP(ModuleName.LP1); int outWaferLp2 = GetWaferCountOutofLP(ModuleName.LP2); if (outWaferLp1 <= GetMaxOutWafer() && outWaferLp2 <= GetMaxOutWafer()) { foreach (var lp in _lstLps) { if (GetWaferCountOutofLP(lp.Module) >= GetMaxOutWafer()) continue; if (GetWaferCountInJobQueue(lp.Module) > 0 && WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 0)) { SlotItem position = GetNextWaferInJobQueue(lp.Module); if (position != null && CheckWaferPmTemperatureIsOk(position.Module, position.Slot)) { if (lp.IsReadyForPick(ModuleName.EfemRobot, position.Slot)) { if (_efemRobot.Pick(position.Module, position.Slot, Hand.Blade1)) { lp.WaitTransfer(ModuleName.EfemRobot, EnumTransferType.Pick, position.Slot); return; } } } } } } } else { if (GetWaferCountOutofLP(ModuleName.System) < GetMaxOutWafer() && GetWaferCountInJobQueue(ModuleName.System) > 0 && WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 0)) { SlotItem position = GetNextWaferInJobQueue(ModuleName.System); if (position != null && CheckWaferPmTemperatureIsOk(position.Module, position.Slot)) { SchedulerLoadPort lp = (SchedulerLoadPort)GetModule(position.Module.ToString()); if (lp.IsReadyForPick(ModuleName.EfemRobot, position.Slot)) { if (_efemRobot.Pick(position.Module, position.Slot, Hand.Blade1)) { lp.WaitTransfer(ModuleName.EfemRobot, EnumTransferType.Pick, position.Slot); return; } } } } } if (!_efemRobot.IsAvailable) return; //place if (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 0) && !CheckWaferNeedProcess(ModuleName.EfemRobot, 0)) { SlotItem destination = GetWaferReturnLoadPort(_efemRobot.Module, 0); if (destination != null) { SchedulerLoadPort lp = (SchedulerLoadPort)GetModule(destination.Module.ToString()); if (lp.IsReadyForPlace(ModuleName.EfemRobot, destination.Slot)) { if (_efemRobot.Place(destination.Module, destination.Slot, Hand.Blade1)) { lp.WaitTransfer(ModuleName.EfemRobot, EnumTransferType.Place, destination.Slot); return; } } } } } private void MonitorEfemRobotBufferTask() { if (!_efemRobot.IsAvailable) return; //place to buffer if (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 0) && CheckWaferNeedAlign(ModuleName.EfemRobot, 0)) { foreach (var buffer in _lstBuffers) { if (!buffer.IsAvailable || !WaferManager.Instance.CheckNoWafer(buffer.Module, 0)) continue; if (_isInParallelMode && GetWaferCountOutofLP(ModuleName.LP1)<=1 && GetWaferCountOutofLP(ModuleName.LP2) <= 1) { int origin = WaferManager.Instance.GetWafer(ModuleName.EfemRobot, 0).OriginStation; if (origin == (int)ModuleName.LP1 && _mapLpAligner[ModuleName.LP1] != buffer.Module) continue; if (origin == (int)ModuleName.LP2 && _mapLpAligner[ModuleName.LP2] != buffer.Module) continue; } if (!buffer.IsReadyForPlace(ModuleName.EfemRobot, 0)) continue; if (_efemRobot.Place(buffer.Module, 0, Hand.Blade1)) { buffer.WaitTransfer(ModuleName.EfemRobot, EnumTransferType.Place, 0); return; } } } if (!_efemRobot.IsAvailable) return; //pick from buffer if (WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 0)) { ModuleName pickBuffer = ModuleName.System; int waferOriginSlot = int.MaxValue; foreach (var schedulerBuffer in _lstBuffers) { if (!schedulerBuffer.IsAvailable || WaferManager.Instance.CheckNoWafer(schedulerBuffer.Module, 0) || CheckWaferNeedAlign(schedulerBuffer.Module, 0 )) continue; WaferInfo wafer = WaferManager.Instance.GetWafer(schedulerBuffer.Module, 0); bool pickAllowed = CheckBufferWaferHasAvailableTarget(schedulerBuffer.Module, 0); if (pickAllowed) { int slot = wafer.OriginSlot; if (slot < waferOriginSlot) { waferOriginSlot = slot; pickBuffer = schedulerBuffer.Module; } } } if (pickBuffer != ModuleName.System) { SchedulerModule buffer = GetModule(pickBuffer.ToString()); if (buffer.IsReadyForPick(ModuleName.EfemRobot, 0)) { if (_efemRobot.Pick(buffer.Module, 0, Hand.Blade1)) { buffer.WaitTransfer(ModuleName.EfemRobot, EnumTransferType.Pick, 0); return; } } } } } private void MonitorPMTask() { bool robotWaferAssigned = false; bool aligner1WaferAssigned = false; bool aligner2WaferAssigned = false; foreach (var pm in _lstPms) { if (!pm.IsAvailable) continue; if (WaferManager.Instance.CheckHasWafer(pm.Module, 0)) { if (CheckWaferNeedProcess(pm.Module, 0, pm.Module)) { WaferInfo wafer = WaferManager.Instance.GetWafer(pm.Module, 0); var recipeName = pm.Module == ModuleName.PMA ? wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepParameter["PMARecipe"] : wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepParameter["PMBRecipe"]; if (pm.Process((string)recipeName, false, true)) { WaferManager.Instance.GetWafer(pm.Module, 0).NextSequenceStep++; continue; } } else { if (!pm.IsReadyForPick(ModuleName.EfemRobot, 0)) { pm.PrepareTransfer(ModuleName.EfemRobot, EnumTransferType.Pick, 0); } } } else //no wafer { if (WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 0) && WaferManager.Instance.CheckNoWafer(ModuleName.Aligner1, 0) && WaferManager.Instance.CheckNoWafer(ModuleName.Aligner2, 0) ) { ModuleName source = ModuleName.System; if (_isInParallelMode) { foreach (var moduleName in _mapLpPm) { if (moduleName.Value == pm.Module) source = moduleName.Key; } } SlotItem item = GetNextWaferInJobQueue(source); if (item != null && CheckWaferNeedProcess(item.Module, item.Slot, pm.Module)) { if (GetWaferTemperatureSetInRecipe(item.Module, item.Slot, pm.Module, out float temp) && !pm.IsTemperatureReady(temp)) { pm.Preheating(temp); continue; } } } if (!pm.IsAvailable) continue; if (!robotWaferAssigned && CheckWaferNeedProcess(ModuleName.EfemRobot, 0, pm.Module) && CheckWaferNextProcessIn(ModuleName.EfemRobot, 0, pm.Module)) { robotWaferAssigned = true; GetWaferTemperatureSetInRecipe(ModuleName.EfemRobot, 0, pm.Module, out float temp); if (!pm.IsReadyForPlace(ModuleName.EfemRobot, 0) || !pm.IsTemperatureReady(temp)) { pm.PrepareTransfer(ModuleName.EfemRobot, EnumTransferType.Place, 0, temp); } } if (!pm.IsAvailable) continue; if (!aligner1WaferAssigned && CheckWaferNeedProcess(ModuleName.Aligner1, 0, pm.Module)) { aligner1WaferAssigned = true; GetWaferTemperatureSetInRecipe(ModuleName.Aligner1, 0, pm.Module, out float temp); if (!pm.IsReadyForPlace(ModuleName.EfemRobot, 0) || !pm.IsTemperatureReady(temp)) { pm.PrepareTransfer(ModuleName.EfemRobot, EnumTransferType.Place, 0, temp); } } if (!pm.IsAvailable) continue; if (!aligner2WaferAssigned && CheckWaferNeedProcess(ModuleName.Aligner2, 0, pm.Module)) { aligner2WaferAssigned = true; GetWaferTemperatureSetInRecipe(ModuleName.Aligner2, 0, pm.Module, out float temp); if (!pm.IsReadyForPlace(ModuleName.EfemRobot, 0) || !pm.IsTemperatureReady(temp)) { pm.PrepareTransfer(ModuleName.EfemRobot, EnumTransferType.Place, 0, temp); } } if (!pm.IsAvailable) continue; } } } #endregion Module task #region Logic Check private bool CheckSequencePmReady(SequenceInfo seq, out string reason) { 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)) { PM pm = DEVICE.GetDevice(module.ToString()); if (pm.IsInstalled) { hasPm = true; break; } } else { hasPm = true; // other modules default ok } } if (!hasPm) { reason = $"Step {i + 1} no valid PM, " + string.Join("|", stepInfo.StepModules); return false; } } reason = ""; return true; } protected int GetUnprocessedWaferCount() { int count = 0; foreach (ProcessJobInfo pj in _lstProcessJobs) { if (pj.State == EnumProcessJobState.Processing || pj.State == EnumProcessJobState.Paused) count += GetUnprocessedWaferCount(pj); } return count; } protected int GetUnprocessedWaferCount(ProcessJobInfo pj) { int count = 0; 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) continue; if (CheckWaferNeedProcess(pj.SlotWafers[i].Item1, pj.SlotWafers[i].Item2)) count++; } return count; } protected 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 && CheckWaferNeedProcess(pj.SlotWafers[i].Item1, pj.SlotWafers[i].Item2)) return false; } return true; } private bool CheckWaferNextStepIsAlign(ModuleName module, int slot) { 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)) return true; return false; } 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 = 0; 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 bool CheckWaferPmTemperatureIsOk(ModuleName waferModule, int waferSlot) { WaferInfo wafer = WaferManager.Instance.GetWafer(waferModule, waferSlot); if (wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) return false; var seq = wafer.ProcessJob.Sequence; for (int i = 0; i < seq.Steps.Count; i++) { SequenceStepInfo stepInfo = seq.Steps[i]; bool stepOk = false; foreach (var module in stepInfo.StepModules) { if (ModuleHelper.IsPm(module)) { float tempNeeded = (float)seq.Steps[i].StepParameter[module == ModuleName.PMA ? "PMATemp" : "PMBTemp"]; if ((GetModule(module.ToString()) as SchedulerPM).IsTemperatureReady(tempNeeded)) { stepOk = true; break; } } else { stepOk = true; } } if (!stepOk) return false; } return true; } private bool CheckWaferNeedAlign(ModuleName waferModule, int waferSlot) { 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; if (!wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules .Contains(ModuleName.Aligner1) && !wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules .Contains(ModuleName.Aligner2)) return false; return true; } private bool CheckWaferNextProcessIn(ModuleName waferModule, int waferSlot, ModuleName chamber) { WaferInfo wafer = WaferManager.Instance.GetWafer(waferModule, waferSlot); if (wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.ProcessJob.Sequence.Steps == null || wafer.ProcessJob.Sequence.Steps.Count <= wafer.NextSequenceStep || wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep] == null || wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules == null) { return false; } if (_isInParallelMode && _mapLpPm[(ModuleName)wafer.OriginStation] != chamber) return false; return wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(chamber); } public bool CheckWaferNeedProcess(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)) { if (processIn == ModuleName.System) return true; if (_isInParallelMode && _mapLpPm[(ModuleName) wafer.OriginStation] != processIn) return false; if (wafer.ProcessJob.Sequence.Steps[i].StepModules .Contains(processIn)) return true; } } return false; } private bool CheckVacuumWaferHasAvailableTarget(ModuleName waferModule, int waferSlot) { WaferInfo wafer = WaferManager.Instance.GetWafer(waferModule, waferSlot); //System.Diagnostics.Trace.Assert(!wafer.IsEmpty); //if (CheckWaferNeedProcess(waferModule, waferSlot)) //{ // // Check for an empty PM // foreach (var pm in _lstPms) // { // if (WaferManager.Instance.CheckNoWafer(pm.Module, 0)) // { // if (pm.IsAvailable == false) // continue; // if (!CheckWaferNextProcessIn(waferModule, waferSlot, pm.Module)) // continue; // return true; // } // } //} //else { // No more processing needed, check for a outbound BUFFER slot foreach (var buffer in _lstBuffers) { if (!buffer.IsAvailable) continue; if (WaferManager.Instance.CheckNoWafer(buffer.Module, 0)) { return true; } } } return false; } private bool CheckBufferWaferHasAvailableTarget(ModuleName waferModule, int waferSlot) { WaferInfo wafer = WaferManager.Instance.GetWafer(waferModule, waferSlot); //System.Diagnostics.Trace.Assert(!wafer.IsEmpty); if (!CheckWaferNeedProcess(waferModule, waferSlot)) return true; // Check for an empty PM foreach (var pm in _lstPms) { if (WaferManager.Instance.CheckNoWafer(pm.Module, 0)) { if (pm.IsAvailable == false) continue; if (!CheckWaferNextProcessIn(waferModule, waferSlot, pm.Module)) continue; return true; } } return false; } 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 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 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)) 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 int GetWaferCountOutofLP(ModuleName lp) { int count = 0; var lstPosition = new List() { ModuleName.EfemRobot, ModuleName.Aligner1, ModuleName.Aligner2, ModuleName.PMA, ModuleName.PMB, }; foreach (var moduleName in lstPosition) { if (WaferManager.Instance.CheckHasWafer(moduleName, 0)) { if (lp == ModuleName.System || WaferManager.Instance.GetWafer(moduleName, 0).OriginStation == (int) lp) count++; } } return count; } private SlotItem GetWaferReturnLoadPort(ModuleName module, int slot) { WaferInfo wafer = WaferManager.Instance.GetWafer(module, slot); if (!wafer.IsEmpty) { return new SlotItem((ModuleName)wafer.OriginStation, wafer.OriginSlot); } return null; } private int GetWaferCountInJobQueue(ModuleName sourceLp) { int count = 0; foreach (var lp in _lstLps) { for (int i = 0; i < 25; i++) { if (CheckWaferNeedProcess(lp.Module, i) && (sourceLp==ModuleName.System || sourceLp==lp.Module)) count++; } } return count; } private SlotItem GetNextWaferInJobQueue(ModuleName sourceLp) { foreach (var cj in _lstControlJobs) { if (sourceLp!=ModuleName.System && (cj.Module !=sourceLp.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 (CheckWaferNeedProcess(pjSlotWafer.Item1, pjSlotWafer.Item2)) return new SlotItem(pjSlotWafer.Item1, pjSlotWafer.Item2); } } } } } return null; } private int GetMaxOutWafer() { return _isInParallelMode ? 1 : 2; } #endregion Logic Check } }