|
@@ -2,12 +2,14 @@
|
|
|
using System.Collections.Generic;
|
|
|
using System.Linq;
|
|
|
|
|
|
+using Aitex.Core.Common;
|
|
|
using Aitex.Core.RT.Routine;
|
|
|
using Aitex.Core.RT.SCCore;
|
|
|
using Aitex.Sorter.Common;
|
|
|
using Aitex.Core.RT.Log;
|
|
|
using Aitex.Core.Util;
|
|
|
using Aitex.Core.RT.DataCenter;
|
|
|
+using Aitex.Core.RT.RecipeCenter;
|
|
|
using Aitex.Core.RT.Fsm;
|
|
|
|
|
|
using MECF.Framework.Common.Jobs;
|
|
@@ -15,6 +17,7 @@ using MECF.Framework.Common.Routine;
|
|
|
using MECF.Framework.Common.Equipment;
|
|
|
using MECF.Framework.Common.SubstrateTrackings;
|
|
|
using MECF.Framework.Common.Schedulers;
|
|
|
+using MECF.Framework.Common.DBCore;
|
|
|
|
|
|
using Venus_Core;
|
|
|
|
|
@@ -82,6 +85,9 @@ namespace Venus_RT.Modules
|
|
|
Dictionary<SlotItem, Guid> _vacWaferTargets = new Dictionary<SlotItem, Guid>();
|
|
|
Dictionary<SlotItem, Guid> _atmWaferTargets = new Dictionary<SlotItem, Guid>();
|
|
|
|
|
|
+ private SchedulerFACallback _faCallback;
|
|
|
+ private SchedulerDBCallback _dbCallback;
|
|
|
+
|
|
|
private bool _isCycleMode;
|
|
|
private int _cycleSetPoint = 0;
|
|
|
private int _cycledCount = 0;
|
|
@@ -90,6 +96,9 @@ namespace Venus_RT.Modules
|
|
|
#region public interface
|
|
|
public AutoCycle()
|
|
|
{
|
|
|
+ _faCallback = new SchedulerFACallback();
|
|
|
+ _dbCallback = new SchedulerDBCallback();
|
|
|
+
|
|
|
InitModules();
|
|
|
|
|
|
DATA.Subscribe("Scheduler.CycledCount", () => _cycledCount);
|
|
@@ -123,6 +132,162 @@ namespace Venus_RT.Modules
|
|
|
{
|
|
|
}
|
|
|
|
|
|
+ public void CreateJob(Dictionary<string, object> param)
|
|
|
+ {
|
|
|
+ _isCycleMode = SC.GetValue<bool>("System.IsCycleMode");
|
|
|
+ _cycleSetPoint = _isCycleMode ? SC.GetValue<int>("System.CycleCount") : 0;
|
|
|
+
|
|
|
+
|
|
|
+ string[] slotSequence = (string[])param["SlotSequence"];
|
|
|
+ string jobId = (string)param["JobId"];
|
|
|
+ string module = (string)param["Module"];
|
|
|
+ //bool autoStart = (bool)param["AutoStart"];
|
|
|
+ string lotId = jobId;
|
|
|
+ if (param.ContainsKey("LotId"))
|
|
|
+ lotId = (string)param["LotId"];
|
|
|
+
|
|
|
+ if (slotSequence.Length != SC.GetValue<int>("EFEM.LoadPort.SlotNumber"))
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"slot sequence parameter not valid, length is {slotSequence.Length}, should be {SC.GetValue<int>("EFEM.LoadPort.SlotNumber")}");
|
|
|
+ _faCallback.JobCreateFailed(module, lotId, jobId, "");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!ModuleHelper.IsLoadPort(ModuleHelper.Converter(module)))
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"{module} should be LoadPort");
|
|
|
+ _faCallback.JobCreateFailed(module, lotId, jobId, "");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (string.IsNullOrEmpty(jobId))
|
|
|
+ {
|
|
|
+ jobId = "CJ_Local_" + module;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_lstControlJobs.Exists(x => x.Name == jobId))
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"{jobId} already created");
|
|
|
+ _faCallback.JobCreateFailed(module, lotId, jobId, "");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ SchedulerLoadPort lp = _atmSchedulers[ModuleHelper.Converter(module)] as SchedulerLoadPort;
|
|
|
+ if (!lp.CheckReadyRunJob())
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"{module} not ready");
|
|
|
+ _faCallback.JobCreateFailed(module, lotId, jobId, "");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ ControlJobInfo cj = new ControlJobInfo();
|
|
|
+ cj.Name = jobId;
|
|
|
+ cj.Module = module;
|
|
|
+ cj.LotName = lotId;
|
|
|
+ cj.LotInnerId = Guid.NewGuid();
|
|
|
+ cj.LotWafers = new List<WaferInfo>();
|
|
|
+ cj.SetState(EnumControlJobState.WaitingForStart);
|
|
|
+
|
|
|
+ Dictionary<string, bool[]> seqSlot = new Dictionary<string, bool[]>();
|
|
|
+ Dictionary<string, List<Tuple<ModuleName, int>>> seqSlotWafers = new Dictionary<string, List<Tuple<ModuleName, int>>>();
|
|
|
+ Dictionary<string, string> indexSequence = new Dictionary<string, string>();
|
|
|
+
|
|
|
+ bool enableGroupBySequence = SC.GetValue<bool>("Scheduler.GroupWaferBySequence");
|
|
|
+ for (int i = 0; i < SC.GetValue<int>("EFEM.LoadPort.SlotNumber"); i++)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(slotSequence[i]) || string.IsNullOrEmpty(slotSequence[i].Trim()))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ string groupName = enableGroupBySequence ? slotSequence[i].Trim() : i.ToString();
|
|
|
+ indexSequence[groupName] = slotSequence[i];
|
|
|
+ if (!seqSlot.ContainsKey(groupName))
|
|
|
+ {
|
|
|
+ seqSlot[groupName] = new bool[SC.GetValue<int>("EFEM.LoadPort.SlotNumber")];
|
|
|
+ }
|
|
|
+ if (!seqSlotWafers.ContainsKey(groupName))
|
|
|
+ {
|
|
|
+ seqSlotWafers[groupName] = new List<Tuple<ModuleName, int>>();
|
|
|
+ }
|
|
|
+ seqSlot[groupName][i] = true;
|
|
|
+
|
|
|
+ if (!WaferManager.Instance.CheckHasWafer(module, i))
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job wafer: {module} slot {i + 1} not in the carrier");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!WaferManager.Instance.CheckWafer(ModuleHelper.Converter(module), i, WaferStatus.Normal))
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job wafer: {module} slot {i + 1} status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Status}");
|
|
|
+ _faCallback.JobCreateFailed(module, lotId, jobId, "");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState != EnumWaferProcessStatus.Idle)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job wafer: {module} slot {i + 1} process status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState}");
|
|
|
+ _faCallback.JobCreateFailed(module, lotId, jobId, "");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ cj.LotWafers.Add(WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i));
|
|
|
+ WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).SequenceName = slotSequence[i];
|
|
|
+
|
|
|
+ seqSlotWafers[groupName].Add(Tuple.Create(ModuleHelper.Converter(module), i));
|
|
|
+
|
|
|
+ cj.JobWaferSize = WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Size;
|
|
|
+
|
|
|
+ LOG.Write(eEvent.EV_ROUTER, ModuleName.System, $"Assigned wafer job, wafer {module}.{i + 1}, sequence: {slotSequence[i]}");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (seqSlotWafers.Count == 0)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"job has not assign wafer");
|
|
|
+ _faCallback.JobCreateFailed(module, lotId, jobId, "");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<ProcessJobInfo> pjs = new List<ProcessJobInfo>();
|
|
|
+ string[] seqs = seqSlot.Keys.ToArray();
|
|
|
+ for (int i = 0; i < seqs.Length; i++)
|
|
|
+ {
|
|
|
+ ProcessJobInfo pj = new ProcessJobInfo();
|
|
|
+ pj.Name = jobId + "_" + (i + 1);
|
|
|
+ pj.Sequence = SequenceInfoHelper.GetInfo(indexSequence[seqs[i]]);
|
|
|
+ pj.ControlJobName = cj.Name;
|
|
|
+ pj.LotName = lotId;
|
|
|
+ pj.SlotWafers = seqSlotWafers[seqs[i]];
|
|
|
+ pj.SetState(EnumProcessJobState.Queued);
|
|
|
+
|
|
|
+ if (!CheckSequencePmReady(pj.Sequence, null, out _, out string reason))
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"no valid chamber for the {reason}");
|
|
|
+ _faCallback.JobCreateFailed(module, lotId, jobId, "");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!SC.GetValue<bool>("System.IsATMMode") && !CheckSequenceRecipeFileValid(pj.Sequence, out reason))
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"recipe file not valid in the sequence, {reason}");
|
|
|
+ _faCallback.JobCreateFailed(module, lotId, jobId, "");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ pjs.Add(pj);
|
|
|
+ }
|
|
|
+
|
|
|
+ _dbCallback.LotUpdate(cj);
|
|
|
+ foreach (var pj in pjs)
|
|
|
+ {
|
|
|
+ cj.ProcessJobNameList.Add(pj.Name);
|
|
|
+ _lstProcessJobs.Add(pj);
|
|
|
+ }
|
|
|
+
|
|
|
+ _lstControlJobs.Add(cj);
|
|
|
+ //AssociatedPMWithLP(cj);
|
|
|
+
|
|
|
+ _faCallback.JobCreated(cj, GetFirstProcessJob(cj));
|
|
|
+ }
|
|
|
+
|
|
|
public bool CheckAllJobDone()
|
|
|
{
|
|
|
foreach (var cj in _lstControlJobs)
|
|
@@ -151,6 +316,141 @@ namespace Venus_RT.Modules
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ public ProcessJobInfo GetFirstProcessJob(ControlJobInfo cj)
|
|
|
+ {
|
|
|
+ foreach (var pj in _lstProcessJobs)
|
|
|
+ {
|
|
|
+ if (pj.ControlJobName == cj.Name)
|
|
|
+ return pj;
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ public void AbortJob(string jobName)
|
|
|
+ {
|
|
|
+ ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName);
|
|
|
+ if (cj == null)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"abort job rejected, not found job with id {jobName}");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<ProcessJobInfo> pjAbortList = new List<ProcessJobInfo>();
|
|
|
+ foreach (var pj in _lstProcessJobs)
|
|
|
+ {
|
|
|
+ if (pj.ControlJobName == cj.Name)
|
|
|
+ {
|
|
|
+ pj.SetState(EnumProcessJobState.Aborting);
|
|
|
+ pjAbortList.Add(pj);
|
|
|
+
|
|
|
+ int unprocessed = 0;
|
|
|
+ int aborted = 0;
|
|
|
+ WaferInfo[] wafers = WaferManager.Instance.GetWaferByProcessJob(pj.Name);
|
|
|
+ foreach (var waferInfo in wafers)
|
|
|
+ {
|
|
|
+ waferInfo.ProcessJob = null;
|
|
|
+ waferInfo.NextSequenceStep = 0;
|
|
|
+ if (waferInfo.ProcessState != EnumWaferProcessStatus.Completed)
|
|
|
+ unprocessed++;
|
|
|
+ }
|
|
|
+ JobDataRecorder.EndPJ(pj.InnerId.ToString(), aborted, unprocessed);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _faCallback.JobAborted(cj, GetFirstProcessJob(cj));
|
|
|
+ _dbCallback.LotFinished(cj);
|
|
|
+ foreach (var pj in pjAbortList)
|
|
|
+ {
|
|
|
+ _lstProcessJobs.Remove(pj);
|
|
|
+ }
|
|
|
+
|
|
|
+ _lstControlJobs.Remove(cj);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void StopJob(string jobName)
|
|
|
+ {
|
|
|
+ ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName);
|
|
|
+ if (cj == null)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"stop job rejected, not found job with id {jobName}");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var pj in _lstProcessJobs)
|
|
|
+ {
|
|
|
+ if (pj.ControlJobName == cj.Name)
|
|
|
+ {
|
|
|
+ pj.SetState(EnumProcessJobState.Stopping);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _faCallback.JobStopped(cj, GetFirstProcessJob(cj));
|
|
|
+ _dbCallback.LotFinished(cj);
|
|
|
+ }
|
|
|
+ public void ResumeJob(string jobName)
|
|
|
+ {
|
|
|
+ ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName);
|
|
|
+ if (cj == null)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"resume job rejected, not found job with id {jobName}");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cj.State == EnumControlJobState.Paused)
|
|
|
+ {
|
|
|
+ cj.SetState(EnumControlJobState.Executing);
|
|
|
+ }
|
|
|
+
|
|
|
+ _faCallback.JobResumed(cj, GetFirstProcessJob(cj));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void PauseJob(string jobName)
|
|
|
+ {
|
|
|
+ ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName);
|
|
|
+ if (cj == null)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"pause job rejected, not found job with id {jobName}");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cj.State == EnumControlJobState.Executing)
|
|
|
+ {
|
|
|
+ cj.SetState(EnumControlJobState.Paused);
|
|
|
+ }
|
|
|
+ _faCallback.JobPaused(cj, GetFirstProcessJob(cj));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public void StartJob(string jobName)
|
|
|
+ {
|
|
|
+ ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName);
|
|
|
+ if (cj == null)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ROUTER, ModuleName.System, $"start job rejected, not found job with id {jobName}");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cj.State == EnumControlJobState.WaitingForStart)
|
|
|
+ {
|
|
|
+ cj.SetState(EnumControlJobState.Executing);
|
|
|
+ //PreJobClean(cj);
|
|
|
+
|
|
|
+ (_atmSchedulers[ModuleHelper.Converter(cj.Module)] as SchedulerLoadPort).NoteJobStart();
|
|
|
+
|
|
|
+ cj.StartTime = DateTime.Now;
|
|
|
+
|
|
|
+ _dbCallback.LotCreated(cj);
|
|
|
+ _faCallback.JobStarted(cj, GetFirstProcessJob(cj));
|
|
|
+
|
|
|
+ //if (!_blockingWatcher.IsRunning)
|
|
|
+ // _blockingWatcher.Restart();
|
|
|
+
|
|
|
+ //ResetTraceFlag();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
#endregion
|
|
|
|
|
|
private void InitModules()
|
|
@@ -693,37 +993,36 @@ namespace Venus_RT.Modules
|
|
|
{
|
|
|
var inSlots = new List<int>();
|
|
|
|
|
|
- return inSlots;
|
|
|
-
|
|
|
|
|
|
//if (GetSystemInnerWaferCount() >= SystemInternalWaferCount || GetBufferWaferCount() > _maxBufferWaferCount)
|
|
|
- // return new SlotItem(ModuleName.System, -1);
|
|
|
-
|
|
|
- //foreach (var cj in _lstControlJobs)
|
|
|
- //{
|
|
|
- // if (lpModule != ModuleName.System && (cj.Module != lpModule.ToString()))
|
|
|
- // continue;
|
|
|
-
|
|
|
- // if (cj.State == EnumControlJobState.Executing)
|
|
|
- // {
|
|
|
- // foreach (var pj in _lstProcessJobs)
|
|
|
- // {
|
|
|
- // if (pj.ControlJobName == cj.Name && pj.State == EnumProcessJobState.Processing)
|
|
|
- // {
|
|
|
- // foreach (var pjSlotWafer in pj.SlotWafers)
|
|
|
- // {
|
|
|
- // if (CheckWaferNeedProcessAndPMAvailable(pjSlotWafer.Item1, pjSlotWafer.Item2))
|
|
|
- // {
|
|
|
- // return new SlotItem(pjSlotWafer.Item1, pjSlotWafer.Item2);
|
|
|
- // }
|
|
|
-
|
|
|
- // }
|
|
|
- // }
|
|
|
- // }
|
|
|
- // }
|
|
|
- //}
|
|
|
-
|
|
|
- //return new SlotItem(ModuleName.System, -1);
|
|
|
+ //return new SlotItem(ModuleName.System, -1);
|
|
|
+
|
|
|
+ foreach (var cj in _lstControlJobs)
|
|
|
+ {
|
|
|
+ if (lp != ModuleName.System && (cj.Module != lp.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);
|
|
|
+ inSlots.Add(pjSlotWafer.Item2);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return inSlots;
|
|
|
}
|
|
|
|
|
|
private bool ProcessAlignerEFEMRobotTask(ModuleName aligner)
|
|
@@ -808,5 +1107,124 @@ namespace Venus_RT.Modules
|
|
|
}
|
|
|
|
|
|
#endregion Atm System
|
|
|
+
|
|
|
+ #region Sequence validation
|
|
|
+ private bool CheckSequencePmReady(SequenceInfo seq, List<ModuleName> pmOccupied, out List<ModuleName> pmUsed, out string reason)
|
|
|
+ {
|
|
|
+ pmUsed = new List<ModuleName>();
|
|
|
+ reason = "";
|
|
|
+ bool pmIsReady = true;
|
|
|
+
|
|
|
+ for (int i = 0; i < seq.Steps.Count; i++)
|
|
|
+ {
|
|
|
+ SequenceStepInfo stepInfo = seq.Steps[i];
|
|
|
+ bool hasPm = false;
|
|
|
+ foreach (var module in stepInfo.StepModules)
|
|
|
+ {
|
|
|
+ if (!ModuleHelper.IsPm(module))
|
|
|
+ {
|
|
|
+ hasPm = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ModuleHelper.IsInstalled(module) && (pmOccupied == null || !pmOccupied.Contains(module)))
|
|
|
+ {
|
|
|
+ hasPm = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!pmUsed.Contains(module))
|
|
|
+ pmUsed.Add(module);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pmIsReady && !hasPm)
|
|
|
+ {
|
|
|
+ reason = $"Step {i + 1} no valid PM, " + string.Join("|", stepInfo.StepModules);
|
|
|
+ pmIsReady = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return pmIsReady;
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool CheckSequenceRecipeFileValid(SequenceInfo seq, out string reason)
|
|
|
+ {
|
|
|
+ for (int i = 0; i < seq.Steps.Count; i++)
|
|
|
+ {
|
|
|
+ SequenceStepInfo stepInfo = seq.Steps[i];
|
|
|
+ foreach (var module in stepInfo.StepModules)
|
|
|
+ {
|
|
|
+ if (ModuleHelper.IsPm(module))
|
|
|
+ {
|
|
|
+ string attr = $"{module}Recipe";
|
|
|
+ if (stepInfo.StepParameter.ContainsKey(attr)
|
|
|
+ && !string.IsNullOrEmpty((string)stepInfo.StepParameter[attr]))
|
|
|
+ {
|
|
|
+ var recipeName = (string)stepInfo.StepParameter[attr];
|
|
|
+ if (!string.IsNullOrEmpty(recipeName))
|
|
|
+ {
|
|
|
+ var recipeContent =
|
|
|
+ RecipeFileManager.Instance.LoadRecipe($"", recipeName, false);
|
|
|
+ if (string.IsNullOrEmpty(recipeContent))
|
|
|
+ {
|
|
|
+ reason = $"Can not find recipe file{recipeName}";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ reason = "";
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool CheckWaferNeedProcessAndPMAvailable(ModuleName waferModule, int waferSlot, ModuleName processIn = ModuleName.System)
|
|
|
+ {
|
|
|
+ WaferInfo wafer = WaferManager.Instance.GetWafer(waferModule, waferSlot);
|
|
|
+
|
|
|
+ if (wafer.IsEmpty)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ for (int i = wafer.NextSequenceStep; i < wafer.ProcessJob.Sequence.Steps.Count; i++)
|
|
|
+ {
|
|
|
+ var hasPmAvailable = false;
|
|
|
+ foreach (var stepModule in wafer.ProcessJob.Sequence.Steps[i].StepModules)
|
|
|
+ {
|
|
|
+ if (!ModuleHelper.IsPm(stepModule))
|
|
|
+ break;
|
|
|
+
|
|
|
+ var pm = _vacSchedulers[stepModule] as SchedulerPM;
|
|
|
+ if (pm != null)
|
|
|
+ {
|
|
|
+ if (pm.IsAvailable || (pm.IsOnline && pm.Task != SchedulerModule.TaskType.PreJobProcess))
|
|
|
+ {
|
|
|
+ hasPmAvailable = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!hasPmAvailable)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (processIn == ModuleName.System)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ if (wafer.ProcessJob.Sequence.Steps[i].StepModules
|
|
|
+ .Contains(processIn))
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
}
|
|
|
}
|