using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Aitex.Core.Common; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.Event; using Aitex.Core.RT.Fsm; using Aitex.Core.RT.Log; using Aitex.Core.RT.OperationCenter; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using Aitex.Sorter.Common; using MECF.Framework.Common.DataCenter; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.Schedulers; using MECF.Framework.Common.SubstrateTrackings; using MECF.Framework.RT.ModuleLibrary.PMModules; using MECF.Framework.RT.ModuleLibrary.SystemModules; namespace EfemDualSchedulerLib.Schedulers { public class SchedulerPM : SchedulerModule { public override bool IsAvailable { get { if (!SC.IsATMMode) return _pm.IsReady && _pm.IsOnline && CheckTaskDone() && !_waitCompletejobClean; return _pm.IsReady && _pm.IsOnline && CheckTaskDone(); } } public override bool IsOnline { get { return _pm.IsOnline; } } public override bool IsError { get { return _pm.IsError; } } public bool IsIdle { get { return _pm.IsReady && _pm.IsOnline && CheckTaskDone(); } } private PMModuleBase _pm = null; private double _paramTemp1; private double _paramTemp2; private ModuleName _taskRobot; private EnumTransferType _taskTransferType; private int[] _taskSlot; private DeviceTimer _timer = new DeviceTimer(); protected readonly string _statNamePM1ProcessedWaferCount; protected readonly string _statNamePM2ProcessedWaferCount; protected readonly string _statNamePM1ProcessTime; protected readonly string _statNamePM2ProcessTime; public RD_TRIG IdlePurgeTrig { get; set; } = new RD_TRIG(); public Stopwatch IdlePurgeTimer { get; set; } = new Stopwatch(); public int IdlePurgeTime { get; set; } public int IdlePurgeNextRunTime { get; set; } public RD_TRIG IdleCleanTrig { get; set; } = new RD_TRIG(); public Stopwatch IdleCleanTimer { get; set; } = new Stopwatch(); public int IdleCleanTime { get; set; } public int IdleCleanNextRunTime { get; set; } private bool _waitPreJobClean; private bool _waitCompletejobClean; private R_TRIG _trigIdleCleanFailed = new R_TRIG(); private R_TRIG _trigIdlePurgeFailed = new R_TRIG(); public RD_TRIG PreJobCleanTrig { get; set; } = new RD_TRIG(); public Stopwatch PreJobCleanTimer { get; set; } = new Stopwatch(); public int PreJobCleanTime { get; set; } //protected readonly string _statNameRFTime; //protected readonly string _statNameMWTime; public SchedulerPM(ModuleName chamber) : base(chamber.ToString()) { _pm = EquipmentManager.Modules[chamber] as PMModuleBase; _pm.IsOnline = true; _statNamePM1ProcessedWaferCount = $"{chamber}.ProcessedWaferCount1"; _statNamePM2ProcessedWaferCount = $"{chamber}.ProcessedWaferCount2"; _statNamePM1ProcessTime = $"{chamber}.ProcessTime1"; _statNamePM2ProcessTime = $"{chamber}.ProcessTime2"; //_statNameRFTime = $"{chamber}.RFTime"; //_statNameMWTime = $"{chamber}.MWTime"; System.Diagnostics.Trace.Assert(_pm != null); } public bool CheckTempReady(double temp1, double temp2) { return _pm.CheckTempReady(temp1, temp2); } public bool CheckTransferTempReady(double temp1, double temp2) { return _pm.CheckTransferTempReady(temp1, temp2); } public bool CheckTemp1Alarm(double recipeTemp1) { return _pm.CheckTemp1Alarm(recipeTemp1); } public bool CheckTemp2Alarm(double recipeTemp2) { return _pm.CheckTemp2Alarm(recipeTemp2); } public override bool IsReadyForPick(ModuleName robot, Hand blade, int slot) { return _pm.CheckReadyForTransfer(robot, blade, slot, EnumTransferType.Pick, out _) && WaferManager.Instance.CheckHasWafer(ModuleHelper.Converter(_module), slot); } public override bool IsReadyForPlace(ModuleName robot, Hand blade, int slot) { return _pm.CheckReadyForTransfer(robot, blade, slot, EnumTransferType.Place, out _) && WaferManager.Instance.CheckNoWafer(ModuleHelper.Converter(_module), slot); } public override bool PrepareTransfer(ModuleName robot, EnumTransferType type, int[] slot) { _task = TaskType.PrepareTransfer; _taskRobot = robot; _taskSlot = slot; _taskTransferType = type; if (!_pm.PrepareTransfer(robot, Hand.Blade1, slot, type, 0, 0, false, out string reason)) { LOG.Write(reason); return false; } Array.ForEach(slot,p=> LogTaskStart(_task, $"{robot} {type} slot {p + 1}")); return true; } public bool Preheating(double temperature1, double temperature2) { _task = TaskType.Heating; _paramTemp1 = temperature1; _paramTemp2 = temperature2; if (_pm.InvokePreHeat(temperature1, temperature2)) { LOG.Write("Can not PreHeat"); return false; } LogTaskStart(_task, $"preheat to {temperature1},{temperature2}"); return true; } //public bool IsPrepareTransfer(ModuleName robot, EnumTransferType type, int slot) //{ // return _task == TaskType.PrepareTransfer && _taskRobot == robot && _taskSlot == slot && // _taskTransferType == type; //} public override bool Process(string recipeName, bool isCleanRecipe, bool withWafer) { _task = TaskType.Process; //if (SC.GetValue("System.IsATMMode")) //{ // _timer.Start(SC.GetValue("System.ATMProcessDelayTime") * 1000); // //_entityTaskToken = (int)FSM_MSG.TIMER; // WaferManager.Instance.GetWafer(Module, 0).ProcessState = EnumWaferProcessStatus.InProcess; // WaferManager.Instance.GetWafer(Module, 1).ProcessState = EnumWaferProcessStatus.InProcess; //} //else { if (!_pm.Process(recipeName, isCleanRecipe, withWafer, out string reason)) { LOG.Write(reason); return false; } } if (HasWafer(0)) UpdatePM1Stats_data(recipeName, isCleanRecipe); if (HasWafer(1)) UpdatePM2Stats_data(recipeName, isCleanRecipe); LogTaskStart(_task, $"recipe: {recipeName}, clean: {isCleanRecipe}, with wafer: {withWafer}"); return true; } public virtual void UpdatePM1Stats_data(string recipeName, bool isClean) { if (!isClean) { int value1 = StatsDataManager.Instance.Increase(_statNamePM1ProcessedWaferCount); int value2 = StatsDataManager.Instance.Increase(_statNamePM1ProcessTime); LOG.Write($"{_statNamePM1ProcessedWaferCount} counter increase 1 to {value1}"); LOG.Write($"{_statNamePM1ProcessTime} counter increase 1 to {value2}"); } } public virtual void UpdatePM2Stats_data(string recipeName, bool isClean) { if (!isClean) { int value1 = StatsDataManager.Instance.Increase(_statNamePM2ProcessedWaferCount); int value2 = StatsDataManager.Instance.Increase(_statNamePM2ProcessTime); LOG.Write($"{_statNamePM2ProcessedWaferCount} counter increase 1 to {value1}"); LOG.Write($"{_statNamePM2ProcessTime} counter increase 1 to {value2}"); } } public bool CheckTaskDone() { bool ret = false; switch (_task) { case TaskType.None: ret = true; break; case TaskType.PrepareTransfer: ret = _taskSlot.All(p => _pm.CheckReadyForTransfer(_taskRobot, Hand.Blade1, p, _taskTransferType, out _)); break; case TaskType.Heating: ret = CheckTempReady(_paramTemp1, _paramTemp2); break; case TaskType.Process: case TaskType.IdlePurgeProcess: case TaskType.IdleCleanProcess: case TaskType.PreJobProcess: case TaskType.CompleteJobProcess: //if (SC.GetValue("System.IsATMMode")) //{ // ret = _timer.IsTimeout(); // if (ret) // { // WaferManager.Instance.GetWafer(Module, 0).ProcessState = EnumWaferProcessStatus.Completed; // WaferManager.Instance.GetWafer(Module, 1).ProcessState = EnumWaferProcessStatus.Completed; // } //} //else { ret = _pm.IsReady; } break; } if (ret && _task != TaskType.None) { LogTaskDone(_task, ""); _task = TaskType.None; } return ret; } public bool CheckInProcess() { return _task == TaskType.Process; } public bool CloseSlitValve() { return _pm.CloseSlitValve(out _); } public bool CheckSlitValveClose() { return _pm.CheckSlitValveClose(); } public bool Monitor() { return true; } #region clean task public void InitClean() { DATA.Subscribe($"{Module}.IdlePurgeNextRunTime", () => IdlePurgeNextRunTime); DATA.Subscribe($"{Module}.IdleCleanNextRunTime", () => IdleCleanNextRunTime); OP.Subscribe($"{Module}.ResetIdlePurgeTime", (string cmd, object[] args) => { ResetIdlePurgeTime(); return true; }); OP.Subscribe($"{Module}.ResetIdleCleanTime", (string cmd, object[] args) => { ResetIdleCleanTime(); return true; }); } internal void ResetIdlePurgeTime() { IdlePurgeTimer.Restart(); } internal void ResetIdleCleanTime() { IdleCleanTimer.Restart(); } public bool ResetClean(bool resetPurge, bool resetClean, bool jobClean) { if (resetPurge) { IdlePurgeTrig.RST = true; IdlePurgeTimer.Stop(); IdlePurgeTime = 0; } if (resetClean) { IdleCleanTrig.RST = true; IdleCleanTimer.Stop(); IdleCleanTime = 0; } if (jobClean) { _waitPreJobClean = false; _waitCompletejobClean = false; PreJobCleanTrig.RST = true; PreJobCleanTimer.Stop(); } return true; } public void MonitorCleanTasks() { if (SC.IsATMMode) return; var hasWafer = WaferManager.Instance.CheckHasWafer(Module, 0) || WaferManager.Instance.CheckHasWafer(Module, 1); var enablePurge = false;//SC.GetValue($"{Module}.IdlePurge.IsEnabled"); var enableClean = SC.GetValue($"{Module}.IdleClean.IsEnabled"); #region Idle Purge IdlePurgeTrig.CLK = (IsIdle || _task != TaskType.IdlePurgeProcess) && enablePurge && !hasWafer; if (IdlePurgeTrig.R) { LOG.Write($"{Module} idle purge start calc elapse time"); IdlePurgeTimer.Restart(); } if (IdlePurgeTrig.T) { LOG.Write($"{Module} idle purge stopped calc elapse time"); ResetClean(true, false, false); } if (IdlePurgeTimer.IsRunning) { IdlePurgeTime = (int)IdlePurgeTimer.ElapsedMilliseconds / 1000; int idlePurgeTimeSetPoint = SC.GetValue($"{Module}.IdlePurge.IdlePurgeTime"); var nextRunTime = idlePurgeTimeSetPoint - IdlePurgeTime; if (nextRunTime < 0) nextRunTime = 0; if (enablePurge) { if (nextRunTime <= 0 && IsAvailable) { var recipe = SC.GetStringValue($"{Module}.IdlePurge.IdlePurgeRecipe"); if (string.IsNullOrEmpty(recipe)) { _trigIdlePurgeFailed.CLK = true; LOG.Write($"{Module} Idle purge can not run, recipe is empty"); ResetClean(true, false, false); } else if (hasWafer) { _trigIdlePurgeFailed.CLK = true; LOG.Write($"{Module} Idle purge can not run, has wafer"); ResetClean(true, false, false); } else { _trigIdlePurgeFailed.CLK = false; LOG.Write($"{Module} Start run idle purge recipe {recipe}"); IdlePurgeProcess("ALD\\Process\\" + recipe); } if (_trigIdlePurgeFailed.Q) { EV.PostWarningLog(Module.ToString(), "Can not run idle purge recipe"); } } } else { nextRunTime = 0; } IdlePurgeNextRunTime = nextRunTime; } else { IdlePurgeNextRunTime = 0; } #endregion #region Idle Clean IdleCleanTrig.CLK = (IsIdle || _task != TaskType.IdleCleanProcess) && enableClean && !hasWafer; if (IdleCleanTrig.R) { LOG.Write($"{Module} idle clean start calc elapse time"); IdleCleanTimer.Restart(); } if (IdleCleanTrig.T) { LOG.Write($"{Module} idle clean stopped calc elapse time"); ResetClean(false, true, false); } if (IdleCleanTimer.IsRunning) { IdleCleanTime = (int)IdleCleanTimer.ElapsedMilliseconds / 1000; int idleCleanTimeSetPoint = SC.GetValue($"{Module}.IdleClean.IdleCleanTime"); var nextRunTime = idleCleanTimeSetPoint - IdleCleanTime; if (nextRunTime < 0) nextRunTime = 0; if (enableClean) { if (nextRunTime <= 0 && IsAvailable) { var recipe = SC.GetStringValue($"{Module}.IdleClean.IdleCleanRecipe"); if (string.IsNullOrEmpty(recipe)) { _trigIdleCleanFailed.CLK = true; LOG.Write($"{Module} Idle clean can not run, recipe is empty"); ResetClean(false, true, false); } else if (hasWafer) { _trigIdleCleanFailed.CLK = true; LOG.Write($"{Module} Idle clean can not run, has wafer"); ResetClean(false, true, false); } else { _trigIdleCleanFailed.CLK = false; LOG.Write($"{Module} Start run idle Clean recipe {recipe}"); IdleCleanProcess($"{Module}\\" + recipe); } if (_trigIdleCleanFailed.Q) { EV.PostWarningLog(Module.ToString(), "Can not run idle clean recipe"); } } } else { nextRunTime = 0; } IdleCleanNextRunTime = nextRunTime; } else { IdleCleanNextRunTime = 0; } #endregion #region PreJob Clean var enableJobClean = SC.GetValue($"{Module}.JobClean.IsEnabled"); var enablePreJobClean = SC.GetValue($"{Module}.JobClean.EnablePreJobClean"); var enableCompleteJobClean = SC.GetValue($"{Module}.JobClean.EnableCompleteJobClean"); if (enableJobClean && enableCompleteJobClean && _waitCompletejobClean && IsIdle) { var recipe = SC.GetStringValue($"{Module}.JobClean.CompleteJobCleanRecipe"); if (string.IsNullOrEmpty(recipe)) { EV.PostWarningLog(Module.ToString(), "complete job clean can not run, recipe is empty"); } else if (hasWafer) { EV.PostWarningLog(Module.ToString(), "complete job clean can not run, has wafer"); } else { EV.PostInfoLog(Module.ToString(), $"Start run complete job clean recipe {recipe}"); CompleteJobProcess($"{Module}\\" + recipe); } _waitCompletejobClean = false; } PreJobCleanTrig.CLK = enableJobClean && enablePreJobClean && IsIdle; if (PreJobCleanTrig.R) { PreJobCleanTimer.Restart(); } if (PreJobCleanTrig.T) { ResetClean(false, false, true); } if (enableJobClean && enablePreJobClean && _waitPreJobClean && PreJobCleanTimer.IsRunning && IsIdle) { PreJobCleanTime = (int)PreJobCleanTimer.ElapsedMilliseconds / 1000; int PreJobCleanIdleTimeSP = SC.GetValue($"{Module}.JobClean.IdleCleanTime"); if (PreJobCleanTime >= PreJobCleanIdleTimeSP) { var recipe = SC.GetStringValue($"{Module}.JobClean.PreJobCleanRecipe"); if (string.IsNullOrEmpty(recipe)) { EV.PostWarningLog(Module.ToString(), "pre job clean can not run, recipe is empty"); } else if (hasWafer) { EV.PostWarningLog(Module.ToString(), "pre job clean can not run, has wafer"); } else { EV.PostInfoLog(Module.ToString(), $"Start run pre job clean recipe {recipe}"); PreJobProcess($"{Module}\\" + recipe); } } } #endregion } public bool IdlePurgeProcess(string recipeName) { _task = TaskType.IdlePurgeProcess; if (!_pm.Process(recipeName, true, false, out string reason)) { LOG.Write(reason); return false; } LogTaskStart(_task, $"recipe: {recipeName}, clean: {true}, with wafer: {false}"); return true; } public bool IdleCleanProcess(string recipeName) { _task = TaskType.IdleCleanProcess; if (!_pm.Process(recipeName, true, false, out string reason)) { LOG.Write(reason); return false; } LogTaskStart(_task, $"recipe: {recipeName}, clean: {true}, with wafer: {false}"); return true; } public void PreJobClean() { var enableJobClean = SC.GetValue($"{Module}.JobClean.IsEnabled"); var enablePreJobClean = SC.GetValue($"{Module}.JobClean.EnablePreJobClean"); var enableCompleteJobClean = SC.GetValue($"{Module}.JobClean.EnableCompleteJobClean"); _waitPreJobClean = enableJobClean && enablePreJobClean; } public bool PreJobProcess(string recipeName) { _task = TaskType.PreJobProcess; if (!_pm.Process(recipeName, true, false, out string reason)) { LOG.Write(reason); return false; } LogTaskStart(_task, $"recipe: {recipeName}, clean: {true}, with wafer: {false}"); return true; } public void CompleteJobClean() { var enableJobClean = SC.GetValue($"{Module}.JobClean.IsEnabled"); var enablePreJobClean = SC.GetValue($"{Module}.JobClean.EnablePreJobClean"); var enableCompleteJobClean = SC.GetValue($"{Module}.JobClean.EnableCompleteJobClean"); _waitCompletejobClean = enableJobClean && enableCompleteJobClean; } public bool CompleteJobProcess(string recipeName) { _task = TaskType.CompleteJobProcess; if (!_pm.Process(recipeName, true, false, out string reason)) { LOG.Write(reason); return false; } LogTaskStart(_task, $"recipe: {recipeName}, clean: {true}, with wafer: {false}"); return true; } #endregion /// /// 检查参数代表的wafer下一站是否是本模块 /// /// 已有wafer的Module /// 已有wafer的Slot /// public override bool CheckWaferNextStepIsThisModule(ModuleName module, int slot) { if (!WaferManager.Instance.CheckHasWafer(module, slot)) return false; WaferInfo wafer = WaferManager.Instance.GetWafer(module, slot); if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) return false; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count) return false; if (!wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules.Contains(module)) return false; return true; } /// /// 检查参数代表的wafer下一站是否是本模块thisSlot /// /// 已有wafer的Module /// 已有wafer的Slot /// 本模块slot位置 /// public override bool CheckWaferNextStepIsThisModuleSlot(ModuleName module, int slot, int thisSlot) { 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 (!wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepParameter["SlotSelection"].ToString().Contains(thisSlot.ToString())) return false; return true; } /// /// 判断下一步的模块是否有Wafer /// /// /// public override bool CheckWaferNextStepModuleNoWafer(int slot, bool needIgnoreRobot = true) { 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; var step = wafer.NextSequenceStep; if (step >= wafer.ProcessJob.Sequence.Steps.Count) return false; List lstModuleName = wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules; if (lstModuleName.Count > 0) { if (needIgnoreRobot && lstModuleName.Any(x => x == ModuleName.EfemRobot || x == ModuleName.TMRobot)) { step += 1; if (step >= wafer.ProcessJob.Sequence.Steps.Count) return false; lstModuleName = wafer.ProcessJob.Sequence.Steps[step].StepModules; } for (int i = 0; i < lstModuleName.Count; i++) { var moduleName = lstModuleName[i]; if (ModuleHelper.IsPm(moduleName)) { var stepParameters = wafer.ProcessJob.Sequence.Steps[step].StepParameter; if(stepParameters != null && stepParameters.Count > 0 && stepParameters.ContainsKey(moduleName.ToString())) { var slotList = stepParameters[moduleName.ToString()].ToString().Split(','); if(slotList != null) { foreach(var slotIndex in slotList) { if (WaferManager.Instance.CheckNoWafer(moduleName, Convert.ToInt32(slotIndex))) { return true; } } } } } } } return false; } } }