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 Aitex.Core.RT.RecipeCenter; using Venus_RT.Modules; using Venus_RT.Modules.PMs; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.Schedulers; using MECF.Framework.Common.SubstrateTrackings; using Venus_RT.Devices; using Venus_Core; using MECF.Framework.Common.DataCenter; using System.IO; namespace Venus_RT.Scheduler { public class SchedulerPM : SchedulerModule { public override bool IsAvailable { get { return _entity.IsIdle && _entity.IsInclude && (_entity.IsOnline || !Singleton.Instance.IsAutoMode) && CheckTaskDone(); } } public override bool IsOnline { get { return _entity != null && _entity.IsOnline; } } public override bool IsError { get { return _entity.IsError; } } public override bool IsIdle { get { return _entity.IsIdle; } } public override bool IsVac { get { return _entity.IsVac; } } public override bool IsAtm { get { return _entity.IsAtm; } } public override int TimeToReady { get { return _entity.TimeToReady; } } 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 JetPMBase _pm; private int _idleCleanOpt = 0; private int _idleCleanHourSetPoint; private int _idleCleanWaferSetPoint; private int _idleCleanWaferCount; private string _idleCleanRecipe; private DateTime _lastRunTime = new DateTime(); private float _paramTemp; public float Temperature { get { //if (SC.GetValue($"{Module}.Chiller.EnableChiller")) //{ // return _pm.CoolantOutletTempFB; //} //return _pm.SubstrateTempFB; return _pm.CoolantOutletTempFB; } } public SchedulerPM(ModuleName chamber) : base(chamber.ToString()) { _entity = Singleton.Instance.GetPM(chamber); _pm = DEVICE.GetDevice(Module.ToString()); EventWaferArrived += WaferArrived; LoadIdleCleanOpt(); } 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 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, RecipeType recipeType, WaferInfo wafer) { bool withWafer; if (recipeType == RecipeType.Clean) { withWafer = false; } else { withWafer = true; } _task = TaskType.Process; LogTaskStart(_task, $"recipe: {recipeName}, clean: {isCleanRecipe}, with wafer: {withWafer}"); if (RouteManager.IsATMMode) { int time = SC.GetValue("System.ATMCyclePMDelayTime"); _timerProcess.Start(time * 1000); LOG.Write(eEvent.EV_SCHEDULER, Module, $"System run in ATM mode, process skipped, delay {time} seconds"); WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.InProcess); return true; } else { if(isCleanRecipe) { _entityTaskToken = _entity.InvokeClean(recipeName, "", RecipeType.Clean); } else { _entityTaskToken = _entity.InvokeProcess(recipeName, wafer.WaferOrigin, RecipeType.Process); } 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 == EnumTransferType.Swap ? EnumTransferType.Pick : _taskTransferType, _taskSlot, _taskBlade); break; case TaskType.PostTransfer: ret = _entity.CheckAcked(_entityTaskToken); 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 (RouteManager.IsATMMode) { ret = _timerProcess.IsTimeout() && _entity.IsIdle; if (ret) WaferManager.Instance.UpdateWaferProcessStatus(Module, 0, EnumWaferProcessStatus.Completed); } else { ret = _entity.CheckAcked(_entityTaskToken) && _entity.IsIdle; } 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, "", RecipeType.Clean); 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, "", RecipeType.Clean); LogTaskStart(_task, $"recipe: {recipeName}, clean: {true}, with wafer: {false}"); return _entityTaskToken != (int)FSM_MSG.NONE; } public void PreJobClean() { } public bool PreJobProcess(string recipeName) { _task = TaskType.PreJobProcess; _entityTaskToken = _entity.InvokeClean(recipeName, "", RecipeType.Clean); LogTaskStart(_task, $"recipe: {recipeName}, clean: {true}, with wafer: {false}"); return _entityTaskToken != (int)FSM_MSG.NONE; } public bool CompleteJobProcess(string recipeName) { _task = TaskType.CompleteJobProcess; _entityTaskToken = _entity.InvokeClean(recipeName, "", RecipeType.Clean); LogTaskStart(_task, $"recipe: {recipeName}, clean: {true}, with wafer: {false}"); return _entityTaskToken != (int)FSM_MSG.NONE; } private void WaferArrived(object sender, EventArgs e) { if(WaferManager.Instance.CheckHasWafer(Module, 0)) { WaferInfo wafer = WaferManager.Instance.GetWafer(Module, 0); string recipeName = wafer.ProcessJob.Sequence.GetRecipe(Module); if (recipeName.Length > 0) { Process(recipeName, false, RecipeType.Process, wafer); } else { LOG.Write(eEvent.WARN_SCHEDULER, Module, "Cannot run process as get recipe failed."); } } else { LOG.Write(eEvent.WARN_SCHEDULER, Module, "Cannot run process as PM is empty"); } } public override void ResetTask() { _entity.ResetRecipeTime(); base.ResetTask(); } #region clean task private void LoadIdleCleanOpt() { // Load Idle Clean Config _idleCleanOpt = SC.GetValue($"{Module}.IdleClean.Option"); _idleCleanHourSetPoint = SC.GetValue($"{Module}.IdleClean.IdleCleanTime"); _idleCleanWaferSetPoint = SC.GetValue($"{Module}.IdleClean.IdleCleanWaferCount"); _idleCleanWaferCount = SC.GetValue($"{Module}.IdleClean.WaferCountSinceLastClean"); _idleCleanRecipe = SC.GetStringValue($"{Module}.IdleClean.IdleCleanRecipe"); string lastRun = SC.GetStringValue($"{Module}.IdleClean.LastRunTime"); if (lastRun.Length > 10) { try { _lastRunTime = DateTime.Parse(lastRun); } catch { lastRun = string.Empty; } } if (lastRun.Length <= 10) { _lastRunTime = DateTime.Now; SC.SetItemValueFromString($"{Module}.IdleClean.LastRunTime", _lastRunTime.ToString()); } // validate the idle clean recipe name string path = Path.Combine("Recipes", Module.ToString(), "Clean", $"{_idleCleanRecipe}.rcp"); string recipeContent = RecipeFileManager.Instance.LoadRecipeByPath(path); Recipe recipe = Recipe.Load(recipeContent); if (recipe == null) { _idleCleanRecipe = string.Empty; } } public void WaferProcessDone() { _lastRunTime = DateTime.Now; SC.SetItemValueFromString($"{Module}.IdleClean.LastRunTime", _lastRunTime.ToString()); _idleCleanWaferCount++; SC.SetItemValue($"{Module}.IdleClean.IdleCleanWaferCount", _idleCleanWaferCount); } private void ResetIdleCleanCounter() { _lastRunTime = DateTime.Now; SC.SetItemValueFromString($"{Module}.IdleClean.LastRunTime", _lastRunTime.ToString()); _idleCleanWaferCount = 0; SC.SetItemValue($"{Module}.IdleClean.IdleCleanWaferCount", _idleCleanWaferCount); } public bool RunIdleCleanTask() { if(_idleCleanOpt == 1) { TimeSpan span = DateTime.Now - _lastRunTime; if(span.TotalHours >= _idleCleanHourSetPoint) { Process(_idleCleanRecipe, true, RecipeType.Clean, null); ResetIdleCleanCounter(); return true; } } else if(_idleCleanOpt == 2) { if(_idleCleanWaferCount >= _idleCleanWaferSetPoint) { Process(_idleCleanRecipe, true, RecipeType.Clean, null); ResetIdleCleanCounter(); return true; } } return false; } public bool RunJobCleanTask(string jobCleanRecipe) { // validate the job clean recipe name //string recipeContent = RecipeFileManager.Instance.LoadRecipe(Module.ToString(), jobCleanRecipe, false); string path = Path.Combine( "Recipes", Module.ToString(),"Clean" , $"{jobCleanRecipe}.rcp"); string recipeContent = RecipeFileManager.Instance.LoadRecipeByPath(path); if (recipeContent.Length == 0) { return false; } Recipe recipe = Recipe.Load(recipeContent); if (recipe == null) { LOG.Write(eEvent.WARN_ROUTER, Module, $"Loading clean recipe: {path} failed"); return false; } return Process(jobCleanRecipe, true, RecipeType.Clean, null); } #endregion } }