using Aitex.Core.RT.Log; using MECF.Framework.Common.Jobs; using System; using System.Collections.Generic; using MECF.Framework.Common.Equipment; using System.Linq; using System.Text; using System.Threading.Tasks; using Aitex.Core.Common; using MECF.Framework.Common.SubstrateTrackings; using MECF.Framework.Common.DBCore; using Aitex.Core.RT.DataCenter; using Aitex.Core.Util; using CyberX8_RT.Modules; using CyberX8_RT.Schedulers; using Aitex.Core.RT.SCCore; using MECF.Framework.Common.RecipeCenter; using Aitex.Core.RT.RecipeCenter; using Aitex.Common.Util; using System.Collections.Concurrent; using MECF.Framework.Common.WaferHolder; using CyberX8_RT.Modules.Loader; using SecsGem.Core.ItemModel; namespace CyberX8_RT.Dispatch { public class JobProcesser : Singleton { #region 常量 private const string PRODUCTION = "Production"; private const string SEQUENCE_TYPE = "SequenceType"; #endregion #region 内部变量 /// /// control jobs /// private List _lstControlJobs = new List(); /// /// process jobs /// private List _lstProcessJobs = new List(); private SchedulerFACallback _faCallback; private SchedulerDBCallback _dbCallback; private Dictionary _loadportControlJobDic = new Dictionary(); /// /// recipe processjob集合(key-recipe ppid,value-Process job集合) /// private ConcurrentDictionary> _recipeProcessJobs = new ConcurrentDictionary>(); /// /// recipe集合 /// private List _recipeList = new List(); /// /// recipe锁 /// private object _recipeLocker = new object(); #endregion #region 属性 /// /// ControlJob集合 /// public List ControlJobInfos { get { return _lstControlJobs; } } /// /// ProcessJob集合 /// public List ProcessJobInfos { get { return _lstProcessJobs; } } #endregion /// /// 构造函数 /// public JobProcesser() { _faCallback = new SchedulerFACallback(); _dbCallback = new SchedulerDBCallback(); InitializeData(); } /// /// 初始化数据 /// private void InitializeData() { for (int i = 1; i <= 3; i++) { _loadportControlJobDic[$"LP{i}"] = null; string lp = $"LP{i}"; DATA.Subscribe($"{lp}.CurrentControlJob", () => _loadportControlJobDic[lp], SubscriptionAttribute.FLAG.IgnoreSaveDB); } DATA.Subscribe("Scheduler.CjNameList", () => Array.ConvertAll(_lstControlJobs.ToArray(), x => x.Name.ToString()).ToList(), SubscriptionAttribute.FLAG.IgnoreSaveDB); } /// /// 创建任务 /// /// /// /// public bool CreateJob(Dictionary param, out string reason) { reason = ""; string[] slotSequence = (string[])param["SlotSequence"]; string jobId = (string)param["JobId"]; string module = (string)param["Module"]; if (string.IsNullOrEmpty(jobId)) { jobId = "CJ_Local_" + module; } string lotId = jobId; if (param.ContainsKey("LotId")) lotId = (string)param["LotId"]; int cycleNumber = param.ContainsKey("CycleNumber") ? (int)param["CycleNumber"] : 1; if (slotSequence.Length != SC.GetValue("EFEM.LoadPort.SlotNumber")) { reason = $"slot sequence parameter not valid, length is {slotSequence.Length}, should be {SC.GetValue("EFEM.LoadPort.SlotNumber")}"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } string sequenceType = PRODUCTION; if (param.ContainsKey(SEQUENCE_TYPE)) { sequenceType = param[SEQUENCE_TYPE].ToString(); } ModuleName lpModule = ModuleHelper.Converter(module); if (!ModuleHelper.IsLoadPort(lpModule)) { reason = $"{module} should be LoadPort"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } if (_lstControlJobs.Exists(x => x.Name == jobId)) { reason = $"{jobId} already created"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } int system_waferSize = SC.GetValue("System.WaferSize"); ControlJobInfo cj = new ControlJobInfo(); cj.Name = jobId; cj.Module = module; cj.LotName = lotId; cj.LotInnerId = Guid.NewGuid(); cj.LotWafers = new List(); cj.SetState(EnumControlJobState.WaitingForStart); cj.SequenceNameList = slotSequence; cj.CycleNumber = cycleNumber; Dictionary seqSlot = new Dictionary(); Dictionary>> seqSlotWafers = new Dictionary>>(); Dictionary indexSequence = new Dictionary(); bool enableGroupBySequence = SC.GetValue("Scheduler.GroupWaferBySequence"); int waferCount = 0; for (int i = 0; i < SC.GetValue("EFEM.LoadPort.SlotNumber"); 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[SC.GetValue("EFEM.LoadPort.SlotNumber")]; } if (!seqSlotWafers.ContainsKey(groupName)) { seqSlotWafers[groupName] = new List>(); } seqSlot[groupName][i] = true; if (!WaferManager.Instance.CheckHasWafer(module, i)) { reason = $"job wafer: {module} slot {i + 1} not in the carrier"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), 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}"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } if (WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState != EnumWaferProcessStatus.Idle) { reason = $"job wafer: {module} slot {i + 1} process status is {WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).ProcessState}"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } //--- 2024-03-21 增加了waferinfo 相应的lotId信息 start--- WaferInfo waferInfo = WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i); cj.LotWafers.Add(waferInfo); waferInfo.SequenceName = slotSequence[i]; waferInfo.LotId = lotId; //--- 2024-03-21 增加了waferinfo 相应的lotId信息 end--- seqSlotWafers[groupName].Add(Tuple.Create(ModuleHelper.Converter(module), i)); cj.JobWaferSize = WaferManager.Instance.GetWafer(ModuleHelper.Converter(module), i).Size; waferCount++; LOG.WriteLog(eEvent.EV_ROUTER, ModuleName.System.ToString(), $"Assigned wafer job, wafer {module}.{i + 1}, sequence: {slotSequence[i]}"); } if (seqSlotWafers.Count == 0) { reason = $"job has not assign wafer"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } //AutoIdle if (Singleton.Instance.IsAutoIdle) { bool waferCacheCheck = WaferManager.Instance.CheckStartUpCache(ref reason); if (!waferCacheCheck) { return true; } bool waferHolderCacheCheck = WaferHolderManager.Instance.CheckStartUpWaferHolderBuffer(ref reason); if (!waferHolderCacheCheck) { return false; } LoaderEntity loaderEntity = Singleton.Instance.GetModule(ModuleName.Loader1.ToString()); if (loaderEntity != null && loaderEntity.WaferHolderInfo != null) { reason = "Loader Has WaferHolder"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } UpdateAllBufferCellWaferHollderToLoader(); } List pjs = new List(); string[] seqs = seqSlot.Keys.ToArray(); for (int i = 0; i < seqs.Length; i++) { ProcessJobInfo pj = new ProcessJobInfo(); pj.Name = jobId + "_" + (i + 1); pj.ControlJobName = cj.Name; pj.LotName = lotId; pj.SlotWafers = seqSlotWafers[seqs[i]]; pj.SetState(EnumProcessJobState.Queued); string sequenceDirectory = sequenceType == PRODUCTION ? PathManager.GetProductionRecipeDir():PathManager.GetEngineeringRecipeDir(); string recipeFullPath = sequenceDirectory + $"\\{indexSequence[seqs[i]]}.seq.rcp"; SequenceRecipe sequenceRecipe = RecipeFileManager.Instance.LoadGenericityRecipe(recipeFullPath); if (sequenceRecipe==null) { reason = $"sequence recipe file {pj.Sequence} is not exist"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } sequenceRecipe.SequenceType = sequenceType; if (!SchedulerSequenceRecipeManager.Instance.CheckSequenceRecipeAvaible(sequenceRecipe,ref reason)) { return false; } if(!SchedulerSequenceRecipeManager.Instance.ExistAvaibleProcessCell(sequenceRecipe,true,true)) { reason = "sequence meets no avaible cell"; return false; } pj.SequenceRecipe= sequenceRecipe; WaferSize waferSize = (WaferSize)pj.SequenceRecipe.SubstrateSize; if (system_waferSize != pj.SequenceRecipe.SubstrateSize) { reason = $"sequence wafersize {pj.SequenceRecipe.SubstrateSize} is not matched system wafersize {system_waferSize}"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } string crsType = pj.SequenceRecipe.CrsType; List chemistries = SchedulerSequenceRecipeManager.Instance.GetSequenceChemistry(pj.SequenceRecipe); if (ControlJobInfos.Count == 0) { bool avaibleWaferHolder= SchedulerManager.Instance.ExistAvaibleBufferWaferHolder(waferSize, crsType, chemistries); if (!avaibleWaferHolder) { reason = $"buffer has not enough wafer shuttle"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } } else { int waferHolderCount = SchedulerManager.Instance.GetWaferHolderInfoCountByWaferSize(waferSize, crsType, chemistries); if (waferHolderCount == 0) { reason = $"buffer has not enough wafer shuttle"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } } pjs.Add(pj); } _dbCallback.LotUpdate(cj); foreach (var pj in pjs) { cj.ProcessJobNameList.Add(pj.Name); _lstProcessJobs.Add(pj); } _lstControlJobs.Add(cj); //AssociatedPMWithLP(cj); _loadportControlJobDic[cj.Module] = cj; CycleManager.Instance.UpdateLoadportSp(cj.Module,cycleNumber); return true; } /// /// 更新所有BufferCellWaferHolder可以拉至Loader /// private void UpdateAllBufferCellWaferHollderToLoader() { List schedulerModules = SchedulerManager.Instance.GetModuleTypeScheduleModules(ModuleType.Buffer); foreach (SchedulerModule item in schedulerModules) { WaferHolderInfo waferHolderInfo = WaferHolderManager.Instance.GetWaferHolder(item.Module.ToString()); if (waferHolderInfo != null) { waferHolderInfo.IsToLoader = true; } } } /// /// 增加processjob至字典中 /// /// public void AddRecipeProcessJob(ProcessJobInfo processJobInfo) { List jobs = null; if(_recipeProcessJobs.ContainsKey(processJobInfo.SequenceRecipe.Ppid)) { jobs = _recipeProcessJobs[processJobInfo.SequenceRecipe.Ppid]; } else { jobs = new List(); _recipeProcessJobs[processJobInfo.SequenceRecipe.Ppid]=jobs; } jobs.Add(processJobInfo); lock (_recipeLocker) { if(!_recipeList.Contains(processJobInfo.SequenceRecipe.Ppid)) { _recipeList.Add(processJobInfo.SequenceRecipe.Ppid); } } } /// /// 获取启动recipe的Processjob集合 /// /// public List GetRecipeStateProcessJobs(EnumProcessJobState enumProcessJobState) { string recipe = ""; lock (_recipeLocker) { if (_recipeList.Count != 0) { recipe = _recipeList[0]; } } List processJobInfos = new List(); if(!string.IsNullOrEmpty(recipe)&&_recipeProcessJobs.ContainsKey(recipe)) { List recipeProcessJobs = _recipeProcessJobs[recipe]; foreach (ProcessJobInfo processJobInfo in recipeProcessJobs) { if (processJobInfo.State == enumProcessJobState) { processJobInfos.Add(processJobInfo); } } } return processJobInfos; } /// /// 移除recipe processjob字典中processjob /// /// private void RemoveRecipeProcessJob(ProcessJobInfo processJobInfo) { List jobs = null; if (_recipeProcessJobs.ContainsKey(processJobInfo.SequenceRecipe.Ppid)) { jobs = _recipeProcessJobs[processJobInfo.SequenceRecipe.Ppid]; if(jobs.FindIndex(O => O.InnerId == processJobInfo.InnerId)>=0) { jobs.Remove(processJobInfo); } if (jobs.Count == 0) { _recipeProcessJobs.TryRemove(processJobInfo.SequenceRecipe.Ppid, out jobs); lock (_recipeLocker) { _recipeList.Remove(processJobInfo.SequenceRecipe.Ppid); } } } } /// /// 暂停Job /// /// /// /// public bool PauseJob(string jobName, out string reason) { reason = ""; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { reason = $"pause job rejected, not found job with id {jobName}"; return false; } if (cj.State == EnumControlJobState.Executing) { cj.SetState(EnumControlJobState.Paused); } _faCallback.JobPaused(cj); return true; } /// /// 恢复Job /// /// /// /// public bool ResumeJob(string jobName, out string reason) { reason = ""; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { reason = $"resume job rejected, not found job with id {jobName}"; //LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System, $"resume job rejected, not found job with id {jobName}"); return false; } if (cj.State == EnumControlJobState.Paused) { cj.SetState(EnumControlJobState.Executing); } _faCallback.JobResumed(cj); return true; } /// /// 启动任务 /// /// /// /// public bool StartJob(string jobName, out string reason) { reason = ""; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { reason = $"start job rejected, not found job with id {jobName}"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } if (cj.State == EnumControlJobState.WaitingForStart) { cj.SetState(EnumControlJobState.Executing); cj.StartTime = DateTime.Now; _dbCallback.LotCreated(cj); _faCallback.JobStarted(cj); CycleManager.Instance.InitializeLoadPortData(cj.Module); foreach(string item in cj.ProcessJobNameList) { ProcessJobInfo pj=_lstProcessJobs.Find(O=>O.Name== item); if(pj !=null) { AddRecipeProcessJob(pj); } } } return true; } /// /// 停止任务 /// /// /// /// public bool StopJob(string jobName, out string reason) { reason = ""; ControlJobInfo cj = _lstControlJobs.Find(x => x.Name == jobName); if (cj == null) { reason = $"stop job rejected, not found job with id {jobName}"; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), reason); return false; } if (cj.CycleNumber > 1) { cj.CycleNumber = 1; LOG.WriteLog(eEvent.WARN_ROUTER, ModuleName.System.ToString(), $"stop controljob {cj.Name} cycle number changed to 1"); } int processJobCount = 0; foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == cj.Name) { pj.SetState(EnumProcessJobState.Stopping); int count = 0; foreach (var pjSlotWafer in pj.SlotWafers) { if (WaferManager.Instance.CheckHasWafer(pjSlotWafer.Item1, pjSlotWafer.Item2)) { WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); if (WaferTaskManager.Instance.Contains(wafer.WaferID)) { continue; } if (wafer.ProcessState == EnumWaferProcessStatus.Idle) { wafer.ProcessState = EnumWaferProcessStatus.Canceled; count++; LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"{pjSlotWafer.Item1}.{pjSlotWafer.Item2} cancel process"); } } } if (count == pj.SlotWafers.Count) { processJobCount++; } } } if (processJobCount == _lstProcessJobs.Count) { List lst = _lstProcessJobs.ToList(); foreach (var pj in lst) { _lstProcessJobs.Remove(pj); RemoveRecipeProcessJob(pj); LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"controljob {cj.Name} processjob {pj.SequenceRecipe.Ppid} removed"); } } if (processJobCount == _lstProcessJobs.Count) { ClearLoadPortControlJob(cj); _lstControlJobs.Remove(cj); _faCallback.JobStopped(cj); _dbCallback.LotFinished(cj); LOG.WriteLog(eEvent.EV_SCHEDULER, "System", $"controljob {cj.Name} removed"); } return true; } /// /// 获取正在执行的ControlJob /// /// public ControlJobInfo GetRunningControlJob() { return _lstControlJobs.Find(O => O.State == EnumControlJobState.Executing); } /// /// 获取正在执行的ControlJob集合 /// /// public List getRunningControlJobs() { return _lstControlJobs.FindAll(O => O.State == EnumControlJobState.Executing); } /// /// ControlJob任务完成 /// public void CompleteControlJob(ControlJobInfo controlJobInfo) { controlJobInfo.SetState(EnumControlJobState.Completed); controlJobInfo.EndTime = DateTime.Now; _dbCallback.LotFinished(controlJobInfo); _faCallback.JobFinished(controlJobInfo); List pjRemoveList = new List(); foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == controlJobInfo.Name) pjRemoveList.Add(pj); } foreach (var pj in pjRemoveList) { _lstProcessJobs.Remove(pj); RemoveRecipeProcessJob(pj); } ClearLoadPortControlJob(controlJobInfo); _lstControlJobs.Remove(controlJobInfo); } /// /// 清除LoadPort ControlJob /// /// private void ClearLoadPortControlJob(ControlJobInfo controlJobInfo) { string[] keys = _loadportControlJobDic.Keys.ToArray(); foreach(string key in keys) { ControlJobInfo tmp= _loadportControlJobDic[key]; if(tmp!=null&&tmp.Name==controlJobInfo.Name) { _loadportControlJobDic[key] = null; } } } /// /// 重新启动任务 /// /// public void RestartControlJob(ControlJobInfo controlJobInfo) { List pjRemoveList = new List(); foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == controlJobInfo.Name) pjRemoveList.Add(pj); } foreach (var pj in pjRemoveList) { RemoveRecipeProcessJob(pj); } pjRemoveList.Clear(); controlJobInfo.SetState(EnumControlJobState.Executing); controlJobInfo.InnerId= Guid.NewGuid(); controlJobInfo.LotInnerId= Guid.NewGuid(); _dbCallback.LotCreated(controlJobInfo); foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == controlJobInfo.Name) { pj.SetState(EnumProcessJobState.Queued); pj.InnerId = Guid.NewGuid(); foreach (var pjSlotWafer in pj.SlotWafers) { WaferInfo wafer = WaferManager.Instance.GetWafer(pjSlotWafer.Item1, pjSlotWafer.Item2); wafer.ProcessJob = null; wafer.NextSequenceStep = 0; wafer.ProcessState = EnumWaferProcessStatus.Idle; } AddRecipeProcessJob(pj); } } } /// /// 获取Job对象 /// /// /// public ControlJobInfo GetControlJobInfoByName(string name) { return _lstControlJobs.Find(O=>O.Name == name); } /// /// 根据ProcessJob获取ControlJob /// /// /// public ControlJobInfo GetControlJobInfoByProcessJob(ProcessJobInfo processJobInfo) { return _lstControlJobs.Find(O => O.Name == processJobInfo.ControlJobName); } /// /// System Abort /// public void SystemAbort() { List controlJobInfos = _lstControlJobs.ToList(); foreach(var item in controlJobInfos) { StopJob(item.Name, out string reason); } } /// /// 移除所有job /// public void RemoveAllJob() { List controlJobInfos = _lstControlJobs.ToList(); foreach (var item in controlJobInfos) { List pjRemoveList = new List(); foreach (var pj in _lstProcessJobs) { if (pj.ControlJobName == item.Name) pjRemoveList.Add(pj); } foreach (var pj in pjRemoveList) { _lstProcessJobs.Remove(pj); RemoveRecipeProcessJob(pj); } ClearLoadPortControlJob(item); _lstControlJobs.Remove(item); } WaferTaskManager.Instance.RemoveAllTask(); WaferHolderTaskManager.Instance.RemoveAllTask(); SchedulerManager.Instance.ResetSchedulerModule(); } /// /// 获取loadport的controljob /// /// /// public ControlJobInfo GetControlJobInfoByLoadPort(string loadPort) { return _loadportControlJobDic.ContainsKey(loadPort)?_loadportControlJobDic[loadPort]:null; } } }