using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using Aitex.Core.Common; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.Device; using Aitex.Core.RT.Event; using Aitex.Core.RT.Fsm; using Aitex.Core.RT.Log; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using Aitex.Sorter.Common; using VirgoRT.Module; using VirgoRT.Modules; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.Schedulers; using MECF.Framework.Common.SubstrateTrackings; using VirgoRT.Devices; namespace VirgoRT.Scheduler { public class SchedulerPM : SchedulerModule { public override bool IsAvailable { get { if (!SC.GetValue("System.IsATMMode")) return _entity.IsIdle && _entity.IsOnline && CheckTaskDone() && !_waitCompletejobClean; return _entity.IsIdle && _entity.IsOnline && CheckTaskDone(); } } public override bool IsOnline { get { return _entity != null && _entity.IsOnline; } } public override bool IsError { get { return _entity.IsError; } } public bool IsIdle { get { return _entity.IsIdle && _entity.IsOnline && CheckTaskDone() ; } } public TaskType Task => _task; private PMEntity _entity = null; private ModuleName _taskRobot; private EnumTransferType _taskTransferType; private Hand _taskBlade; private int _taskSlot; private int _entityTaskToken = (int)FSM_MSG.NONE; private DeviceTimer _timerProcess = new DeviceTimer(); private JetPM _pm; private float _paramTemp; public float Temperature { get { if (SC.GetValue($"{Module}.Chiller.EnableChiller")) { return _pm.CoolantOutletTempFB; } return _pm.SubstrateTempFB; } } public RD_TRIG IdlePurgeTrig { get; set; } public Stopwatch IdlePurgeTimer { get; set; } public int IdlePurgeTime { get; set; } public int IdlePurgeNextRunTime { get; set; } public RD_TRIG IdleCleanTrig { get; set; } public Stopwatch IdleCleanTimer { get; set; } 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; } public Stopwatch PreJobCleanTimer { get; set; } public int PreJobCleanTime { get; set; } public SchedulerPM(ModuleName chamber) : base(chamber.ToString()) { switch (chamber) { case ModuleName.PMA: _entity = Singleton.Instance.PMA; break; case ModuleName.PMB: _entity = Singleton.Instance.PMB; break; } _pm = DEVICE.GetDevice(Module.ToString()); IdlePurgeTrig = new RD_TRIG(); IdlePurgeTimer = new Stopwatch(); IdleCleanTrig = new RD_TRIG(); IdleCleanTimer = new Stopwatch(); PreJobCleanTrig = new RD_TRIG(); PreJobCleanTimer = new Stopwatch(); } public void InitClean() { DATA.Subscribe($"{Module}.IdlePurgeNextRunTime", () => IdlePurgeNextRunTime); DATA.Subscribe($"{Module}.IdleCleanNextRunTime", () => IdleCleanNextRunTime); } public override bool IsReadyForPick(ModuleName robot, int slot, Hand blade) { return _entity.IsPrepareTransferReady(robot, EnumTransferType.Pick, slot, blade) && WaferManager.Instance.CheckHasWafer(ModuleHelper.Converter(_module), slot); } public override bool IsReadyForPlace(ModuleName robot, int slot, Hand blade) { return _entity.IsPrepareTransferReady(robot, EnumTransferType.Place, slot, blade) && WaferManager.Instance.CheckNoWafer(ModuleHelper.Converter(_module), slot); } public bool CheckSlitDoorDown() { return _pm.CheckSlitDoorClose(); } public bool IsTemperatureReady(float temp) { return Math.Abs(Temperature - temp) < SC.GetValue("System.MaxTemperatureToleranceTransferWafer"); } public bool IsPrepareTransfer(ModuleName robot, EnumTransferType type, int slot) { return _task == TaskType.PrepareTransfer && _taskRobot == robot && _taskSlot == slot && _taskTransferType == type; } public override bool PrepareTransfer(ModuleName robot, EnumTransferType type, int slot) { _task = TaskType.PrepareTransfer; _taskRobot = robot; _taskSlot = slot; _taskTransferType = type; _entityTaskToken = _entity.InvokePrepareTransfer(robot, type, slot); LogTaskStart(_task, $"{robot} {type} slot {slot + 1}"); return _entityTaskToken != (int)FSM_MSG.NONE; } public bool PrepareTransfer(ModuleName robot, EnumTransferType type, int slot, float temp, Hand blade) { _task = TaskType.PrepareTransfer; _taskRobot = robot; _taskSlot = slot; _taskBlade = blade; _taskTransferType = type; _entityTaskToken = _entity.InvokePrepareTransfer(robot, type, slot, temp); LogTaskStart(_task, $"{robot} {type} slot {slot + 1}"); return _entityTaskToken != (int)FSM_MSG.NONE; } public bool PrepareTransfer(ModuleName robot, EnumTransferType type, int slot, float temp, Hand blade, WaferSize size) { _task = TaskType.PrepareTransfer; _taskRobot = robot; _taskSlot = slot; _taskBlade = blade; _taskTransferType = type; _entityTaskToken = _entity.InvokePrepareTransfer(robot, type, slot, temp, size); LogTaskStart(_task, $"{robot} {type} slot {slot + 1}, {size}"); return _entityTaskToken != (int)FSM_MSG.NONE; } public override bool PostTransfer(ModuleName robot, EnumTransferType type, int slot) { StopWaitTransfer(robot); _task = TaskType.PostTransfer; _taskRobot = robot; _taskSlot = slot; _taskTransferType = type; _entityTaskToken = _entity.InvokePostTransfer(robot, type, slot); LogTaskStart(_task, $"{robot} {type} slot {slot + 1}"); return _entityTaskToken != (int)FSM_MSG.NONE; } public override bool PostTransfer(ModuleName robot) { StopWaitTransfer(robot); _task = TaskType.PostTransfer; _taskRobot = robot; _taskSlot = _inTransferSlot; _taskTransferType = _inTransferType; _entityTaskToken = _entity.InvokePostTransfer(robot, _inTransferType, _inTransferSlot); LogTaskStart(_task, $"{robot} {_inTransferType} slot {_inTransferSlot + 1}"); return _entityTaskToken != (int)FSM_MSG.NONE; } public override bool Process(string recipeName, bool isCleanRecipe, bool withWafer, WaferInfo wafer) { _task = TaskType.Process; LogTaskStart(_task, $"recipe: {recipeName}, clean: {isCleanRecipe}, with wafer: {withWafer}"); if (SC.GetValue("System.IsATMMode")) { int time = SC.GetValue("System.ATMCyclePMDelayTime"); _timerProcess.Start(time * 1000); EV.PostInfoLog("Scheduler", $"System run in ATM mode, process skipped, delay {time} seconds"); _pm.SetLiftPin(VirgoCommon.MovementPosition.Down, out _); _entityTaskToken = (int)PMEntity.MSG.MoveLiftPin; WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.InProcess); return true; } else { _entityTaskToken = _entity.InvokeProcess(recipeName, wafer.WaferOrigin, withWafer); return _entityTaskToken != (int)FSM_MSG.NONE; } } public override bool Preheating(float temperature) { _task = TaskType.Heating; _paramTemp = temperature; _entityTaskToken = _entity.InvokePreHeat(temperature); LogTaskStart(_task, $"preheat to {temperature}"); return _entityTaskToken != (int)FSM_MSG.NONE; } public bool CheckTaskDone() { bool ret = false; switch (_task) { case TaskType.None: ret = true; break; case TaskType.PrepareTransfer: ret = _entity.CheckAcked(_entityTaskToken) && _entity.IsPrepareTransferReady(_taskRobot, _taskTransferType, _taskSlot, _taskBlade); break; case TaskType.PostTransfer: ret = _entity.CheckAcked(_entityTaskToken) && _pm.CheckSlitDoorClose(); break; case TaskType.Heating: ret = _entity.CheckAcked(_entityTaskToken) && IsTemperatureReady(_paramTemp); break; case TaskType.Process: case TaskType.IdlePurgeProcess: case TaskType.IdleCleanProcess: case TaskType.PreJobProcess: case TaskType.CompleteJobProcess: if (SC.GetValue("System.IsATMMode")) { ret = _timerProcess.IsTimeout() && _entity.IsIdle && (_entityTaskToken != (int)PMEntity.MSG.MoveLiftPin || _pm.CheckLiftDown()); if (ret) WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Completed); } else { ret = _entity.CheckAcked(_entityTaskToken) && _entity.IsProcessed(); } break; } if (ret && _task != TaskType.None) { LogTaskDone(_task, ""); _task = TaskType.None; } return ret; } public bool Monitor() { return true; } public void SetPMAuto() { _entity.SetAuto(); } public bool IdlePurgeProcess(string recipeName) { _task = TaskType.IdlePurgeProcess; _entityTaskToken = _entity.InvokeClean(recipeName, "", false); LogTaskStart(_task, $"recipe: {recipeName}, clean: {false}, with wafer: {false}"); return _entityTaskToken != (int)FSM_MSG.NONE; } public bool IdleCleanProcess(string recipeName) { _task = TaskType.IdleCleanProcess; _entityTaskToken = _entity.InvokeClean(recipeName, "", false); LogTaskStart(_task, $"recipe: {recipeName}, clean: {true}, with wafer: {false}"); return _entityTaskToken != (int)FSM_MSG.NONE; } public void PreJobClean() { var enableJobClean = SC.GetValue($"{Module}.JobClean.IsEnabled"); var enablePreJobClean = SC.GetValue($"{Module}.JobClean.EnablePreJobClean"); _waitPreJobClean = enableJobClean && enablePreJobClean; } public bool PreJobProcess(string recipeName) { _task = TaskType.PreJobProcess; _entityTaskToken = _entity.InvokeClean(recipeName, "", false); LogTaskStart(_task, $"recipe: {recipeName}, clean: {true}, with wafer: {false}"); return _entityTaskToken != (int)FSM_MSG.NONE; } public void CompleteJobClean() { var enableJobClean = SC.GetValue($"{Module}.JobClean.IsEnabled"); var enableCompleteJobClean = SC.GetValue($"{Module}.JobClean.EnableCompleteJobClean"); _waitCompletejobClean = enableJobClean && enableCompleteJobClean; } public bool CompleteJobProcess(string recipeName) { _task = TaskType.CompleteJobProcess; _entityTaskToken = _entity.InvokeClean(recipeName, "", false); LogTaskStart(_task, $"recipe: {recipeName}, clean: {true}, with wafer: {false}"); return _entityTaskToken != (int)FSM_MSG.NONE; } #region clean task 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.GetValue("System.IsATMMode")) return; var hasWafer = WaferManager.Instance.CheckHasWafer(Module, 0); 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 (PreJobCleanTimer.IsRunning && _waitPreJobClean) { 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 } #endregion } }