3432 lines
126 KiB
C#
3432 lines
126 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Windows.Media.Imaging;
|
||
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.DataCenter;
|
||
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 SicRT.Equipments.Schedulers;
|
||
using SicRT.Modules.Schedulers;
|
||
using SicRT.Scheduler;
|
||
|
||
|
||
namespace SicRT.Modules
|
||
{
|
||
public partial class AutoTransfer : SchedulerModuleFactory
|
||
{
|
||
public class TrayInfo
|
||
{
|
||
public int TrayModule { get; set; }
|
||
public int TraySlot { get; set; }
|
||
public int TrayProcessCount { get; set; }
|
||
}
|
||
|
||
private List<ControlJobInfo> _lstNeedMapJob = new List<ControlJobInfo>();
|
||
private List<ControlJobInfo> _lstControlJobs = new List<ControlJobInfo>();
|
||
private List<ProcessJobInfo> _lstProcessJobs = new List<ProcessJobInfo>();
|
||
private List<TrayInfo> _lstProcessedTrayInfo = new List<TrayInfo>(); //保存石墨盘的进入腔体的信息(最多4次工艺需要换盘)
|
||
|
||
private int _maxTrayCount = 2; //可以同时运行的石墨盘总数
|
||
private bool _cassProcessOnebyOne = false; //Cassette是左右交互取片还是一个Cassette取完再取另一个
|
||
private R_TRIG NotifyTrayExhausted = new R_TRIG();
|
||
|
||
private const string LogSource = "Scheduler";
|
||
private SchedulerDBCallback _dbCallback;
|
||
//private const string StatsNameTotalRunningWafer = "TotalRunningWafer";
|
||
|
||
public bool CassetteFromAToB => SC.GetValue<bool>("System.Scheduler.CassetteFromAToB");
|
||
|
||
private bool _isCycleMode;
|
||
private int _cycleSetPoint = 0;
|
||
private int _cycledCount = 0;
|
||
private int _cycledWafer = 0;
|
||
private string _curRecipeName = "";
|
||
private string _curSequenceName = "";
|
||
|
||
private bool _isModuleErrorPrevious;
|
||
private bool[] _isPMErrorPrevious = new bool[2];
|
||
|
||
private string _timeBuffer1 { get; set; }
|
||
private string _timeBuffer2 { get; set; }
|
||
private string _timeBuffer3 { get; set; }
|
||
|
||
private Dictionary<string, DateTime> _bufferWaferInfo = new Dictionary<string, DateTime>();
|
||
|
||
private string _sAutoTransferConditionText;
|
||
public string AutoTransferConditionText
|
||
{
|
||
get
|
||
{
|
||
return _sAutoTransferConditionText;
|
||
}
|
||
set
|
||
{
|
||
_sAutoTransferConditionText = value;
|
||
}
|
||
}
|
||
private string _sWhichCondition;
|
||
public string WhichCondition
|
||
{
|
||
get
|
||
{
|
||
return _sWhichCondition;
|
||
}
|
||
set
|
||
{
|
||
_sWhichCondition = value;
|
||
}
|
||
}
|
||
//
|
||
|
||
public AutoTransfer()
|
||
{
|
||
_dbCallback = new SchedulerDBCallback();
|
||
|
||
DATA.Subscribe("Scheduler.IsCycleMode", () => _isCycleMode);
|
||
DATA.Subscribe("Scheduler.CycledCount", () => _cycledCount);
|
||
DATA.Subscribe("Scheduler.CycleSetPoint", () => _cycleSetPoint);
|
||
DATA.Subscribe("Scheduler.RecipeName", () => _curRecipeName);
|
||
DATA.Subscribe("Scheduler.SequenceName", () => _curSequenceName);
|
||
|
||
DATA.Subscribe("Scheduler.TimeBuffer1", () => _timeBuffer1);
|
||
DATA.Subscribe("Scheduler.TimeBuffer2", () => _timeBuffer2);
|
||
DATA.Subscribe("Scheduler.TimeBuffer3", () => _timeBuffer3);
|
||
|
||
//显示所需条件信息用
|
||
DATA.Subscribe("Scheduler.AutoTransferConditionText", () => AutoTransferConditionText);
|
||
//要显示哪个任务的条件
|
||
DATA.Subscribe("Scheduler.WhichCondition", () => WhichCondition);
|
||
|
||
//DATA.Subscribe("CassAL.SlotSequenceList", () => currentSlotSequenceList["CassAL"]);
|
||
DATA.Subscribe("CassAL.LocalJobName", () =>
|
||
{
|
||
var jb = _lstControlJobs.FirstOrDefault(x => x.Module == "CassAL");
|
||
if (jb != null)
|
||
return jb.Name;
|
||
return "";
|
||
});
|
||
DATA.Subscribe("CassAL.LocalJobStatus", () =>
|
||
{
|
||
var jb = _lstControlJobs.FirstOrDefault(x => x.Module == "CassAL");
|
||
if (jb != null)
|
||
return jb.State.ToString();
|
||
return "";
|
||
});
|
||
//DATA.Subscribe("CassAR.SlotSequenceList", () => currentSlotSequenceList["CassAR"]);
|
||
DATA.Subscribe("CassAR.LocalJobName", () =>
|
||
{
|
||
var jb = _lstControlJobs.FirstOrDefault(x => x.Module == "CassAR");
|
||
if (jb != null)
|
||
return jb.Name;
|
||
return "";
|
||
});
|
||
DATA.Subscribe("CassAR.LocalJobStatus", () =>
|
||
{
|
||
var jb = _lstControlJobs.FirstOrDefault(x => x.Module == "CassAR");
|
||
if (jb != null)
|
||
return jb.State.ToString();
|
||
return "";
|
||
});
|
||
|
||
OP.Subscribe($"{ModuleName.TM}.ResetTask", (string cmd, object[] args) =>
|
||
{
|
||
_tmRobot.ResetTask();
|
||
return true;
|
||
});
|
||
|
||
OP.Subscribe($"{ModuleName.EFEM}.ResetTask", (string cmd, object[] args) =>
|
||
{
|
||
_waferRobot.ResetTask();
|
||
_trayRobot.ResetTask();
|
||
return true;
|
||
});
|
||
|
||
OP.Subscribe($"{ModuleName.WaferRobot}.ResetTask", (string cmd, object[] args) =>
|
||
{
|
||
_waferRobot.ResetTask();
|
||
return true;
|
||
});
|
||
|
||
OP.Subscribe($"{ModuleName.TrayRobot}.ResetTask", (string cmd, object[] args) =>
|
||
{
|
||
_trayRobot.ResetTask();
|
||
return true;
|
||
});
|
||
}
|
||
|
||
public bool HasJobRunning
|
||
{
|
||
get { return _lstControlJobs.Count > 0; }
|
||
}
|
||
|
||
|
||
public void Clear()
|
||
{
|
||
_tmRobot.ResetTask();
|
||
foreach (var pm in _lstPms)
|
||
{
|
||
pm.ResetTask();
|
||
}
|
||
foreach (var cass in _lstCassettes)
|
||
{
|
||
cass.ResetTask();
|
||
}
|
||
_load.ResetTask();
|
||
_unload.ResetTask();
|
||
_buffer.ResetTask();
|
||
_cassetteBL.ResetTask();
|
||
|
||
_lstControlJobs.Clear();
|
||
_lstProcessJobs.Clear();
|
||
_bufferWaferInfo.Clear();
|
||
}
|
||
|
||
public void ResetTask()
|
||
{
|
||
if (!_load.IsOnline)
|
||
{
|
||
_load.ResetTask();
|
||
}
|
||
if (!_unload.IsOnline)
|
||
{
|
||
_unload.ResetTask();
|
||
}
|
||
if (!_buffer.IsOnline)
|
||
{
|
||
_buffer.ResetTask();
|
||
}
|
||
if (!_cassetteBL.IsOnline)
|
||
{
|
||
_cassetteBL.ResetTask();
|
||
}
|
||
|
||
foreach (var pm in _lstPms)
|
||
{
|
||
if (!pm.IsOnline)
|
||
{
|
||
pm.ResetTask();
|
||
}
|
||
}
|
||
foreach (var cass in _lstCassettes)
|
||
{
|
||
if (!cass.IsOnline)
|
||
{
|
||
cass.ResetTask();
|
||
}
|
||
}
|
||
}
|
||
|
||
public void GetConfig()
|
||
{
|
||
_cycledCount = 0;
|
||
|
||
_isCycleMode = SC.GetValue<bool>("System.IsCycleMode");
|
||
_cycleSetPoint = _isCycleMode ? SC.GetValue<int>("System.CycleCount") : 0;
|
||
_maxTrayCount = _lstPms.Count * 2;
|
||
}
|
||
|
||
#region Job Management
|
||
|
||
public bool CreateJob(Dictionary<string, object> param)
|
||
{
|
||
string reason = "";
|
||
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 (CheckModuleHaveWaferWithNoJob(out reason))
|
||
{
|
||
EV.PostWarningLog(LogSource, $"{reason}");
|
||
return false;
|
||
}
|
||
|
||
if (!ValidateSequence(slotSequence, out reason))
|
||
{
|
||
EV.PostWarningLog(LogSource, $"{reason}");
|
||
return false;
|
||
}
|
||
|
||
if (slotSequence.Length != 25)
|
||
{
|
||
reason = $"slot sequence parameter not valid, length is {slotSequence.Length}, should be 25";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return false;
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(jobId))
|
||
{
|
||
jobId = "CJ_Local_" + module + DateTime.Now;
|
||
}
|
||
|
||
if (_lstControlJobs.Exists(x => x.Name == jobId))
|
||
{
|
||
reason = $"LotID : {jobId} already created";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return false;
|
||
}
|
||
|
||
|
||
ControlJobInfo cj = new ControlJobInfo();
|
||
cj.Name = jobId;
|
||
cj.Module = module;
|
||
cj.LotName = jobId;
|
||
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>("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<Tuple<ModuleName, int>>();
|
||
}
|
||
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);
|
||
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);
|
||
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);
|
||
return false;
|
||
}
|
||
if (wafer.ProcessState != EnumWaferProcessStatus.Idle)
|
||
{
|
||
reason = $"specifies wafer: {module} slot {i + 1} process state is not idle";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return false;
|
||
}
|
||
if (wafer.ProcessJob != null)
|
||
{
|
||
reason = $"specifies wafer: {module} slot {i + 1} ProcessJob is not null";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return false;
|
||
}
|
||
if (wafer.SubstE90Status != EnumE90Status.NeedProcessing)
|
||
{
|
||
reason = $"specifies wafer: {module} slot {i + 1} SubstE90Status is not NeedProcessing";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
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);
|
||
return false;
|
||
}
|
||
|
||
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 innerReason))
|
||
//{
|
||
// reason = $"no valid chamber for the {innerReason}";
|
||
// EV.PostWarningLog(LogSource, reason);
|
||
// return false;
|
||
//}
|
||
//if (!CheckSequenceRecipeFileValid(pj.Sequence, out reason))
|
||
//{
|
||
// reason = $"recipe file not valid in the sequence, {reason}";
|
||
// EV.PostWarningLog(LogSource, reason);
|
||
// return false;
|
||
//}
|
||
|
||
//if (!CheckSequenceOrderOk(pj.Sequence, out reason))
|
||
//{
|
||
// reason = $"sequence path not valid, {reason}";
|
||
// EV.PostWarningLog(LogSource, reason);
|
||
// 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++;
|
||
}
|
||
}
|
||
|
||
CarrierManager.Instance.DeleteCarrier(cj.Module);
|
||
CarrierManager.Instance.CreateCarrier(cj.Module);
|
||
CarrierManager.Instance.UpdateCarrierId(cj.Module, $"{cj.Module} {DateTime.Now}");
|
||
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 CreateJobEx(Dictionary<string, object> param)
|
||
{
|
||
string reason = "";
|
||
string[] slotSequence = (string[])param["SlotSequence"];
|
||
string jobId = (string)param["JobId"];
|
||
string module = (string)param["Module"];
|
||
bool autoStart = (bool)param["AutoStart"];
|
||
|
||
if (slotSequence.Length != 25)
|
||
{
|
||
reason = $"slot sequence parameter not valid, slot count is {slotSequence.Length}, should be 25";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return false;
|
||
}
|
||
|
||
|
||
if (!ValidateSequence(slotSequence, out reason))
|
||
{
|
||
EV.PostWarningLog(LogSource, $"{reason}");
|
||
return false;
|
||
}
|
||
|
||
|
||
if (string.IsNullOrEmpty(jobId))
|
||
{
|
||
jobId = "CJ_Local_" + module;
|
||
}
|
||
|
||
for (int i = 0; i < 25; i++)
|
||
{
|
||
if (string.IsNullOrEmpty(slotSequence[i]) || string.IsNullOrEmpty(slotSequence[i].Trim()))
|
||
{
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (CheckModuleHaveWaferWithNoJob(out reason))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
|
||
|
||
|
||
//bool cassAHasWafer = false;
|
||
//bool cassBHasWafer = false;
|
||
//for (int i = 0; i < 25; i++)
|
||
//{
|
||
// if (_cassetteAL.HasWafer(i))
|
||
// {
|
||
// cassAHasWafer = true;
|
||
// break;
|
||
// }
|
||
//}
|
||
//for (int i = 0; i < 25; i++)
|
||
//{
|
||
// if (_cassetteAR.HasWafer(i))
|
||
// {
|
||
// cassBHasWafer = true;
|
||
// break;
|
||
// }
|
||
//}
|
||
|
||
////从CassetteA出发,运行流程后回到另一个Cassette,需要一个Cassette有片子,一个Cassette没有片子
|
||
//if (_cassetteFromAToB)
|
||
//{
|
||
// if (cassAHasWafer && cassBHasWafer)
|
||
// {
|
||
// EV.PostWarningLog(LogSource, $"Cassette AL and Cassette AR both have wafer,Can not Run Sequence From CassetteAL to CassetteAR!");
|
||
// return;
|
||
// }
|
||
//}
|
||
|
||
//for (int i = 0; i < 25; i++)
|
||
//{
|
||
// if (_cassetteAL.HasWafer(i))
|
||
// {
|
||
// WaferInfo wafer = _cassetteAL.GetWaferInfo(i);
|
||
// if (wafer.ProcessState == EnumWaferProcessStatus.Idle)
|
||
// {
|
||
// wafer.NextSequenceStep = 0;
|
||
// }
|
||
// }
|
||
// if (_cassetteAR.HasWafer(i))
|
||
// {
|
||
// WaferInfo wafer = _cassetteAR.GetWaferInfo(i);
|
||
// if (wafer.ProcessState == EnumWaferProcessStatus.Idle)
|
||
// {
|
||
// wafer.NextSequenceStep = 0;
|
||
// }
|
||
// }
|
||
//}
|
||
|
||
Dictionary<string, bool[]> seqSlot = new Dictionary<string, bool[]>();
|
||
Dictionary<string, List<int>> seqSlotWafers = new Dictionary<string, List<int>>();
|
||
Dictionary<string, string> indexSequence = new Dictionary<string, string>();
|
||
|
||
bool enableGroupBySequence = false;
|
||
for (int i = 0; i < 25; i++)
|
||
{
|
||
if (string.IsNullOrEmpty(slotSequence[i]) || string.IsNullOrEmpty(slotSequence[i].Trim()))
|
||
continue;
|
||
|
||
string groupName = enableGroupBySequence ? slotSequence[i].Trim() : i.ToString();
|
||
indexSequence[groupName] = slotSequence[i];
|
||
|
||
if (!seqSlot.ContainsKey(groupName))
|
||
{
|
||
seqSlot[groupName] = new bool[25];
|
||
}
|
||
|
||
if (!seqSlotWafers.ContainsKey(groupName))
|
||
{
|
||
seqSlotWafers[groupName] = new List<int>();
|
||
}
|
||
|
||
seqSlot[groupName][i] = true;
|
||
seqSlotWafers[groupName].Add(i);
|
||
}
|
||
|
||
if (seqSlotWafers.Count == 0)
|
||
{
|
||
reason = $"Can not create job, no wafer assigned";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return false;
|
||
}
|
||
|
||
List<ProcessJobInfo> pjs = new List<ProcessJobInfo>();
|
||
string[] seqs = seqSlot.Keys.ToArray();
|
||
|
||
List<string> pjIDs = new List<string>();
|
||
for (int i = 0; i < seqs.Length; i++)
|
||
{
|
||
var name = jobId + "_" + (i + 1);
|
||
|
||
if (!CreateProcessJob(name, indexSequence[seqs[i]], seqSlotWafers[seqs[i]], autoStart)) //ProcessJob创建失败直接返回,不再创建ControlJob
|
||
{
|
||
return false;
|
||
|
||
}
|
||
|
||
pjIDs.Add(name);
|
||
}
|
||
|
||
if (!CreateControlJob(jobId, module, pjIDs, autoStart))
|
||
{
|
||
foreach (var pjName in pjIDs)
|
||
{
|
||
var pj = _lstProcessJobs.FirstOrDefault(x => x.Name == pjName);
|
||
if (pj != null)
|
||
_lstProcessJobs.Remove(pj);
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
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;
|
||
|
||
if (sequence.Steps != null)
|
||
{
|
||
int currentIndex = 0;
|
||
if (sequence.Steps[currentIndex] == null || sequence.Steps[currentIndex].StepModules.Count <= 0 || sequence.Steps[currentIndex].StepModules[0] != ModuleName.Aligner)
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]}, {currentIndex + 1}st step should be Aligner";
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if (sequence.Steps[currentIndex].AlignAngle < 0 || sequence.Steps[currentIndex].AlignAngle > 360)
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]},{currentIndex + 1}st step Aligner angle parameter is not valid";
|
||
return false;
|
||
}
|
||
}
|
||
|
||
currentIndex++;
|
||
if (sequence.Steps[currentIndex] == null || sequence.Steps[currentIndex].StepModules.Count <= 0 || sequence.Steps[currentIndex].StepModules[0] != ModuleName.Load)
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]}, {currentIndex + 1}st step should be Load";
|
||
return false;
|
||
}
|
||
|
||
currentIndex++;
|
||
if (sequence.Steps[currentIndex] == null || sequence.Steps[currentIndex].StepModules.Count <= 0 || sequence.Steps[currentIndex].StepModules[0] != ModuleName.Buffer)
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]}, {currentIndex + 1}st step should be Buffer";
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if (!sequence.Steps[currentIndex].StepParameter.ContainsKey("SlotSelection"))
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]},{currentIndex + 1}st step the parameter 'SlotSelection' of Buffer cannot be found.";
|
||
return false;
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(sequence.Steps[currentIndex].StepParameter["SlotSelection"]
|
||
.ToString()))
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]},{currentIndex + 1}st step the parameter 'SlotSelection' of Buffer is not specified.";
|
||
return false;
|
||
}
|
||
|
||
if(sequence.Steps[currentIndex].StepParameter["SlotSelection"].ToString().Contains("3"))
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]},{currentIndex + 1}st step the option '3' cannot be selected in parameter 'SlotSelection' of Buffer.";
|
||
return false;
|
||
}
|
||
|
||
if (!sequence.Steps[currentIndex].StepParameter.ContainsKey("BufferType") || !sequence.Steps[currentIndex].StepParameter["BufferType"].ToString().Contains("Heat"))
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]},{currentIndex + 1}st step Buffer BufferType parameter is not valid";
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
currentIndex++;
|
||
if (sequence.Steps[currentIndex] == null || sequence.Steps[currentIndex].StepModules.Count <= 0 || sequence.Steps[currentIndex].StepModules.Any(pm => !ModuleHelper.IsPm(pm)))
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]}, {currentIndex + 1}st step should be PM";
|
||
return false;
|
||
}
|
||
|
||
if (sequence.Steps.Count == 7)
|
||
{
|
||
currentIndex++;
|
||
if (sequence.Steps[currentIndex] == null || sequence.Steps[currentIndex].StepModules.Count <= 0 || sequence.Steps[currentIndex].StepModules[0] != ModuleName.Buffer)
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]}, {currentIndex + 1}st step should be Buffer";
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if (!sequence.Steps[currentIndex].StepParameter.ContainsKey("SlotSelection"))
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]},{currentIndex + 1}st step the parameter 'SlotSelection' of Buffer cannot be found.";
|
||
return false;
|
||
}
|
||
|
||
if(sequence.Steps[currentIndex].StepParameter["SlotSelection"].ToString() != "3")
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]},{currentIndex + 1}st step only the '3' option must be selected in the parameter 'SlotSelection' of Buffer. ";
|
||
return false;
|
||
}
|
||
|
||
if (!sequence.Steps[currentIndex].StepParameter.ContainsKey("BufferType") || !sequence.Steps[currentIndex].StepParameter["BufferType"].ToString().Contains("Cooling"))
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]},{currentIndex + 1}st step the parameter 'BufferType' of Buffer must be 'Cooling'.";
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
currentIndex++;
|
||
if (sequence.Steps[currentIndex] == null || sequence.Steps[currentIndex].StepModules.Count <= 0 || sequence.Steps[currentIndex].StepModules[0] != ModuleName.UnLoad)
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]}, {currentIndex + 1}st step should be Unload";
|
||
return false;
|
||
}
|
||
|
||
currentIndex++;
|
||
if (sequence.Steps[currentIndex] == null || sequence.Steps[currentIndex].StepModules.Count <= 0 || sequence.Steps[currentIndex].StepModules[0] != ModuleName.Aligner)
|
||
{
|
||
reason = $"Invalid sequence {seqs[i]}, {currentIndex + 1}st step should be Aligner";
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if (sequence.Steps[0].AlignAngle < 0 || sequence.Steps[0].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;
|
||
}
|
||
|
||
public bool CreateControlJob(string jobId, string module, List<string> pjIDs, bool isAutoStart)
|
||
{
|
||
if (_lstControlJobs.Exists(x => x.Name == jobId))
|
||
{
|
||
EV.PostWarningLog(LogSource, $"{jobId} is already created");
|
||
return false;
|
||
}
|
||
|
||
if (!ModuleHelper.IsCassette(ModuleHelper.Converter(module)))
|
||
{
|
||
EV.PostWarningLog(LogSource, $"{module} should be Cassete");
|
||
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.LotName = jobId;
|
||
cj.LotInnerId = Guid.NewGuid();
|
||
cj.LotWafers = new List<WaferInfo>();
|
||
cj.SetState(EnumControlJobState.WaitingForStart);
|
||
|
||
|
||
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<Tuple<ModuleName, int>>();
|
||
foreach (var slotWafer in pj.SlotWafers)
|
||
{
|
||
var _module = GetModule(module);
|
||
if (!_module.HasWafer(slotWafer.Item2))
|
||
{
|
||
EV.PostWarningLog(LogSource, $"job wafer: {module} slot {slotWafer.Item2 + 1} not in the carrier");
|
||
return false;
|
||
}
|
||
if (!_module.CheckWaferStatus(slotWafer.Item2, WaferStatus.Normal))
|
||
{
|
||
EV.PostWarningLog(LogSource, $"job wafer: {module} slot {slotWafer.Item2 + 1} status is {_module.GetWaferInfo(slotWafer.Item2).Status}");
|
||
return false;
|
||
}
|
||
if (_module.GetWaferInfo(slotWafer.Item2).ProcessState != EnumWaferProcessStatus.Idle)
|
||
{
|
||
EV.PostWarningLog(LogSource, $"job wafer: {module} slot {slotWafer.Item2 + 1} process status is {_module.GetWaferInfo(slotWafer.Item2).ProcessState}");
|
||
return false;
|
||
}
|
||
|
||
slotWafers.Add(Tuple.Create(ModuleHelper.Converter(module), slotWafer.Item2));
|
||
|
||
|
||
|
||
totalWafer++;
|
||
}
|
||
|
||
pj.ControlJobName = cj.Name;
|
||
|
||
cj.ProcessJobNameList.Add(pj.Name);
|
||
pj.SlotWafers = slotWafers;
|
||
foreach (var pjSlotWafer in pj.SlotWafers)
|
||
{
|
||
WaferInfo wafer = GetModule(pjSlotWafer.Item1).GetWaferInfo(pjSlotWafer.Item2);
|
||
cj.LotWafers.Add(wafer);
|
||
wafer.ProcessJob = pj;
|
||
wafer.ProcessJobID = pj.Sequence.Name;
|
||
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);
|
||
}
|
||
}
|
||
|
||
_lstControlJobs.Add(cj);
|
||
|
||
CarrierManager.Instance.DeleteCarrier(cj.Module);
|
||
CarrierManager.Instance.CreateCarrier(cj.Module);
|
||
CarrierManager.Instance.UpdateCarrierId(cj.Module, $"{cj.Module} {DateTime.Now}");
|
||
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<int> slotNumbers/*0 based*/, bool isAutoStart)
|
||
{
|
||
var sequenceInfo = SequenceInfoHelper.GetInfo(sequenceName);
|
||
|
||
//模块顺序检查
|
||
List<int> lstLoad = new List<int>();
|
||
List<int> lstBuffer = new List<int>();
|
||
List<int> lstUnLoad = new List<int>();
|
||
List<int> lstPM = new List<int>();
|
||
|
||
|
||
for (int i = 0; i < sequenceInfo.Steps.Count; i++)
|
||
{
|
||
if (sequenceInfo.Steps[i].StepModules.Count == 0)
|
||
{
|
||
return false;
|
||
}
|
||
if (sequenceInfo.Steps[i].StepModules.Any(pm => ModuleHelper.IsPm(pm)))
|
||
{
|
||
lstPM.Add(i);
|
||
}
|
||
if (sequenceInfo.Steps[i].StepModules.Any(load => load == ModuleName.Load || load == ModuleName.LoadLock))
|
||
{
|
||
lstLoad.Add(i);
|
||
}
|
||
if (sequenceInfo.Steps[i].StepModules.Any(unload => unload == ModuleName.UnLoad))
|
||
{
|
||
lstUnLoad.Add(i);
|
||
}
|
||
if (sequenceInfo.Steps[i].StepModules.Any(buff => buff == ModuleName.Buffer))
|
||
{
|
||
lstBuffer.Add(i);
|
||
}
|
||
}
|
||
|
||
if (lstPM.Count <= 0)
|
||
{
|
||
EV.PostWarningLog(LogSource, $"Sequence must have pm step info!");
|
||
return false;
|
||
}
|
||
if (lstLoad.Count > 1)
|
||
{
|
||
EV.PostWarningLog(LogSource, $"Sequence can not contain {lstLoad.Count} Load Step!");
|
||
return false;
|
||
}
|
||
if (lstUnLoad.Count > 1)
|
||
{
|
||
EV.PostWarningLog(LogSource, $"Sequence can not contain {lstUnLoad.Count} UnLoad Step!");
|
||
return false;
|
||
}
|
||
if (lstBuffer.Count > 0)
|
||
{
|
||
foreach (int bufferId in lstBuffer)
|
||
{
|
||
if (lstLoad.Count == 1)
|
||
{
|
||
if (bufferId < lstLoad[0])
|
||
{
|
||
EV.PostWarningLog(LogSource, $"Sequence Load Step Must Before Buffer Step!");
|
||
return false;
|
||
}
|
||
}
|
||
if (lstUnLoad.Count == 1)
|
||
{
|
||
if (bufferId > lstUnLoad[0])
|
||
{
|
||
EV.PostWarningLog(LogSource, $"Sequence Buffer Step Must Before UnLoad Step!");
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
var slotWafers = new List<Tuple<ModuleName, int>>();
|
||
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;
|
||
}
|
||
|
||
|
||
internal void StopJob(string jobName)
|
||
{
|
||
//ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName);
|
||
//if (cj == null)
|
||
//{
|
||
// EV.PostWarningLog(LogSource, $"stop job rejected, not found job with id {jobName}");
|
||
// return;
|
||
//}
|
||
foreach (var cj in _lstControlJobs)
|
||
{
|
||
foreach (var pj in _lstProcessJobs)
|
||
{
|
||
if (pj.ControlJobName == cj.Name)
|
||
{
|
||
pj.SetState(EnumProcessJobState.Stopping);
|
||
}
|
||
}
|
||
|
||
if (_cycleSetPoint > 0)
|
||
{
|
||
_cycleSetPoint = _cycledCount;
|
||
}
|
||
}
|
||
}
|
||
|
||
internal void AbortJob(string jobName)
|
||
{
|
||
ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName);
|
||
if (cj == null)
|
||
{
|
||
EV.PostWarningLog(LogSource, $"abort job rejected, not found job with id {jobName}");
|
||
return;
|
||
}
|
||
|
||
int unprocessed_cj = 0;
|
||
int aborted_cj = 0;
|
||
|
||
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++;
|
||
unprocessed_cj++;
|
||
}
|
||
}
|
||
JobDataRecorder.EndPJ(pj.InnerId.ToString(), aborted, unprocessed);
|
||
}
|
||
}
|
||
|
||
foreach (var pj in pjAbortList)
|
||
{
|
||
_lstProcessJobs.Remove(pj);
|
||
}
|
||
|
||
_lstControlJobs.Remove(cj);
|
||
|
||
_dbCallback.LotFinished(cj);
|
||
JobDataRecorder.EndCJ(cj.InnerId.ToString(), aborted_cj, unprocessed_cj);
|
||
}
|
||
|
||
public void ResumeJob(string jobName)
|
||
{
|
||
//ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName);
|
||
//if (cj == null)
|
||
//{
|
||
// EV.PostWarningLog(LogSource, $"resume job rejected, not found job with id {jobName}");
|
||
// return;
|
||
//}
|
||
|
||
foreach (var cj in _lstControlJobs)
|
||
{
|
||
if (cj.State == EnumControlJobState.Paused)
|
||
{
|
||
cj.SetState(EnumControlJobState.Executing);
|
||
}
|
||
}
|
||
}
|
||
|
||
internal void PauseJob(string jobName)
|
||
{
|
||
//ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName);
|
||
//if (cj == null)
|
||
//{
|
||
// EV.PostWarningLog(LogSource, $"pause job rejected, not found job with id {jobName}");
|
||
// return;
|
||
//}
|
||
|
||
foreach (var cj in _lstControlJobs)
|
||
{
|
||
if (cj.State == EnumControlJobState.Executing)
|
||
{
|
||
cj.SetState(EnumControlJobState.Paused);
|
||
}
|
||
}
|
||
}
|
||
|
||
internal void StartJob(string jobName)
|
||
{
|
||
GetConfig();
|
||
|
||
//ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName);
|
||
//if (cj == null)
|
||
//{
|
||
// EV.PostWarningLog(LogSource, $"start job rejected, not found job with id {jobName}");
|
||
// return;
|
||
//}
|
||
////StartPreprocess();
|
||
//if (_isCycleMode)
|
||
//{
|
||
// if (CheckAllJobDone())
|
||
// {
|
||
// _cycledWafer = 0;
|
||
// _cycledCount = 0;
|
||
// }
|
||
//}
|
||
|
||
foreach (var cj in _lstControlJobs)
|
||
{
|
||
if (cj.State == EnumControlJobState.WaitingForStart)
|
||
{
|
||
cj.BeginTime = DateTime.Now;
|
||
cj.LotInnerId = Guid.NewGuid();
|
||
_dbCallback.LotCreated(cj);
|
||
|
||
cj.SetState(EnumControlJobState.Executing);
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// Start Auto Transfer
|
||
/// </summary>
|
||
/// <param name="objs"></param>
|
||
/// <returns></returns>
|
||
public Result Start(params object[] objs)
|
||
{
|
||
GetConfig();
|
||
_cycledWafer = 0;
|
||
_cycledCount = 0;
|
||
//_totalWaferWhenStart = StatsDataManager.Instance.GetValue(StatsNameTotalRunningWafer);
|
||
|
||
bool hasPmOnline = false;
|
||
|
||
foreach (var schedulerPm in _lstPms)
|
||
{
|
||
if (schedulerPm.IsOnline && !schedulerPm.IsError)
|
||
{
|
||
hasPmOnline = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (_pm1.IsError)
|
||
{
|
||
EV.PostWarningLog("Scheduler", "can not change to auto mode, at least one process chamber be online and no error");
|
||
return Result.FAIL;
|
||
}
|
||
|
||
if (_pm1.IsService)
|
||
{
|
||
EV.PostWarningLog("Scheduler", "can not change to auto mode, PM is Service Mode");
|
||
return Result.FAIL;
|
||
}
|
||
|
||
if (!hasPmOnline)
|
||
{
|
||
EV.PostWarningLog("Scheduler", "can not change to auto mode, at least one process chamber be online and no error");
|
||
return Result.FAIL;
|
||
}
|
||
|
||
if (!_tmRobot.IsOnline || _tmRobot.IsError)
|
||
{
|
||
EV.PostWarningLog("Scheduler", "can not change to auto mode, TM robot should be online and no error");
|
||
return Result.FAIL;
|
||
}
|
||
|
||
return Result.RUN;
|
||
}
|
||
|
||
|
||
public Result Monitor()
|
||
{
|
||
NotifyTrayExhausted.CLK = CheckTrayExhausted();
|
||
if (NotifyTrayExhausted.Q)
|
||
{
|
||
EV.PostAlarmLog("System", "Tray ProcessCount Exhausted , No Tray for next Process !");
|
||
}
|
||
|
||
ControlJobInfo cjActive = _lstControlJobs.Find(x => x.State == EnumControlJobState.Executing);
|
||
if (cjActive != null)
|
||
{
|
||
MonitorModuleTasks();
|
||
}
|
||
|
||
MonitorModuleError();
|
||
|
||
MonitorModuleState();
|
||
|
||
MonitorJobTasks();
|
||
|
||
MonitorCleanTasks();
|
||
|
||
return Result.RUN;
|
||
}
|
||
|
||
#region Job task
|
||
public Result MonitorJobTasks()
|
||
{
|
||
UpdateProcessJobStatus();
|
||
|
||
UpdateControlJobStatus();
|
||
|
||
StartNewJob();
|
||
|
||
return Result.RUN;
|
||
}
|
||
|
||
protected bool CheckAllWaferReturned(ProcessJobInfo pj, bool checkAllProcessed)
|
||
{
|
||
for (int i = 0; i < pj.SlotWafers.Count; ++i)
|
||
{
|
||
WaferInfo wafer = GetModule(GetWaferReturnedCassette(pj.SlotWafers[i].Item1)).GetWaferInfo(pj.SlotWafers[i].Item2);
|
||
if (wafer.IsEmpty)
|
||
return false;
|
||
if (checkAllProcessed && GetModule(GetWaferReturnedCassette(pj.SlotWafers[i].Item1)).CheckWaferNeedProcess(pj.SlotWafers[i].Item2))
|
||
return false;
|
||
if (wafer.ProcessJob == null || wafer.ProcessJob.InnerId != pj.InnerId)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
private ModuleName GetWaferReturnedCassette(ModuleName moduleFrom)
|
||
{
|
||
if (CassetteFromAToB)
|
||
{
|
||
if (moduleFrom == ModuleName.CassAL)
|
||
{
|
||
return ModuleName.CassAR;
|
||
}
|
||
else if (moduleFrom == ModuleName.CassAR)
|
||
{
|
||
return ModuleName.CassAL;
|
||
}
|
||
|
||
}
|
||
return moduleFrom;
|
||
}
|
||
|
||
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 (GetModule(schedulerPm.Module).CheckNeedRunClean(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())
|
||
{
|
||
//if (CheckWaferSequenceStepDone(ModuleName.LoadLock, 0))
|
||
if (_unload.IsAvailable && _buffer.IsAvailable && _tmRobot.IsAvailable && _load.IsAvailable
|
||
&& _waferRobot.IsAvailable && _unload.NoTray(0)
|
||
&& _waferRobot.IsAvailable && _tmRobot.NoTray(0)
|
||
&& _waferRobot.IsAvailable && _load.NoTray(0))
|
||
{
|
||
pj.SetState(EnumProcessJobState.ProcessingComplete);
|
||
JobDataRecorder.EndPJ(pj.InnerId.ToString(), 0, 0);
|
||
}
|
||
}
|
||
}
|
||
else if (pj.State == EnumProcessJobState.Stopping)
|
||
{
|
||
if (CheckAllWaferReturned(pj, false) && CheckAllPmCleaned(pj) && CheckAllDummyWaferReturned())
|
||
{
|
||
//if (CheckWaferSequenceStepDone(ModuleName.LoadLock, 0))
|
||
if (_unload.IsAvailable && _buffer.IsAvailable && _tmRobot.IsAvailable && _load.IsAvailable
|
||
&& _waferRobot.IsAvailable && _unload.NoTray(0)
|
||
&& _waferRobot.IsAvailable && _tmRobot.NoTray(0)
|
||
&& _waferRobot.IsAvailable && _load.NoTray(0))
|
||
{
|
||
pj.SetState(EnumProcessJobState.ProcessingComplete);
|
||
JobDataRecorder.EndPJ(pj.InnerId.ToString(), 0, 0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void UpdateControlJobStatus()
|
||
{
|
||
if (_lstControlJobs.Count == 0)
|
||
return;
|
||
|
||
bool allControlJobComplete = true;
|
||
List<ControlJobInfo> cjRemoveList = new List<ControlJobInfo>();
|
||
foreach (var cj in _lstControlJobs)
|
||
{
|
||
if (cj.State == EnumControlJobState.Executing)
|
||
{
|
||
bool allPjCompleted = true;
|
||
foreach (var pjName in cj.ProcessJobNameList)
|
||
{
|
||
var pj = _lstProcessJobs.Find(x => x.Name == pjName);
|
||
if (pj == null)
|
||
{
|
||
LOG.Error($"Not find pj named {pjName} in {cj.Name}");
|
||
continue;
|
||
}
|
||
if (pj.State != EnumProcessJobState.Complete && pj.State != EnumProcessJobState.ProcessingComplete)
|
||
{
|
||
allPjCompleted = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (allPjCompleted)
|
||
{
|
||
cj.SetState(EnumControlJobState.Completed);
|
||
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);
|
||
_dbCallback.LotFinished(cj);
|
||
}
|
||
}
|
||
|
||
if (cj.State == EnumControlJobState.Completed)
|
||
{
|
||
cjRemoveList.Add(cj);
|
||
}
|
||
|
||
allControlJobComplete = allControlJobComplete && cj.State == EnumControlJobState.Completed;
|
||
}
|
||
|
||
if (_isCycleMode && _cycledCount < _cycleSetPoint)
|
||
{
|
||
int countPerCycle = 0;
|
||
int countProcessed = 0;
|
||
foreach (var pj in _lstProcessJobs)
|
||
{
|
||
foreach (var pjSlotWafer in pj.SlotWafers)
|
||
{
|
||
countPerCycle++;
|
||
WaferInfo wafer = GetModule(pjSlotWafer.Item1).GetWaferInfo(pjSlotWafer.Item2);
|
||
if (!wafer.IsEmpty && !GetModule(pjSlotWafer.Item1).CheckWaferNeedProcess(pjSlotWafer.Item2))
|
||
countProcessed++;
|
||
}
|
||
}
|
||
|
||
_cycledWafer = _cycledCount * countPerCycle + countProcessed;
|
||
//StatsDataManager.Instance.SetValue(StatsNameTotalRunningWafer, _totalWaferWhenStart + _cycledWafer);
|
||
|
||
|
||
if (allControlJobComplete)
|
||
{
|
||
_cycledCount++;
|
||
|
||
if (_cycledCount < _cycleSetPoint)
|
||
{
|
||
foreach (var cj in _lstControlJobs)
|
||
{
|
||
cj.SetState(EnumControlJobState.Executing);
|
||
}
|
||
foreach (var pj in _lstProcessJobs)
|
||
{
|
||
pj.SetState(EnumProcessJobState.Queued);
|
||
pj.InnerId = Guid.NewGuid();
|
||
foreach (var pjSlotWafer in pj.SlotWafers)
|
||
{
|
||
WaferInfo wafer = GetModule(pjSlotWafer.Item1).GetWaferInfo(pjSlotWafer.Item2);
|
||
|
||
wafer.ProcessJob = null;
|
||
wafer.NextSequenceStep = 0;
|
||
wafer.ProcessState = EnumWaferProcessStatus.Idle;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
foreach (var cj in cjRemoveList)
|
||
{
|
||
List<ProcessJobInfo> pjRemoveList = new List<ProcessJobInfo>();
|
||
foreach (var pj in _lstProcessJobs)
|
||
{
|
||
if (pj.ControlJobName == cj.Name)
|
||
pjRemoveList.Add(pj);
|
||
}
|
||
|
||
foreach (var pj in pjRemoveList)
|
||
{
|
||
_lstProcessJobs.Remove(pj);
|
||
}
|
||
|
||
_lstControlJobs.Remove(cj);
|
||
}
|
||
|
||
ControlJobInfo cjActived = null;
|
||
foreach (var cj in _lstControlJobs)
|
||
{
|
||
if (cj.State == EnumControlJobState.Executing)
|
||
{
|
||
cjActived = cj;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void StartNewJob()
|
||
{
|
||
foreach (var cj in _lstControlJobs)
|
||
{
|
||
if (cj.State == EnumControlJobState.Executing)
|
||
{
|
||
foreach (var pjName in cj.ProcessJobNameList)
|
||
{
|
||
var pj = _lstProcessJobs.Find(x => x.Name == pjName);
|
||
if (pj == null)
|
||
{
|
||
LOG.Error($"Not find pj named {pjName} in {cj.Name}");
|
||
continue;
|
||
}
|
||
|
||
if (pj.State == EnumProcessJobState.Queued)
|
||
{
|
||
ActiveProcessJob(pj);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public bool CheckAllJobDone()
|
||
{
|
||
foreach (var cj in _lstControlJobs)
|
||
{
|
||
if (cj.State == EnumControlJobState.Executing || cj.State == EnumControlJobState.Paused)
|
||
return false;
|
||
}
|
||
if (_lstControlJobs.Count == 0)
|
||
{
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private bool ActiveProcessJob(ProcessJobInfo pj)
|
||
{
|
||
//_lstPms.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) && _lstPms.Exists(x => x.Module == module))
|
||
// {
|
||
// _lstPms.Add(_lstPms.Find(x => x.Module == module));
|
||
// }
|
||
// }
|
||
//}
|
||
|
||
foreach (var pjSlotWafer in pj.SlotWafers)
|
||
{
|
||
WaferInfo wafer = GetModule(pjSlotWafer.Item1).GetWaferInfo(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(), null, cj.InnerId.ToString(), pj.Name, cj.Module, cj.Module, pj.SlotWafers.Count);
|
||
pj.SetState(EnumProcessJobState.Processing);
|
||
|
||
return true;
|
||
}
|
||
#endregion
|
||
|
||
#region Module task
|
||
|
||
public Result MonitorModuleTasks()
|
||
{
|
||
//Load和UnLoad不能同时抽气
|
||
//System.Diagnostics.Debug.Assert(!((_tmRobot.IsInPumping && _load.IsInPumping) || (_tmRobot.IsInPumping && _unload.IsInPumping) || (_unload.IsInPumping && _load.IsInPumping)),$"检测到 _load.IsInPumping :{_load.IsInPumping}, _unload.IsInPumping :{_unload.IsInPumping}, _tmRobot.IsInPumping :{_tmRobot.IsInPumping}");
|
||
if ((_tmRobot.IsInPumping && _load.IsInPumping)
|
||
|| (_tmRobot.IsInPumping && _unload.IsInPumping)
|
||
|| (_unload.IsInPumping && _load.IsInPumping))
|
||
EV.PostAlarmLog("Schedule", $"检测到 _load.IsInPumping :{_load.IsInPumping}, _unload.IsInPumping :{_unload.IsInPumping}, _tmRobot.IsInPumping :{_tmRobot.IsInPumping}");
|
||
|
||
|
||
MonitorPMTask();
|
||
MonitorBufferTask();
|
||
MonitorAlignerTask();
|
||
|
||
MonitorTmRobotTask();
|
||
MonitorWaferRobotTask();
|
||
MonitorTrayRobotTask();
|
||
|
||
MonitorLoadTask();
|
||
MonitorUnLoadTask();
|
||
|
||
return Result.RUN;
|
||
}
|
||
|
||
private void MonitorBufferTask()
|
||
{
|
||
if (!_buffer.IsAvailable)
|
||
{
|
||
return;
|
||
}
|
||
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
bool canExcute = _buffer.HasWafer(i) && _buffer.CheckWaferNextStepIsThis(_buffer.Module, i);
|
||
if (canExcute)
|
||
{
|
||
WaferInfo bufferWafer = _buffer.GetWaferInfo(i);
|
||
|
||
int bufferSetValue = 0;
|
||
if (!GetWaferSequenceNextValue(ModuleName.Buffer, i, "BufferType", out string strBufferType))
|
||
{
|
||
continue;
|
||
}
|
||
if (!GetWaferSequenceNextValue(ModuleName.Buffer, i, "SetValue", out string strBufferSetValue))
|
||
{
|
||
continue;
|
||
}
|
||
if (!Int32.TryParse(strBufferSetValue, out bufferSetValue))
|
||
{
|
||
continue;
|
||
}
|
||
WaferInfo wafer = _buffer.GetWaferInfo(i);
|
||
//分别判断冷却和加热方式温度是否达到
|
||
if (strBufferType == "HeatByTemp")
|
||
{
|
||
if (_bufferWaferInfo.ContainsKey(bufferWafer.InnerId.ToString()))
|
||
{
|
||
DateTime dtStartTime = _bufferWaferInfo[bufferWafer.InnerId.ToString()];
|
||
double pastTime = (DateTime.Now - dtStartTime).TotalSeconds;
|
||
|
||
//选择By温度5秒后再判断温度
|
||
if (pastTime > 5)
|
||
{
|
||
if (_buffer.GetTemperature() >= bufferSetValue)
|
||
wafer.NextSequenceStep++;
|
||
_bufferWaferInfo.Remove(bufferWafer.InnerId.ToString());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
_bufferWaferInfo.Add(bufferWafer.InnerId.ToString(), DateTime.Now);
|
||
}
|
||
}
|
||
else if (strBufferType == "HeatByTime")
|
||
{
|
||
if (_bufferWaferInfo.ContainsKey(bufferWafer.InnerId.ToString()))
|
||
{
|
||
DateTime dtStartTime = _bufferWaferInfo[bufferWafer.InnerId.ToString()];
|
||
double pastTime = (DateTime.Now - dtStartTime).TotalSeconds;
|
||
|
||
if (i == 0)
|
||
{
|
||
_timeBuffer1 = pastTime > bufferSetValue ? "0" : (bufferSetValue - pastTime).ToString();
|
||
}
|
||
else if (i == 1)
|
||
{
|
||
_timeBuffer2 = pastTime > bufferSetValue ? "0" : (bufferSetValue - pastTime).ToString();
|
||
}
|
||
if (i == 2)
|
||
{
|
||
_timeBuffer3 = pastTime > bufferSetValue ? "0" : (bufferSetValue - pastTime).ToString();
|
||
}
|
||
|
||
if (pastTime > bufferSetValue)
|
||
{
|
||
wafer.NextSequenceStep++;
|
||
_bufferWaferInfo.Remove(bufferWafer.InnerId.ToString());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
_bufferWaferInfo.Add(bufferWafer.InnerId.ToString(), DateTime.Now);
|
||
}
|
||
}
|
||
else if (strBufferType == "CoolingByTime")
|
||
{
|
||
if (_bufferWaferInfo.ContainsKey(bufferWafer.InnerId.ToString()))
|
||
{
|
||
DateTime dtStartTime = _bufferWaferInfo[bufferWafer.InnerId.ToString()];
|
||
double pastTime = (DateTime.Now - dtStartTime).TotalSeconds;
|
||
|
||
if (i == 0)
|
||
{
|
||
_timeBuffer1 = pastTime > bufferSetValue ? "0" : (bufferSetValue - pastTime).ToString();
|
||
}
|
||
else if (i == 1)
|
||
{
|
||
_timeBuffer2 = pastTime > bufferSetValue ? "0" : (bufferSetValue - pastTime).ToString();
|
||
}
|
||
if (i == 2)
|
||
{
|
||
_timeBuffer3 = pastTime > bufferSetValue ? "0" : (bufferSetValue - pastTime).ToString();
|
||
}
|
||
|
||
if (pastTime > bufferSetValue)
|
||
{
|
||
wafer.NextSequenceStep++;
|
||
_bufferWaferInfo.Remove(bufferWafer.InnerId.ToString());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
_bufferWaferInfo.Add(bufferWafer.InnerId.ToString(), DateTime.Now);
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
//Place和Pick条件都一样
|
||
if (!_buffer.IsReadyForPlace(ModuleName.TMRobot, 0))
|
||
{
|
||
_buffer.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 0);
|
||
}
|
||
}
|
||
|
||
private void MonitorLoadTask()
|
||
{
|
||
if (!_load.IsAvailable)
|
||
{
|
||
return;
|
||
}
|
||
|
||
bool canExcute = _load.HasWafer(0) && _load.HasTrayAndNotExceedProcessCount(0);
|
||
if (canExcute)
|
||
{
|
||
if (!_load.CheckWaferTrayGrouped())
|
||
{
|
||
_load.GroupWaferTray();
|
||
return;
|
||
}
|
||
else if (_load.CheckWaferNextStepIsThis(ModuleName.LoadLock, 0) && !_unload.IsInPumping && !_tmRobot.IsInPumping)
|
||
{
|
||
_load.Purge(_load.GetWaferPurgeCount(0), _load.GetWaferPumpDelayTime(0));
|
||
|
||
_load.GetWaferInfo(0).NextSequenceStep++;
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (_load.HasWafer(0) && _load.HasTrayAndNotExceedProcessCount(0))
|
||
{
|
||
//有Wafer和石墨盘,等待TM来取,此时Unload必须不在动作
|
||
if (!_load.IsReadyForPick(ModuleName.TMRobot, 0) && !_unload.IsInPumping && !_tmRobot.IsInPumping)
|
||
{
|
||
_load.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Pick, 0);
|
||
return;
|
||
}
|
||
}
|
||
else if ((_tmRobot.NoWafer(0) && _tmRobot.HasTrayAndExceedProcessCount(0))
|
||
|| (_buffer.NoWafer(0) && _buffer.HasTrayAndExceedProcessCount(0))
|
||
|| (_buffer.NoWafer(1) && _buffer.HasTrayAndExceedProcessCount(1))
|
||
|| (_buffer.NoWafer(2) && _buffer.HasTrayAndExceedProcessCount(2)))
|
||
{
|
||
if (!_load.IsReadyForPlace(ModuleName.TMRobot, 0) && !_unload.IsInPumping && !_tmRobot.IsInPumping)
|
||
{
|
||
_load.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 0);
|
||
return;
|
||
}
|
||
}
|
||
else if (_load.NoWafer(0))
|
||
{
|
||
//Load只有石墨盘,要么是WaferRobot来放,要么是TrayRobot来取
|
||
if (!_load.IsReadyForPlace(ModuleName.WaferRobot, 0) && !_unload.IsInPumping && !_tmRobot.IsInPumping)
|
||
{
|
||
_load.PrepareTransfer(ModuleName.WaferRobot, EnumTransferType.Place, 0);
|
||
return;
|
||
}
|
||
}
|
||
else if (_load.HasTrayAndExceedProcessCount(0))
|
||
{
|
||
if (!_load.IsReadyForPick(ModuleName.TrayRobot, 0) && !_unload.IsInPumping && !_tmRobot.IsInPumping)
|
||
{
|
||
_load.PrepareTransfer(ModuleName.TrayRobot, EnumTransferType.Pick, 0);
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
|
||
//啥也没有,要么是等工艺做完TMRobot来放(TM手上有Tray,无Wafer),要么是还没做工艺等TrayRobot来放
|
||
if (_tmRobot.HasTray(0) && _tmRobot.NoWafer(0)
|
||
|| _unload.HasTray(0)
|
||
|| (_buffer.HasTray(0) && _buffer.NoWafer(0))
|
||
|| (_buffer.HasTray(1) && _buffer.NoWafer(1))
|
||
|| (_buffer.HasTray(2) && _buffer.NoWafer(2)))
|
||
{
|
||
if (!_load.IsReadyForPlace(ModuleName.TMRobot, 0) && !_unload.IsInPumping && !_tmRobot.IsInPumping)
|
||
{
|
||
_load.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 0);
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!_load.IsReadyForPlace(ModuleName.TrayRobot, 0) && !_unload.IsInPumping && !_tmRobot.IsInPumping)
|
||
{
|
||
_load.PrepareTransfer(ModuleName.TrayRobot, EnumTransferType.Place, 0);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
private void MonitorUnLoadTask()
|
||
{
|
||
if (!_unload.IsAvailable)
|
||
{
|
||
return;
|
||
}
|
||
|
||
//UnLoad
|
||
|
||
if (_unload.FirstDetectWaferArrive(0) || _unload.FirstDetectWaferLeave(0))
|
||
{
|
||
_unload.ResetPurgedAndSeparatedStatus();
|
||
}
|
||
|
||
bool canExcuteUnLoad = _unload.HasWafer(0); //&& _unload.GetWaferInfo(0).ProcessState == EnumWaferProcessStatus.Completed; //并且闸板阀关上
|
||
if (canExcuteUnLoad)
|
||
{
|
||
if (!_unload.CheckCoolingCompleted())
|
||
{
|
||
GetWaferSequenceCoolingTime(_unload.Module, 0, out int coolingTime);
|
||
_unload.Cooling(true, coolingTime);
|
||
return;
|
||
}
|
||
|
||
if (_unload.CheckCoolingCompleted() && !_unload.CheckWaferTraySeparated())
|
||
{
|
||
_unload.SeparateWaferTray();
|
||
return;
|
||
}
|
||
|
||
if (_unload.CheckWaferNextStepIsThis(ModuleName.UnLoad, 0) && _unload.NoTray(0) && _unload.CheckCoolingCompleted() && _unload.CheckWaferTraySeparated() && !_unload.CheckPurged() && !_load.IsInPumping && !_tmRobot.IsInPumping)
|
||
{
|
||
_unload.Purge(_unload.GetWaferPurgeCount(0), _unload.GetWaferPumpDelayTime(0));
|
||
_unload.GetWaferInfo(0).NextSequenceStep++;
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (_unload.HasTray(0))
|
||
{
|
||
if (!_unload.CheckPurged() && _unload.CheckWaferTraySeparated() && !_unload.IsReadyForPick(ModuleName.TMRobot, 0) && !_load.IsInPumping && !_tmRobot.IsInPumping)
|
||
{
|
||
_unload.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Pick, 0);
|
||
return;
|
||
}
|
||
}
|
||
else if (_unload.HasWafer(0))
|
||
{
|
||
if (_unload.CheckPurged() && _unload.CheckWaferTraySeparated() && !_unload.IsReadyForPick(ModuleName.WaferRobot, 0) && !_load.IsInPumping && !_tmRobot.IsInPumping)
|
||
{
|
||
_unload.PrepareTransfer(ModuleName.WaferRobot, EnumTransferType.Pick, 0);
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!_unload.IsReadyForPlace(ModuleName.TMRobot, 0) && !_load.IsInPumping && !_tmRobot.IsInPumping)
|
||
{
|
||
_unload.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 0);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void MonitorAlignerTask()
|
||
{
|
||
if (!_aligner.IsAvailable)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (_aligner.HasWafer(0) && _aligner.CheckWaferNextStepIsThis(ModuleName.Aligner, 0))
|
||
{
|
||
_aligner.Aligning();
|
||
_aligner.GetWaferInfo(0).NextSequenceStep++;
|
||
}
|
||
}
|
||
|
||
private void MonitorPMTask()
|
||
{
|
||
foreach (var pm in _lstPms)
|
||
{
|
||
if (!pm.IsAvailable)
|
||
continue;
|
||
|
||
if (_pm1.HasWafer(0))
|
||
{
|
||
if (_pm1.CheckNeedRunClean(out bool withWafer, out string recipe) && withWafer
|
||
&& GetModule(pm.Module).GetWaferInfo(0).Status == WaferStatus.Dummy
|
||
&& GetModule(pm.Module).GetWaferInfo(0).ProcessState == EnumWaferProcessStatus.Wait)
|
||
{
|
||
pm.Process(recipe, true, withWafer);
|
||
continue;
|
||
}
|
||
|
||
if (GetModule(pm.Module).CheckWaferNeedProcess(0, pm.Module))
|
||
{
|
||
WaferInfo wafer = GetModule(pm.Module).GetWaferInfo(0);
|
||
if (pm.Process(wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].RecipeName, false, true))
|
||
{
|
||
GetModule(pm.Module).GetWaferInfo(0).NextSequenceStep++;
|
||
continue;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!pm.IsReadyForPick(ModuleName.TMRobot, 0))
|
||
{
|
||
pm.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Pick, 0);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (GetModule(pm.Module).CheckNeedRunClean(out bool withWafer, out string recipe) && !withWafer)
|
||
{
|
||
pm.Process(recipe, true, withWafer);
|
||
continue;
|
||
}
|
||
|
||
if (!pm.IsReadyForPlace(ModuleName.TMRobot, 0))
|
||
{
|
||
pm.PrepareTransfer(ModuleName.TMRobot, EnumTransferType.Place, 0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void MonitorTmRobotTask()
|
||
{
|
||
if (!_tmRobot.IsAvailable)
|
||
return;
|
||
|
||
foreach (var pm in _lstPms)
|
||
{
|
||
if (pm.IsWaitTransfer(ModuleName.TMRobot))
|
||
pm.StopWaitTransfer(ModuleName.TMRobot);
|
||
}
|
||
|
||
if (_buffer.IsWaitTransfer(ModuleName.TMRobot))
|
||
_buffer.StopWaitTransfer(ModuleName.TMRobot);
|
||
|
||
if (_load.IsWaitTransfer(ModuleName.TMRobot))
|
||
_load.StopWaitTransfer(ModuleName.TMRobot);
|
||
|
||
if (_unload.IsWaitTransfer(ModuleName.TMRobot))
|
||
_unload.StopWaitTransfer(ModuleName.TMRobot);
|
||
|
||
|
||
MonitorTmRobotPMTask();
|
||
|
||
MonitorTmRobotBufferTask();
|
||
|
||
MonitorTmRobotLoadTask();
|
||
|
||
MonitorTmRobotUnLoadTask();
|
||
}
|
||
|
||
private void MonitorWaferRobotTask()
|
||
{
|
||
foreach (var cass in _lstCassettes)
|
||
{
|
||
if (cass.IsWaitTransfer(ModuleName.WaferRobot))
|
||
cass.StopWaitTransfer(ModuleName.WaferRobot);
|
||
}
|
||
|
||
if (_aligner.IsWaitTransfer(ModuleName.WaferRobot))
|
||
_aligner.StopWaitTransfer(ModuleName.WaferRobot);
|
||
|
||
if (_load.IsWaitTransfer(ModuleName.WaferRobot))
|
||
_load.StopWaitTransfer(ModuleName.WaferRobot);
|
||
|
||
if (_unload.IsWaitTransfer(ModuleName.WaferRobot))
|
||
_unload.StopWaitTransfer(ModuleName.WaferRobot);
|
||
|
||
MonitorWaferRobotCassetteTask();
|
||
MonitorWaferRobotUnLoadTask();
|
||
MonitorWaferRobotAligerTask();
|
||
MonitorWaferRobotLoadTask();
|
||
}
|
||
|
||
private void MonitorTrayRobotTask()
|
||
{
|
||
if (_cassetteBL.IsWaitTransfer(ModuleName.TrayRobot))
|
||
_cassetteBL.StopWaitTransfer(ModuleName.TrayRobot);
|
||
|
||
if (_load.IsWaitTransfer(ModuleName.TrayRobot))
|
||
_load.StopWaitTransfer(ModuleName.TrayRobot);
|
||
|
||
MonitorTrayRobotLoadTask();
|
||
|
||
MonitorTrayRobotCassetteTask();
|
||
}
|
||
|
||
private void MonitorTmRobotLoadTask()
|
||
{
|
||
if (!_load.IsAvailable)
|
||
return;
|
||
if (!_tmRobot.IsAvailable)
|
||
return;
|
||
|
||
//place Robot有Tray无Wafer,Load无Tray
|
||
bool canPlace = _tmRobot.HasTrayAndExceedProcessCount(0) && _tmRobot.NoWafer(0) && _load.NoTray(0);
|
||
if (canPlace)
|
||
{
|
||
if (_load.IsReadyForPlace(ModuleName.TMRobot, 0) && !_unload.IsInPumping)
|
||
{
|
||
if (_tmRobot.Place(_load.Module, 0, Hand.Blade1))
|
||
{
|
||
_load.WaitTransfer(ModuleName.TMRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!_load.IsAvailable)
|
||
return;
|
||
if (!_tmRobot.IsAvailable)
|
||
return;
|
||
|
||
//place Robot有Tray无Wafer,Load无Tray
|
||
canPlace = _tmRobot.HasTray(0) && _tmRobot.NoWafer(0) && _load.NoTray(0) && _load.HasWafer(0);
|
||
if (canPlace)
|
||
{
|
||
if (_load.IsReadyForPlace(ModuleName.TMRobot, 0) && !_unload.IsInPumping)
|
||
{
|
||
if (_tmRobot.Place(_load.Module, 0, Hand.Blade1))
|
||
{
|
||
_load.WaitTransfer(ModuleName.TMRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
if (!_load.IsAvailable)
|
||
return;
|
||
if (!_tmRobot.IsAvailable)
|
||
return;
|
||
|
||
//pick TM无Tray,需要Process,下一个位置没有Tray
|
||
bool canPick = _tmRobot.NoTray(0) && _load.CheckWaferNeedProcess(0) && !_load.CheckWaferNextStepIsThis(ModuleName.LoadLock, 0) && _load.CheckWaferNextStepModuleNoTray(0);
|
||
if (canPick)
|
||
{
|
||
//需要完成组合和Purge
|
||
if (!_load.CheckWaferTrayGrouped())
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (_load.IsReadyForPick(ModuleName.TMRobot, 0) && !_unload.IsInPumping)
|
||
{
|
||
if (_tmRobot.Pick(_load.Module, 0, Hand.Blade1))
|
||
{
|
||
_load.WaitTransfer(ModuleName.TMRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
private void MonitorTmRobotUnLoadTask()
|
||
{
|
||
if (!_unload.IsAvailable)
|
||
return;
|
||
if (!_tmRobot.IsAvailable)
|
||
return;
|
||
|
||
//UnLoad取盘只关心LoadLock是否有空位
|
||
|
||
//place Robot有Wafer,UnLoad无Tray无Wafer
|
||
bool canPlaceUnLoad = _tmRobot.HasWafer(0)
|
||
&& _unload.NoTray(0)
|
||
&& _unload.NoWafer(0)
|
||
&& (_aligner.CheckWaferNextStepIsThis(ModuleName.TMRobot, 0) || _unload.CheckWaferNextStepIsThis(ModuleName.TMRobot, 0) || _tmRobot.CheckWaferSequenceStepDone(0))
|
||
;//&& _tmRobot.GetWaferInfo(0).ProcessState == EnumWaferProcessStatus.Completed;
|
||
if (canPlaceUnLoad)
|
||
{
|
||
if (_unload.IsReadyForPlace(ModuleName.TMRobot, 0) && !_load.IsInPumping)
|
||
{
|
||
if (_tmRobot.Place(_unload.Module, 0, Hand.Blade1))
|
||
{
|
||
_unload.WaitTransfer(ModuleName.TMRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
if (!_unload.IsAvailable)
|
||
return;
|
||
if (!_tmRobot.IsAvailable)
|
||
return;
|
||
|
||
//pick UnLoad有Tray,TM无Tray,LoadLock没有Tray,UnLoad分离完成
|
||
bool canPickUnLoad = _tmRobot.NoTray(0)
|
||
&& _unload.HasTray(0)
|
||
&& _load.NoTray(0);
|
||
//&& _unload.GetWaferInfo(0).ProcessState == EnumWaferProcessStatus.Completed;
|
||
if (canPickUnLoad)
|
||
{
|
||
//需要UnLoad把石墨盘和Wafer分离完成
|
||
if (!_unload.CheckWaferTraySeparated())
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (_unload.IsReadyForPick(ModuleName.TMRobot, 0) && !_load.IsInPumping)
|
||
{
|
||
if (_tmRobot.Pick(_unload.Module, 0, Hand.Blade1))
|
||
{
|
||
_unload.WaitTransfer(ModuleName.TMRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
private void MonitorTmRobotBufferTask()
|
||
{
|
||
if (!_tmRobot.IsAvailable)
|
||
return;
|
||
if (!_buffer.IsAvailable)
|
||
return;
|
||
|
||
//place Buffer位置没有Tray,Robot有Wafer,下一步骤是Buffer
|
||
bool canPalce = _tmRobot.HasWafer(0) && _buffer.CheckWaferNextStepIsThis(ModuleName.TMRobot, 0);
|
||
if (canPalce)
|
||
{
|
||
SlotItem bufferEmptySlot = GetEmptyBufferSlot(_tmRobot.GetWaferInfo(0));
|
||
if (bufferEmptySlot == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (_buffer.IsReadyForPlace(ModuleName.TMRobot, bufferEmptySlot.Slot))
|
||
{
|
||
if (_tmRobot.Place(_buffer.Module, bufferEmptySlot.Slot, Hand.Blade1))
|
||
{
|
||
_buffer.WaitTransfer(ModuleName.TMRobot);
|
||
return;
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (!_tmRobot.IsAvailable)
|
||
return;
|
||
if (!_buffer.IsAvailable)
|
||
return;
|
||
|
||
//place 最后没有新的wafer需要工艺时,完成工艺后的Tray放入Buffer中
|
||
canPalce = _tmRobot.HasTray(0) && _tmRobot.NoWafer(0);
|
||
if (canPalce)
|
||
{
|
||
if (_tmRobot.HasTrayAndExceedProcessCount(0) && _load.NoTray(0) && _load.NoWafer(0))
|
||
return;
|
||
|
||
if (!_tmRobot.CheckWaferNeedProcess(0)
|
||
&& GetWaferInJobQueue() == null
|
||
&& !_load.CheckWaferNeedProcess(0)
|
||
&& !_aligner.CheckWaferNeedProcess(0)
|
||
&& !_waferRobot.CheckWaferNeedProcess(0))
|
||
{
|
||
SlotItem bufferEmptySlot = null;
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
if (_buffer.NoTray(i) && _buffer.NoWafer(i))
|
||
{
|
||
bufferEmptySlot = new SlotItem(ModuleName.Buffer, i);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (bufferEmptySlot == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (_buffer.IsReadyForPlace(ModuleName.TMRobot, bufferEmptySlot.Slot))
|
||
{
|
||
if (_tmRobot.Place(_buffer.Module, bufferEmptySlot.Slot, Hand.Blade1))
|
||
{
|
||
_buffer.WaitTransfer(ModuleName.TMRobot);
|
||
return;
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
if (!_tmRobot.IsAvailable)
|
||
return;
|
||
if (!_buffer.IsAvailable)
|
||
return;
|
||
|
||
//pick 当前步(UnLoad没有Wafer),Buffer有Tray,机械手没有Tray
|
||
bool canPick = _tmRobot.NoTray(0);
|
||
if (canPick)
|
||
{
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
if (_buffer.HasTray(i) && !_buffer.CheckWaferNextStepIsThis(ModuleName.Buffer, i))
|
||
{
|
||
int bufferSetValue = 0;
|
||
if (!GetWaferSequenceCurrentValue(ModuleName.Buffer, i, "BufferType", out string strBufferType))
|
||
{
|
||
continue;
|
||
}
|
||
if (!GetWaferSequenceCurrentValue(ModuleName.Buffer, i, "SetValue", out string strBufferSetValue))
|
||
{
|
||
continue;
|
||
}
|
||
if (!Int32.TryParse(strBufferSetValue, out bufferSetValue))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
//分别判断冷却和加热方式温度是否达到
|
||
if (strBufferType == "HeatByTemp" && _buffer.GetTemperature() < bufferSetValue)
|
||
{
|
||
continue;
|
||
}
|
||
else if (strBufferType == "CoolingByTemp" && _buffer.GetTemperature() > bufferSetValue)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
//如果已经完成所有步骤或者下一步需要经过UnLoad,UnLoad不允许有盘
|
||
if (_buffer.CheckWaferSequenceStepDone(i) || _unload.CheckWaferNextStepIsThis(ModuleName.Buffer, i) || _aligner.CheckWaferNextStepIsThis(ModuleName.Buffer, i))
|
||
{
|
||
if (_unload.HasWafer(0) || _unload.HasTray(0))
|
||
{
|
||
continue;
|
||
}
|
||
}
|
||
|
||
//下一步如果去PM1,判断PM1是否准备好
|
||
if (_pm1.CheckWaferNextStepIsThis(ModuleName.Buffer, i))
|
||
{
|
||
if (!_pm1.IsAvailable || _pm1.HasTray(0) || _pm1.HasWafer(0) || !_pm1.IsReadyForPlace(ModuleName.TMRobot, 0) ||
|
||
!_pm1.CheckBufferToPMTemp())
|
||
continue;
|
||
}
|
||
|
||
if (_buffer.CheckWaferNextStepModuleNoTray(i))
|
||
{
|
||
if (_buffer.IsReadyForPick(ModuleName.TMRobot, i))
|
||
{
|
||
if (_tmRobot.Pick(_buffer.Module, i, Hand.Blade1))
|
||
{
|
||
_buffer.WaitTransfer(ModuleName.TMRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
if (!_tmRobot.IsAvailable)
|
||
return;
|
||
if (!_buffer.IsAvailable)
|
||
return;
|
||
|
||
//pick 工艺开始时,如果Buffer中有Tray时,优先取Buffer中的Tray,而不是Cst中Tray
|
||
canPick = _tmRobot.NoTray(0)
|
||
&& _load.IsReadyForPlace(ModuleName.TMRobot, 0)
|
||
&& _load.NoTray(0)
|
||
//&& _load.HasWafer(0)
|
||
&& ((GetWaferInJobQueue() != null || _aligner.CheckWaferNeedProcess(0) || _waferRobot.CheckWaferNeedProcess(0)) || _load.CheckWaferNeedProcess(0));
|
||
if (canPick)
|
||
{
|
||
//优先取出超过ProcessCount的Tray
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
if (_buffer.HasTray(i) && _buffer.NoWafer(i))
|
||
{
|
||
if (_buffer.IsReadyForPick(ModuleName.TMRobot, i) && _buffer.HasTrayAndExceedProcessCount(i))
|
||
{
|
||
if (_tmRobot.Pick(_buffer.Module, i, Hand.Blade1))
|
||
{
|
||
_buffer.WaitTransfer(ModuleName.TMRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
if (_buffer.HasTray(i) && _buffer.NoWafer(i))
|
||
{
|
||
if (_buffer.IsReadyForPick(ModuleName.TMRobot, i) && _load.IsReadyForPlace(ModuleName.TMRobot, 0))
|
||
{
|
||
if (_tmRobot.Pick(_buffer.Module, i, Hand.Blade1))
|
||
{
|
||
_buffer.WaitTransfer(ModuleName.TMRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void MonitorTmRobotPMTask()
|
||
{
|
||
if (!_tmRobot.IsAvailable)
|
||
return;
|
||
|
||
|
||
//place to pm
|
||
bool blade0HasWaferAndNeedProcess = _tmRobot.HasWafer(0) && _tmRobot.CheckWaferNeedProcess(0);
|
||
|
||
if (blade0HasWaferAndNeedProcess)
|
||
{
|
||
foreach (var pm in _lstPms)
|
||
{
|
||
if (!pm.IsAvailable
|
||
|| !GetModule(pm.Module).NoWafer(0)
|
||
|| !pm.IsReadyForPlace(ModuleName.TMRobot, 0)
|
||
|| !pm.CheckTempBelow900())
|
||
continue;
|
||
|
||
//bool cleanRequired = CheckNeedRunClean(pm.Module, out bool withWafer, out _);
|
||
|
||
//if (cleanRequired)
|
||
//{
|
||
// if (!withWafer)
|
||
// continue;
|
||
|
||
// bool blade0DummyPlace = _tmRobot.HasWafer(0)
|
||
// && CheckWaferNeedProcess(ModuleName.TMRobot, 0, pm.Module)
|
||
// && WaferManager.Instance.CheckWaferIsDummy(ModuleName.TMRobot, 0);
|
||
//bool blade1DummyPlace =_tmRobot.HasWafer(1)
|
||
// && CheckWaferNeedProcess(ModuleName.TMRobot, 1, pm.Module)
|
||
// && WaferManager.Instance.CheckWaferIsDummy(ModuleName.TMRobot, 1);
|
||
// if (blade0DummyPlace || blade1DummyPlace)
|
||
// {
|
||
// Hand placeBlade = blade0DummyPlace ? Hand.Blade1 : Hand.Blade2;
|
||
// if (blade0DummyPlace && blade1DummyPlace)
|
||
// {
|
||
// if ( _tmRobot.GetWaferInfo(1).OriginSlot <
|
||
// _tmRobot.GetWaferInfo(0).OriginSlot)
|
||
// {
|
||
// placeBlade = Hand.Blade2;
|
||
// }
|
||
// }
|
||
|
||
// if (_tmRobot.Place(pm.Module, 0, placeBlade))
|
||
// {
|
||
// pm.WaitTransfer(ModuleName.TMRobot);
|
||
// return;
|
||
// }
|
||
// }
|
||
|
||
// continue;
|
||
//}
|
||
|
||
bool blade0Place = _tmRobot.HasWafer(0) && _tmRobot.CheckWaferNeedProcess(0, pm.Module);
|
||
if (blade0Place)
|
||
{
|
||
Hand placeBlade = Hand.Blade1;
|
||
|
||
if (_tmRobot.Place(pm.Module, 0, placeBlade))
|
||
{
|
||
pm.WaitTransfer(ModuleName.TMRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!_tmRobot.IsAvailable)
|
||
return;
|
||
|
||
//pick from pm
|
||
if (_tmRobot.NoWafer(0) && _tmRobot.NoTray(0))
|
||
{
|
||
Hand pickBlade = Hand.Blade1;
|
||
|
||
ModuleName pickPm = ModuleName.System;
|
||
foreach (var schedulerPm in _lstPms)
|
||
{
|
||
//增加温度低于900才能Pick的限制
|
||
if (!schedulerPm.IsAvailable
|
||
|| GetModule(schedulerPm.Module).NoWafer(0)
|
||
|| GetModule(schedulerPm.Module).CheckWaferNeedProcess(0, schedulerPm.Module)
|
||
|| _tmRobot.HasWafer((int)pickBlade)
|
||
|| !schedulerPm.CheckTempBelow900())
|
||
continue;
|
||
|
||
//如果下一步是Buffer
|
||
if (_buffer.CheckWaferNextStepIsThis(schedulerPm.Module, 0))
|
||
{
|
||
SlotItem bufferEmptySlot = GetEmptyBufferSlot(GetModule(schedulerPm.Module).GetWaferInfo(0));
|
||
if (bufferEmptySlot == null)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
else if (_unload.HasWafer(0) || _unload.HasTray(0))
|
||
{
|
||
return;
|
||
}
|
||
|
||
pickPm = schedulerPm.Module;
|
||
break;
|
||
}
|
||
|
||
if (pickPm != ModuleName.System)
|
||
{
|
||
SchedulerModule pm = GetModule(pickPm.ToString());
|
||
if (pm.IsReadyForPick(ModuleName.TMRobot, 0))
|
||
{
|
||
if (_tmRobot.Pick(pm.Module, 0, pickBlade))
|
||
{
|
||
pm.WaitTransfer(ModuleName.TMRobot);
|
||
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void MonitorWaferRobotLoadTask()
|
||
{
|
||
if (!_waferRobot.IsAvailable)
|
||
return;
|
||
if (!_load.IsAvailable)
|
||
return;
|
||
|
||
|
||
//place Load没有Wafer有Tray,Robot有Wafer,步骤没有完成,下一步不是Aligner (其它:SlitValve都关了,大气)
|
||
bool canPlace = _waferRobot.HasWafer(0)
|
||
&& _load.NoWafer(0)
|
||
&& _load.NoTray(0)
|
||
&& _waferRobot.GetWaferInfo(0).Status != WaferStatus.Dummy
|
||
&& !_aligner.CheckWaferNextStepIsThis(ModuleName.WaferRobot, 0)
|
||
&& _waferRobot.CheckWaferNeedProcess(0);
|
||
|
||
if (canPlace)
|
||
{
|
||
if (_load.IsReadyForPlace(ModuleName.WaferRobot, 0))
|
||
{
|
||
if (_waferRobot.Place(_load.Module, 0, Hand.Blade1))
|
||
{
|
||
_load.WaitTransfer(ModuleName.WaferRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void MonitorWaferRobotUnLoadTask()
|
||
{
|
||
if (!_waferRobot.IsAvailable)
|
||
return;
|
||
if (!_unload.IsAvailable)
|
||
return;
|
||
|
||
//pick UnLoad有Wafer没有Tray,Robot没有Wafer,步骤都完成了或者下一步的模块没有片子 (其它:UnLoad 夹爪夹住,顶针下降,SlitValve都关了,大气)
|
||
bool canPick = _waferRobot.NoWafer(0)
|
||
&& _unload.HasWafer(0)
|
||
&& _unload.NoTray(0)
|
||
&& (_unload.CheckWaferNextStepModuleNoWafer(0) || _unload.CheckWaferSequenceStepDone(0))
|
||
&& _unload.GetWaferInfo(0).Status != WaferStatus.Dummy;
|
||
|
||
if (canPick)
|
||
{
|
||
//下一步是Aligner并且Aligner上不允许有片子
|
||
if (_aligner.CheckWaferNextStepIsThis(ModuleName.UnLoad, 0))
|
||
{
|
||
if (_aligner.HasWafer(0))
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (_unload.IsReadyForPick(ModuleName.WaferRobot, 0))
|
||
{
|
||
if (_waferRobot.Pick(_unload.Module, 0, Hand.Blade1))
|
||
{
|
||
_unload.WaitTransfer(ModuleName.WaferRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void MonitorWaferRobotAligerTask()
|
||
{
|
||
if (!_waferRobot.IsAvailable || !_aligner.IsAvailable)
|
||
{
|
||
return;
|
||
}
|
||
|
||
//place 任务还没完成,下一步是Aligner,Robot有片子,Aligner没片子
|
||
bool canPlaceAligner = _aligner.NoWafer(0)
|
||
&& _waferRobot.HasWafer(0)
|
||
&& (_aligner.CheckWaferNextStepIsThis(ModuleName.WaferRobot, 0) || _waferRobot.CheckWaferSequenceStepDone(0))
|
||
&& _waferRobot.GetWaferInfo(0).Status != WaferStatus.Dummy;
|
||
if (canPlaceAligner)
|
||
{
|
||
if (_aligner.IsReadyForPlace(ModuleName.WaferRobot, 0))
|
||
{
|
||
if (_waferRobot.Place(_aligner.Module, 0, Hand.Blade1))
|
||
{
|
||
_aligner.WaitTransfer(ModuleName.WaferRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!_waferRobot.IsAvailable || !_aligner.IsAvailable)
|
||
{
|
||
return;
|
||
}
|
||
|
||
//pick Robot没片子,Aligner有片子
|
||
bool canPickAligner = _aligner.HasWafer(0)
|
||
&& _waferRobot.NoWafer(0)
|
||
&& _aligner.GetWaferInfo(0).Status != WaferStatus.Dummy
|
||
&& !_aligner.CheckWaferNextStepIsThis(ModuleName.Aligner, 0)
|
||
&& _load.CheckWaferNextStepIsThis(ModuleName.Aligner, 0)
|
||
&& _load.IsReadyForPlace(ModuleName.WaferRobot, 0);
|
||
|
||
//pick Aligner wafer to load
|
||
if (canPickAligner)
|
||
{
|
||
//需要运行工艺,Load不能有片子,Load不能有Tray
|
||
if (_aligner.CheckWaferNeedProcess(0))
|
||
{
|
||
if (_load.HasWafer(0)
|
||
|| _load.HasTray(0)
|
||
|| _tmRobot.HasTrayAndExceedProcessCount(0)
|
||
|| _buffer.HasTrayAndExceedProcessCount(0)
|
||
|| _buffer.HasTrayAndExceedProcessCount(1)
|
||
|| _buffer.HasTrayAndExceedProcessCount(2))
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (_aligner.IsReadyForPick(ModuleName.WaferRobot, 0))
|
||
{
|
||
if (_waferRobot.Pick(_aligner.Module, 0, Hand.Blade1))
|
||
{
|
||
_aligner.WaitTransfer(ModuleName.WaferRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
//pick Aligner wafer to cst
|
||
bool canPick = _aligner.HasWafer(0)
|
||
&& _waferRobot.NoWafer(0)
|
||
&& _aligner.GetWaferInfo(0).Status != WaferStatus.Dummy
|
||
&& !_aligner.CheckWaferNextStepIsThis(ModuleName.Aligner, 0)
|
||
&& _aligner.CheckWaferSequenceStepDone(0);
|
||
|
||
//WaferRobot从Aligner Pick Wafer不应检查Load腔条件
|
||
|
||
if (canPick)
|
||
{
|
||
if (_aligner.IsReadyForPick(ModuleName.WaferRobot, 0))
|
||
{
|
||
if (_waferRobot.Pick(_aligner.Module, 0, Hand.Blade1))
|
||
{
|
||
_aligner.WaitTransfer(ModuleName.WaferRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
private void MonitorWaferRobotCassetteMapTask()
|
||
{
|
||
//if (!_waferRobot.IsAvailable)
|
||
// return;
|
||
|
||
//if (_waferRobot.HasWafer(0))
|
||
//{
|
||
// return;
|
||
//}
|
||
//else
|
||
//{
|
||
// ControlJobInfo cjActive = _lstNeedMapJob.Find(x => x.State == EnumControlJobState.WaitingForStart);
|
||
// if (cjActive != null)
|
||
// {
|
||
// if (_waferRobot.Map(ModuleHelper.Converter(cjActive.Module)))
|
||
// {
|
||
|
||
// }
|
||
// }
|
||
//}
|
||
}
|
||
|
||
private void MonitorWaferRobotCassetteTask()
|
||
{
|
||
if (!_waferRobot.IsAvailable)
|
||
return;
|
||
|
||
//place 任务完成了,Robot有片子,Cassette没有片子
|
||
bool canPlaceCassette = _waferRobot.HasWafer(0)
|
||
&& _waferRobot.GetWaferInfo(0).Status != WaferStatus.Dummy
|
||
&& _waferRobot.CheckWaferSequenceStepDone(0);
|
||
if (canPlaceCassette)
|
||
{
|
||
WaferInfo wafer = _waferRobot.GetWaferInfo(0);
|
||
if (GetWaferReturnedCassette((ModuleName)wafer.OriginStation) == ModuleName.CassAL)
|
||
{
|
||
if (_cassetteAL.IsAvailable && _cassetteAL.IsReadyForPlace(ModuleName.WaferRobot, wafer.OriginSlot))
|
||
{
|
||
if (_waferRobot.Place(_cassetteAL.Module, wafer.OriginSlot, Hand.Blade1))
|
||
{
|
||
_cassetteAL.WaitTransfer(ModuleName.WaferRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
else if (GetWaferReturnedCassette((ModuleName)wafer.OriginStation) == ModuleName.CassAR)
|
||
{
|
||
if (_cassetteAR.IsAvailable && _cassetteAR.IsReadyForPlace(ModuleName.WaferRobot, wafer.OriginSlot))
|
||
{
|
||
if (_waferRobot.Place(_cassetteAR.Module, wafer.OriginSlot, Hand.Blade1))
|
||
{
|
||
_cassetteAR.WaitTransfer(ModuleName.WaferRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!_waferRobot.IsAvailable)
|
||
return;
|
||
|
||
//pick Robot没有片子,下一步的模块没有片子
|
||
bool canPick = _waferRobot.NoWafer(0);
|
||
SlotItem position = GetWaferInJobQueue();
|
||
if (canPick && position != null)
|
||
{
|
||
//下一步是Aligner并且Aligner上不允许有片子
|
||
if (_aligner.CheckWaferNextStepIsThis(position.Module, position.Slot))
|
||
{
|
||
if (_aligner.HasWafer(0))
|
||
{
|
||
return;
|
||
}
|
||
////如果UnLoad有Wafer,而且下一步是Aligner,则不能取
|
||
//if (CheckWaferNextStepIsAligner(ModuleName.UnLoad, 0))
|
||
//{
|
||
// return;
|
||
//}
|
||
}
|
||
else if (_load.HasWafer(0)
|
||
|| _load.NoTray(0)
|
||
|| _load.HasTrayAndExceedProcessCount(0)
|
||
|| !_load.IsReadyForPlace(ModuleName.WaferRobot, 0)
|
||
|| _tmRobot.HasTrayAndExceedProcessCount(0)
|
||
|| _buffer.HasTrayAndExceedProcessCount(0)
|
||
|| _buffer.HasTrayAndExceedProcessCount(1)
|
||
|| _buffer.HasTrayAndExceedProcessCount(2))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (GetRunWaferCount() >= _maxTrayCount)//超过可以运行的石墨盘最大数
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (position.Module == ModuleName.CassAR)
|
||
{
|
||
if (_cassetteAR.IsAvailable && _cassetteAR.IsReadyForPick(ModuleName.WaferRobot, 0))
|
||
{
|
||
if (_waferRobot.Pick(position.Module, position.Slot, Hand.Blade1))
|
||
{
|
||
_cassetteAR.WaitTransfer(ModuleName.WaferRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
else if (position.Module == ModuleName.CassAL)
|
||
{
|
||
if (_cassetteAL.IsAvailable && _cassetteAL.IsReadyForPick(ModuleName.WaferRobot, 0))
|
||
{
|
||
if (_waferRobot.Pick(position.Module, position.Slot, Hand.Blade1))
|
||
{
|
||
_cassetteAL.WaitTransfer(ModuleName.WaferRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
private void MonitorTrayRobotLoadTask()
|
||
{
|
||
//----------------------------------
|
||
string s = "";
|
||
if (WhichCondition == "TrayRobotLoad")
|
||
{
|
||
s += AddC("_trayRobot.IsAvailable", _trayRobot.IsAvailable.ToString(), "True");
|
||
s += AddC("_load.IsAvailable", _load.IsAvailable.ToString(), "True");
|
||
s += "canPlace:\r\n";
|
||
s += AddC("_load.NoTray(0)", _load.NoTray(0).ToString(), "True");
|
||
s += AddC("_trayRobot.HasTray(0)", _trayRobot.HasTray(0).ToString(), "True");
|
||
}
|
||
AutoTransferConditionText = s;
|
||
//----------------------------------
|
||
//
|
||
if (!_trayRobot.IsAvailable)
|
||
return;
|
||
if (!_load.IsAvailable)
|
||
return;
|
||
|
||
//place UnLoad没有Tray,Load没有Tray,Robot有Tray,Cassette或Aligner有任务未完成
|
||
bool canPlace = _load.NoTray(0)
|
||
&& _trayRobot.HasTrayAndNotExceedProcessCount(0)
|
||
&& _unload.NoTray(0)
|
||
&& _load.HasWafer(0)
|
||
&& (GetWaferInJobQueue() != null || (_aligner.HasWafer(0) && _aligner.CheckWaferNeedProcess(0)) || (_waferRobot.HasWafer(0) && _waferRobot.CheckWaferNeedProcess(0)) || (_load.HasWafer(0) && _load.CheckWaferNeedProcess(0)));
|
||
if (canPlace)
|
||
{
|
||
//TMRobot有Tray无Wafer也不能放
|
||
if (_tmRobot.NoWafer(0) && _tmRobot.HasTray(0))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (_load.IsReadyForPlace(ModuleName.TrayRobot, 0))
|
||
{
|
||
if (_trayRobot.Place(_load.Module, 0, Hand.Blade1))
|
||
{
|
||
_load.WaitTransfer(ModuleName.TrayRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!_trayRobot.IsAvailable)
|
||
return;
|
||
if (!_load.IsAvailable)
|
||
return;
|
||
|
||
|
||
//pick Load有Tray,判断CassatteA和Aligner还有片子没有跑PM工艺就不需要Pick
|
||
bool canPick = _load.HasTrayAndExceedProcessCount(0);
|
||
if (canPick)
|
||
{
|
||
//if (_load.HasTray(0) )// && _load.GetWaferInfo(0).TrayProcessCount < _trayMaxProcessCount)
|
||
//{
|
||
//if (GetWaferInJobQueue() != null)
|
||
//{
|
||
// return;
|
||
//}
|
||
//}
|
||
SlotItem emptyTraySlot = GetTrayOrignSlot(ModuleName.LoadLock, 0);// GetEmptyTraySlot(13); //先取Wafer再取石墨盘
|
||
if (emptyTraySlot != null && _load.HasTrayAndExceedProcessCount(0))
|
||
{
|
||
if (_load.IsReadyForPick(ModuleName.TrayRobot, 0))
|
||
{
|
||
if (_trayRobot.Pick(_load.Module, 0, Hand.Blade1))
|
||
{
|
||
_load.WaitTransfer(ModuleName.TrayRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
private void MonitorTrayRobotCassetteTask()
|
||
{
|
||
if (!_trayRobot.IsAvailable)
|
||
return;
|
||
|
||
if (!_cassetteBL.IsAvailable)
|
||
return;
|
||
|
||
//place Robot有盘 其它逻辑跟Load的Pick一致
|
||
bool canPlace = _trayRobot.HasTrayAndExceedProcessCount(0)
|
||
|| _load.HasTray(0)
|
||
|| (GetWaferInJobQueue() == null && _aligner.NoWafer(0) && _waferRobot.NoWafer(0) && _load.NoWafer(0));
|
||
if (canPlace)
|
||
{
|
||
SlotItem slotItem = GetTrayOrignSlot(ModuleName.TrayRobot, 0); //GetEmptyTraySlot(13);
|
||
if (slotItem != null)
|
||
{
|
||
if (_cassetteBL.IsReadyForPlace(ModuleName.TrayRobot, slotItem.Slot))
|
||
{
|
||
if (_trayRobot.Place(slotItem.Module, slotItem.Slot, Hand.Blade1))
|
||
{
|
||
_cassetteBL.WaitTransfer(ModuleName.TrayRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//pick Casstte有Tray,UnLoad没有Tray,Load没有Tray(各个腔体运行的石墨盘总数小于设定值)
|
||
if (GetCurrentTrayCount() >= _maxTrayCount)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!_trayRobot.IsAvailable)
|
||
return;
|
||
|
||
if (!_cassetteBL.IsAvailable)
|
||
return;
|
||
|
||
if (CheckHasTrayAndNoWafer(new List<Tuple<ModuleName, int>>()
|
||
{
|
||
new Tuple<ModuleName, int>(ModuleName.Buffer,0),
|
||
new Tuple<ModuleName, int>(ModuleName.Buffer,1),
|
||
new Tuple<ModuleName, int>(ModuleName.Buffer,2),
|
||
new Tuple<ModuleName, int>(ModuleName.TMRobot,0),
|
||
new Tuple<ModuleName, int>(ModuleName.LoadLock,0),
|
||
})) return;
|
||
|
||
bool canPick = (_aligner.HasWafer(0) && _aligner.CheckWaferNeedProcess(0))
|
||
|| (_waferRobot.HasWafer(0) && _waferRobot.CheckWaferNeedProcess(0))
|
||
|| (_load.HasWafer(0) && _load.CheckWaferNeedProcess(0))
|
||
|| GetWaferInJobQueue() != null;
|
||
if (canPick)
|
||
{
|
||
SlotItem slotItem = GetTraySlot(8);
|
||
if (slotItem != null && _load.NoTray(0) && _trayRobot.NoTray(0))
|
||
{
|
||
if (_cassetteBL.IsReadyForPick(ModuleName.TrayRobot, slotItem.Slot))
|
||
{
|
||
if (_trayRobot.Pick(slotItem.Module, slotItem.Slot, Hand.Blade1))
|
||
{
|
||
_cassetteBL.WaitTransfer(ModuleName.TrayRobot);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region Logic Check
|
||
|
||
/// <summary>
|
||
/// 获得Buffer的方式和数值
|
||
/// </summary>
|
||
/// <param name="module"></param>
|
||
/// <param name="slot"></param>
|
||
/// <param name="coolingType"></param>
|
||
/// <param name="setValue"></param>
|
||
/// <returns></returns>
|
||
private bool GetWaferSequenceNextValue(ModuleName module, int slot, string nodeName, out string nodeValue)
|
||
{
|
||
nodeValue = "";
|
||
|
||
if (!GetModule(module).HasWafer(slot))
|
||
return false;
|
||
|
||
WaferInfo wafer = GetModule(module).GetWaferInfo(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;
|
||
|
||
nodeValue = wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepParameter[nodeName].ToString();
|
||
if (String.IsNullOrEmpty(nodeValue))
|
||
{
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
private bool GetWaferSequenceCurrentValue(ModuleName module, int slot, string nodeName, out string nodeValue)
|
||
{
|
||
nodeValue = "";
|
||
|
||
if (!GetModule(module).HasWafer(slot))
|
||
return false;
|
||
|
||
WaferInfo wafer = GetModule(module).GetWaferInfo(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 - 1].StepModules.Contains(module))
|
||
return false;
|
||
|
||
nodeValue = wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep - 1].StepParameter[nodeName].ToString();
|
||
if (String.IsNullOrEmpty(nodeValue))
|
||
{
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
private bool GetWaferSequenceCoolingTime(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 = 0;
|
||
//coolingTime = SC.GetValue<int>("Unload.DefaultCoolingTime");
|
||
//EV.PostWarningLog("Scheduler", $"Sequence step Unload cooling time is not valid, instead with the SC default value {coolingTime} seconds");
|
||
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
private bool CheckHasTrayAndNoWafer(List<Tuple<ModuleName, int>> needCheckPositions)
|
||
{
|
||
foreach (var positionTuple in needCheckPositions)
|
||
{
|
||
var module = GetModule(positionTuple.Item1);
|
||
if (module.HasTray(positionTuple.Item2) && module.NoWafer(positionTuple.Item2))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private bool CheckTrayExhausted()
|
||
{
|
||
return !(_buffer.HasTrayAndNotExceedProcessCount(0)
|
||
| _buffer.HasTrayAndNotExceedProcessCount(1)
|
||
| _buffer.HasTrayAndNotExceedProcessCount(2)
|
||
| _cassetteBL.HasTrayAndNotExceedProcessCount(0)
|
||
| _cassetteBL.HasTrayAndNotExceedProcessCount(1)
|
||
| _cassetteBL.HasTrayAndNotExceedProcessCount(2)
|
||
| _cassetteBL.HasTrayAndNotExceedProcessCount(3)
|
||
| _cassetteBL.HasTrayAndNotExceedProcessCount(4)
|
||
| _cassetteBL.HasTrayAndNotExceedProcessCount(5)
|
||
| _cassetteBL.HasTrayAndNotExceedProcessCount(6)
|
||
| _cassetteBL.HasTrayAndNotExceedProcessCount(7)
|
||
| _load.HasTrayAndNotExceedProcessCount(0)
|
||
| _unload.HasTrayAndNotExceedProcessCount(0)
|
||
| _tmRobot.HasTrayAndNotExceedProcessCount(0)
|
||
| _trayRobot.HasTrayAndNotExceedProcessCount(0)
|
||
| _pm1.HasTrayAndNotExceedProcessCount(0));
|
||
}
|
||
|
||
public bool CheckBufferWaferHasJob()
|
||
{
|
||
WaferInfo wafer = _buffer.GetWaferInfo(0);
|
||
|
||
if (wafer.IsEmpty)
|
||
{
|
||
return false;
|
||
}
|
||
if (wafer.ProcessJob == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
ProcessJobInfo pj = _lstProcessJobs.Find(x => x.InnerId == wafer.ProcessJob.InnerId);
|
||
if (pj == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
public bool CheckWaferProcessModuleIsAvailable(ModuleName waferModule, int waferSlot)
|
||
{
|
||
WaferInfo wafer = GetModule(waferModule).GetWaferInfo(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 module in wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules)
|
||
//{
|
||
//if (GetModule(module).NoWafer(0)
|
||
// && _lstPms.Find(x => x.Module == module).IsAvailable
|
||
// && !CheckNeedRunClean(module, out bool _, out string _))
|
||
// return true;
|
||
//}
|
||
|
||
foreach (var step in wafer.ProcessJob.Sequence.Steps)
|
||
{
|
||
foreach (var module in step.StepModules)
|
||
{
|
||
if (module.ToString().StartsWith("PM")
|
||
&& GetModule(module).NoWafer(0)
|
||
&& _lstPms.Find(x => x.Module == module).IsAvailable
|
||
&& !GetModule(module).CheckNeedRunClean(out bool _, out string _))
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
|
||
private SlotItem GetWaferInJobQueue()
|
||
{
|
||
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 == ModuleName.CassAL || pjSlotWafer.Item1 == ModuleName.CassAR)
|
||
{
|
||
if (GetModule(pjSlotWafer.Item1).CheckWaferNeedProcess(pjSlotWafer.Item2))
|
||
return new SlotItem(pjSlotWafer.Item1, pjSlotWafer.Item2);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private SlotItem GetTraySlot(int slotCount)
|
||
{
|
||
foreach (var cj in _lstControlJobs)
|
||
{
|
||
if (cj.State == EnumControlJobState.Executing)
|
||
{
|
||
for (int i = 0; i < slotCount; i++)
|
||
{
|
||
if (_cassetteBL.HasTrayAndNotExceedProcessCount(i))
|
||
{
|
||
return new SlotItem(ModuleName.CassBL, i);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
private SlotItem GetTrayOrignSlot(ModuleName module, int slot)
|
||
{
|
||
WaferInfo wafer = GetModule(module).GetWaferInfo(slot);
|
||
if (wafer != null && GetModule(module).HasTray(slot) && _cassetteBL.NoTray(wafer.TrayOriginSlot))
|
||
{
|
||
return new SlotItem(ModuleName.CassBL, wafer.TrayOriginSlot);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
//private SlotItem GetEmptyTraySlot(int slotCount)
|
||
//{
|
||
// foreach (var cj in _lstControlJobs)
|
||
// {
|
||
// if (cj.State == EnumControlJobState.Executing)
|
||
// {
|
||
// for (int i = 0; i < slotCount; i++)
|
||
// {
|
||
//if (_cassetteBL.NoTray(i))
|
||
// {
|
||
// return new SlotItem(ModuleName.CassBL, i);
|
||
// }
|
||
// }
|
||
// }
|
||
// }
|
||
// return null;
|
||
//}
|
||
|
||
/// <summary>
|
||
/// 获取空位置
|
||
/// </summary>
|
||
/// <param name="isHeat"></param>
|
||
/// <returns></returns>
|
||
private SlotItem GetEmptyBufferSlot(WaferInfo wafer)
|
||
{
|
||
foreach (var cj in _lstControlJobs)
|
||
{
|
||
if (cj.State == EnumControlJobState.Executing)
|
||
{
|
||
string strSots = wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepParameter["SlotSelection"].ToString();
|
||
if (strSots == null)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
string[] slots = strSots.Split(',');
|
||
for (int i = 0; i < slots.Length; i++)
|
||
{
|
||
int slot = 0;
|
||
if (Int32.TryParse(slots[i], out slot))
|
||
{
|
||
if (_buffer.NoTray(slot - 1))
|
||
{
|
||
return new SlotItem(ModuleName.Buffer, slot - 1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取各个模块的Wafer总数量
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private int GetRunWaferCount()
|
||
{
|
||
int waferCount = 0;
|
||
if (_load.HasWafer(0))
|
||
{
|
||
waferCount++;
|
||
}
|
||
if (_unload.HasWafer(0))
|
||
{
|
||
waferCount++;
|
||
}
|
||
if (_tmRobot.HasWafer(0))
|
||
{
|
||
waferCount++;
|
||
}
|
||
foreach (SchedulerPM pm in _lstPms)
|
||
{
|
||
if (GetModule(pm.Module).HasWafer(0))
|
||
{
|
||
waferCount++;
|
||
}
|
||
}
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
if (_buffer.HasWafer(i))
|
||
{
|
||
waferCount++;
|
||
}
|
||
}
|
||
return waferCount;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取各个模块的石墨盘总数量
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private int GetCurrentTrayCount()
|
||
{
|
||
int trayCount = 0;
|
||
if (_load.HasTray(0))
|
||
{
|
||
trayCount++;
|
||
}
|
||
if (_unload.HasTray(0))
|
||
{
|
||
trayCount++;
|
||
}
|
||
if (_tmRobot.HasTray(0))
|
||
{
|
||
trayCount++;
|
||
}
|
||
foreach (SchedulerPM pm in _lstPms)
|
||
{
|
||
if (GetModule(pm.Module).HasTray(0))
|
||
{
|
||
trayCount++;
|
||
}
|
||
}
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
if (_buffer.HasTray(i))
|
||
{
|
||
trayCount++;
|
||
}
|
||
}
|
||
return trayCount;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取ProcessJob可以同时运行工艺的Wafer总数
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private int GetCurrentWaferCount()
|
||
{
|
||
int waferCountDiv = 0;
|
||
int cassWaferCount = 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)
|
||
{
|
||
cassWaferCount += pj.SlotWafers.Count;
|
||
foreach (var pjSlotWafer in pj.SlotWafers)
|
||
{
|
||
var module = GetModule(pjSlotWafer.Item1);
|
||
if (module.HasWafer(pjSlotWafer.Item2) && !module.CheckWaferNeedProcess(pjSlotWafer.Item2))
|
||
{
|
||
waferCountDiv++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return cassWaferCount - waferCountDiv;
|
||
|
||
//int waferCount = 0;
|
||
//if (_load.HasWafer(0))
|
||
//{
|
||
// waferCount++;
|
||
//}
|
||
//for (int i = 0; i < 3; i++)
|
||
//{
|
||
//if (_buffer.HasWafer(i))
|
||
// {
|
||
// waferCount++;
|
||
// }
|
||
//}
|
||
|
||
//if (_waferRobot.CheckWaferNeedProcess(0) )
|
||
//{
|
||
// waferCount++;
|
||
//}
|
||
//if (_aligner.CheckWaferNeedProcess(0) )
|
||
//{
|
||
// waferCount++;
|
||
//}
|
||
|
||
//for (int i=0;i<25;i++)
|
||
//{
|
||
// if (CheckWaferNeedProcess(ModuleName.CassAL, i))
|
||
// {
|
||
// waferCount++;
|
||
// }
|
||
// if (CheckWaferNeedProcess(ModuleName.CassAR, i))
|
||
// {
|
||
// waferCount++;
|
||
// }
|
||
//}
|
||
//return waferCount;
|
||
}
|
||
#endregion
|
||
|
||
|
||
|
||
#region Module error
|
||
|
||
public Result MonitorModuleError()
|
||
{
|
||
bool isModuleError = false;
|
||
bool[] isPMError = new bool[2];
|
||
|
||
for (int i = 0; i < isPMError.Length; i++)
|
||
{
|
||
isPMError[i] = false;
|
||
}
|
||
|
||
if (_tmRobot.IsError)
|
||
isModuleError = true;
|
||
|
||
for (int i = 0; i < _lstPms.Count; i++) // PM出错,不影响其他腔体的传片
|
||
{
|
||
if (_lstPms[i].IsError)
|
||
isPMError[i] = true;
|
||
}
|
||
|
||
if (isModuleError && !_isModuleErrorPrevious)
|
||
{
|
||
//var jb1 = _lstControlJobs.Find(x => x.Module == "LP1");
|
||
//if (jb1 != null)
|
||
// PauseJob(jb1.Name);
|
||
|
||
//var jb2 = _lstControlJobs.Find(x => x.Module == "LP2");
|
||
//if (jb2 != null)
|
||
// PauseJob(jb2.Name);
|
||
}
|
||
else if (!isModuleError && _isModuleErrorPrevious)
|
||
{
|
||
Reset();
|
||
//CloseDoor();
|
||
}
|
||
|
||
for (int i = 0; i < _lstPms.Count; i++)
|
||
{
|
||
if (!isPMError[i] && _isPMErrorPrevious[i])
|
||
{
|
||
_lstPms[i].ResetTask();
|
||
}
|
||
|
||
_isPMErrorPrevious[i] = isPMError[i];
|
||
}
|
||
|
||
_isModuleErrorPrevious = isModuleError;
|
||
|
||
return Result.RUN;
|
||
}
|
||
|
||
public Result MonitorModuleState()
|
||
{
|
||
bool controlJobExecuting = false;
|
||
bool isModuleBusy = false;
|
||
|
||
foreach (var controlJob in _lstControlJobs)
|
||
{
|
||
if (controlJob.State == EnumControlJobState.Executing)
|
||
controlJobExecuting = true;
|
||
}
|
||
|
||
//if (!controlJobExecuting)
|
||
// return Result.RUN;
|
||
|
||
//if (_tmRobot.IsOnline && _tmRobot.Entity.IsBusy)
|
||
// isModuleBusy = true;
|
||
|
||
//if (_efemRobot.IsOnline && _efemRobot.Entity.IsBusy)
|
||
// isModuleBusy = true;
|
||
|
||
//foreach (var pm in _lstPms) // PM出错,不影响其他腔体的传片
|
||
//{
|
||
// if (pm.IsOnline && pm.Entity.IsBusy)
|
||
// isModuleBusy = true;
|
||
//}
|
||
|
||
//foreach (var ll in _lstLls)
|
||
//{
|
||
// if (ll.IsOnline && ll.Entity.IsBusy)
|
||
// isModuleBusy = true;
|
||
//}
|
||
|
||
//foreach (var lp in _lstLps)
|
||
//{
|
||
// if (lp.IsOnline && lp.Entity.IsBusy)
|
||
// isModuleBusy = true;
|
||
//}
|
||
|
||
//if (_aligner.IsOnline && _aligner.Entity.IsBusy)
|
||
// isModuleBusy = true;
|
||
|
||
//if (isModuleBusy)
|
||
//{
|
||
// _timer.Stop();
|
||
// _started = false;
|
||
//}
|
||
//else
|
||
//{
|
||
// if (!_started)
|
||
// {
|
||
// _timer.Start(_deadLockTimeout * 1000);
|
||
// _started = true;
|
||
// }
|
||
//}
|
||
|
||
//_trigDeadLock.CLK = _timer.IsTimeout();
|
||
|
||
//if (_trigDeadLock.Q)
|
||
//{
|
||
// EV.PostWarningLog("System", "A deadlock has occurred");
|
||
//}
|
||
|
||
return Result.RUN;
|
||
}
|
||
|
||
public Result MonitorCleanTasks()
|
||
{
|
||
//if (!_isInited)
|
||
//{
|
||
// _isInited = true;
|
||
// InitClean();
|
||
//}
|
||
//foreach (var pm in _lstPms)
|
||
//{
|
||
// pm.MonitorCleanTasks();
|
||
//}
|
||
|
||
return Result.RUN;
|
||
}
|
||
|
||
private bool CheckModuleHaveWaferWithNoJob(out string reason)
|
||
{
|
||
reason = "";
|
||
if (_lstControlJobs.Count > 0)
|
||
{
|
||
reason = "lstControlJobs.Count > 0";
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if (_buffer.HasWafer(0) || _buffer.HasWafer(1) || _buffer.HasWafer(2))
|
||
{
|
||
reason = $"Buffer have wafer!";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return true;
|
||
}
|
||
|
||
if (_tmRobot.HasWafer(0))
|
||
{
|
||
reason = $"TmRobot have wafer!";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return true;
|
||
|
||
}
|
||
if (_aligner.HasWafer(0))
|
||
{
|
||
reason = $"Aligner have wafer!";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return true;
|
||
|
||
}
|
||
if (_waferRobot.HasWafer(0))
|
||
{
|
||
reason = $"WaferRobot have wafer!";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return true;
|
||
}
|
||
if (_load.HasWafer(0))
|
||
{
|
||
reason = $"Load have wafer!";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return true;
|
||
|
||
}
|
||
if (_load.HasTray(0))
|
||
{
|
||
reason = $"Load have Tray!";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return true;
|
||
|
||
}
|
||
if (_unload.HasWafer(0))
|
||
{
|
||
reason = $"UnLoad have wafer!";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return true;
|
||
|
||
}
|
||
if (_unload.HasTray(0))
|
||
{
|
||
reason = $"UnLoad have Tray!";
|
||
EV.PostWarningLog(LogSource, reason);
|
||
return true;
|
||
|
||
}
|
||
|
||
return false;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// Add condition string
|
||
/// </summary>
|
||
/// <param name="s"></param>
|
||
/// <param name="v"></param>
|
||
/// <returns></returns>
|
||
private string AddC(string s, string v, string sShould)
|
||
{
|
||
return (s + ":" + v + ", Should be:" + sShould + "\r\n");
|
||
}
|
||
|
||
}
|
||
} |