using System.Collections.Generic; using Aitex.Core.Common; using DocumentFormat.OpenXml.Wordprocessing; using MECF.Framework.Common.Schedulers; namespace SicRT.Modules.Schedulers { public class SchedulerModule { #region Variables protected enum TaskType { None, PrepareTransfer, Pick, Place, PickAndPlace, Preprocess, Process, PostProcess, OpenCover, CloseCover, Load, Unload, Align, TransferTarget, Cooling, WarmUp, Vent, Pump, Purge, Group, Separate, Map } /// /// 访问变量时先锁定,避免多线程竞争问题。 /// protected readonly object SyncRoot = new object(); private R_TRIG[] firstDetectWaferArriveTrigs = new R_TRIG[25]; private R_TRIG[] firstDetectWaferLeaveTrigs = new R_TRIG[25]; private R_TRIG[] firstDetectTrayArriveTrigs = new R_TRIG[25]; private R_TRIG[] firstDetectTrayLeaveTrigs = new R_TRIG[25]; protected string _module; protected TaskType _task = TaskType.None; /// /// 检测调度器TransferTarget任务是否超时。 /// private readonly DeviceTimer _timTransferTargetStatusTimeout; private ModuleName _inProcessRobot; #endregion #region Constructors protected SchedulerModule(string module) { _module = module; for (var i = 0; i < firstDetectWaferArriveTrigs.Length; i++) { firstDetectWaferArriveTrigs[i] = new R_TRIG(); } for (var i = 0; i < firstDetectWaferLeaveTrigs.Length; i++) { firstDetectWaferLeaveTrigs[i] = new R_TRIG(); } for (var i = 0; i < firstDetectTrayArriveTrigs.Length; i++) { firstDetectTrayArriveTrigs[i] = new R_TRIG(); } for (var i = 0; i < firstDetectTrayLeaveTrigs.Length; i++) { firstDetectTrayLeaveTrigs[i] = new R_TRIG(); } _timTransferTargetStatusTimeout = new DeviceTimer(); } #endregion #region Properties public ModuleName Module => ModuleHelper.Converter(_module); public virtual bool IsAvailable { get; } public virtual bool IsOnline { get; } public virtual bool IsError { get; } public virtual bool IsReady { get; } public virtual bool IsService { get; } #endregion #region Methods public string GetTask() { return _task.ToString(); } /// /// 检查调度器是否空闲。 /// /// /// 调度器空闲的条件除调度器无任务执行,还需要判断Module的状态是否为Idle或Error。 /// 因为如果调度器执行过程中,Module发生错误,也应该将调度器标记为空闲,否则调度器可能卡死而导致Auto流程无法继续。 /// 以下为调度器和Module可能出现冲突的状态,和判决方法: /// 1. 如果调度器无任务,但Module还未Idle,则说明上个Routine还未执行完毕,则认为Task还未完成。 /// 2. 如果调度器有任务,但Module已经Idle或Error,则说明Routine已执行完毕,但可能出错,则认为Task完成。 /// /// 调度器执行的任务是否结束。 /// 调度器关联的Module的状态机是否Idle或Alarm /// protected bool SuperCheckTaskDone(bool isTaskDone, bool fsmStopped) { // Transfer状态需特殊对待。该状态在调度器的CheckTaskDone()中未处理,因为机械手的异常可能导致被操作的调度器卡死在Transfer任务中。 // 因此需要建立超时机制,如果调度器停留在Transfer状态时间过长,则可能是机械手发生异常,需要自动退出Transfer任务。 if (_task == TaskType.TransferTarget) { if (_timTransferTargetStatusTimeout.IsTimeout()) { // 强制结束TransferTarget任务 _task = TaskType.None; _timTransferTargetStatusTimeout.Stop(); isTaskDone = true; } else { // 调度器仍然在Transfer任务中 return false; } } isTaskDone &= fsmStopped; if (isTaskDone) { if (_task != TaskType.None) { LogTaskDone(_task, ""); _task = TaskType.None; } return true; } if (fsmStopped) { LogTaskDone(_task, "Task Failed"); _task = TaskType.None; return true; } return false; } protected void LogTaskStart(TaskType cmd, string message) { EV.PostInfoLog("Scheduler", $"Task Start: {_module},{cmd} {message}"); } protected void LogTaskDone(TaskType cmd, string message) { EV.PostInfoLog("Scheduler", $"Task Done: {_module},{cmd} {message}"); } public virtual void ResetTask() { _task = TaskType.None; _timTransferTargetStatusTimeout.Stop(); } public bool WaitTransfer(ModuleName robot) { _task = TaskType.TransferTarget; _inProcessRobot = robot; _timTransferTargetStatusTimeout.Start(30000); LogTaskStart(_task, $"Note {robot} in transfer"); return true; } public bool IsWaitTransfer(ModuleName robot) { return _task == TaskType.TransferTarget && _inProcessRobot == robot; } public virtual bool StopWaitTransfer(ModuleName robot) { LogTaskDone(_task, $"Note {robot} transfer complete"); _inProcessRobot = ModuleName.System; _timTransferTargetStatusTimeout.Stop(); _task = TaskType.None; return true; } public WaferInfoRt GetWaferInfo(int slot) { return WaferManager.Instance.GetWafer(ModuleHelper.Converter(_module), slot); } public bool CheckWaferStatus(int slot, WaferStatus waferStatus) { return WaferManager.Instance.CheckWafer(ModuleHelper.Converter(_module), slot, waferStatus); } public bool HasWafer(int slot) { return WaferManager.Instance.CheckHasWafer(ModuleHelper.Converter(_module), slot); } public bool NoWafer(int slot) { return WaferManager.Instance.CheckNoWafer(ModuleHelper.Converter(_module), slot); } public bool HasTray(int slot) { return WaferManager.Instance.CheckHasTray(ModuleHelper.Converter(_module), slot); } public bool NoTray(int slot) { return WaferManager.Instance.CheckNoTray(ModuleHelper.Converter(_module), slot); } public bool FirstDetectWaferArrive(int slot) { firstDetectWaferArriveTrigs[slot].CLK = HasWafer(slot); return firstDetectWaferArriveTrigs[slot].Q; } public bool FirstDetectWaferLeave(int slot) { firstDetectWaferLeaveTrigs[slot].CLK = NoWafer(slot); return firstDetectWaferLeaveTrigs[slot].Q; } public bool FirstDetectTrayArrive(int slot) { firstDetectTrayArriveTrigs[slot].CLK = HasTray(slot); return firstDetectTrayArriveTrigs[slot].Q; } public bool FirstDetectTrayLeave(int slot) { firstDetectTrayLeaveTrigs[slot].CLK = NoTray(slot); return firstDetectTrayLeaveTrigs[slot].Q; } public bool HasTrayAndExceedProcessCount(int slot) { var wi = WaferManager.Instance.GetWafer(ModuleHelper.Converter(_module), slot); return wi.TrayState == TrayStatus.Normal && wi.TrayProcessCount <=0; } public bool HasTrayAndNotExceedProcessCount(int slot) { var wi = WaferManager.Instance.GetWafer(ModuleHelper.Converter(_module), slot); if (wi is null) return false; return wi.TrayState == TrayStatus.Normal && wi.TrayProcessCount >0; } public virtual bool CheckWaferNextStepIsThis(ModuleName module, int slot) { if (!WaferManager.Instance.CheckHasWafer(module, slot)) return false; var 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 /// /// /// public virtual bool CheckWaferNextStepModuleNoWafer(int slot) { if (!WaferManager.Instance.CheckHasWafer(Module, slot)) return false; var 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; List lstModuleName = wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules; if (lstModuleName.Count > 0) { for (int i = 0; i < lstModuleName.Count; i++) { if (WaferManager.Instance.CheckNoWafer(lstModuleName[i], 0)) { return true; } } } return false; } public virtual bool CheckWaferNextStepModuleNoTray(int slot) { if (!WaferManager.Instance.CheckHasWafer(Module, slot)) return false; var 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; List lstModuleName = wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules; if (lstModuleName.Count > 0) { for (int j = 0; j < lstModuleName.Count; j++) { if (lstModuleName[j] == ModuleName.Buffer) { if (CheckWaferNeedProcess(slot, ModuleName.PM1)) { if (WaferManager.Instance.CheckNoTray(ModuleName.Buffer, 0)) { return true; } } else if (CheckWaferNeedProcess(slot, ModuleName.PM2)) { if (WaferManager.Instance.CheckNoTray(ModuleName.Buffer, 1)) { return true; } } } else if (WaferManager.Instance.CheckNoTray(lstModuleName[j], 0)) { return true; } } } return false; } /// /// 检查指定槽位的Wafer是否完成Sequence。 /// /// 槽位 /// public virtual bool CheckWaferSequenceStepDone(int slot) { var wafer = WaferManager.Instance.GetWafer(Module, slot); if (wafer.IsWaferEmpty) { return false; } if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null) { return true; } if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count && wafer.ProcessJob.Sequence.Steps.Count > 0) { return true; } return false; } public virtual bool CheckWaferNeedProcess(int waferSlot, ModuleName processIn = ModuleName.System) { var wafer = WaferManager.Instance.GetWafer(Module, waferSlot); if (wafer.IsWaferEmpty) return false; //if (wafer.Status == WaferStatus.Dummy && wafer.ProcessState == WaferProcessStatus.Wait) //{ // if (ModuleHelper.IsPm(processIn)) // { // return (CheckNeedRunClean(out bool withWafer, out _) && withWafer); // } // return true; //} if (wafer.ProcessJob == null || wafer.ProcessJob.Sequence == null || wafer.ProcessJob.Sequence.Steps == null) return false; if (wafer.NextSequenceStep >= wafer.ProcessJob.Sequence.Steps.Count || wafer.ProcessJob.Sequence.Steps[wafer.NextSequenceStep].StepModules == null) return false; for (int i = wafer.NextSequenceStep; i < wafer.ProcessJob.Sequence.Steps.Count; i++) { foreach (var stepModule in wafer.ProcessJob.Sequence.Steps[i].StepModules) { if (ModuleHelper.IsPm(stepModule)) { if (processIn != ModuleName.System) { return stepModule == processIn ? true : false; } else { return true; } } } } return false; } public virtual bool CheckNeedRunClean(out bool withWafer, out string recipeName) { recipeName = string.Empty; withWafer = false; //SequenceInfo seq = GetCurrentSequenceInfo(); //int waferProcessed = StatsDataManager.Instance.GetValue($"{Module}.WaferProcessedSincePreviousClean"); //if (seq == null) // return false; //foreach (var stepInfo in seq.Steps) //{ // if (!stepInfo.StepModules.Contains(Module)) // continue; // if (stepInfo.StepParameter.ContainsKey("CleanIntervalWaferless") // && int.TryParse((string)stepInfo.StepParameter["CleanIntervalWaferless"], out int interval) // && stepInfo.StepParameter.ContainsKey("CleanRecipeWaferless") // && !string.IsNullOrEmpty((string)stepInfo.StepParameter["CleanRecipeWaferless"])) // { // if (interval > 0 && waferProcessed >= interval) // { // recipeName = (string)stepInfo.StepParameter["CleanRecipeWaferless"]; // withWafer = false; // return true; // } // } // if (stepInfo.StepParameter.ContainsKey("CleanIntervalWafer") // && int.TryParse((string)stepInfo.StepParameter["CleanIntervalWafer"], out interval) // && stepInfo.StepParameter.ContainsKey("CleanRecipeWafer") // && !string.IsNullOrEmpty((string)stepInfo.StepParameter["CleanRecipeWafer"])) // { // if (interval > 0 && waferProcessed >= interval) // { // recipeName = (string)stepInfo.StepParameter["CleanRecipeWafer"]; // withWafer = true; // return true; // } // } //} return false; } public virtual bool IsReadyForPick(ModuleName robot, int slot) { return true; } public virtual bool IsReadyForPlace(ModuleName robot, int slot) { return true; } public virtual bool PrepareTransfer(ModuleName robot, EnumTransferType type, int slot) { return true; } public virtual bool PostTransfer(ModuleName robot, EnumTransferType type, int slot) { return true; } public virtual bool Process(string recipeName, bool isCleanRecipe, bool withDummyWafer) { return true; } public virtual bool Cooling(bool coolingType, int coolingTime) { return true; } public virtual bool Aligning() { return true; } public virtual bool WarmUp(int warmUpTime) { return true; } public virtual bool CheckWaferTraySeparated() { return true; } public virtual bool CheckWaferTrayGrouped() { return true; } public virtual bool CheckSlitValveClosed() //检查闸板阀是否关闭 { return true; } #endregion } }