using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Timers; 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.OperationCenter; using Aitex.Core.RT.RecipeCenter; using Aitex.Core.RT.Routine; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using Aitex.Sorter.Common; using MECF.Framework.Common.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.ModuleLibrary.PMModules; using MECF.Framework.RT.ModuleLibrary.SystemModules; using MECF.Framework.RT.ModuleLibrary.SystemModules.Routines; using EfemDualSchedulerLib.Schedulers; using MECF.Framework.Common.DataCenter; using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.LoadLocks; using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.LoadPorts; using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.PMs; using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Robots; using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.TMs; using JetVirgoPM.PMs.RecipeExecutors; namespace EfemDualSchedulerLib { /// /// 自动传输 /// public partial class AutoTransfer : SchedulerModuleFactory, IAutoTransfer { //const Hand Hand_Dual = Hand.Blade1; //const Hand Hand_Single = Hand.Blade2; //用于避免两爪都有wafer,放了一片就又去LoadPort Pick private bool EfemRobotPickAllowable = true; private bool _isRunningInParallelMode; private List _mapTarget = new List(); private List _lstControlJobs = new List(); private List _lstProcessJobs = new List(); //private Object _locker = new Object(); private List _lstPms = new List(); private List _lstLps = new List(); private List _lstLls = new List(); private List _lstBuffers = new List(); private List _lstPmsCurrentSequence = new List(); private R_TRIG _trigLp1JobComplete = new R_TRIG(); private R_TRIG _trigLp2JobComplete = new R_TRIG(); private R_TRIG _trigLp3JobComplete = new R_TRIG(); private Dictionary _temperatureSetpointDic = new Dictionary(); private Dictionary _temperatureTrigDic = new Dictionary(); private Dictionary> currentSlotSequenceList = new Dictionary>() { {"LP1",Enumerable.Repeat("",25).ToList()}, {"LP2",Enumerable.Repeat("",25).ToList()}, {"LP3",Enumerable.Repeat("",25).ToList()} }; private const string LogSource = "Scheduler"; public string LPHadCreateJob { get; set; } //private bool _isCycleMode; //private int _cycleSetPoint = 0; //private int _cycledCount = 0; //private int _cycledWafer = 0; private SchedulerFACallback _faCallback; private SchedulerDBCallback _dbCallback; //private float _throughput; //private DeviceTimer _throughputTimer = new DeviceTimer(); //private int _processedWaferCountInFinishedCJ; class CycleData { public DateTime StartTime { get; private set; } public TimeSpan EscapeTime => StartTime == DateTime.MinValue ? TimeSpan.Zero : DateTime.Now.Subtract(StartTime); public bool IsCycleMode => SC.GetValue("System.IsCycleMode"); public string CycleBy => SC.GetStringValue("System.CycleBy"); public int CycleCountSetPoint => SC.GetValue("System.CycleCount");// 设置的循环次数 public int CycledCount { get; private set; } // 已处理的循环次数 public int CycleWaferCountSetPoint => SC.GetValue("System.CycleWaferCount");// 设置的晶圆片数 public int CycledWaferCount { get; private set; } // 已处理的晶圆片数 public void Start() { LOG.Info("Cycle: Start cycle data"); StartTime = DateTime.Now; } public void Reset() { LOG.Info("Cycle: Reset cycle data"); CycledWaferCount = 0; CycledCount = 0; } //public void BeginOrAddCycle(int setpoint) //{ // if (IsCycleMode) // add // { // CycleWaferCountSetPoint += setpoint; // LOG.Info($"Cycle: Add {setpoint} wafers, total cycle count = {CycleWaferCountSetPoint}"); // } // else // begin // { // IsCycleMode = true; // StartTime = DateTime.Now; // CycleWaferCountSetPoint = setpoint; // CycledWaferCount = 0; // LOG.Info($"Cycle: Begin {setpoint} wafers"); // } //} public bool IsCycleEnd() { if (!IsCycleMode) return true; if (CycleBy == "WaferCount" && CycledWaferCount >= CycleWaferCountSetPoint) { SC.SetItemValue("System.IsCycleMode", false); Reset(); return true; } else if (CycleBy == "LotCount" && CycledCount >= CycleCountSetPoint) { SC.SetItemValue("System.IsCycleMode", false); Reset(); return true; } return false; } public void Increase(int waferCount, int count = 1) { IncreaseWaferCount(waferCount); IncreaseCount(count); } public void IncreaseWaferCount(int waferCount) { CycledWaferCount += waferCount; LOG.Info($"Cycle: finished {CycledWaferCount} wafer(s)"); } public void IncreaseCount(int count = 1) { CycledCount += count; LOG.Info($"Cycle: finished {CycledCount} Count(s)"); } } class ThroughputData { public bool IsCalcing { get; private set; } = false; public DateTime CacledLastTime { get; private set; } = DateTime.MinValue; // 创建时间或者计算时间 public double CalcedTotalTimeSeconds { get; private set; } = 0; // 总的计算时间 public int CalcedProcessedCount { get; private set; } = 0; // 已计算完的总数(control job) public double TotalTimeSeconds { get; private set; } public DateTime CaclingLastTime { get; private set; } = DateTime.MinValue; // 创建时间或者计算时间 public int CalcingProcessedCount { get; private set; } = 0;// 正在计算的总数(control job) public int ProcessedCount => CalcedProcessedCount + CalcingProcessedCount; public double Throughput { get; private set; } public double LiveThroughput { get; private set; } public void BeginCalc() { if (IsCalcing) return; IsCalcing = true; CalcedTotalTimeSeconds = 0; CacledLastTime = DateTime.Now; CaclingLastTime = DateTime.Now; LOG.Info("Throughput: Begin"); } public void Pause() { if (!IsCalcing) { IsCalcing = false; LOG.Info("Throughput: Pause"); } } public void Reset() { CacledLastTime = DateTime.Now; CalcedTotalTimeSeconds = 0; CalcedProcessedCount = 0; CaclingLastTime = DateTime.Now; CalcedProcessedCount = 0; CalcingProcessedCount = 0; Throughput = 0; LiveThroughput = 0; LOG.Info("Throughput: Reset"); } public void UpdateCalced(int calcedCount) { CalcedProcessedCount += calcedCount; CalcingProcessedCount -= calcedCount; if (CalcingProcessedCount < 0) CalcingProcessedCount = 0; CalcedTotalTimeSeconds += DateTime.Now.Subtract(CacledLastTime).TotalSeconds; CacledLastTime = DateTime.Now; } public void UpdateCalcing(int calcingCount) { DateTime now = DateTime.Now; double seconds_this = now.Subtract(CaclingLastTime).TotalSeconds; int calcingCount_this = calcingCount - CalcingProcessedCount; double count_this = ProcessedCount + calcingCount; LiveThroughput = 3600 / (seconds_this / calcingCount_this); TotalTimeSeconds = CalcedTotalTimeSeconds + seconds_this; Throughput = 3600 * count_this / TotalTimeSeconds; LOG.Info($"Throughput: {Throughput}, Finished {count_this} wafer(s) in {(int)TotalTimeSeconds} seconds;" + $" {LiveThroughput} last wafer in {(int)seconds_this} seconds"); CaclingLastTime = now; CalcingProcessedCount = calcingCount; } } private CycleData _CycleData = new CycleData(); private ThroughputData _throughputData = new ThroughputData(); private bool _isAutoUnloadFoup { get { return SC.GetValue("EFEM.LoadPort.EnableAutoUnloadFoup"); } } private ModuleName _TMRobotBlade1Station = ModuleName.System; private Dictionary _loadlockCooling = new Dictionary(); Queue tmRobotActions = new Queue() { }; Queue efemRobotActions = new Queue() { }; public AutoTransfer(EquipmentManager equipment) { _faCallback = new SchedulerFACallback(); _dbCallback = new SchedulerDBCallback(); if (SC.GetValueOrDefault("System.SetUp.PMA.IsInstalled")) _lstPms.Add(_pm1); if (SC.GetValueOrDefault("System.SetUp.PMB.IsInstalled")) _lstPms.Add(_pm2); //if (SC.GetValueOrDefault("System.SetUp.PMC.IsInstalled")) // _lstPms.Add(_pm3); if (SC.GetValueOrDefault("System.SetUp.LLA.IsInstalled")) _lstLls.Add(_loadlockA); if (SC.GetValueOrDefault("System.SetUp.LLB.IsInstalled")) _lstLls.Add(_loadlockB); if (SC.GetValueOrDefault("System.SetUp.LP1.IsInstalled")) _lstLps.Add(_lp1); if (SC.GetValueOrDefault("System.SetUp.LP2.IsInstalled")) _lstLps.Add(_lp2); if (SC.GetValueOrDefault("System.SetUp.LP3.IsInstalled")) _lstLps.Add(_lp3); //临时办法,设备异常后进行reset,重新home后reset机械手task OP.Subscribe($"{ModuleName.TM}.ResetTask", (string cmd, object[] args) => { _tmRobot.ResetTask(); return true; }); OP.Subscribe($"{ModuleName.EFEM}.ResetTask", (string cmd, object[] args) => { _efem.ResetTask(); return true; }); DATA.Subscribe("Scheduler.IsCycleMode", () => _CycleData.IsCycleMode); DATA.Subscribe("Scheduler.CycleBy", () => _CycleData.CycleBy); DATA.Subscribe("Scheduler.CycleCountSetPoint", () => _CycleData.CycleCountSetPoint); DATA.Subscribe("Scheduler.CycledCount", () => _CycleData.CycledCount); DATA.Subscribe("Scheduler.CycleWaferCountSetPoint", () => _CycleData.CycleWaferCountSetPoint); DATA.Subscribe("Scheduler.CycledWaferCount", () => _CycleData.CycledWaferCount); DATA.Subscribe("Scheduler.CycleEscapeTime", () => _CycleData.EscapeTime); DATA.Subscribe("Scheduler.Throughput", () => _throughputData.Throughput); DATA.Subscribe("Scheduler.LiveThroughput", () => _throughputData.LiveThroughput); DATA.Subscribe("System.Scheduler.RunningRecipeList", () => _GetRunningRecipeList()); DATA.Subscribe("Scheduler.CjIdList", () => _GetRunningCjIdList()); DATA.Subscribe("LP1.LocalJobName", () => { var jb = _lstControlJobs.Find(x => x.Module == "LP1"); if (jb != null) return jb.Name; return ""; }); DATA.Subscribe($"LP1.JobLotName", () => { var cj = _lstControlJobs.FirstOrDefault(x => x.Module == "LP1"); if (cj != null) return cj.LotName; return ""; }); DATA.Subscribe("LP1.LocalJobStatus", () => { var jb = _lstControlJobs.Find(x => x.Module == "LP1"); if (jb != null) return jb.State.ToString(); return ""; }); DATA.Subscribe("LP1.CurrentRecipeList", () => { List result = Enumerable.Repeat("", 25).ToList(); for (int i = 0; i < _lstProcessJobs.Count; i++) { foreach (var wafers in _lstProcessJobs[i].SlotWafers) { if (wafers.Item1 == ModuleName.LP1) { result[wafers.Item2] = _lstProcessJobs[i].Sequence.Name; } } } return result; }); DATA.Subscribe("LP2.LocalJobName", () => { var jb = _lstControlJobs.Find(x => x.Module == "LP2"); if (jb != null) return jb.Name; return ""; }); DATA.Subscribe($"LP2.JobLotName", () => { var cj = _lstControlJobs.FirstOrDefault(x => x.Module == "LP2"); if (cj != null) return cj.LotName; return ""; }); DATA.Subscribe("LP2.LocalJobStatus", () => { var jb = _lstControlJobs.Find(x => x.Module == "LP2"); if (jb != null) return jb.State.ToString(); return ""; }); DATA.Subscribe("LP2.CurrentRecipeList", () => { List result = Enumerable.Repeat("", 25).ToList(); for (int i = 0; i < _lstProcessJobs.Count; i++) { foreach (var wafers in _lstProcessJobs[i].SlotWafers) { if (wafers.Item1 == ModuleName.LP2) { result[wafers.Item2] = _lstProcessJobs[i].Sequence.Name; } } } return result; }); DATA.Subscribe("LP3.LocalJobName", () => { var jb = _lstControlJobs.Find(x => x.Module == "LP3"); if (jb != null) return jb.Name; return ""; }); DATA.Subscribe($"LP3.JobLotName", () => { var cj = _lstControlJobs.FirstOrDefault(x => x.Module == "LP3"); if (cj != null) return cj.LotName; return ""; }); DATA.Subscribe("LP3.LocalJobStatus", () => { var jb = _lstControlJobs.Find(x => x.Module == "LP3"); if (jb != null) return jb.State.ToString(); return ""; }); DATA.Subscribe("LP3.CurrentRecipeList", () => { List result = Enumerable.Repeat("", 25).ToList(); for (int i = 0; i < _lstProcessJobs.Count; i++) { foreach (var wafers in _lstProcessJobs[i].SlotWafers) { if (wafers.Item1 == ModuleName.LP3) { result[wafers.Item2] = _lstProcessJobs[i].Sequence.Name; } } } return result; }); tmRobotActions.Enqueue(MonitorTmRobotLoadLockPickTask); tmRobotActions.Enqueue(MonitorTmRobotPMAPlaceTask); tmRobotActions.Enqueue(MonitorTmRobotPMBPlaceTask); tmRobotActions.Enqueue(MonitorTmRobotPMAPickTask); tmRobotActions.Enqueue(MonitorTmRobotPMBPickTask); tmRobotActions.Enqueue(MonitorTmRobotLoadLockPlaceTask); tmRobotActions.Enqueue(MonitorTmRobotGoToTask); efemRobotActions.Enqueue(MonitorEfemRobotAlignerPickTask); efemRobotActions.Enqueue(MonitorEfemRobotAlignerPlaceTask); efemRobotActions.Enqueue(MonitorEfemRobotLoadLockPlaceTask); efemRobotActions.Enqueue(MonitorEfemRobotLoadLockPickTask); efemRobotActions.Enqueue(MonitorEfemRobotLoadPortPickTask); efemRobotActions.Enqueue(MonitorEfemRobotLoadPortPlaceTask); } private List _GetRunningCjIdList() { return _lstControlJobs/*.Where(o => o.State == EnumControlJobState.Executing || o.State == EnumControlJobState.Paused)*/.Select(o => o.InnerId.ToString()).ToList(); } const string StrProcessRecipe = "ProcessRecipe"; const string StrRecipe = "Recipe"; private List _GetRunningRecipeList() { var runningCjList = _lstControlJobs.Where(o => o.State == EnumControlJobState.Executing || o.State == EnumControlJobState.Paused).Select(o => o.Name).ToList(); return _lstProcessJobs.Where(o => runningCjList.Contains(o.ControlJobName)) .SelectMany(a => a.Sequence.Steps.SelectMany(b => b.StepParameter.Where(c => c.Key == StrProcessRecipe || c.Key == StrRecipe))).Select(d => d.Value as string) .Where(o => !string.IsNullOrWhiteSpace(o)).ToList(); } public bool HasJobRunning { get { return _lstControlJobs.Count > 0; } } public void Clear() { _tmRobot.ResetTask(); _efem.ResetTask(); foreach (var pm in _lstPms) { pm.ResetTask(); } //foreach (var lp in _lstLps) //{ // lp.ResetTask(); //} _lstControlJobs.Clear(); _lstProcessJobs.Clear(); _temperatureSetpointDic.Clear(); _temperatureTrigDic.Clear(); _CycleData.Reset(); } #region Job Management public bool CreateJob(Dictionary param) { string reason; string[] slotSequence = (string[])param["SlotSequence"]; string jobId = (string)param["JobId"]; string module = (string)param["Module"]; LPHadCreateJob = (string)param["Module"]; bool autoStart = (bool)param["AutoStart"]; string lotId = jobId; if (param.ContainsKey("LotId")) lotId = (string)param["LotId"]; if (!ValidateSequence(slotSequence, out reason)) { EV.PostWarningLog(LogSource, $"{reason}"); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (slotSequence.Length != 25) { reason = $"slot sequence parameter not valid, length is {slotSequence.Length}, should be 25"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (!ModuleHelper.IsLoadPort(ModuleHelper.Converter(module))) { reason = $"{module} should be LP"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (string.IsNullOrEmpty(jobId)) { jobId = "CJ_Local_" + module; } if (_lstControlJobs.Exists(x => x.Name == jobId)) { reason = $"LotID : {jobId} already created"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } SchedulerLoadPort lp = GetModule(module) as SchedulerLoadPort; if (!lp.CheckReadyRunJob()) { reason = $"{module} not ready"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } ControlJobInfo cj = new ControlJobInfo(); cj.Name = jobId; cj.Module = module; cj.LotName = jobId; cj.LotInnerId = Guid.NewGuid(); cj.LotWafers = new List(); cj.SetState(EnumControlJobState.WaitingForStart); Dictionary seqSlot = new Dictionary(); Dictionary>> seqSlotWafers = new Dictionary>>(); Dictionary indexSequence = new Dictionary(); bool enableGroupBySequence = SC.GetValue("System.Scheduler.GroupWaferBySequence"); string WaferAssociationInfo = $"WaferAssociationInfo({module}):"; for (int i = 0; i < 25; i++) { WaferAssociationInfo = WaferAssociationInfo + string.Format(" slot{0} -- {1};", i + 1, slotSequence[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)) { reason = $"job wafer: {module} slot {i + 1} not in the carrier"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (!WaferManager.Instance.CheckWafer(ModuleHelper.Converter(module), i, WaferStatus.Normal)) { reason = $"job wafer: {module} slot {i + 1} status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Status}"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } var wafer = WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i); if (wafer == null || wafer.IsEmpty) { reason = $"specifies wafer: {module} slot {i + 1} not in the carrier"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (!ModuleHelper.IsLoadPort(ModuleHelper.Converter(wafer.WaferOrigin.Split('.')[0]))) { reason = $"specifies wafer: {module} slot {i + 1} not origin LP"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (wafer.ProcessState != EnumWaferProcessStatus.Idle) { reason = $"specifies wafer: {module} slot {i + 1} process state is not idle"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } seqSlotWafers[groupName].Add(Tuple.Create(ModuleHelper.Converter(module), i)); cj.LotWafers.Add(WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i)); WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).PPID = slotSequence[i]; } currentSlotSequenceList[module] = slotSequence.Reverse().ToList(); EV.PostInfoLog(LogSource, WaferAssociationInfo); if (seqSlotWafers.Count == 0) { reason = $"Can not create job, no wafer assigned"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } 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 innerReason)) { reason = $"no valid chamber for the {innerReason}"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (!CheckSequenceRecipeFileValid(pj.Sequence, out reason)) { reason = $"recipe file not valid in the sequence, {reason}"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (!CheckSequenceOrderOk(pj.Sequence, out reason)) { reason = $"sequence path not valid, {reason}"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } if (CheckSequenceNeedDummyWafer(pj.Sequence)) { reason = $"create job failed, no valid dummy wafer exist"; EV.PostWarningLog(LogSource, reason); _faCallback.JobCreateFailed(module, lotId, jobId, ""); return false; } pjs.Add(pj); } foreach (var pj in pjs) { cj.ProcessJobNameList.Add(pj.Name); _lstProcessJobs.Add(pj); } _lstControlJobs.Add(cj); int totalWafer = 0; foreach (var pj in pjs) { foreach (var pjSlotWafer in pj.SlotWafers) { WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); wafer.ProcessJob = pj; WaferDataRecorder.SetCjInfo(wafer.InnerId.ToString(), cj.InnerId.ToString()); WaferDataRecorder.SetWaferSequence(wafer.InnerId.ToString(), pj.Sequence.Name); WaferDataRecorder.SetWaferLotId(wafer.InnerId.ToString(), jobId); WaferManager.Instance.UpdateWaferLotId(pjSlotWafer.Item1, pjSlotWafer.Item2, jobId); totalWafer++; } } _faCallback.JobCreated(cj, GetFirstProcessJob(cj)); if (_CycleData.IsCycleMode) { _CycleData.Start(); } CarrierInfo carrier = CarrierManager.Instance.GetCarrier(cj.Module); JobDataRecorder.StartCJ(cj.InnerId.ToString(), carrier.InnerId.ToString(), cj.Name, cj.Module, cj.Module, totalWafer); return true; } public bool CreateControlJob(string jobId, string module, List pjIDs, bool isAutoStart) { if (_lstControlJobs.Exists(x => x.Name == jobId)) { EV.PostWarningLog(LogSource, $"{jobId} is already created"); return false; } if (!ModuleHelper.IsLoadPort(ModuleHelper.Converter(module))) { EV.PostWarningLog(LogSource, $"{module} should be LoadPort"); return false; } LoadPort lp = DEVICE.GetDevice($"{module}"); if (!lp.IsIdle) { EV.PostWarningLog(LogSource, $"{module} is not idle"); return false; } if (!lp.IsMapped) { EV.PostWarningLog(LogSource, $"{module} is not mapped"); return false; } if (!lp.IsPresent) { EV.PostWarningLog(LogSource, $"{module} is not present"); return false; } foreach (var _lp in _lstLps) { if (_lp.Module.ToString() == module) { if (!_lp.IsOnline) { EV.PostWarningLog(LogSource, $"{module} is not online"); return false; } if (_lp.IsError) { EV.PostWarningLog(LogSource, $"{module} is error"); return false; } } } if (_lstProcessJobs.Count <= 0) //判断上一步ProcessJob是否创建成功 { EV.PostWarningLog(LogSource, $"process job is not exist"); return false; } ControlJobInfo cj = new ControlJobInfo(); cj.Name = jobId; cj.Module = module; cj.SetState(EnumControlJobState.WaitingForStart); cj.LotName = jobId; cj.LotWafers = new List(); int totalWafer = 0; foreach (var pjName in pjIDs) { var pj = _lstProcessJobs.FirstOrDefault(x => x.Name == pjName); if (pj == null) { LOG.Info($"not find {pjName} while create control job"); continue; } var slotWafers = new List>(); foreach (var slotWafer in pj.SlotWafers) { if (!WaferManager.Instance.CheckHasWafer(module, slotWafer.Item2)) { EV.PostWarningLog(LogSource, $"job wafer: {module} slot {slotWafer.Item2 + 1} not in the carrier"); return false; } if (!WaferManager.Instance.CheckWafer(ModuleHelper.Converter(module), slotWafer.Item2, WaferStatus.Normal)) { EV.PostWarningLog(LogSource, $"job wafer: {module} slot {slotWafer.Item2 + 1} status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), slotWafer.Item2).Status}"); return false; } if (WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), slotWafer.Item2).ProcessState != EnumWaferProcessStatus.Idle) { EV.PostWarningLog(LogSource, $"job wafer: {module} slot {slotWafer.Item2 + 1} process status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), slotWafer.Item2).ProcessState}"); return false; } slotWafers.Add(Tuple.Create(ModuleHelper.Converter(module), slotWafer.Item2)); totalWafer++; } pj.ControlJobName = jobId; cj.ProcessJobNameList.Add(pj.Name); pj.SlotWafers = slotWafers; foreach (var pjSlotWafer in pj.SlotWafers) { WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); cj.LotWafers.Add(wafer); WaferDataRecorder.SetCjInfo(wafer.InnerId.ToString(), cj.InnerId.ToString()); WaferDataRecorder.SetWaferSequence(wafer.InnerId.ToString(), pj.Sequence.Name); //WaferDataRecorder.SetWaferLotId(wafer.InnerId.ToString(), jobId); } } _lstControlJobs.Add(cj); CarrierInfo carrier = CarrierManager.Instance.GetCarrier(cj.Module); JobDataRecorder.StartCJ(cj.InnerId.ToString(), carrier.InnerId.ToString(), cj.Name, cj.Module, cj.Module, totalWafer); return true; } public bool CreateProcessJob(string jobId, string sequenceName, List slotNumbers, bool isAutoStart) { var sequenceInfo = SequenceInfoHelper.GetInfo(sequenceName); #region 模块检查 if (!CheckSequenceAlignerReady(sequenceInfo, out string reason)) { EV.PostWarningLog(LogSource, $"Aligner is not ready for the {reason}"); return false; } if (!CheckSequencePMReady(sequenceInfo, out reason)) { EV.PostWarningLog(LogSource, $"PM is not ready for the {reason}"); return false; } #endregion #region Sequence检查 if (!CheckSequenceAlignerOrder(sequenceInfo, out reason)) { EV.PostWarningLog(LogSource, $"Aligner order is not valid for the {reason}"); return false; } if (!CheckSequencePMOrder(sequenceInfo, out reason)) { EV.PostWarningLog(LogSource, $"PM order is not valid, {reason}"); return false; } #endregion var slotWafers = new List>(); foreach (var slot in slotNumbers) { slotWafers.Add(Tuple.Create(ModuleName.System, slot)); } ProcessJobInfo pj = new ProcessJobInfo(); pj.Name = jobId; pj.Sequence = sequenceInfo; pj.SlotWafers = slotWafers; pj.SetState(EnumProcessJobState.Queued); _lstProcessJobs.Add(pj); return true; } private bool CheckSequenceAlignerReady(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.IsAligner(module)) { if (!_aligner.IsOnline) { reason = $"Step {i + 1}, {module} is not online"; return false; } //if (!_aligner.IsIdle) //{ // reason = $"Step {i + 1}, {module} is not idle"; // return false; //} if (_aligner.IsError) { reason = $"Step {i + 1}, {module} is error"; return false; } } } } reason = string.Empty; return true; } private bool CheckSequenceAlignerOrder(SequenceInfo seq, out string reason) { int alignerCount = 0; int alignerIndex = -1; int llIndex = -1; int pmIndex = -1; for (int i = 0; i < seq.Steps.Count; i++) { SequenceStepInfo stepInfo = seq.Steps[i]; foreach (var module in stepInfo.StepModules) { if (ModuleHelper.IsAligner(module)) { alignerCount++; alignerIndex = i; if (i == seq.Steps.Count - 1) { reason = $"Step {i + 1}, {module} can not be the last step"; return false; } } if (ModuleHelper.IsLoadLock(module)) { if (llIndex == -1 || llIndex > i) llIndex = i; } if (ModuleHelper.IsPm(module)) { pmIndex = i; } } } if (alignerCount >= 2) { reason = $"Step {alignerIndex + 1}, Aligner step can appear once at most in sequence"; return false; } else if (alignerCount == 1) { if (llIndex != -1 && alignerIndex > llIndex) { reason = $"Step {alignerIndex + 1}, Aligner step should be in front of LoadLock step"; return false; } if (pmIndex != -1 && alignerIndex > pmIndex) { reason = $"Step {alignerIndex + 1}, Aligner step should be in front of PM step"; return false; } } reason = string.Empty; return true; } private bool CheckSequencePMReady(SequenceInfo seq, out string reason) { for (int i = 0; i < seq.Steps.Count; i++) { SequenceStepInfo stepInfo = seq.Steps[i]; if (stepInfo.StepModules.Count == 0) { reason = $"Step {i + 1}, no PM is selected"; return false; } foreach (var module in stepInfo.StepModules) { if (ModuleHelper.IsPm(module)) { foreach (var pm in _lstPms) { if (pm.Module == module) { if (!pm.IsOnline) { reason = $"Step {i + 1}, selected {module} is not online"; return false; } //if (!pm.IsIdle) //{ // reason = $"Step {i + 1}, selected {module} is not idle"; // return false; //} if (pm.IsError) { reason = $"Step {i + 1}, selected {module} is error"; return false; } #region Check Recipe File Valid var recipeContent = RecipeFileManager.Instance.LoadRecipe("", stepInfo.StepParameter[$"{module}Recipe"].ToString(), false); if (string.IsNullOrEmpty(recipeContent)) { reason = $"Step {i + 1}, can not find recipe file {stepInfo.RecipeName}"; return false; } #endregion } } } } } reason = ""; return true; } private bool CheckSequencePMOrder(SequenceInfo seq, out string reason) { int pmCount = 0; for (int i = 0; i < seq.Steps.Count; i++) { SequenceStepInfo stepInfo = seq.Steps[i]; foreach (var module in stepInfo.StepModules) { if (ModuleHelper.IsPm(module)) { pmCount++; break; } } } if (pmCount == 0) { reason = $"Sequence must contains PM step"; return false; } if (pmCount >= 2) { reason = $"PM step only can appear once in sequence"; return false; } reason = string.Empty; return true; } internal ProcessJobInfo GetFirstProcessJob(ControlJobInfo cj) { foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) return pj; } return null; } public bool 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 false; } foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) { pj.SetState(EnumProcessJobState.Stopping); } } _faCallback.JobStopped(cj, GetFirstProcessJob(cj)); _dbCallback.LotFinished(cj); return true; } public bool 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 false; } int unprocessed_cj = 0; int aborted_cj = 0; 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++; unprocessed_cj++; } } JobDataRecorder.EndPJ(pj.InnerId.ToString(), aborted, unprocessed); } } foreach (var pj in pjAbortList) { _lstProcessJobs.Remove(pj); } _lstControlJobs.Remove(cj); JobDataRecorder.EndCJ(cj.InnerId.ToString(), aborted_cj, unprocessed_cj); _faCallback.JobAborted(cj, GetFirstProcessJob(cj)); _dbCallback.LotFinished(cj); _temperatureSetpointDic.Clear(); _temperatureTrigDic.Clear(); return true; } public bool 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 false; } if (cj.State == EnumControlJobState.Paused) { cj.SetState(EnumControlJobState.Executing); } _faCallback.JobResumed(cj, GetFirstProcessJob(cj)); return true; } public bool 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 false; } if (cj.State == EnumControlJobState.WaitingForStart) { cj.SetState(EnumControlJobState.Executing); (GetModule(cj.Module) as SchedulerLoadPort).NoteJobStart(); //if (cj.IsCycleMode) //{ // _CycleData.BeginOrAddCycle(cj.CycleWaferCountSP); //} cj.BeginTime = DateTime.Now; cj.LotInnerId = Guid.NewGuid(); _dbCallback.LotCreated(cj); _faCallback.JobStarted(cj, GetFirstProcessJob(cj)); //foreach (var pj in _lstProcessJobs) //{ // if (pj.ControlJobName == cj.Name) // { // pj.SetState(EnumProcessJobState.Processing); // } //} } return true; } public bool 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 false; } if (cj.State == EnumControlJobState.Executing) { cj.SetState(EnumControlJobState.Paused); } _faCallback.JobPaused(cj, GetFirstProcessJob(cj)); return true; } public void Abort() { } public bool IsCJExisted(string CJID) { return _lstControlJobs.Exists(x => x.Name == CJID); } public void ModuleError(string moduleName) { GetModule(moduleName).ResetTask(); PauseJob(string.Empty); } 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); } } #endregion public Result Start(params object[] objs) { _ValueDict.Clear(); _CycleData.Reset(); var waferInfo = ""; bool hasPmOnline = false; foreach (var schedulerPm in _lstPms) { if (schedulerPm.IsAvailable) { hasPmOnline = true; if (WaferManager.Instance.CheckHasWafer(schedulerPm.Module, 0)) waferInfo += $"{(waferInfo == "" ? "" : ", ")}{schedulerPm.Module}"; } } bool hasTMOnline = false; if (_tmRobot.IsAvailable) hasTMOnline = true; if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0)) waferInfo += $"{(waferInfo == "" ? "" : ", ")}{ModuleName.TMRobot} primary arm"; if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1)) waferInfo += $"{(waferInfo == "" ? "" : ", ")}{ModuleName.TMRobot} secondly arm"; if (!hasPmOnline || !hasTMOnline) { var content = hasPmOnline ? "" : "both process chamber"; content += hasTMOnline ? "" : $"{(content == "" ? "" : ", ")}TM"; EV.PostWarningLog("Scheduler", $"can not change to auto mode, {content} is busy or offline"); return Result.FAIL; } if (waferInfo != "") { EV.PostWarningLog("Scheduler", $"can not change to auto mode, {waferInfo} wafer presence."); return Result.FAIL; } if (!_efem.IsOnline) { EV.PostWarningLog("Scheduler", $"can not change to auto mode, EFEM is offline"); return Result.FAIL; } return Result.RUN; } public Result Monitor() { for (int i = 0; i < 25; i++) { ProcessJobInfo pj = WaferManager.Instance.GetWafer(_lp1.Module, i).ProcessJob; if (pj == null) break; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == pj.ControlJobName); if (cj == null) break; if (_lp1.FirstDetectWaferArrive(i)) { _faCallback.JobWaferEnd(cj, pj, _lp1.Module.ToString(), i); } } for (int i = 0; i < 25; i++) { ProcessJobInfo pj = WaferManager.Instance.GetWafer(_lp2.Module, i).ProcessJob; if (pj == null) break; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == pj.ControlJobName); if (cj == null) break; if (_lp2.FirstDetectWaferArrive(i)) { _faCallback.JobWaferEnd(cj, pj, _lp2.Module.ToString(), i); } } MonitorJobTasks(); MonitorCleanTasks(); ControlJobInfo cjActive = _lstControlJobs.Find(x => x.State == EnumControlJobState.Executing); if (cjActive != null) { MonitorModuleTasks(); } return Result.RUN; } #region Job task public Result MonitorJobTasks() { UpdateParallelMode(); CalcingThroughput(); UpdateProcessJobStatus(); UpdateControlJobStatus(); //if (GetUnprocessedWaferCount() < SC.GetValue("System.Scheduler.WaferCountBelowWhichStartNewProcessJob")) { StartNewJob(); } return Result.RUN; } //only for pj in process protected int GetUnprocessedWaferCount() { int count = 0; foreach (ProcessJobInfo pj in _lstProcessJobs) { if (pj.State == EnumProcessJobState.Processing || pj.State == EnumProcessJobState.Paused) count += GetUnprocessedWaferCount(pj); } if (CheckWaferNeedProcess(WaferManager.Instance.GetWafer(ModuleName.EfemRobot, 0))) count++; if (CheckWaferNeedProcess(WaferManager.Instance.GetWafer(ModuleName.EfemRobot, 1))) count++; if (CheckWaferNeedProcess(WaferManager.Instance.GetWafer(ModuleName.Aligner, 0))) count++; 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; } protected bool CheckAllDummyWaferReturned() { foreach (var schedulerPm in _lstPms) { if (WaferManager.Instance.CheckWaferIsDummy(schedulerPm.Module, 0)) return false; } if (WaferManager.Instance.CheckWaferIsDummy(_tmRobot.Module, 0)) return false; if (WaferManager.Instance.CheckWaferIsDummy(_tmRobot.Module, 1)) return false; return true; } protected bool CheckAllPmCleaned(ProcessJobInfo pj) { foreach (var schedulerPm in _lstPms) { if (CheckNeedRunClean(schedulerPm.Module, out _, out _)) { foreach (var sequenceStepInfo in pj.Sequence.Steps) { if (sequenceStepInfo.StepModules.Contains(schedulerPm.Module)) return false; } } } return true; } private void UpdateProcessJobStatus() { foreach (var pj in _lstProcessJobs) { if (pj.State == EnumProcessJobState.Processing) { if (CheckAllWaferReturned(pj, true) && CheckAllPmCleaned(pj) && CheckAllDummyWaferReturned()) { pj.SetState(EnumProcessJobState.ProcessingComplete); JobDataRecorder.EndPJ(pj.InnerId.ToString(), 0, 0); } } else if (pj.State == EnumProcessJobState.Stopping) { if (CheckAllWaferReturned(pj, false) && CheckAllPmCleaned(pj) && CheckAllDummyWaferReturned()) { pj.SetState(EnumProcessJobState.ProcessingComplete); JobDataRecorder.EndPJ(pj.InnerId.ToString(), 0, 0); } } } } private void UpdateParallelMode() { bool enableParallel = SC.GetValue("System.Scheduler.IsRunInParallelMode"); int hasJobLpCount = 0; _isRunningInParallelMode = enableParallel; List pmUsed = new List(); foreach (var lp in _lstLps) { var cj = _lstControlJobs.Find(x => x.Module == lp.Module.ToString()); if (cj != null) { hasJobLpCount++; 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) && !pmUsed.Contains(module)) pmUsed.Add(module); } } } } } _isRunningInParallelMode = enableParallel && hasJobLpCount > 1 && pmUsed.Count > 1; } private void UpdateControlJobStatus() { if (_lstControlJobs.Count == 0) return; 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.FirstOrDefault(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) { if (!_CycleData.IsCycleMode || (_CycleData.IsCycleMode && _CycleData.IsCycleEnd())) CompleteJobClean(cj); _FinishCJ(cj); cj.EndTime = DateTime.Now; string strInfo = $"{cj.Module} has finished: \r\n\r\nProcessed wafer number: {cj.LotWafers.Count} \r\nStart time: {cj.BeginTime} \r\nEnd time: {cj.EndTime} \r\nDuration: {(cj.EndTime - cj.BeginTime).TotalSeconds:F0} sec"; EV.PostInfoLog("Scheduler", strInfo); if (SC.GetValue("System.Job.EnablePopDialogWhenJobDone")) { EV.PostPopDialogMessage(EventLevel.Information, "System Information", strInfo); } } } } if (_lstControlJobs.Any(cj => cj.State == EnumControlJobState.Completed) && _CycleData.IsCycleMode && !_CycleData.IsCycleEnd()) { var completeCJs = from cj in _lstControlJobs where cj.State == EnumControlJobState.Completed select cj; var otherCJs = from cj in _lstControlJobs where cj.State != EnumControlJobState.Completed select cj; _lstControlJobs = otherCJs.Concat(completeCJs).ToList(); _lstControlJobs.ForEach(cj => { if (cj.State == EnumControlJobState.Completed) { _CycleData.IncreaseCount(); if (_CycleData.IsCycleEnd()) return; cj.SetState(EnumControlJobState.Executing); cj.BeginTime = DateTime.Now; cj.LotWafers.ForEach(x => { x.NextSequenceStep = 0; x.ProcessState = EnumWaferProcessStatus.Idle; }); 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; } pj.SetState(EnumProcessJobState.Queued); } } }); } foreach (var cj in _lstControlJobs) { if (cj.State == EnumControlJobState.Completed && (!_CycleData.IsCycleMode || _CycleData.IsCycleMode && _CycleData.IsCycleEnd()) && (!_isAutoUnloadFoup || (_isAutoUnloadFoup && (GetModule(cj.Module.ToString()) as SchedulerLoadPort).IsUnloaded))) { (GetModule(cj.Module) as SchedulerLoadPort).NoteJobComplete(); cjRemoveList.Add(cj); } } foreach (var cj in cjRemoveList) { List pjRemoveList = _lstProcessJobs.Where(o => o.ControlJobName == cj.Name).ToList(); pjRemoveList.ForEach(o => _lstProcessJobs.Remove(o)); _lstControlJobs.Remove(cj); CalcedThroughput(pjRemoveList); } ControlJobInfo cjActived = null; foreach (var cj in _lstControlJobs) { if (cj.State == EnumControlJobState.Executing) { cjActived = cj; break; } } if (cjActived != null) { bool allPjResouceOk = true; string reason = string.Empty; 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.Complete && pj.State != EnumProcessJobState.ProcessingComplete) { if (!CheckSequencePmReady(pj.Sequence, null, out _, out reason)) { allPjResouceOk = false; break; } } } if (!allPjResouceOk) { cjActived.SetState(EnumControlJobState.Paused); EV.PostWarningLog(LogSource, $"{cjActived.Module} paused, {reason}"); } } } private void _FinishCJ(ControlJobInfo cj) { cj.SetState(EnumControlJobState.Completed); int unprocessed_cj = 0; int aborted_cj = 0; foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) { 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++; unprocessed_cj++; } } JobDataRecorder.EndPJ(pj.InnerId.ToString(), aborted, unprocessed); } } JobDataRecorder.EndCJ(cj.InnerId.ToString(), aborted_cj, unprocessed_cj); _faCallback.JobFinished(cj, GetFirstProcessJob(cj)); _dbCallback.LotFinished(cj); } //private bool _HandleFinishedCycleCJ(ControlJobInfo cj) //{ // //if (!cj.IsCycleMode) return false; // int countProcessed = 0; // foreach (var pjName in cj.ProcessJobNameList) // { // var pj = _lstProcessJobs.FirstOrDefault(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) // { // foreach (var pjSlotWafer in pj.SlotWafers) // { // WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); // if (!wafer.IsEmpty && !CheckWaferNeedProcess(pjSlotWafer.Item1, pjSlotWafer.Item2)) // countProcessed++; // } // } // } // _CycleData.Increase(countProcessed); // int leftCount = _CycleData.CycleWaferCountSetPoint - _CycleData.CycledWaferCount; // // finished cycle // if (leftCount <= 0) return false; // // 移除老的cj // var pjList = _lstProcessJobs.Where(o => o.ControlJobName == cj.Name).ToList(); // CalcedThroughput(pjList); // pjList.ForEach(o => _lstProcessJobs.Remove(o)); // _lstControlJobs.Remove(cj); // // 模拟 LP unload / load // { // ModuleName mName = ModuleHelper.Converter(cj.Module); // var wafers_old = WaferManager.Instance.GetWafers(mName).ToList(); // WaferManager.Instance.DeleteWafer(mName, 0, SC.GetValue($"EFEM.LoadPort.CassetteSlotNumber")); // CarrierManager.Instance.DeleteCarrier(cj.Module); // CarrierManager.Instance.CreateCarrier(cj.Module); // foreach (var wafer in wafers_old) // { // if (wafer.Status != WaferStatus.Normal) continue; // var newWafer = WaferManager.Instance.CreateWafer(mName, wafer.Slot, wafer.Status); // newWafer.PPID = wafer.PPID; // CarrierManager.Instance.RegisterCarrierWafer(cj.Module, wafer.Slot, newWafer); // } // } // // 产生新的cj // ControlJobInfo newCj = new ControlJobInfo(); // newCj.Name = cj.Name; // newCj.Module = cj.Module; // newCj.LotName = cj.LotName; // //newCj.IsCycleMode = cj.IsCycleMode; // //newCj.CycleWaferCountSP = cj.CycleWaferCountSP; // //newCj.CycledWaferCount = _CycleData.CycledWaferCount; // newCj.LotInnerId = Guid.NewGuid(); // newCj.LotWafers = cj.LotWafers.Take(leftCount).ToList(); // int addedCount = 0; // List newPjList = new List(); // //int toAddCount = Math.Min(leftCount, cj.LotWafers.Count); // for (int ii = 0; ii < cj.ProcessJobNameList.Count; ii++) // { // string pjName = cj.ProcessJobNameList[ii]; // var pj = pjList.FirstOrDefault(o => o.ControlJobName == cj.Name && o.Name == pjName); // if (pj == null) continue; // int toAddCount = Math.Min(leftCount - addedCount, cj.LotWafers.Count); // if (toAddCount <= 0) break; // addedCount += toAddCount; // ProcessJobInfo newPj = new ProcessJobInfo(); // newPj.Name = pj.Name; // newPj.Sequence = pj.Sequence; // newPj.ControlJobName = pj.ControlJobName; // newPj.SlotWafers = pj.SlotWafers.Take(toAddCount).ToList(); // newPj.SetState(EnumProcessJobState.Queued); // _lstProcessJobs.Add(newPj); // newCj.ProcessJobNameList.Add(pj.Name); // newPjList.Add(newPj); // } // _lstControlJobs.Add(newCj); // // 开始新的cj // newCj.SetState(EnumControlJobState.Executing); // newCj.LotInnerId = Guid.NewGuid(); // newCj.BeginTime = DateTime.Now; // _dbCallback.LotCreated(newCj); // newPjList.ForEach(o => ActiveProcessJob(o)); // return true; //} public bool CheckAllJobDone() { if (_lp1.IsAvailable) { _trigLp1JobComplete.CLK = CheckJobComplete(ModuleName.LP1.ToString()); if (_trigLp1JobComplete.Q) { if (_isAutoUnloadFoup) { _lp1.Unload(); } EV.Notify("CARRIER_PROCESS_COMPLETE", new SerializableDictionary() { { "PortID", 1}, { "PORT_ID", 1}, { "CarrierID", CarrierManager.Instance.GetCarrier(_lp1.Module.ToString()).CarrierId}, { "CAR_ID", CarrierManager.Instance.GetCarrier(_lp1.Module.ToString()).CarrierId} }); } } if (_lp2.IsAvailable) { _trigLp2JobComplete.CLK = CheckJobComplete(ModuleName.LP2.ToString()); if (_trigLp2JobComplete.Q) { if (_isAutoUnloadFoup) { _lp2.Unload(); } EV.Notify("CARRIER_PROCESS_COMPLETE", new SerializableDictionary() { { "PortID", 2}, { "PORT_ID", 2}, { "CarrierID", CarrierManager.Instance.GetCarrier(_lp2.Module.ToString()).CarrierId}, { "CAR_ID", CarrierManager.Instance.GetCarrier(_lp2.Module.ToString()).CarrierId} }); } } if (_lp3.IsAvailable) { _trigLp3JobComplete.CLK = CheckJobComplete(ModuleName.LP3.ToString()); if (_trigLp3JobComplete.Q) { if (_isAutoUnloadFoup) { _lp3.Unload(); } EV.Notify("CARRIER_PROCESS_COMPLETE", new SerializableDictionary() { { "PortID", 3 }, { "PORT_ID", 3 }, { "CarrierID", CarrierManager.Instance.GetCarrier(_lp3.Module.ToString()).CarrierId }, { "CAR_ID", CarrierManager.Instance.GetCarrier(_lp3.Module.ToString()).CarrierId } }); } } foreach (var cj in _lstControlJobs) { if (cj.State == EnumControlJobState.Executing || cj.State == EnumControlJobState.Paused) return false; } if (!_lp1.IsAvailable || !_lp2.IsAvailable || !_lp3.IsAvailable) return false; return true; } private bool CheckJobComplete(string lp = null) { if (_lstProcessJobs.Count == 0) return false; if (_lstControlJobs.Count == 0) return false; if (_CycleData.IsCycleMode && !_CycleData.IsCycleEnd()) return false; if (lp != null) { var cj = _lstControlJobs.Find(x => x.Module == lp); if (cj == null) return false; } foreach (var pj in _lstProcessJobs) { if (lp == null) { if (pj.State != EnumProcessJobState.ProcessingComplete) { return false; } } } foreach (var cj in _lstControlJobs) { if (lp == null) { if (cj.State != EnumControlJobState.Completed) { return false; } } else { if (cj.Module == lp && cj.State != EnumControlJobState.Completed) { return false; } } } return true; } private void StartNewJob() { ControlJobInfo cjActived = null; bool enableParallel = SC.GetValue("System.Scheduler.IsRunInParallelMode"); List pmOccupied = GetPmUsedInRunningPj(); var orderedLstCJs = _lstControlJobs.OrderBy(x => x.BeginTime); foreach (var cj in orderedLstCJs) { 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) { var pmIsReady = CheckSequencePmReady(pj.Sequence, pmOccupied, out var pmUsed, out string reason); if (pmUsed.Count > 0) { var needPreClean = pmUsed.Any(x => { return SC.GetValue($"{x.ToString()}.JobClean.IsEnabled") && SC.GetValue($"{x.ToString()}.JobClean.EnablePreJobClean"); }); if (needPreClean && !pmIsReady) break; ActiveProcessJob(pj); foreach (var moduleName in pmUsed) { if (!pmOccupied.Contains(moduleName)) pmOccupied.Add(moduleName); } break; } } } if (!_isRunningInParallelMode) break; } } 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 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 && !pm.IsError) // { // 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; //} 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; } PMModuleBase pm = EquipmentManager.Modules[module] as PMModuleBase; if (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 CheckSequenceRecipeFileValidTobedone(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)) { var recipeContent = RecipeFileManager.Instance.LoadRecipe($"{module}\\", stepInfo.RecipeName, false); if (string.IsNullOrEmpty(recipeContent)) { reason = $"Can not find recipe file {module}\\{stepInfo.RecipeName}"; return false; } string cleanRecipeName = ""; if (stepInfo.CleanInterval > 0 && stepInfo.StepParameter.ContainsKey("CleanRecipeNoWafer") && !string.IsNullOrEmpty((string)stepInfo.StepParameter["CleanRecipeNoWafer"])) { cleanRecipeName = (string)stepInfo.StepParameter["CleanRecipeNoWafer"]; if (!string.IsNullOrEmpty(cleanRecipeName)) { recipeContent = RecipeFileManager.Instance.LoadRecipe($"{module}\\", cleanRecipeName, false); if (string.IsNullOrEmpty(recipeContent)) { reason = $"Can not find recipe file {module}\\{cleanRecipeName}"; return false; } } } if (stepInfo.CleanInterval > 0 && stepInfo.StepParameter.ContainsKey("CleanRecipeWafer") && !string.IsNullOrEmpty((string)stepInfo.StepParameter["CleanRecipeWafer"])) { cleanRecipeName = (string)stepInfo.StepParameter["CleanRecipeWafer"]; if (!string.IsNullOrEmpty(cleanRecipeName)) { recipeContent = RecipeFileManager.Instance.LoadRecipe($"{module}\\", cleanRecipeName, false); if (string.IsNullOrEmpty(recipeContent)) { reason = $"Can not find recipe file {module}\\{cleanRecipeName}"; 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)) { var recipeContent = RecipeFileManager.Instance.LoadRecipe("", stepInfo.StepParameter[$"{module}Recipe"].ToString(), false); if (string.IsNullOrEmpty(recipeContent)) { reason = $"Can not find recipe file {stepInfo.StepParameter[$"{module}Recipe"].ToString()}"; return false; } } } } reason = ""; return true; } private bool CheckSequenceOrderOk(SequenceInfo seq, out string reason) { reason = ""; bool foundPm = false; bool isLLBeforePm = false; bool isLLAfterPm = false; bool isAlignerAfterLL = 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 (!foundPm && ModuleHelper.IsLoadLock(module)) { isLLBeforePm = true; } if (foundPm && ModuleHelper.IsLoadLock(module)) { isLLAfterPm = true; } if (ModuleHelper.IsAligner(module) && (isLLBeforePm || foundPm)) { isAlignerAfterLL = true; } } } if (!isLLBeforePm) { reason = $"not found LL before PM;"; return false; } if (!isLLAfterPm) { reason = $"not found LL after PM;"; return false; } if (!foundPm) { reason = $"not found PM in the sequence file;"; return false; } if (isAlignerAfterLL) { reason = "Aligner after LL not support"; return false; } return true; } private bool CheckSequenceNeedDummyWafer(SequenceInfo seq) { bool needDummyWafer = 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)) { if (stepInfo.CleanInterval > 0 && stepInfo.StepParameter.ContainsKey("CleanRecipeWafer") && !string.IsNullOrEmpty((string)stepInfo.StepParameter["CleanRecipeWafer"])) { needDummyWafer = true; break; } } } if (needDummyWafer) break; } return needDummyWafer; } private bool ActiveProcessJob(ProcessJobInfo pj) { //pm is ok if (!CheckSequencePmReady(pj.Sequence, null, out _, out string reason)) { EV.PostWarningLog(LogSource, $"can not active {pj.Name}, {reason}"); return false; } _lstPmsCurrentSequence.Clear(); 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)) { var pm = _lstPms.Find(x => x.Module == module); if (pm != null) _lstPmsCurrentSequence.Add(pm); } } } 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; } #endregion #region Module task public Result MonitorModuleTasks() { MonitorPMTask(); MonitorEfemRobotTask(); for (int i = 0; i < tmRobotActions.Count; i++) { MonitorTmRobotTask(); } //MonitorLoadLockTask(); MonitorInterlockTask(); MonitorAlignerTask(); return Result.RUN; } private void MonitorInterlockTask() { foreach (var cj in _lstControlJobs) { if (cj.State == EnumControlJobState.Executing || cj.State == EnumControlJobState.Paused || cj.State == EnumControlJobState.WaitingForStart) { var lp = GetModule(cj.Module); if (CarrierManager.Instance.CheckNoCarrier(lp.Module, 0)) { EV.PostAlarmLog("System", "Cassette removed while job not finished."); //Singleton.Instance.PostMsg(RouteManager.MSG.ERROR); } } } } private void MonitorAlignerTask() { if (!_aligner.IsAvailable) return; if (CheckWaferNextStepIsAlign(_aligner.Module, 0)) { WaferInfo wafer = WaferManager.Instance.GetWafer(_aligner.Module, 0); GetWaferSequenceAlignAngle(_aligner.Module, 0, out double angle); if (_aligner.Align(angle)) { wafer.NextSequenceStep++; } WaferDataRecorder.SetWaferNotchAngle(wafer.InnerId.ToString(), (float)angle); } } //private void MonitorBufferTask() //{ // foreach (var buffer in _lstBuffers) // { // if (!buffer.IsAvailable) // continue; // } //} private void MonitorLoadLockTask() { // bool isPrepareTransferForEfemobotPlace = GetWaferCountInJobQueue() > 0; // foreach (var ll in _lstLls) // { // if (ll.Entity.IsPrepareTransfer) // { // return; // } // var deviceLL = DEVICE.GetDevice(ll.Module.ToString()); // //if (ll.CheckAtAtm() && (ll.Entity.IsCooling || deviceLL.CheckDoorOpen())) // if (deviceLL.CheckDoorOpen()) // { // var wafers = WaferManager.Instance.GetWafers(ll.Module).ToList(); // int emptySlot = 0; // foreach (var wafer in wafers) // { // if (wafer.IsEmpty || wafer.ProcessState != EnumWaferProcessStatus.Idle) // emptySlot++; // } // if (emptySlot >= GetWaferCountInJobQueue()) // { // isPrepareTransferForEfemobotPlace = false; // break; // } // } // } // foreach (var ll in _lstLls) // { // if (!ll.IsAvailable) // continue; // var deviceLL = DEVICE.GetDevice(ll.Module.ToString()); // var wafers = WaferManager.Instance.GetWafers(ll.Module).ToList(); // var isEmptyLL = true; // bool isPrepareTransferForEfemobotPick = true; // for (int i = 0; i <= 5;) // { // var wafer0 = WaferManager.Instance.GetWafer(ll.Module, i); // var wafer1 = WaferManager.Instance.GetWafer(ll.Module, i + 1); // if (!wafer0.IsEmpty || !wafer1.IsEmpty) // isEmptyLL = false; // if (wafer0.IsEmpty && wafer1.IsEmpty && GetVacuumWafer() > 0) // isPrepareTransferForEfemobotPick = false; // if ((!wafer0.IsEmpty && wafer0.ProcessState == EnumWaferProcessStatus.Idle) || // (!wafer1.IsEmpty && wafer1.ProcessState == EnumWaferProcessStatus.Idle)) // isPrepareTransferForEfemobotPick = false; // i += 2; // } // if (isEmptyLL) // isPrepareTransferForEfemobotPick = false; // if (isPrepareTransferForEfemobotPlace && isEmptyLL && (_tmDevice.CheckSlitValveClose(ll.Module) || GetVacuumWafer() == 0) && // !ll.Entity.IsPrepareTransferReady(ModuleName.EfemRobot, EnumTransferType.Place)) // { // ll.PrepareTransfer(ModuleName.EfemRobot, EnumTransferType.Place, 0); // return; // } // if (isPrepareTransferForEfemobotPick) // { // if (!ll.Entity.IsPrepareTransferReady(ModuleName.EfemRobot, EnumTransferType.Pick)) // { // if (!_loadlockCooling.ContainsKey(ll.Module)) // _loadlockCooling.Add(ll.Module, false); // _loadlockCooling[ll.Module] = false; // ll.PrepareTransfer(ModuleName.EfemRobot, EnumTransferType.Pick, 0); // return; // } // //TODO, cooling 要在 Open ATM door 之前做,PrepareTransfer 需要分开 [2021/7/3 TerryLu] // if (ll.Entity.IsPreCoolingDone()) // { // foreach (var wafer in wafers) // { // if (wafer != null && !wafer.IsEmpty && GetWaferSequenceLoadLockCoolingTime(ll.Module, wafer.Slot, out int coolingTime)) // { // _loadlockCooling[ll.Module] = true; // ll.Cooling(coolingTime); // break; // } // } // if (_loadlockCooling[ll.Module]) // { // foreach (var wafer in wafers) // { // if (wafer != null && !wafer.IsEmpty) // { // wafer.NextSequenceStep++; // } // } // } // } // } // bool isPrepareTransferForTMRobot = true; // bool isAllEmpty = true; // foreach (var wafer in wafers) // { // if (!wafer.IsEmpty) // isAllEmpty = false; // if (wafer.IsEmpty && GetATMWafer() > 0) // { // isPrepareTransferForTMRobot = false; // break; // } // if (wafer.ProcessState != EnumWaferProcessStatus.Idle) // { // isPrepareTransferForTMRobot = false; // break; // } // } // if (isAllEmpty) // { // if (GetVacuumWafer() == 0) // { // isPrepareTransferForTMRobot = false; // } // else // { // foreach (var anotherLL in _lstLls) // { // if (!anotherLL.IsOnline || anotherLL == ll) // continue; // var anotherLLWafers = WaferManager.Instance.GetWafers(anotherLL.Module).ToList(); // int count = 0; // for (int i = 0; i <= 5;) // { // if (WaferManager.Instance.CheckNoWafer(anotherLL.Module, i) && WaferManager.Instance.CheckNoWafer(anotherLL.Module, i + 1)) // { // count++; // } // i += 2; // } // if (count >= GetVacuumWafer()) // { // isPrepareTransferForTMRobot = false; // break; // } // } // } // } // if (isPrepareTransferForTMRobot && !ll.Entity.IsPrepareTransferReady(ModuleName.TMRobot, EnumTransferType.Pick)) // { // ll.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Pick, 0); // continue; // } // } } private void MonitorPMTask() { foreach (var pm in _lstPms) { if (!pm.IsAvailable) continue; if (WaferManager.Instance.CheckHasWafer(pm.Module, 0) || WaferManager.Instance.CheckHasWafer(pm.Module, 1)) { if (CheckNeedRunClean(pm.Module, out bool withWafer, out string recipe) && withWafer && WaferManager.Instance.GetWafer(pm.Module, 0).Status == WaferStatus.Dummy && WaferManager.Instance.GetWafer(pm.Module, 0).ProcessState == EnumWaferProcessStatus.Wait && WaferManager.Instance.GetWafer(pm.Module, 1).Status == WaferStatus.Dummy && WaferManager.Instance.GetWafer(pm.Module, 1).ProcessState == EnumWaferProcessStatus.Wait) { pm.Process(recipe, true, withWafer); continue; } var wafer0 = WaferManager.Instance.GetWafer(pm.Module, 0); var wafer1 = WaferManager.Instance.GetWafer(pm.Module, 1); if (CheckCanProcess(pm.Module)) { WaferInfo wafer = wafer0.Status != WaferStatus.Empty ? wafer0 : wafer1; if (pm.Process(wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepParameter[$"{pm.Module}Recipe"].ToString(), false, true)) { if (wafer0.Status != WaferStatus.Empty) wafer0.NextSequenceStep++; if (wafer1.Status != WaferStatus.Empty) wafer1.NextSequenceStep++; continue; } } } else { if (CheckNeedRunClean(pm.Module, out bool withWafer, out string recipe) && !withWafer) { pm.Process(recipe, true, withWafer); continue; } if (WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 0) && WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 1) ) { var _moduleName = ModuleName.LP1; if (LPHadCreateJob != "LP1") { _moduleName = ModuleName.LP2; } SlotItem item = GetNextWaferInJobQueue(_moduleName); if (item != null && CheckWaferNeedProcessPre(item.Module, item.Slot, pm.Module)) { if (GetWaferTemperatureSetInRecipe(item.Module, item.Slot, pm.Module, out double temp1, out double temp2) && !pm.CheckTempReady(temp1, temp2)) { if (temp1 != 0 && temp2 != 0) { pm.Preheating(temp1, temp2); continue; } } } } } //if (!pm.IsAvailable) // continue; //var wafers = FindAllLoadLockSlotForTMRobotPick(); //if (wafers.Count > 0) //{ // if (wafers.Any(x => CheckWaferNeedProcess(x.Item1.Module, x.Item2, pm.Module))) // { // for (int i = 0; i < WaferManager.Instance.GetWafers(pm.Module).Length; i++) // { // if (WaferManager.Instance.CheckNoWafer(pm.Module, i) && !pm.IsReadyForPlace(ModuleName.TMRobot, Hand.Blade1, i)) // { // pm.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, i); // return; // } // } // } //} //else //{ // if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) // && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1) // && WaferManager.Instance.CheckNoWafer(pm.Module, 0) // && WaferManager.Instance.CheckNoWafer(pm.Module, 1) // && _tmRobot.IsAvailable // && !pm.CheckSlitValveClose()) // { // pm.CloseSlitValve(); // return; // } //} } } private void MonitorTmRobotTask() { if (!_tmRobot.IsAvailable) return; //TM robot is idle, release all the target foreach (var ll in _lstLls) { if (ll.IsWaitTransfer(ModuleName.TMRobot)) ll.StopWaitTransfer(ModuleName.TMRobot); } foreach (var pm in _lstPms) { if (pm.IsWaitTransfer(ModuleName.TMRobot)) pm.StopWaitTransfer(ModuleName.TMRobot); } //MonitorTmRobotDummyWaferBufferTask(); //if (!_tmRobot.IsAvailable) // return; if (!_tmRobot.IsAvailable) return; var act = tmRobotActions.Peek(); act.Invoke(); if (!_tmRobot.IsAvailable) return; if (act == MonitorTmRobotLoadLockPickTask) { List> availableLoadLockSlots = FindAllLoadLockSlotForTMRobotPick(); if (availableLoadLockSlots.Count > 0) { Tuple firstSlot = GetWaferOrderInJobQueue(availableLoadLockSlots); if (firstSlot != null) { SchedulerLoadLock ll = firstSlot.Item1; int pickSlot = firstSlot.Item2; if (!ll.IsAvailable) return; } } } if (act == MonitorTmRobotLoadLockPlaceTask && _lstLls.All(x => !x.IsAvailable)) return; tmRobotActions.Enqueue(tmRobotActions.Dequeue()); //MonitorTmRobotLoadLockPickTask(); //if (!_tmRobot.IsAvailable) // return; //MonitorTmRobotPMPickTask(); //if (!_tmRobot.IsAvailable) // return; //MonitorTmRobotLoadLockPlaceTask(); //if (!_tmRobot.IsAvailable) // return; //MonitorTmRobotPMPlaceTask(); //if (!_tmRobot.IsAvailable) // return; //MonitorTmRobotGoToTask(); } private void MonitorTmRobotLoadLockPlaceTask() { if (!_tmRobot.IsAvailable) return; //place to ll bool blade0HasWaferAndProcessed = _tmRobot.Blade1Enable && WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) && CheckWaferNextStepIsLoadLock(ModuleName.TMRobot, 0); bool blade1HasWaferAndProcessed = _tmRobot.Blade2Enable && WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1) && CheckWaferNextStepIsLoadLock(ModuleName.TMRobot, 1); if (blade0HasWaferAndProcessed || blade1HasWaferAndProcessed) { Hand placeBlade = blade0HasWaferAndProcessed ? Hand.Blade1 : Hand.Blade2; foreach (var ll in _lstLls) { if (!ll.IsAvailable) continue; if (!CheckWaferNextStepIsLoadLock(ModuleName.TMRobot, (int)placeBlade, ll.Module)) continue; int? placeSlot = GetAvilableLoadLockSlotForTMRobotPlace(ll); if (placeSlot != null) { if (_tmRobot.Place(ll.Module, (int)placeSlot, placeBlade, 0, 0, false)) { WaferManager.Instance.GetWafer(ModuleName.TMRobot, (int)placeBlade).NextSequenceStep++; ll.WaitTransfer(ModuleName.TMRobot); return; } } } } if (GetEmptySlotInPM() < 1) return; } private void MonitorTmRobotLoadLockPickTask() { if (!_tmRobot.IsAvailable) return; if (!CanTmRobotLoadLockPick()) return; //pick from ll if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) || WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1)) { Hand pickBlade; if (_tmRobot.Blade1Enable && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0)) { pickBlade = Hand.Blade1; } else if (_tmRobot.Blade2Enable && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1)) { pickBlade = Hand.Blade2; } else { return; } //if (_lstLls.All(x => !x.IsAvailable)) // return; List> availableLoadLockSlots = FindAllLoadLockSlotForTMRobotPick(); if (availableLoadLockSlots.Count > 0) { Tuple firstSlot = GetWaferOrderInJobQueue(availableLoadLockSlots); if (firstSlot != null) { SchedulerLoadLock ll = firstSlot.Item1; int pickSlot = firstSlot.Item2; if (!ll.IsAvailable) return; if (_tmRobot.Pick(ll.Module, (int)pickSlot, pickBlade, 0, 0, false)) { ll.WaitTransfer(ModuleName.TMRobot); return; } } } } } private R_TRIG tmRobotGotoLoadLockTrig = new R_TRIG(); private R_TRIG tmRobotGotoPMTrig = new R_TRIG(); SchedulerPM gotoPm = null; private void MonitorTmRobotGoToTask() { if (!_tmRobot.IsAvailable) return; //int gotoSlot = Int32.MaxValue; //int gotoModule = -1; //int waferOriginSlot = int.MaxValue; if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1)) { //PMA、PMB都为空,ll中有需要跑的wafer时,准备tmRobot到ll var llWafers = FindAllLoadLockSlotForTMRobotPick(); tmRobotGotoLoadLockTrig.CLK = llWafers.Count > 0 && !CanTmRobotLoadLockPick() && GetEmptySlotInPMA() == 2 && GetEmptySlotInPMB() == 2; if (tmRobotGotoLoadLockTrig.Q) { if (_tmRobot.Goto(llWafers[0].Item1.Module, llWafers[0].Item2, Hand.Blade1)) { llWafers[0].Item1.WaitTransfer(ModuleName.TMRobot); return; } } //PMA、PMB都在工艺过程中,tmRobot移动到先开始工艺的腔体位置 bool PMAInProcess = _pm1.CheckInProcess(); bool PMBInProcess = _pm2.CheckInProcess(); if (PMAInProcess ^ PMBInProcess) { gotoPm = PMAInProcess ? _pm1 : _pm2; } if (!PMAInProcess && !PMBInProcess) { gotoPm = null; } tmRobotGotoPMTrig.CLK = PMAInProcess && PMBInProcess && gotoPm != null; if (tmRobotGotoPMTrig.Q) { if (_tmRobot.Goto(gotoPm.Module, 0, Hand.Blade1)) { gotoPm.WaitTransfer(ModuleName.TMRobot); return; } } } } private bool CanTmRobotLoadLockPick() { if (FindAllLoadLockSlotForTMRobotPick().Count <= 1 && GetUnprocessedWaferCount() >= 1 && WaferManager.Instance.GetWafers(ModuleName.TMRobot).All(x => x.IsEmpty)) //&& (CheckWaferNeedProcess(ModuleName.EfemRobot, 0) || CheckWaferNeedProcess(ModuleName.EfemRobot, 1) || CheckWaferNeedProcess(ModuleName.Aligner, 0))) return false; var tmWafers = WaferManager.Instance.GetWafers(ModuleName.TMRobot); for (int i = 0; i < tmWafers.Length; i++) { if (!tmWafers[i].IsEmpty && !CheckWaferNeedProcess(tmWafers[i])) return false; } foreach (var pm in _lstPms) { var pmWafer0 = WaferManager.Instance.GetWafer(pm.Module, 0); var pmWafer1 = WaferManager.Instance.GetWafer(pm.Module, 1); //如果腔体中两片都满了,则跳过 if (!pmWafer0.IsEmpty && !pmWafer1.IsEmpty) continue; //如果腔体中一片为空,则取一片相同recipeName的放入 if (!pmWafer0.IsEmpty || !pmWafer1.IsEmpty) { var wafer = !pmWafer0.IsEmpty ? pmWafer0 : pmWafer1; if (!CheckWaferNeedProcess(wafer)) continue; //判断TMRobot是否已经存在相同recipeName的wafer for (int i = 0; i < tmWafers.Length; i++) { if (CheckWaferNeedProcess(tmWafers[i]) && tmWafers[i].ProcessJob.Sequence.Name == wafer.ProcessJob.Sequence.Name) return false; } } } return true; } //private void MonitorTmRobotDummyWaferBufferTask() //{ // if (!_tmRobot.IsAvailable) // return; // //place to buffer // bool blade0HasWaferAndProcessedDummy = WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) && // WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0).Status == WaferStatus.Dummy && // WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0).ProcessState == EnumWaferProcessStatus.Completed; // bool blade1HasWaferAndProcessedDummy = WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1) && // WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1).Status == WaferStatus.Dummy && // WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1).ProcessState == EnumWaferProcessStatus.Completed; // if (blade0HasWaferAndProcessedDummy || blade1HasWaferAndProcessedDummy) // { // Hand placeBlade = blade0HasWaferAndProcessedDummy ? Hand.Blade1 : Hand.Blade2; // if (blade0HasWaferAndProcessedDummy && blade1HasWaferAndProcessedDummy) // { // if (WaferManager.Instance.GetWafer(ModuleName.TMRobot, 1).OriginSlot < // WaferManager.Instance.GetWafer(ModuleName.TMRobot, 0).OriginSlot) // { // placeBlade = Hand.Blade2; // } // } // int placeSlot = WaferManager.Instance.GetWafer(ModuleName.TMRobot, (int)placeBlade).OriginSlot; // if (WaferManager.Instance.CheckNoWafer(_pm1.Module, placeSlot) && _pm1.IsReadyForPlace(ModuleName.TMRobot, placeBlade, placeSlot)) // { // if (_tmRobot.Place(_pm1.Module, placeSlot, placeBlade)) // { // _pm1.WaitTransfer(ModuleName.TMRobot); // return; // } // } // } // if (!_tmRobot.IsAvailable) // return; // //pick from buffer // foreach (var schedulerPm in _lstPms) // { // if (!CheckNeedRunClean(schedulerPm.Module, out bool withWafer, out string _) || !withWafer) // continue; // if (WaferManager.Instance.CheckHasWafer(schedulerPm.Module, 0)) // continue; // if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) || // WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1)) // { // Hand pickBlade = WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) // ? Hand.Blade1 // : Hand.Blade2; // for (int i = 0; i < 2; i++) // { // if ((WaferManager.Instance.GetWafer(_pm1.Module, i).ProcessState == EnumWaferProcessStatus.Wait) // && _pm1.IsReadyForPick(ModuleName.TMRobot, pickBlade, i)) // { // if (_tmRobot.Pick(_pm1.Module, i, pickBlade)) // { // _pm1.WaitTransfer(ModuleName.TMRobot); // return; // } // } // } // } // } //} private void MonitorTmRobotPMAPlaceTask() { //if (!_pm1.IsAvailable) // return; //if (_tmRobot.HasWafer(0) && CheckWaferNeedProcess(ModuleName.TMRobot, 0, _pm1.Module)) //{ // if (WaferManager.Instance.CheckNoWafer(_pm1.Module, 0) && !_pm1.IsReadyForPlace(ModuleName.TMRobot, Hand.Blade1, 0)) // { // _pm1.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 0); // return; // } // if (WaferManager.Instance.CheckNoWafer(_pm1.Module, 1) && !_pm1.IsReadyForPlace(ModuleName.TMRobot, Hand.Blade1, 1)) // { // _pm1.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 1); // return; // } //} //if (_tmRobot.HasWafer(1) && CheckWaferNeedProcess(ModuleName.TMRobot, 1, _pm1.Module)) //{ // if (WaferManager.Instance.CheckNoWafer(_pm1.Module, 0) && !_pm1.IsReadyForPlace(ModuleName.TMRobot, Hand.Blade1, 0)) // { // _pm1.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 0); // return; // } // if (WaferManager.Instance.CheckNoWafer(_pm1.Module, 1) && !_pm1.IsReadyForPlace(ModuleName.TMRobot, Hand.Blade1, 1)) // { // _pm1.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 1); // return; // } //} if (!_tmRobot.IsAvailable) return; //place bool blade0HasWaferAndNeedProcesse = _tmRobot.Blade1Enable && WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) && CheckWaferNeedProcess(ModuleName.TMRobot, 0); bool blade1HasWaferAndNeedProcesse = _tmRobot.Blade2Enable && WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1) && CheckWaferNeedProcess(ModuleName.TMRobot, 1); if (blade0HasWaferAndNeedProcesse || blade1HasWaferAndNeedProcesse) { Hand placeBlade = blade0HasWaferAndNeedProcesse ? Hand.Blade1 : Hand.Blade2; var wafer = WaferManager.Instance.GetWafer(ModuleName.TMRobot, (int)placeBlade); //foreach (var schedulerPm in _lstPms) { var schedulerPm = _pm1; if (!schedulerPm.IsAvailable) return; var pmWafer0 = WaferManager.Instance.GetWafer(schedulerPm.Module, 0); var pmWafer1 = WaferManager.Instance.GetWafer(schedulerPm.Module, 1); //如果腔体中两片都满了,则不允许放入 if (!pmWafer0.IsEmpty && !pmWafer1.IsEmpty) return; //如果腔体中两片都为空,则取一片放入 if (pmWafer0.IsEmpty && pmWafer1.IsEmpty) { if (WaferManager.Instance.CheckNoWafer(schedulerPm.Module, 0) && CheckCanPlaceWaferToPM(schedulerPm.Module, wafer)) { if (GetWaferTemperatureSetInRecipe(ModuleName.TMRobot, (int)placeBlade, ModuleName.PMA, out double temp1, out double temp2)) { if (_tmRobot.Place(schedulerPm.Module, 0, placeBlade, temp1, temp2, true)) { schedulerPm.WaitTransfer(ModuleName.TMRobot); return; } } } } //如果腔体中一片为空,则取一片相同recipeName的放入 if (!pmWafer0.IsEmpty || !pmWafer1.IsEmpty) { var pmWafer = !pmWafer0.IsEmpty ? pmWafer0 : pmWafer1; if (!CheckWaferNeedProcess(pmWafer)) return; int placeSlot = pmWafer0.IsEmpty ? 0 : 1; if (WaferManager.Instance.CheckNoWafer(schedulerPm.Module, placeSlot) && CheckCanPlaceWaferToPM(schedulerPm.Module, wafer)) { if (GetWaferTemperatureSetInRecipe(ModuleName.TMRobot, (int)placeBlade, ModuleName.PMA, out double temp1, out double temp2)) { if (_tmRobot.Place(schedulerPm.Module, placeSlot, placeBlade, temp1, temp2, true)) { schedulerPm.WaitTransfer(ModuleName.TMRobot); return; } } } } } } } private void MonitorTmRobotPMBPlaceTask() { //if (!_pm2.IsAvailable) // return; //if (_tmRobot.HasWafer(0) && CheckWaferNeedProcess(ModuleName.TMRobot, 0, _pm2.Module)) //{ // if (WaferManager.Instance.CheckNoWafer(_pm2.Module, 0) && !_pm2.IsReadyForPlace(ModuleName.TMRobot, Hand.Blade1, 0)) // { // _pm2.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 0); // return; // } // if (WaferManager.Instance.CheckNoWafer(_pm2.Module, 1) && !_pm2.IsReadyForPlace(ModuleName.TMRobot, Hand.Blade1, 1)) // { // _pm2.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 1); // return; // } //} //if (_tmRobot.HasWafer(1) && CheckWaferNeedProcess(ModuleName.TMRobot, 1, _pm2.Module)) //{ // if (WaferManager.Instance.CheckNoWafer(_pm2.Module, 0) && !_pm2.IsReadyForPlace(ModuleName.TMRobot, Hand.Blade1, 0)) // { // _pm2.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 0); // return; // } // if (WaferManager.Instance.CheckNoWafer(_pm2.Module, 1) && !_pm2.IsReadyForPlace(ModuleName.TMRobot, Hand.Blade1, 1)) // { // _pm2.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 1); // return; // } //} if (!_tmRobot.IsAvailable) return; //place bool blade0HasWaferAndNeedProcesse = _tmRobot.Blade1Enable && WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) && CheckWaferNeedProcess(ModuleName.TMRobot, 0); bool blade1HasWaferAndNeedProcesse = _tmRobot.Blade2Enable && WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1) && CheckWaferNeedProcess(ModuleName.TMRobot, 1); if (blade0HasWaferAndNeedProcesse || blade1HasWaferAndNeedProcesse) { Hand placeBlade = blade0HasWaferAndNeedProcesse ? Hand.Blade1 : Hand.Blade2; var wafer = WaferManager.Instance.GetWafer(ModuleName.TMRobot, (int)placeBlade); //foreach (var schedulerPm in _lstPms) { var schedulerPm = _pm2; if (!schedulerPm.IsAvailable) return; var pmWafer0 = WaferManager.Instance.GetWafer(schedulerPm.Module, 0); var pmWafer1 = WaferManager.Instance.GetWafer(schedulerPm.Module, 1); //如果腔体中两片都满了,则不允许放入 if (!pmWafer0.IsEmpty && !pmWafer1.IsEmpty) return; //如果腔体中两片都为空,则取一片放入 if (pmWafer0.IsEmpty && pmWafer1.IsEmpty) { if (WaferManager.Instance.CheckNoWafer(schedulerPm.Module, 0) && CheckCanPlaceWaferToPM(schedulerPm.Module, wafer)) { if (GetWaferTemperatureSetInRecipe(ModuleName.TMRobot, (int)placeBlade, ModuleName.PMB, out double temp1, out double temp2)) { if (_tmRobot.Place(schedulerPm.Module, 0, placeBlade, temp1, temp2, true)) { schedulerPm.WaitTransfer(ModuleName.TMRobot); return; } } } } //如果腔体中一片为空,则取一片相同recipeName的放入 if (!pmWafer0.IsEmpty || !pmWafer1.IsEmpty) { var pmWafer = !pmWafer0.IsEmpty ? pmWafer0 : pmWafer1; if (!CheckWaferNeedProcess(pmWafer)) return; int placeSlot = pmWafer0.IsEmpty ? 0 : 1; if (WaferManager.Instance.CheckNoWafer(schedulerPm.Module, placeSlot) && CheckCanPlaceWaferToPM(schedulerPm.Module, wafer)) { if (GetWaferTemperatureSetInRecipe(ModuleName.TMRobot, (int)placeBlade, ModuleName.PMB, out double temp1, out double temp2)) { if (_tmRobot.Place(schedulerPm.Module, placeSlot, placeBlade, temp1, temp2, true)) { schedulerPm.WaitTransfer(ModuleName.TMRobot); return; } } } } } } } private void MonitorTmRobotPMAPickTask() { if (!_tmRobot.IsAvailable) return; // pick if ((WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) || WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1))) { Hand pickBlade; if (_tmRobot.Blade1Enable && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0)) { pickBlade = Hand.Blade1; } else if (_tmRobot.Blade2Enable && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1)) { pickBlade = Hand.Blade2; } else { return; } //foreach (var schedulerPm in _lstPms) { var schedulerPm = _pm1; if (!schedulerPm.IsAvailable) return; if (CheckCanPickWaferFromPM(schedulerPm.Module, 0)) { if (GetWaferTemperatureSetInRecipe(schedulerPm.Module, 0, ModuleName.PMA, out double temp1, out double temp2)) { if (_tmRobot.Pick(schedulerPm.Module, 0, pickBlade, temp1, temp2, true)) { schedulerPm.WaitTransfer(ModuleName.TMRobot); return; } } } if (CheckCanPickWaferFromPM(schedulerPm.Module, 1)) { if (GetWaferTemperatureSetInRecipe(schedulerPm.Module, 1, ModuleName.PMA, out double temp1, out double temp2)) { if (_tmRobot.Pick(schedulerPm.Module, 1, pickBlade, temp1, temp2, true)) { schedulerPm.WaitTransfer(ModuleName.TMRobot); return; } } } } } } private void MonitorTmRobotPMBPickTask() { if (!_tmRobot.IsAvailable) return; // pick if ((WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) || WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1))) { Hand pickBlade; if (_tmRobot.Blade1Enable && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0)) { pickBlade = Hand.Blade1; } else if (_tmRobot.Blade2Enable && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1)) { pickBlade = Hand.Blade2; } else { return; } //foreach (var schedulerPm in _lstPms) { var schedulerPm = _pm2; if (!schedulerPm.IsAvailable) return; if (CheckCanPickWaferFromPM(schedulerPm.Module, 0)) { if (GetWaferTemperatureSetInRecipe(schedulerPm.Module, 0, ModuleName.PMB, out double temp1, out double temp2)) { if (_tmRobot.Pick(schedulerPm.Module, 0, pickBlade, temp1, temp2, true)) { schedulerPm.WaitTransfer(ModuleName.TMRobot); return; } } } if (CheckCanPickWaferFromPM(schedulerPm.Module, 1)) { if (GetWaferTemperatureSetInRecipe(schedulerPm.Module, 1, ModuleName.PMB, out double temp1, out double temp2)) { if (_tmRobot.Pick(schedulerPm.Module, 1, pickBlade, temp1, temp2, true)) { schedulerPm.WaitTransfer(ModuleName.TMRobot); return; } } } } } } static readonly List llNameList = new List() { "IsAvailable", "Task", "IsOnline", "IsError" , "IsIdle", "IsBusy", "IsPrepareTransfer", "IsCooling", "IsAtm", "IsVacuum", "Cooling", "WaitTransfer" , "Wafer0", "Wafer1", "Wafer2", "Wafer3", "Wafer4", "Wafer5" , "NextStep0", "NextStep1", "NextStep2", "NextStep3", "NextStep4", "NextStep5" , "need0", "need1", "need2", "need3", "need4", "need5"}; static readonly List efemNameList = new List() { "IsAvailable","IsOnline","IsReady","Task","IsError"," WaitTransfer" , "Wafer0", "Wafer1", "Wafer2" , "NextStep0", "NextStep1", "NextStep2" , "need0", "need1", "need2"}; static readonly List tmRobotNameList = new List() { "IsAvailable","IsOnline","IsError","Task","IsError"," WaitTransfer" , "Wafer0", "Wafer1", "Wafer2", "Wafer3" , "NextStep0", "NextStep1", "NextStep2", "NextStep3" , "need0", "need1", "need2", "need3"}; static readonly List pmNameList = new List() { "IsAvailable","IsOnline","IsError","TaskDone","IsError"," WaitTransfer" , "Wafer0", "Wafer1", "Wafer2", "Wafer3" , "NextStep0", "NextStep1", "NextStep2", "NextStep3" , "need0", "need1", "need2", "need3"}; static readonly List lpNameList = new List() { "IsAvailable","Task","IsOnline","IsError","IsReady"}; private int _GetWaferNextStepId(ModuleName module, int slotId) { if (!WaferManager.Instance.IsWaferSlotLocationValid(module, slotId)) return -1; var wafer = WaferManager.Instance.GetWafer(module, slotId); return wafer.NextSequenceStep; } private string _GetWaferOrigin(ModuleName module, int slotId) { if (!WaferManager.Instance.IsWaferSlotLocationValid(module, slotId)) return ""; var wafer = WaferManager.Instance.GetWafer(module, slotId); return wafer.WaferOrigin; } private void MonitorEfemRobotTask() { if (!_efem.IsAvailable) return; ////efem robot is idle, release all the target foreach (var ll in _lstLls) { if (ll.IsWaitTransfer(ModuleName.EfemRobot)) ll.StopWaitTransfer(ModuleName.EfemRobot); } if (_aligner.IsWaitTransfer(ModuleName.EfemRobot)) _aligner.StopWaitTransfer(ModuleName.EfemRobot); foreach (var lp in _lstLps) { if (lp.IsWaitTransfer(ModuleName.EfemRobot)) lp.StopWaitTransfer(ModuleName.EfemRobot); } if (!_efem.IsAvailable) return; var act = efemRobotActions.Peek(); act.Invoke(); if (!_efem.IsAvailable) return; efemRobotActions.Enqueue(efemRobotActions.Dequeue()); //if (!_efem.IsAvailable) // return; //MonitorEfemRobotAlignerTask(); //if (!_efem.IsAvailable) // return; //MonitorEfemRobotLoadLockTask(); //if (_aligner.HasWafer(0) && (!_efem.Blade1Enable || !_efem.Blade2Enable)) // return; //if (_aligner.HasWafer(0) && (_efem.HasWafer(0) || _efem.HasWafer(1))) // return; //if (!_efem.IsAvailable) // return; //MonitorEfemRobotLoadPortTask(); //MonitorEfemRobotGoToTask(); } private void MonitorEfemRobotLoadPortPlaceTask() { if (!_efem.IsAvailable) return; //place bool blade0HasWaferAndProcessed = _efem.Blade1Enable && WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 0) && !CheckWaferNeedProcess(ModuleName.EfemRobot, 0); bool blade1HasWaferAndProcessed = _efem.Blade2Enable && WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 1) && !CheckWaferNeedProcess(ModuleName.EfemRobot, 1); if (blade0HasWaferAndProcessed || blade1HasWaferAndProcessed) { Hand placeBlade = blade0HasWaferAndProcessed ? Hand.Blade1 : Hand.Blade2; if (blade0HasWaferAndProcessed && blade1HasWaferAndProcessed) { if (WaferManager.Instance.GetWafer(ModuleName.EfemRobot, 1).OriginSlot < WaferManager.Instance.GetWafer(ModuleName.EfemRobot, 0).OriginSlot) { placeBlade = Hand.Blade2; } } SlotItem destination = GetWaferReturnLoadPort(ModuleName.EfemRobot, (int)placeBlade); if (destination != null && ModuleHelper.IsLoadPort(destination.Module)) { SchedulerLoadPort lp = (SchedulerLoadPort)GetModule(destination.Module.ToString()); if (lp.IsReadyForPlace(ModuleName.EfemRobot, placeBlade, destination.Slot)) { if (_efem.Place(destination.Module, destination.Slot, placeBlade)) { lp.WaitTransfer(ModuleName.EfemRobot); if (_CycleData.IsCycleMode) _CycleData.IncreaseWaferCount(1); //_cycledTotalWafer++; //if (SC.ContainsItem("System.TotalCycledWafer")) // SC.SetItemValue("System.TotalCycledWafer", _cycledTotalWafer); return; } } } } } private void MonitorEfemRobotLoadPortPickTask() { if (!_efem.IsAvailable) return; if (_aligner.HasWafer(0) && (!_efem.Blade1Enable || !_efem.Blade2Enable)) return; if (_aligner.HasWafer(0) && (_efem.HasWafer(0) || _efem.HasWafer(1))) return; if (_lstLls.Any(x => GetEmptySlotInLoadLock(x) < 4)) return; //pick bool isBlade0Empty = _efem.Blade1Enable && WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 0); bool isBlade1Empty = _efem.Blade2Enable && WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 1); if (GetWaferCountInJobQueue() > 0 && (isBlade0Empty || isBlade1Empty) && EfemRobotPickAllowable) { Hand pickBlade = isBlade1Empty ? Hand.Blade2 : Hand.Blade1; SlotItem position = GetWaferInJobQueue(); if (position != null) { SchedulerLoadPort lp = GetModule(position.Module.ToString()) as SchedulerLoadPort; if (lp != null && lp.IsReadyForPick(ModuleName.EfemRobot, pickBlade, position.Slot)) { //WaferManager.Instance.GetWafer(position.Module, position.Slot).NextSequenceStep++; if (_efem.Pick(position.Module, position.Slot, pickBlade)) { lp.WaitTransfer(ModuleName.EfemRobot); return; } } } } } private bool GetWaferProcessAccess(WaferInfo wafer) { if (wafer.ProcessJob == null || string.IsNullOrEmpty(wafer.ProcessJob.Name)) return false; return GetPMOccupiedCount(wafer.ProcessJob.Name) > GetWaitingProcessWaferCount(wafer.ProcessJob.Name); } private int GetPMOccupiedCount(string pjName) { List location = new List(); foreach (var pj in _lstProcessJobs) { if (pj.Name != pjName) continue; if (pj.State != EnumProcessJobState.Processing) continue; foreach (var step in pj.Sequence.Steps) { foreach (var module in step.StepModules) { if (ModuleHelper.IsPm(module) && !location.Exists(x => x == module)) location.Add(module); } } } return location.Count; } private int GetWaitingProcessWaferCount(string pjName) { List> location = new List>() { Tuple.Create(ModuleName.TMRobot, 0), Tuple.Create(ModuleName.TMRobot, 1), Tuple.Create(ModuleName.LLA, 0), Tuple.Create(ModuleName.LLB, 0), Tuple.Create(ModuleName.AlignerB, 0), Tuple.Create(ModuleName.PMA, 0), Tuple.Create(ModuleName.PMB, 0), Tuple.Create(ModuleName.PMC, 0), }; int count = 0; foreach (var tuple in location) { WaferInfo wafer = WaferManager.Instance.GetWafer(tuple.Item1, tuple.Item2); if (wafer.IsEmpty) continue; if (wafer.ProcessJob != null && wafer.ProcessJob.Name == pjName) count++; } return count; } Dictionary>> _ValueDict = new Dictionary>>(); //private void PrintVariables(string key, List names, params object[] values) //{ // StackTrace trace = new StackTrace(true); // var frames = trace.GetFrames();//1代表上级,2代表上上级,以此类推 // if (frames.Length < 2) return; // var frame1 = frames[1]; // var method1 = frame1.GetMethod(); // if (method1 == null) return; // string methodName = method1.Name; // var newList = values.ToList(); // if (!_ValueDict.ContainsKey(methodName)) // { // _ValueDict[methodName] = new Dictionary>() { { key, newList } }; // LOG.Debug($"{methodName} Key={key} Values({_ToString(names, newList)})"); // } // else // { // var dict = _ValueDict[methodName]; // if (dict.ContainsKey(key)) // { // var list = dict[key]; // StringBuilder builder = new StringBuilder(); // for (int ii = 0; ii < names.Count; ii++) // { // if (list[ii].Equals(newList[ii])) continue; // builder.Append($",{names[ii]}({list[ii]} --> {newList[ii]})"); // list[ii] = newList[ii]; // } // if (builder.Length > 0) // { // LOG.Debug($"{methodName} Key={key} {builder.ToString()}"); // } // } // else // { // _ValueDict[methodName].Add(key, newList); // LOG.Debug($"{methodName} Key={key} Values({_ToString(names, newList)})"); // } // } //} private string _ToString(List names, List objList) { StringBuilder builder = new StringBuilder(); for (int ii = 0; ii < names.Count; ii++) { builder.Append($",{names[ii]}={objList[ii]}"); } return builder.ToString().Trim(','); } private void MonitorEfemRobotLoadLockPickTask() { if (!_efem.IsAvailable) return; foreach (var ll in _lstLls) { if (!ll.IsAvailable) continue; //pick bool blade1EmptyAndEnable = _efem.Blade1Enable && WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 0); bool blade2EmptyAndEnable = _efem.Blade2Enable && WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 1); if (blade1EmptyAndEnable || blade2EmptyAndEnable) { if (CheckWaferNeedProcess(ModuleName.EfemRobot, 0) || CheckWaferNeedProcess(ModuleName.EfemRobot, 1)) continue; Hand pickBlade = blade2EmptyAndEnable ? Hand.Blade2 : Hand.Blade1; int pickSlot = Int32.MaxValue; for (int i = 0; i < 8; i++) { if (!CheckWaferNeedProcess(ll.Module, i) && ll.IsReadyForPick(ModuleName.EfemRobot, pickBlade, i)) { pickSlot = i; break; } } if (pickSlot != Int32.MaxValue) { //WaferManager.Instance.GetWafer(ll.Module, pickSlot).NextSequenceStep++; if (_efem.Pick(ll.Module, pickSlot, pickBlade)) { ll.WaitTransfer(ModuleName.EfemRobot); return; } } } } if (!_efem.IsAvailable) return; } private void MonitorEfemRobotLoadLockPlaceTask() { if (!_efem.IsAvailable) return; //place foreach (var ll in _lstLls) { if (!ll.IsAvailable) continue; bool blade1NeedPlace = _efem.Blade1Enable && WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 0) && CheckWaferNeedProcess(ModuleName.EfemRobot, 0) && !CheckWaferNextStepIsAlign(ModuleName.EfemRobot, 0); bool blade2NeedPlace = _efem.Blade2Enable && WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 1) && CheckWaferNeedProcess(ModuleName.EfemRobot, 1) && !CheckWaferNextStepIsAlign(ModuleName.EfemRobot, 1); if (!blade1NeedPlace && !blade2NeedPlace) { EfemRobotPickAllowable = true; } else { EfemRobotPickAllowable = false; } if (blade1NeedPlace || blade2NeedPlace) { Hand placeBlade = blade1NeedPlace ? Hand.Blade1 : Hand.Blade2; if (!CheckWaferNextStepIsLoadLock(ModuleName.EfemRobot, (int)placeBlade, ll.Module)) continue; int placeSlot = Int32.MaxValue; if (blade1NeedPlace || blade2NeedPlace) { for (int i = 0; i < 6; i++) { if (ll.IsReadyForPlace(ModuleName.EfemRobot, placeBlade, i)) { placeSlot = i; break; } } } if (placeSlot != Int32.MaxValue) { if (_efem.Place(ll.Module, (int)placeSlot, placeBlade)) { WaferManager.Instance.GetWafer(ModuleName.EfemRobot, (int)placeBlade).NextSequenceStep++; ll.WaitTransfer(ModuleName.EfemRobot); return; } } } } } private void MonitorEfemRobotAlignerPickTask() { if (!_efem.IsAvailable) return; if (!_aligner.IsAvailable) return; //pick from aligner if (WaferManager.Instance.CheckHasWafer(ModuleName.Aligner, 0) && !CheckWaferNextStepIsAlign(ModuleName.Aligner, 0)) { bool blade1EmptyAndEnable = _efem.Blade1Enable && WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 0); bool blade2EmptyAndEnable = _efem.Blade2Enable && WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 1); if (blade1EmptyAndEnable || blade2EmptyAndEnable) { Hand pickBlade = blade1EmptyAndEnable ? Hand.Blade1 : Hand.Blade2; //if ((blade1Empty && blade2Empty)) { if (_aligner.IsReadyForPick(ModuleName.EfemRobot, pickBlade, 0)) { if (_efem.Pick(_aligner.Module, 0, pickBlade)) { _aligner.WaitTransfer(ModuleName.EfemRobot); return; } } } } } if (!_efem.IsAvailable) return; } private void MonitorEfemRobotAlignerPlaceTask() { if (!_efem.IsAvailable) return; //place to aligner if (WaferManager.Instance.CheckNoWafer(ModuleName.Aligner, 0)) { bool blade1NeedAlign = _efem.Blade1Enable && WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 0) && CheckWaferNextStepIsAlign(ModuleName.EfemRobot, 0); bool blade2NeedAlign = _efem.Blade2Enable && WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 1) && CheckWaferNextStepIsAlign(ModuleName.EfemRobot, 1); if (blade1NeedAlign || blade2NeedAlign) { Hand placeBlade = blade1NeedAlign ? Hand.Blade1 : Hand.Blade2; if (blade1NeedAlign && blade2NeedAlign && WaferManager.Instance.GetWafer(ModuleName.EfemRobot, 1).OriginSlot < WaferManager.Instance.GetWafer(ModuleName.EfemRobot, 0).OriginSlot) placeBlade = Hand.Blade2; if (_aligner.IsReadyForPlace(ModuleName.EfemRobot, placeBlade, 0)) { if (_efem.Place(_aligner.Module, 0, placeBlade)) { _aligner.WaitTransfer(ModuleName.EfemRobot); return; } } } } } //private void MonitorEfemRobotGoToTask() //{ // if (!_efem.IsAvailable) // return; // SchedulerLoadLock gotoLL = null; // Hand gotoBlade = Hand.Both; // int gotoSlot = int.MaxValue; // bool arm1Empty = WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 0) && WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 1); // bool arm2Empty = WaferManager.Instance.CheckNoWafer(ModuleName.EfemRobot, 2); // foreach (var ll in _lstLls) // { // if (!ll.IsOnline) // continue; // if (!ll.Entity.IsPrepareTransfer) // continue; // if (arm1Empty && arm2Empty) // { // for (int i = 0; i <= 5; i++) // { // var wafer = WaferManager.Instance.GetWafer(ll.Module, i); // if (wafer != null && !wafer.IsEmpty && wafer.ProcessState == EnumWaferProcessStatus.Completed) // { // gotoLL = ll; // gotoBlade = Hand.Both; // gotoSlot = 0; // break; // } // } // } // else // { // if (!arm1Empty && !arm2Empty) // { // for (int i = 0; i <= 1; i++) // { // if (WaferManager.Instance.CheckNoWafer(ll.Module, i) && // WaferManager.Instance.CheckNoWafer(ll.Module, i + 2) && // WaferManager.Instance.CheckNoWafer(ll.Module, i + 4)) // { // gotoBlade = Hand.Both; // gotoSlot = i; // gotoLL = ll; // break; // } // else if (WaferManager.Instance.CheckNoWafer(ll.Module, i) && // WaferManager.Instance.CheckNoWafer(ll.Module, i + 2)) // { // gotoBlade = Hand.Blade1; // gotoSlot = i; // gotoLL = ll; // break; // } // if (ll.IsReadyForPlace(ModuleName.EfemRobot, Hand.Blade2, i + 4)) // { // gotoBlade = Hand.Blade2; // gotoSlot = i + 4; // gotoLL = ll; // break; // } // } // } // else if (!arm1Empty) // { // for (int i = 0; i <= 1; i++) // { // if (WaferManager.Instance.CheckNoWafer(ll.Module, i) && // WaferManager.Instance.CheckNoWafer(ll.Module, i + 2) && // WaferManager.Instance.CheckNoWafer(ll.Module, i + 4)) // { // gotoBlade = Hand.Both; // gotoSlot = i; // gotoLL = ll; // break; // } // } // } // else // { // for (int i = 0; i <= 1; i++) // { // if (ll.IsReadyForPlace(ModuleName.EfemRobot, Hand.Blade2, i + 4)) // { // gotoBlade = Hand.Blade2; // gotoSlot = i + 4; // gotoLL = ll; // break; // } // } // if (gotoSlot == Int32.MaxValue) // { // for (int i = 0; i <= 5; i++) // { // if (ll.IsReadyForPlace(ModuleName.EfemRobot, Hand.Blade2, i)) // { // gotoBlade = Hand.Blade2; // gotoSlot = i + 4; // gotoLL = ll; // break; // } // } // } // } // } // if (gotoLL != null && gotoSlot != int.MaxValue) // break; // } // if (gotoLL != null && gotoSlot != int.MaxValue) // { // if (_efem.PreviousTarget == gotoLL.Module) // return; // if (_efem.Goto(gotoLL.Module, gotoSlot, gotoBlade)) // { // gotoLL.WaitTransfer(ModuleName.EfemRobot); // return; // } // } //} private SlotItem GetNextWaferInJobQueue(ModuleName lpModule) { if (!_isRunningInParallelMode) { 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 null; } //parallel mode Dictionary pjWafer = new Dictionary(); Dictionary> pmUsed = new Dictionary>(); foreach (var cj in _lstControlJobs) { if (cj.State != EnumControlJobState.Executing) continue; 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)) { pjWafer[pj.Name] = new SlotItem(pjSlotWafer.Item1, pjSlotWafer.Item2); break; } } } } } if (pjWafer.Count == 0) return null; if (pjWafer.Count == 1) return pjWafer.Values.First(); string preferPj = pjWafer.Keys.First(); int runningWaferCount = GetWaitingProcessWaferCount(preferPj); foreach (var slotItem in pjWafer) { if (slotItem.Key == preferPj) continue; int count = GetWaitingProcessWaferCount(slotItem.Key); if (count < runningWaferCount) { preferPj = slotItem.Key; runningWaferCount = count; } } return pjWafer[preferPj]; } public 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 = GetModule(stepModule.ToString()) 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; } public bool LoadRecipe(string module, string CurrentRecipeBaseName, out double temp1, out double temp2) { var CurrentRecipeContent = RecipeFileManager.Instance.LoadRecipe("", CurrentRecipeBaseName, true); temp1 = 0; temp2 = 0; if (string.IsNullOrEmpty(CurrentRecipeContent)) { EV.PostInfoLog(ModuleName.System.ToString(), $"error during read recipe file {CurrentRecipeBaseName}"); return false; } if (!Recipe.Parse(module, CurrentRecipeContent, out RecipeHead recipeHead, out var recipeSteps)) { return false; } temp1 = GetChamber1Temp(recipeHead); temp2 = GetChamber2Temp(recipeHead); return true; } public double GetChamber1Temp(RecipeHead CurrentRecipeHead) { if (CurrentRecipeHead != null) { if (!string.IsNullOrEmpty(CurrentRecipeHead.Chamber1Temperature)) { double setpoint = Convert.ToDouble(CurrentRecipeHead.Chamber1Temperature); if (setpoint > 0 && setpoint < 600) return setpoint; } } return 0; } public double GetChamber2Temp(RecipeHead CurrentRecipeHead) { if (CurrentRecipeHead != null) { if (!string.IsNullOrEmpty(CurrentRecipeHead.Chamber2Temperature)) { double setpoint = Convert.ToDouble(CurrentRecipeHead.Chamber2Temperature); if (setpoint > 0 && setpoint < 600) return setpoint; } } return 0; } private bool GetWaferTemperatureSetInRecipe(ModuleName waferModule, int waferSlot, ModuleName pmModule, out double temp1, out double temp2) { 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) { LoadRecipe(pmModule.ToString(), seq.Steps[i].StepParameter[module == ModuleName.PMA ? "PMARecipe" : "PMBRecipe"].ToString(), out double chambertemp1, out double chambertemp2); temp1 = (double)chambertemp1; temp2 = (double)chambertemp2; return true; } } } temp1 = 0; temp2 = 0; LOG.Error($"Can not find wafer {waferModule} {waferSlot + 1} temperature set in recipe"); return false; } #endregion #region Logic Check private bool CheckLoadLockAvaiableForTMRobot() { bool isPrepareTransferForTMobot = GetVacuumWafer() == 0; foreach (var ll in _lstLls) { if (!ll.IsAvailable) continue; var deviceLL = DEVICE.GetDevice(ll.Module.ToString()); var wafers = WaferManager.Instance.GetWafers(ll.Module).ToList(); for (int i = 0; i <= 5;) { var wafer0 = WaferManager.Instance.GetWafer(ll.Module, i); var wafer1 = WaferManager.Instance.GetWafer(ll.Module, i + 1); if ((!wafer0.IsEmpty && wafer0.ProcessState == EnumWaferProcessStatus.Idle) || (!wafer1.IsEmpty && wafer1.ProcessState == EnumWaferProcessStatus.Idle)) { isPrepareTransferForTMobot = true; break; } i += 2; } } return isPrepareTransferForTMobot; } //private bool CheckPMTemperature(string module) //{ // var pmDevice = DEVICE.GetDevice(module); // if (pmDevice != null && _temperatureSetpointDic.ContainsKey(module) && _temperatureSetpointDic[module] > 0) // { // if (!pmDevice.CheckHeater1Temperature(_temperatureSetpointDic[module]) || // !pmDevice.CheckHeater2Temperature(_temperatureSetpointDic[module])) // { // if (!_temperatureTrigDic.ContainsKey(module)) // _temperatureTrigDic.Add(module, new R_TRIG()); // _temperatureTrigDic[module].CLK = true; // if (_temperatureTrigDic[module].Q) // { // EV.PostWarningLog("Scheduler", $"{module} heater temperature is not in the scope"); // } // pmDevice.SetHeater1(_temperatureSetpointDic[module]); // pmDevice.SetHeater2(_temperatureSetpointDic[module]); // } // if (pmDevice.CheckHeater1Temperature(_temperatureSetpointDic[module]) && // pmDevice.CheckHeater2Temperature(_temperatureSetpointDic[module])) // { // return true; // } // } // else // return true; // return false; //} private bool CheckWaferNextProcessIsPM(ModuleName waferModule, int waferSlot) { WaferInfo wafer = WaferManager.Instance.GetWafer(waferModule, waferSlot); if (wafer.IsEmpty) return false; if (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; } return wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Any(x => ModuleHelper.IsPm(x)); } private bool CheckWaferNextProcessIsPMAvaiable(ModuleName waferModule, int waferSlot) { if (!CheckWaferNextProcessIsPM(waferModule, waferSlot)) return false; WaferInfo wafer = WaferManager.Instance.GetWafer(waferModule, waferSlot); foreach (var stepModule in wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules) { if (_lstPms.Any(pm => pm.Module == stepModule && pm.IsAvailable && WaferManager.Instance.CheckNoWafer(pm.Module, waferSlot))) { return true; } } return false; } private bool CheckNeedRunClean(ModuleName module, out bool withWafer, out string recipeName) { recipeName = string.Empty; withWafer = false; return false; SequenceInfo seq = GetCurrentSequenceInfo(); int waferProcessed = StatsDataManager.Instance.GetValue($"{module}.WaferProcessedSincePreviousClean"); if (seq == null) return false; foreach (var stepInfo in seq.Steps) { if (!stepInfo.StepModules.Contains(module)) continue; if (stepInfo.StepParameter.ContainsKey("CleanRecipeNoWafer") && !string.IsNullOrEmpty((string)stepInfo.StepParameter["CleanRecipeNoWafer"])) { if (stepInfo.CleanInterval > 0 && waferProcessed >= stepInfo.CleanInterval) { recipeName = (string)stepInfo.StepParameter["CleanRecipeNoWafer"]; withWafer = false; return true; } } if (stepInfo.StepParameter.ContainsKey("CleanRecipeWafer") && !string.IsNullOrEmpty((string)stepInfo.StepParameter["CleanRecipeWafer"])) { if (stepInfo.CleanInterval > 0 && waferProcessed >= stepInfo.CleanInterval) { recipeName = (string)stepInfo.StepParameter["CleanRecipeWafer"]; withWafer = true; return true; } } } return false; } private bool CheckVacuumWaferHasAvailableTarget(ModuleName waferModule, int waferSlot, ModuleName chamberSwappable) { 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) || pm.Module == chamberSwappable) { if (pm.IsAvailable == false) continue; if (!CheckWaferNextProcessIn(waferModule, waferSlot, pm.Module)) continue; return true; } } } else { // No more processing needed, check for a outbound loadlock slot return true; } return false; } private bool CheckWaferNextProcessIn(ModuleName waferModule, int waferSlot, ModuleName chamber) { WaferInfo wafer = WaferManager.Instance.GetWafer(waferModule, waferSlot); if (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; } return wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(chamber); } private SequenceInfo GetCurrentSequenceInfo() { foreach (var pj in _lstProcessJobs) { if (pj.State == EnumProcessJobState.Processing) { return pj.Sequence; } } return null; } private bool GetWaferSequenceAlignAngle(ModuleName module, int slot, out double angle) { angle = 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.Aligner)) return false; angle = wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].AlignAngle; return true; } private bool GetWaferSequenceLoadLockCoolingTime(ModuleName module, int slot, out int coolingTime) { coolingTime = 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(module)) return false; if (!int.TryParse(wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepParameter["CoolingTime"].ToString(), out coolingTime)) { coolingTime = SC.GetValue("System.DefaultCoolingTime"); EV.PostWarningLog("Scheduler", $"Sequence step LL cooling time is not valid, instead with the SC default value {coolingTime} seconds"); return false; } return true; } private bool CheckWaferNextStepIsLoadLock(ModuleName module, int slot, ModuleName LL = ModuleName.System) { 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 (LL != ModuleName.System && !wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(LL)) return false; if (!wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(ModuleName.LLA) && !wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(ModuleName.LLB)) 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.Aligner) && !wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(ModuleName.AlignerA) && !wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(ModuleName.AlignerB)) return false; return true; } 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.Status == WaferStatus.Dummy && wafer.ProcessState == EnumWaferProcessStatus.Wait) { if (ModuleHelper.IsPm(processIn)) { return (CheckNeedRunClean(processIn, out bool withWafer, out _) && withWafer); } return true; } if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.ProcessJob.Sequence.Steps == null) return false; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count || wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules == null) return false; if (processIn != ModuleName.System && !wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(processIn)) return false; bool hasPm = false; for (int i = wafer.NextSequenceStep; i < wafer.ProcessJob.Sequence.Steps.Count; i++) { foreach (var stepModule in wafer.ProcessJob.Sequence.Steps[i].StepModules) { if (ModuleHelper.IsPm(stepModule)) { hasPm = true; break; } } if (hasPm) break; } if (processIn == ModuleName.System && !hasPm) return false; return true; } public bool CheckWaferNeedProcess(WaferInfo wafer, ModuleName processIn = ModuleName.System) { if (wafer.IsEmpty) return false; if (wafer.Status == WaferStatus.Dummy && wafer.ProcessState == EnumWaferProcessStatus.Wait) { if (ModuleHelper.IsPm(processIn)) { return (CheckNeedRunClean(processIn, out bool withWafer, out _) && withWafer); } return true; } if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.ProcessJob.Sequence.Steps == null) return false; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count || wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules == null) return false; if (processIn != ModuleName.System && !wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(processIn)) return false; bool hasPm = false; for (int i = wafer.NextSequenceStep; i < wafer.ProcessJob.Sequence.Steps.Count; i++) { foreach (var stepModule in wafer.ProcessJob.Sequence.Steps[i].StepModules) { if (ModuleHelper.IsPm(stepModule)) { hasPm = true; break; } } if (hasPm) break; } if (processIn == ModuleName.System && !hasPm) return false; return true; } public bool CheckWaferNeedProcessPre(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 (wafer.ProcessJob.Sequence.Steps[i].StepModules .Contains(processIn)) return true; } } return false; } public bool CheckWaferNeedGotoPM(ModuleName waferModule, int waferSlot, ModuleName processIn = ModuleName.System) { WaferInfo wafer = WaferManager.Instance.GetWafer(waferModule, waferSlot); if (wafer.IsEmpty) return false; if (wafer.Status == WaferStatus.Dummy && wafer.ProcessState == EnumWaferProcessStatus.Wait) { if (ModuleHelper.IsPm(processIn)) { return (CheckNeedRunClean(processIn, out bool withWafer, out _) && withWafer); } return true; } if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.ProcessJob.Sequence.Steps == null) return false; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count || wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules == null) return false; if (processIn != ModuleName.System && !wafer.ProcessJob.Sequence.Steps.Any(x => x.StepModules.Contains(processIn))) return false; bool hasPm = false; for (int i = wafer.NextSequenceStep; i < wafer.ProcessJob.Sequence.Steps.Count; i++) { foreach (var stepModule in wafer.ProcessJob.Sequence.Steps[i].StepModules) { if (ModuleHelper.IsPm(stepModule)) { hasPm = true; break; } } if (hasPm) break; } if (processIn == ModuleName.System && !hasPm) return false; return true; } public bool CheckWaferNeedGotoPM(WaferInfo wafer, ModuleName processIn = ModuleName.System) { if (wafer.IsEmpty) return false; if (wafer.Status == WaferStatus.Dummy && wafer.ProcessState == EnumWaferProcessStatus.Wait) { if (ModuleHelper.IsPm(processIn)) { return (CheckNeedRunClean(processIn, out bool withWafer, out _) && withWafer); } return true; } if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.ProcessJob.Sequence.Steps == null) return false; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count || wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules == null) return false; if (processIn != ModuleName.System && !wafer.ProcessJob.Sequence.Steps.Any(x => x.StepModules.Contains(processIn))) return false; bool hasPm = false; for (int i = wafer.NextSequenceStep; i < wafer.ProcessJob.Sequence.Steps.Count; i++) { foreach (var stepModule in wafer.ProcessJob.Sequence.Steps[i].StepModules) { if (ModuleHelper.IsPm(stepModule)) { hasPm = true; break; } } if (hasPm) break; } if (processIn == ModuleName.System && !hasPm) return false; return true; } public bool CheckWaferSequenceStepDone(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; return true; } public bool CheckWaferProcessModuleIsAvailable(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; foreach (var step in wafer.ProcessJob.Sequence.Steps) { foreach (var module in step.StepModules) { if (WaferManager.Instance.CheckNoWafer(module, 0) && _lstPms.Find(x => x.Module == module).IsAvailable && !CheckNeedRunClean(module, out bool _, out string _)) return true; } } return false; } private int GetATMWafer() { int count = GetWaferCountInJobQueue(); if (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 0)) count++; if (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 1)) count++; if (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 2)) count++; return count; } private int GetVacuumWafer() { int count = 0; foreach (var schedulerPm in _lstPms) { if (schedulerPm == null || (!schedulerPm.IsOnline)) continue; if (WaferManager.Instance.CheckHasWafer(schedulerPm.Module, 0) || WaferManager.Instance.CheckHasWafer(schedulerPm.Module, 1)) count++; } if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) || WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1)) count++; if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 2) || WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 3)) count++; return count; } private int GetVacuumProcessedWafer() { int count = 0; foreach (var schedulerPm in _lstPmsCurrentSequence) { if (schedulerPm == null) continue; if (WaferManager.Instance.CheckHasWafer(schedulerPm.Module, 0) && schedulerPm.IsAvailable && !CheckWaferNeedProcess(schedulerPm.Module, 0) && !WaferManager.Instance.CheckWaferIsDummy(schedulerPm.Module, 0)) count++; } if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) && !CheckWaferNeedProcess(ModuleName.TMRobot, 0) && !WaferManager.Instance.CheckWaferIsDummy(ModuleName.TMRobot, 0)) count++; if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 1) && !CheckWaferNeedProcess(ModuleName.TMRobot, 1) && !WaferManager.Instance.CheckWaferIsDummy(ModuleName.TMRobot, 1)) count++; return count; } private int GetUnprocessedWafer() { int count = 0; foreach (var cj in _lstControlJobs) { 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)) count++; } } } } } foreach (var ll in _lstLls) { if (CheckWaferNeedProcess(ll.Module, 0)) count++; if (CheckWaferNeedProcess(ll.Module, 1)) count++; if (CheckWaferNeedProcess(ll.Module, 2)) count++; if (CheckWaferNeedProcess(ll.Module, 3)) count++; if (CheckWaferNeedProcess(ll.Module, 4)) count++; if (CheckWaferNeedProcess(ll.Module, 5)) count++; } if (CheckWaferNeedProcess(ModuleName.TMRobot, 0)) count++; if (CheckWaferNeedProcess(ModuleName.TMRobot, 1)) count++; if (CheckWaferNeedProcess(ModuleName.TMRobot, 2)) count++; if (CheckWaferNeedProcess(ModuleName.TMRobot, 3)) count++; return count; } private int GetAvailablePmCount() { int count = 0; foreach (var pm in _lstPms) { if (!pm.IsAvailable) continue; if (WaferManager.Instance.CheckNoWafer(pm.Module, 0)) count++; } return count; } private SlotItem GetWaferInJobQueue() { SlotItem slotItem = null; bool enableParallel = SC.GetValue("System.Scheduler.IsRunInParallelMode"); var OrderedLstCJs = _lstControlJobs.OrderBy(x => x.BeginTime); if (enableParallel) { var priorityFindWaferGotoPMA = GetWaferCountLoadLockToPM(ModuleName.PMA) < 1; var priorityFindWaferGotoPMB = GetWaferCountLoadLockToPM(ModuleName.PMB) < 1; foreach (var cj in OrderedLstCJs) { 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 (priorityFindWaferGotoPMA) { if (CheckWaferNeedGotoPM(pjSlotWafer.Item1, pjSlotWafer.Item2, ModuleName.PMA)) { return new SlotItem(pjSlotWafer.Item1, pjSlotWafer.Item2); } } if (priorityFindWaferGotoPMB) { if (CheckWaferNeedGotoPM(pjSlotWafer.Item1, pjSlotWafer.Item2, ModuleName.PMB)) { return new SlotItem(pjSlotWafer.Item1, pjSlotWafer.Item2); } } } } } } } } if (slotItem == null) { foreach (var cj in OrderedLstCJs) { 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 slotItem; } private Tuple GetWaferOrderInJobQueue(List> llSlots) { foreach (var cj in _lstControlJobs) { 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) { foreach (var slot in llSlots) { var wafer = WaferManager.Instance.GetWafer(slot.Item1.Module, slot.Item2); if (wafer.OriginStation == (int)pjSlotWafer.Item1 && wafer.OriginSlot == pjSlotWafer.Item2) return slot; } } } } } } return null; } private bool CheckWaferInJobQueue(ModuleName module, int slot) { foreach (var cj in _lstControlJobs) { 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 (pjSlotWafer.Item1 == module && pjSlotWafer.Item2 == slot && CheckWaferNeedProcess(pjSlotWafer.Item1, pjSlotWafer.Item2)) { return true; } } } } } } return false; } private int GetWaferCountInJobQueue() { int count = 0; foreach (var lp in _lstLps) { for (int i = 0; i < 25; i++) { if (CheckWaferNeedProcess(lp.Module, i)) count++; } } return count; } //private SchedulerLoadLock GetCurrentLoadLockForTMPlace() //{ // foreach (var ll in _lstLls) // { // if (!ll.IsAvailable) // continue; // //if (!ll.CheckAtVacuum()) // // continue; // var wafers = WaferManager.Instance.GetWafers(ll.Module).ToList(); // if (wafers.Any(x => x.ProcessState == EnumWaferProcessStatus.Completed)) // { // for (int i = 0; i < 5;) // { // if ((WaferManager.Instance.CheckNoWafer(ll.Module, i)) // && (WaferManager.Instance.CheckNoWafer(ll.Module, i + 1))) // { // return ll; // } // i += 2; // } // } // } // SchedulerLoadLock currentLoadLock = null; // DateTime dtMin = DateTime.MaxValue; // foreach (var ll in _lstLls) // { // if (!ll.IsAvailable || !ll.CheckAtVacuum()) // continue; // var wafers = WaferManager.Instance.GetWafers(ll.Module).ToList(); // DateTime dtCurrentLL = DateTime.MaxValue; // foreach (var wafer in wafers) // { // if (wafer != null && !wafer.IsEmpty && wafer.SubstHists.Length > 0) // { // var dt = wafer.SubstHists[wafer.SubstHists.Length - 1].AccessTime; // if (dt.CompareTo(dtMin) < 0) // dtCurrentLL = dt; // } // } // if (dtCurrentLL.CompareTo(dtMin) < 0) // { // dtMin = dtCurrentLL; // currentLoadLock = ll; // } // } // foreach (var ll in _lstLls) // { // if (currentLoadLock != null && currentLoadLock != ll) // continue; // for (int i = 0; i < 5;) // { // if ((WaferManager.Instance.CheckNoWafer(ll.Module, i)) // && (WaferManager.Instance.CheckNoWafer(ll.Module, i + 1))) // { // return currentLoadLock; // } // i += 2; // } // currentLoadLock = null;//no empty slot // } // return currentLoadLock; //} //private SchedulerLoadLock GetCurrentLoadLockForTMPick() //{ // SchedulerLoadLock currentLoadLock = null; // DateTime dtMin = DateTime.MaxValue; // foreach (var ll in _lstLls) // { // if (!ll.IsAvailable || !ll.CheckAtVacuum()) // continue; // var wafers = WaferManager.Instance.GetWafers(ll.Module).ToList(); // DateTime dtCurrentLL = DateTime.MaxValue; // foreach (var wafer in wafers) // { // if (wafer != null && !wafer.IsEmpty && wafer.ProcessState == EnumWaferProcessStatus.Idle && wafer.SubstHists.Length > 0) // { // var dt = wafer.SubstHists[wafer.SubstHists.Length - 1].AccessTime; // if (dt.CompareTo(dtMin) < 0) // dtCurrentLL = dt; // } // } // if (dtCurrentLL.CompareTo(dtMin) < 0) // { // dtMin = dtCurrentLL; // currentLoadLock = ll; // } // } // return currentLoadLock; //} //private SchedulerLoadLock GetCurrentLoadLockForEfem() //{ // SchedulerLoadLock currentLoadLock = null; // foreach (var ll in _lstLls) // { // if (!ll.IsOnline) // continue; // var wafers = WaferManager.Instance.GetWafers(ll.Module).ToList(); // if (loadlockDevice.CheckDoorOpen()) // { // foreach (var wafer in wafers) // { // if (wafer != null && !wafer.IsEmpty && wafer.ProcessState == EnumWaferProcessStatus.Idle) // { // currentLoadLock = ll; // break; // } // } // if (!wafers.Any(x => x.IsEmpty)) // { // currentLoadLock = null; // } // } // } // return currentLoadLock; //} private int GetEmptySlotInCurrentLoadLock() { int count = 0; //SchedulerLoadLock currentLoadLock = GetCurrentLoadLockForEfem(); foreach (var ll in _lstLls) { if (!ll.IsOnline) continue; //if (currentLoadLock != null && currentLoadLock != ll) // continue; //var loadlockDevice = DEVICE.GetDevice(ll.Module.ToString()); var wafers = WaferManager.Instance.GetWafers(ll.Module).ToList(); //if (loadlockDevice.CheckDoorOpen()) { for (int i = 0; i <= 1; i++) { var wafer0 = WaferManager.Instance.GetWafer(ll.Module, i); var wafer1 = WaferManager.Instance.GetWafer(ll.Module, i + 2); var wafer2 = WaferManager.Instance.GetWafer(ll.Module, i + 4); if (wafer0 != null && wafer0.IsEmpty) count++; if (wafer1 != null && wafer1.IsEmpty) count++; if (wafer2 != null && wafer2.IsEmpty) count++; if (count > 0) break; } } } if (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 0) && CheckWaferNeedProcess(ModuleName.EfemRobot, 0)) count--; if (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 1) && CheckWaferNeedProcess(ModuleName.EfemRobot, 1)) count--; if (WaferManager.Instance.CheckHasWafer(ModuleName.EfemRobot, 2) && CheckWaferNeedProcess(ModuleName.EfemRobot, 2)) count--; return count < 0 ? 0 : count; } private int GetEmptySlotInLoadLock(SchedulerLoadLock ll) { int count = 0; { if (!ll.IsOnline) return count; var wafers = WaferManager.Instance.GetWafers(ll.Module); for (int i = 0; i < wafers.Length; i++) { if (wafers[i].IsEmpty) count++; } } return count; } private int GetEmptySlotInLoadLock() { int count = 0; foreach (var ll in _lstLls) { if (!ll.IsOnline) continue; var wafers = WaferManager.Instance.GetWafers(ll.Module); for (int i = 0; i < wafers.Length; i++) { if (wafers[i].IsEmpty) count++; } } return count; } private int GetEmptySlotInPM() { int count = 0; foreach (var pm in _lstPms) { if (!pm.IsOnline) continue; var wafers = WaferManager.Instance.GetWafers(pm.Module); for (int i = 0; i < wafers.Length; i++) { if (wafers[i].IsEmpty) count++; } } return count; } private int GetEmptySlotInPMA() { int count = 0; if (!_pm1.IsOnline) return count; var wafers = WaferManager.Instance.GetWafers(_pm1.Module); for (int i = 0; i < wafers.Length; i++) { if (wafers[i].IsEmpty) count++; } return count; } private int GetEmptySlotInPMB() { int count = 0; if (!_pm2.IsOnline) return count; var wafers = WaferManager.Instance.GetWafers(_pm2.Module); for (int i = 0; i < wafers.Length; i++) { if (wafers[i].IsEmpty) count++; } return count; } private bool CheckAlignerWaferCanPick(WaferInfo wafer, ModuleName aligner) { if (wafer == null || wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.ProcessJob.Sequence.Steps == null || wafer.ProcessJob.Sequence.Steps.Count == 0) return false; var stepModules = wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules; int count = 0; int waferCount = 0; if (stepModules == null || stepModules.Count == 0) { return false; } foreach (var pm in _lstPms) { if (pm.IsOnline && stepModules.Contains(pm.Module)) { count++; if (WaferManager.Instance.CheckHasWafer(pm.Module, 0)) waferCount++; } } if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1)) { count++; } else if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) || WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1)) { var robotWafer = WaferManager.Instance.GetWafer(ModuleName.TMRobot, WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, 0) ? 0 : 1); if (robotWafer != null && !robotWafer.IsEmpty && robotWafer.ProcessJob != null && robotWafer.ProcessJob.Sequence != null || robotWafer.ProcessJob.Sequence.Steps != null && robotWafer.ProcessJob.Sequence.Steps.Count > 0) { stepModules = robotWafer.ProcessJob.Sequence.Steps[robotWafer.NextSequenceStep].StepModules; if (stepModules != null && stepModules.Count > 0) { if (stepModules.Contains(aligner)) { count++; } } } } return count - waferCount > 0; } private bool CheckWaferCanPick(WaferInfo wafer) { if (wafer == null || wafer.IsEmpty || wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.ProcessJob.Sequence.Steps == null || wafer.ProcessJob.Sequence.Steps.Count == 0) return false; var stepModules = wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep + 1].StepModules; int count = 0; int waferCount = 0; if (stepModules == null || stepModules.Count == 0) { return false; } if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 0) && WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, 1)) { count++; } return count - waferCount > 0; } 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 bool ValidateSequence(string[] seqs, out string reason) { reason = string.Empty; bool isAllSequenceNull = true; for (int i = 0; i < seqs.Length; i++) { if (string.IsNullOrEmpty(seqs[i])) continue; var sequence = SequenceInfoHelper.GetInfo(seqs[i]); if ((sequence == null || sequence.Steps == null || sequence.Steps.Count == 0) && !string.IsNullOrEmpty(sequence.Name)) { reason = $"Invalid sequence {seqs[i]}"; return false; } isAllSequenceNull = false; foreach (var step in sequence.Steps) { foreach (var module in step.StepModules) { if (!SC.GetValueOrDefault($"System.SetUp.{module}.IsInstalled")) { reason = $"Invalid sequence {seqs[i]}, {module} is uninstalled"; return false; } } if (step.StepModules.Any(x => x == ModuleName.Aligner)) { if (!SC.GetValueOrDefault("System.SetUp.Aligner.IsInstalled")) { reason = $"Invalid sequence {seqs[i]}, Aligner is uninstalled"; return false; } if (step.AlignAngle < 0 || step.AlignAngle > 360) { reason = $"Invalid sequence {seqs[i]}, Aligner angle parameter is not valid"; return false; } } } } if (isAllSequenceNull) { reason = $"Invalid sequence, sequence are all null "; return false; } return true; } private void CalcedThroughput(List pjRemoveList) { int count = 0; foreach (var pj in pjRemoveList) { foreach (var pjSlotWafer in pj.SlotWafers) { WaferInfo waferInfo = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); if (waferInfo.ProcessState == EnumWaferProcessStatus.Completed && waferInfo.SubstTransStatus == SubstrateTransportStatus.AtDestination) { count++; } } } _throughputData.UpdateCalced(count); } private void CalcingThroughput() { if (!_lstControlJobs.Any()) { _throughputData.Pause(); return; } if (!_lstControlJobs.Any(x => x.State == EnumControlJobState.Executing)) { _throughputData.Pause(); return; } _throughputData.BeginCalc(); int count = 0; foreach (var pj in _lstProcessJobs) { foreach (var pjSlotWafer in pj.SlotWafers) { WaferInfo waferInfo = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); if (waferInfo.ProcessState == EnumWaferProcessStatus.Completed && waferInfo.SubstTransStatus == SubstrateTransportStatus.AtDestination) { count++; } } } if (count > _throughputData.CalcingProcessedCount) { _throughputData.UpdateCalcing(count); } } #endregion #region Checked public bool CheckCanProcess(ModuleName pmModule) { var wafer0 = WaferManager.Instance.GetWafer(pmModule, 0); var wafer1 = WaferManager.Instance.GetWafer(pmModule, 1); //如果PM腔体中有wafer,但不需要process,则不允许PM进行Process if (!wafer0.IsEmpty && !CheckWaferNeedProcess(pmModule, 0, pmModule)) { return false; } if (!wafer1.IsEmpty && !CheckWaferNeedProcess(pmModule, 1, pmModule)) { return false; } //如果PM腔体中两片wafer都需要Process,并且recipe名字相同,则允许process if (CheckWaferNeedProcess(pmModule, 0, pmModule) && CheckWaferNeedProcess(pmModule, 1, pmModule) && wafer0.ProcessJob.Sequence.Name == wafer1.ProcessJob.Sequence.Name) { return true; } //如果PM腔体中只有一片wafer需要Process,并且LLA/LLB/TMRobot中找不到相同SequenceName的wafer时,允许process if (CheckWaferNeedProcess(pmModule, 0, pmModule)) { var pmWafer = WaferManager.Instance.GetWafer(pmModule, 0); if (pmWafer.ProcessJob != null) { if (GetSameSequenceUnprocessedWaferCount(new List() { ModuleName.LLA, ModuleName.LLB, ModuleName.TMRobot }, pmWafer.ProcessJob.Sequence.Name) == 0) return true; } } if (CheckWaferNeedProcess(pmModule, 1, pmModule)) { var pmWafer = WaferManager.Instance.GetWafer(pmModule, 1); if (pmWafer.ProcessJob != null) { if (GetSameSequenceUnprocessedWaferCount(new List() { ModuleName.LLA, ModuleName.LLB, ModuleName.TMRobot }, pmWafer.ProcessJob.Sequence.Name) == 0) return true; } } return false; } public bool CheckCanPlaceWaferToPM(ModuleName pmModule, WaferInfo wafer) { if (!_tmRobot.IsAvailable) return false; if (!CheckWaferNeedProcess(wafer, pmModule)) return false; var wafer0 = WaferManager.Instance.GetWafer(pmModule, 0); if (!wafer0.IsEmpty && !CheckWaferNeedProcess(pmModule, 0, pmModule)) { return false; } var wafer1 = WaferManager.Instance.GetWafer(pmModule, 1); if (!wafer1.IsEmpty && !CheckWaferNeedProcess(pmModule, 1, pmModule)) { return false; } if (CheckWaferNeedProcess(wafer0, pmModule) && CheckWaferNeedProcess(wafer, pmModule) && wafer0.ProcessJob.Sequence.Name == wafer.ProcessJob.Sequence.Name) return true; if (CheckWaferNeedProcess(wafer1, pmModule) && CheckWaferNeedProcess(wafer, pmModule) && wafer1.ProcessJob.Sequence.Name == wafer.ProcessJob.Sequence.Name) return true; if (wafer0.IsEmpty && wafer1.IsEmpty && CheckWaferNeedProcess(wafer, pmModule)) return true; return false; } public bool CheckCanPickWaferFromPM(ModuleName pmModule, int slot) { if (!_tmRobot.IsAvailable) return false; var wafer = WaferManager.Instance.GetWafer(pmModule, slot); if (!wafer.IsEmpty && wafer.ProcessJob != null && wafer.ProcessJob.Sequence != null && wafer.ProcessJob.Sequence.Steps != null && wafer.ProcessState == EnumWaferProcessStatus.Completed) return true; return false; } public int GetSameSequenceUnprocessedWaferCount(List modules, string sequenceName) { int count = 0; for (int i = 0; i < modules.Count; i++) { var wafers = WaferManager.Instance.GetWafers(modules[i]); for (int j = 0; j < wafers.Length; j++) { if (CheckWaferNeedProcess(wafers[j]) && wafers[j].ProcessJob != null && wafers[j].ProcessJob.Sequence.Name == sequenceName) count++; } } return count; } public int? GetAvilableLoadLockSlotForEfemRobotPlace(SchedulerLoadLock ll) { if (!ll.IsAvailable) return null; var wafers = WaferManager.Instance.GetWafers(ll.Module); for (int i = 0; i < wafers.Length; i++) { if (wafers[i].IsEmpty && ll.IsReadyForPlace(ModuleName.TMRobot, Hand.Blade1, i)) return i; } return null; } public int? GetAvilableLoadLockSlotForTMRobotPlace(SchedulerLoadLock ll) { if (!ll.IsAvailable) return null; var wafers = WaferManager.Instance.GetWafers(ll.Module); for (int i = 0; i < wafers.Length; i++) { if (wafers[i].IsEmpty && ll.IsReadyForPlace(ModuleName.TMRobot, Hand.Blade1, i)) return i; } return null; } public int? GetAvilableLoadLockSlotForTMRobotPick(SchedulerLoadLock ll) { if (!ll.IsAvailable) return null; var wafers = WaferManager.Instance.GetWafers(ll.Module); foreach (var pm in _lstPms) { var pmWafer0 = WaferManager.Instance.GetWafer(pm.Module, 0); var pmWafer1 = WaferManager.Instance.GetWafer(pm.Module, 1); //如果腔体中两片都满了,则跳过 if (!pmWafer0.IsEmpty && !pmWafer1.IsEmpty) continue; //如果腔体中两片都为空,则取一片放入 if (pmWafer0.IsEmpty && pmWafer1.IsEmpty) { for (int i = 0; i < wafers.Length; i++) { if (!wafers[i].IsEmpty && ll.IsReadyForPick(ModuleName.TMRobot, Hand.Blade1, i) && CheckWaferNeedProcess(ll.Module, i)) return i; } } //如果腔体中一片为空,则取一片相同recipeName的放入 if (!pmWafer0.IsEmpty || !pmWafer1.IsEmpty) { var wafer = !pmWafer0.IsEmpty ? pmWafer0 : pmWafer1; if (!CheckWaferNeedProcess(wafer)) return null; var tmWafers = WaferManager.Instance.GetWafers(ModuleName.TMRobot); //判断TMRobot是否已经存在相同recipeName的wafer for (int i = 0; i < tmWafers.Length; i++) { if (CheckWaferNeedProcess(tmWafers[i]) && tmWafers[i].ProcessJob.Sequence.Name == wafer.ProcessJob.Sequence.Name) return null; } for (int i = 0; i < wafers.Length; i++) { if (!wafers[i].IsEmpty && ll.IsReadyForPick(ModuleName.TMRobot, Hand.Blade1, i) && CheckWaferNeedProcess(ll.Module, i) && wafers[i].ProcessJob.Sequence.Name == wafer.ProcessJob.Sequence.Name) return i; } } } return null; } public List> GetAvilableLoadLockSlotForTMRobotPick() { List> result = new List>(); foreach (var ll in _lstLls) { if (!ll.IsAvailable) continue; var wafers = WaferManager.Instance.GetWafers(ll.Module); foreach (var pm in _lstPms) { var pmWafer0 = WaferManager.Instance.GetWafer(pm.Module, 0); var pmWafer1 = WaferManager.Instance.GetWafer(pm.Module, 1); //如果腔体中两片都满了,则跳过 if (!pmWafer0.IsEmpty && !pmWafer1.IsEmpty) continue; //如果腔体中两片都为空,则取一片放入 if (pmWafer0.IsEmpty && pmWafer1.IsEmpty) { for (int i = 0; i < wafers.Length; i++) { if (!wafers[i].IsEmpty && ll.IsReadyForPick(ModuleName.TMRobot, Hand.Blade1, i) && CheckWaferNeedProcess(ll.Module, i, pm.Module) && !result.Exists(x => x.Item1 == ll && x.Item2 == i)) result.Add(new Tuple(ll, i)); } } //如果腔体中一片为空,则取一片相同recipeName的放入 if (!pmWafer0.IsEmpty || !pmWafer1.IsEmpty) { var wafer = !pmWafer0.IsEmpty ? pmWafer0 : pmWafer1; if (!CheckWaferNeedProcess(wafer)) continue; var tmWafers = WaferManager.Instance.GetWafers(ModuleName.TMRobot); //判断TMRobot是否已经存在相同recipeName的wafer for (int i = 0; i < tmWafers.Length; i++) { if (CheckWaferNeedProcess(tmWafers[i]) && tmWafers[i].ProcessJob.Sequence.Name == wafer.ProcessJob.Sequence.Name) continue; } for (int i = 0; i < wafers.Length; i++) { if (!wafers[i].IsEmpty && ll.IsReadyForPick(ModuleName.TMRobot, Hand.Blade1, i) && CheckWaferNeedProcess(ll.Module, i, pm.Module) && wafers[i].ProcessJob.Sequence.Name == wafer.ProcessJob.Sequence.Name && !result.Exists(x => x.Item1 == ll && x.Item2 == i)) result.Add(new Tuple(ll, i)); } } } } return result; } public List> FindAllLoadLockSlotForTMRobotPick() { List> result = new List>(); foreach (var ll in _lstLls) { var wafers = WaferManager.Instance.GetWafers(ll.Module); foreach (var pm in _lstPms) { var pmWafer0 = WaferManager.Instance.GetWafer(pm.Module, 0); var pmWafer1 = WaferManager.Instance.GetWafer(pm.Module, 1); //如果腔体中两片都满了,则跳过 if (!pmWafer0.IsEmpty && !pmWafer1.IsEmpty) continue; //如果腔体中两片都为空,则取一片放入 if (pmWafer0.IsEmpty && pmWafer1.IsEmpty) { for (int i = 0; i < wafers.Length; i++) { if (!wafers[i].IsEmpty && ll.IsReadyForPick(ModuleName.TMRobot, Hand.Blade1, i) && CheckWaferNeedProcess(ll.Module, i, pm.Module) && !result.Exists(x => x.Item1 == ll && x.Item2 == i)) result.Add(new Tuple(ll, i)); } } //如果腔体中一片为空,则取一片相同recipeName的放入 if (!pmWafer0.IsEmpty || !pmWafer1.IsEmpty) { var wafer = !pmWafer0.IsEmpty ? pmWafer0 : pmWafer1; if (!CheckWaferNeedProcess(wafer)) continue; var tmWafers = WaferManager.Instance.GetWafers(ModuleName.TMRobot); //判断TMRobot是否已经存在相同recipeName的wafer for (int i = 0; i < tmWafers.Length; i++) { if (CheckWaferNeedProcess(tmWafers[i]) && tmWafers[i].ProcessJob.Sequence.Name == wafer.ProcessJob.Sequence.Name) continue; } for (int i = 0; i < wafers.Length; i++) { if (!wafers[i].IsEmpty && ll.IsReadyForPick(ModuleName.TMRobot, Hand.Blade1, i) && CheckWaferNeedProcess(ll.Module, i, pm.Module) && wafers[i].ProcessJob.Sequence.Name == wafer.ProcessJob.Sequence.Name && !result.Exists(x => x.Item1 == ll && x.Item2 == i)) result.Add(new Tuple(ll, i)); } } } } return result; } public int GetWaferCountLoadLockToPM(ModuleName pmModule) { int result = 0; foreach (var ll in _lstLls) { var wafers = WaferManager.Instance.GetWafers(ll.Module); foreach (var pm in _lstPms) { if (pm.Module != pmModule) continue; for (int i = 0; i < wafers.Length; i++) { if (!wafers[i].IsEmpty && CheckWaferNeedProcess(ll.Module, i, pm.Module)) result++; } } } return result; } #endregion } }