using Aurora.Api; using Aurora.Api.Devices; using VisionCommon; using VisionSequence.Pattern; using VisionAligner; using Hermes; using VisionDirector; using System.Collections.Generic; using System.Linq; namespace VisionSequence.Aligner { /// /// 얼라인 믹서 시퀀스 파라미터 /// public class AlignMixerSeqParams : SeqParamsBase { /// Target 얼라인 키 정보 public AlignInfo AlignInfo_Tgt { get; set; } /// Source 얼라인 키 정보 public AlignInfo AlignInfo_Src { get; set; } /// Target 얼라인 결과 public AlignResult AlignResult_Tgt { get; set; } /// FOV별로 이동시킬 절대위치 public Dictionary FovPositions { get; set; } /// 메인 화면 타입 public eDisplayType DisplayType { get; set; } /// /// 얼라인 재시도(Retry) 카운트 초기화 플래그 /// 1) 비전에서 Move와 Retry를 처리할 경우 [True] /// 2) 제어에서 Move와 Retry를 처리하면서, 얼라인 시퀀스를 신규 호출한 경우 [True] /// 3) 제어에서 Move와 Retry를 처리하면서, Retry로 얼라인 시퀀스를 다시 호출한 경우 [False] /// public bool AlignCntClear { get; set; } /// /// 얼라인 믹서 시퀀스 파라미터를 얻어온다. /// /// Target 얼라인 키 정보 /// Source 얼라인 키 정보 /// Target 얼라인 결과가 null이 아닐 경우, 이후 Target 얼라인 스킵 (선택사항) /// FOV별로 이동시킬 절대위치. null일 경우, 자동으로 Adapter에 정의된 티칭위치로 이동 /// 메인 화면 타입 /// 얼라인 횟수 /// 시퀀스 모드 public AlignMixerSeqParams( AlignInfo alignInfo_Tgt, AlignInfo alignInfo_Src, AlignResult alignResult_Tgt = null, Dictionary fovPositions = null, eDisplayType displayType = eDisplayType.Main_1, bool alignCntClear = true, eSeqMode seqMode = eSeqMode.Auto) : base(seqMode) { this.AlignInfo_Tgt = alignInfo_Tgt; this.AlignInfo_Src = alignInfo_Src; this.AlignResult_Tgt = alignResult_Tgt; this.FovPositions = fovPositions; this.DisplayType = displayType; this.AlignCntClear = alignCntClear; } } /// /// Target(고정형)과 Source(이동형)의 얼라인 보정량을 조합하여, 최종적으로 Source를 Target에 맞추는 얼라인 시퀀스 /// Target의 3축(X,Y,T)이 모두 움직일 수가 없기 때문에, Source의 모터를 대신 움직여 얼라인 한다. /// public class AlignMixerSeq : SubSeqBase { private readonly Logger LOG = new Logger(typeof(AlignMixerSeq).Name); private Logger _VisionLog = new Logger(typeof(AlignMixerSeq).Name, VisionKeys.LogKey); private TactTimeChecker _TactTimeChecker; // Target Global Variables private SubSysInfo _SubSysInfo_Tgt; private VisionAligner.Aligner _Aligner_Tgt; private AlignRecipe _AlignRecipe_Tgt; private AlignResult _AlignResult_Tgt; // Source Global Variables private SubSysInfo _SubSysInfo_Src; private VisionAligner.Aligner _Aligner_Src; private AlignRecipe _AlignRecipe_Src; private AlignResult _AlignResult_Src; // Mixed Global Variables private AlignResult _AlignResult_Mixed; // _AlignResult_Tgt + _AlignResult_Src private AlignLimitChecker _AlignLimitChecker_Mixed; private AlignRetryChecker _AlignRetryChecker_Mixed; private AlignMixerSeqParams _Params; private CsvManager _CsvManager = new CsvManager(); private ImageManager _ImageManager = new ImageManager(); private string _SeqName; #region Sequence Id private const string SEQ_ID_INIT = "INIT"; // Dry Run private const string SEQ_ID_CHECK_DRY_RUN = "CHECK_DRY_RUN"; private const string SEQ_ID_FORK_DRY_RUN = "FORK_DRY_RUN"; private const string SEQ_ID_CHECK_ALIGN_RESULT_DRY_RUN_TGT = "CHECK_ALIGN_RESULT_DRY_RUN_TGT"; private const string SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_TGT = "SET_DRY_RUN_SEQ_PARAMS_TGT"; private const string SEQ_ID_DRY_RUN_SEQ_TGT = "DRY_RUN_SEQ_TGT"; private const string SEQ_ID_DRY_RUN_UPDATE_UI_TGT = "DRY_RUN_UPDATE_UI_TGT"; private const string SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_SRC = "SET_DRY_RUN_SEQ_PARAMS_SRC"; private const string SEQ_ID_DRY_RUN_SEQ_SRC = "DRY_RUN_SEQ_SRC"; private const string SEQ_ID_DRY_RUN_UPDATE_UI_SRC = "DRY_RUN_UPDATE_UI_SRC"; private const string SEQ_ID_JOIN_DRY_RUN = "JOIN_DRY_RUN"; // Align private const string SEQ_ID_FORK_ALIGN = "FORK_ALIGN"; private const string SEQ_ID_CHECK_ALIGN_RESULT_TGT = "CHECK_ALIGN_RESULT_TGT"; private const string SEQ_ID_CLEAR_TGT = "CLEAR_TGT"; private const string SEQ_ID_SET_ALIGN_SEQ_PARAMS_TGT = "SET_ALIGN_SEQ_PARAMS_TGT"; private const string SEQ_ID_ALIGN_SEQ_TGT = "ALIGN_SEQ_TGT"; private const string SEQ_ID_CHECK_STATUS_TGT = "CHECK_STATUS_TGT"; private const string SEQ_ID_CLEAR_SRC = "CLEAR_SRC"; private const string SEQ_ID_SET_ALIGN_SEQ_PARAMS_SRC = "SET_ALIGN_SEQ_PARAMS_SRC"; private const string SEQ_ID_ALIGN_SEQ_SRC = "ALIGN_SEQ_SRC"; private const string SEQ_ID_CHECK_STATUS_SRC = "CHECK_STATUS_SRC"; private const string SEQ_ID_JOIN_ALIGN = "JOIN_ALIGN"; private const string SEQ_ID_MARK_NG = "MARK_NG"; private const string SEQ_ID_DISTANCE_NG = "DISTANCE_NG"; // Mix Align Data private const string SEQ_ID_MIX_ALIGN_RESULTS = "MIX_ALIGN_RESULTS"; private const string SEQ_ID_CHECK_STATUS_MIXED = "CHECK_STATUS_MIXED"; private const string SEQ_ID_ALIGN_LIMIT_NG = "ALIGN_LIMIT_NG"; private const string SEQ_ID_RETRY_COUNT_NG = "RETRY_COUNT_NG"; // Apply Offset & Move private const string SEQ_ID_FORK_MOVE = "FORK_MOVE"; private const string SEQ_ID_APPLY_OFFSET = "APPLY_OFFSET"; private const string SEQ_ID_CHECK_MOVE_ENABLED = "CHECK_MOVE_ENABLED"; private const string SEQ_ID_MOVE_DELTA = "MOVE_DELTA"; private const string SEQ_ID_MOVE_FINISHED = "MOVE_FINISHED"; private const string SEQ_ID_UPDATE_UI = "UPDATE_UI"; private const string SEQ_ID_SAVE_CSV = "SAVE_CSV"; private const string SEQ_ID_SAVE_SCREENSHOT = "SAVE_SCREENSHOT"; private const string SEQ_ID_JOIN_MOVE = "JOIN_MOVE"; // Retry private const string SEQ_ID_CHECK_RETRY = "CHECK_RETRY"; private const string SEQ_ID_SEQ_COMPLETE = "SEQ_COMPLETE"; #endregion /// /// 생성자 /// /// 시퀀스 이름 /// Target 얼라인 키 정보 /// Source 얼라인 키 정보 public AlignMixerSeq(string seqName, AlignInfo alignInfo_Tgt, AlignInfo alignInfo_Src) { _SeqName = seqName; _AlignRecipe_Tgt = VisionRecipeManager.GetAlignRecipe(alignInfo_Tgt); _AlignRecipe_Src = VisionRecipeManager.GetAlignRecipe(alignInfo_Src); } private void SetActions(SequenceBuilder sb) { SequenceBuilder dryRunSeqBuilder_tgt = SubSeqManager.CreateSubSeqBuilder(new AlignSeq_DryRun(), typeof(AlignSeq_DryRun).Name); SequenceBuilder dryRunSeqBuilder_src = SubSeqManager.CreateSubSeqBuilder(new AlignSeq_DryRun(), typeof(AlignSeq_DryRun).Name); SequenceBuilder alignSeqBuilder_Tgt = SubSeqManager.CreateSubSeqBuilder(new AlignSubSeq(), typeof(AlignSubSeq).Name); SequenceBuilder alignSeqBuilder_Src = SubSeqManager.CreateSubSeqBuilder(new AlignSubSeq(), typeof(AlignSubSeq).Name); sb.AddAction(SEQ_ID_INIT, Init); // Dry Run sb.AddDecision(SEQ_ID_CHECK_DRY_RUN, CheckDryRun); sb.AddFork(SEQ_ID_FORK_DRY_RUN); sb.AddDecision(SEQ_ID_CHECK_ALIGN_RESULT_DRY_RUN_TGT, CheckAlignResult_Tgt); sb.AddAction(SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_TGT, SetDryRunSeqParams); sb.AddSubsequence(SEQ_ID_DRY_RUN_SEQ_TGT, dryRunSeqBuilder_tgt); sb.AddAction(SEQ_ID_DRY_RUN_UPDATE_UI_TGT, UpdateUI); sb.AddAction(SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_SRC, SetDryRunSeqParams); sb.AddSubsequence(SEQ_ID_DRY_RUN_SEQ_SRC, dryRunSeqBuilder_src); sb.AddAction(SEQ_ID_DRY_RUN_UPDATE_UI_SRC, UpdateUI); sb.AddJoin(SEQ_ID_JOIN_DRY_RUN); // Align sb.AddFork(SEQ_ID_FORK_ALIGN); sb.AddDecision(SEQ_ID_CHECK_ALIGN_RESULT_TGT, CheckAlignResult_Tgt); sb.AddAction(SEQ_ID_CLEAR_TGT, Clear); sb.AddAction(SEQ_ID_SET_ALIGN_SEQ_PARAMS_TGT, SetAlignSeqParams); sb.AddSubsequence(SEQ_ID_ALIGN_SEQ_TGT, alignSeqBuilder_Tgt); sb.AddDecision(SEQ_ID_CHECK_STATUS_TGT, CheckStatus); sb.AddAction(SEQ_ID_CLEAR_SRC, Clear); sb.AddAction(SEQ_ID_SET_ALIGN_SEQ_PARAMS_SRC, SetAlignSeqParams); sb.AddSubsequence(SEQ_ID_ALIGN_SEQ_SRC, alignSeqBuilder_Src); sb.AddDecision(SEQ_ID_CHECK_STATUS_SRC, CheckStatus); sb.AddJoin(SEQ_ID_JOIN_ALIGN); sb.AddAction(SEQ_ID_MARK_NG, MarkNG); sb.AddAction(SEQ_ID_DISTANCE_NG, DistanceNG); // Mix Align Data sb.AddAction(SEQ_ID_MIX_ALIGN_RESULTS, MixAlignResults); sb.AddDecision(SEQ_ID_CHECK_STATUS_MIXED, CheckStatusMixed); sb.AddAction(SEQ_ID_ALIGN_LIMIT_NG, AlignLimitNG); sb.AddAction(SEQ_ID_RETRY_COUNT_NG, RetryCountNG); // Move sb.AddFork(SEQ_ID_FORK_MOVE); sb.AddAction(SEQ_ID_APPLY_OFFSET, ApplyOffset); sb.AddDecision(SEQ_ID_CHECK_MOVE_ENABLED, CheckMoveEnabled); sb.AddAction(SEQ_ID_MOVE_DELTA, MoveDelta); sb.AddAction(SEQ_ID_MOVE_FINISHED, MoveFinished); sb.AddAction(SEQ_ID_UPDATE_UI, UpdateUI); sb.AddAction(SEQ_ID_SAVE_CSV, SaveCsv); sb.AddAction(SEQ_ID_SAVE_SCREENSHOT, SaveScreenShot); sb.AddJoin(SEQ_ID_JOIN_MOVE); // Retry sb.AddDecision(SEQ_ID_CHECK_RETRY, CheckRetry); sb.AddAction(SEQ_ID_SEQ_COMPLETE, SeqComplete); } public override SequenceBuilder Builder(string seqName) { _SeqName = seqName; _TactTimeChecker = new TactTimeChecker(_SeqName); SequenceBuilder sb = new SequenceBuilder(1000); SetActions(sb); sb.OnStopped(OnStopped); sb.OnForceError(OnForceError); sb.Flows( sb.Init > sb[SEQ_ID_INIT] > sb[SEQ_ID_CHECK_DRY_RUN] // DryRun인 경우 > sb[SEQ_ID_FORK_DRY_RUN] > sb[SEQ_ID_CHECK_ALIGN_RESULT_DRY_RUN_TGT] > sb[SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_TGT] > sb[SEQ_ID_DRY_RUN_SEQ_TGT] > sb[SEQ_ID_DRY_RUN_UPDATE_UI_TGT] > sb[SEQ_ID_JOIN_DRY_RUN] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_ALIGN_RESULT_DRY_RUN_TGT] > sb[SEQ_ID_DRY_RUN_UPDATE_UI_TGT] ); sb.Flows( sb[SEQ_ID_FORK_DRY_RUN] > sb[SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_SRC] > sb[SEQ_ID_DRY_RUN_SEQ_SRC] > sb[SEQ_ID_DRY_RUN_UPDATE_UI_SRC] > sb[SEQ_ID_JOIN_DRY_RUN] ); sb.Flows( sb[SEQ_ID_CHECK_DRY_RUN] // DryRun이 아닌 경우 > sb[SEQ_ID_FORK_ALIGN] > sb[SEQ_ID_CLEAR_SRC] > sb[SEQ_ID_SET_ALIGN_SEQ_PARAMS_SRC] > sb[SEQ_ID_ALIGN_SEQ_SRC] > sb[SEQ_ID_CHECK_STATUS_SRC] > sb[SEQ_ID_JOIN_ALIGN] > sb[SEQ_ID_MIX_ALIGN_RESULTS] > sb[SEQ_ID_CHECK_STATUS_MIXED] > sb[SEQ_ID_FORK_MOVE] > sb[SEQ_ID_APPLY_OFFSET] > sb[SEQ_ID_CHECK_MOVE_ENABLED] > sb[SEQ_ID_MOVE_DELTA] > sb[SEQ_ID_MOVE_FINISHED] > sb[SEQ_ID_JOIN_MOVE] > sb[SEQ_ID_CHECK_RETRY] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_FORK_ALIGN] > sb[SEQ_ID_CHECK_ALIGN_RESULT_TGT] ); sb.Flows( sb[SEQ_ID_CHECK_ALIGN_RESULT_TGT] // Target 얼라인을 수행하지 않은 경우 Skip (X) > sb[SEQ_ID_CLEAR_TGT] > sb[SEQ_ID_SET_ALIGN_SEQ_PARAMS_TGT] > sb[SEQ_ID_ALIGN_SEQ_TGT] > sb[SEQ_ID_CHECK_STATUS_TGT] > sb[SEQ_ID_JOIN_ALIGN] ); sb.Flows( sb[SEQ_ID_CHECK_ALIGN_RESULT_TGT] // Target 얼라인을 이미 수행한 경우 Skip (O) > sb[SEQ_ID_CHECK_STATUS_TGT] ); sb.Flows( sb[SEQ_ID_CHECK_STATUS_TGT] // Target Align - Mark NG > sb[SEQ_ID_MARK_NG] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_STATUS_TGT] // Target Align - Distance NG > sb[SEQ_ID_DISTANCE_NG] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_STATUS_SRC] // Retry 후에는 Join할 필요 없이 바로 얼라인 데이터를 Mix시킨다. > sb[SEQ_ID_MIX_ALIGN_RESULTS] ); sb.Flows( sb[SEQ_ID_CHECK_STATUS_SRC] // Source Align - Mark NG > sb[SEQ_ID_MARK_NG] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_STATUS_SRC] // Source Align - Distance NG > sb[SEQ_ID_DISTANCE_NG] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_STATUS_MIXED] // Mixed Align - Limit NG > sb[SEQ_ID_ALIGN_LIMIT_NG] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_STATUS_MIXED] // Mixed Align - Retry Count NG > sb[SEQ_ID_RETRY_COUNT_NG] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_MOVE_ENABLED] > sb[SEQ_ID_MOVE_FINISHED] ); sb.Flows( sb[SEQ_ID_FORK_MOVE] > sb[SEQ_ID_UPDATE_UI] > sb[SEQ_ID_SAVE_CSV] > sb[SEQ_ID_SAVE_SCREENSHOT] > sb[SEQ_ID_JOIN_MOVE] ); sb.Flows( sb[SEQ_ID_CHECK_RETRY] // Retry > sb[SEQ_ID_CLEAR_SRC] ); sb.Flows( sb[SEQ_ID_SEQ_COMPLETE] > sb.Final ); #if SEQ_DRAW sb.Draw($"{GetType().Name}.pdf"); #endif return sb; } private SequenceActionResult Init( SequenceContext context, string id, SequenceActionResult param) { _TactTimeChecker.Start(TactTimeType.ProcSeq, typeof(AlignMixerSeq).Name); if (param.Param != null) { _Params = param.Param as AlignMixerSeqParams; } else { LOG.E($"Param is null."); return null; } _SubSysInfo_Tgt = VisionRecipeManager.GetSubSysInfo(_Params.AlignInfo_Tgt.UnitId, _Params.AlignInfo_Tgt.SubSysId); _Aligner_Tgt = AlignAgentManager.GetAlignAgent(_Params.AlignInfo_Tgt); _Aligner_Tgt.Clear(); _AlignRecipe_Tgt = VisionRecipeManager.GetAlignRecipe(_Params.AlignInfo_Tgt); _AlignResult_Tgt = _Params.AlignResult_Tgt; _SubSysInfo_Src = VisionRecipeManager.GetSubSysInfo(_Params.AlignInfo_Src.UnitId, _Params.AlignInfo_Src.SubSysId); _Aligner_Src = AlignAgentManager.GetAlignAgent(_Params.AlignInfo_Src); _Aligner_Src.Clear(); _AlignRecipe_Src = VisionRecipeManager.GetAlignRecipe(_Params.AlignInfo_Src); _AlignResult_Src = null; _AlignLimitChecker_Mixed = new AlignLimitChecker(_AlignRecipe_Src); _AlignRetryChecker_Mixed = new AlignRetryChecker(_AlignRecipe_Src); _AlignResult_Mixed = null; if (_Params.AlignCntClear) { _Aligner_Tgt.Clear(); _Aligner_Src.Clear(); } _VisionLog.V("[START]"); return SequenceAction.Ok(); } private SequenceActionResult Clear( SequenceContext context, string id, SequenceActionResult param) { if (id == SEQ_ID_CLEAR_TGT) { //_AlignResult_Tgt = null; // Target 얼라인 데이터는 한 번 정해지면 초기화할 필요가 없다. GrabFindSeqUtil.Clear(_SubSysInfo_Tgt); AlignResultItemManager.Clear(_SubSysInfo_Tgt); PatternResultItemManager.Clear(_SubSysInfo_Tgt); } else { _AlignResult_Src = null; GrabFindSeqUtil.Clear(_SubSysInfo_Src); AlignResultItemManager.Clear(_SubSysInfo_Src); PatternResultItemManager.Clear(_SubSysInfo_Src); } return SequenceAction.Ok(); } #region Dry Run private string CheckDryRun( SequenceContext context, string id, ICollection branches, SequenceActionResult param) { if (EquipAdapter.Instance.EquipMode.IsDryrunOrTransferMode()) { return SEQ_ID_FORK_DRY_RUN; } else { return SEQ_ID_FORK_ALIGN; } } private SequenceActionResult SetDryRunSeqParams( SequenceContext context, string id, SequenceActionResult param) { SubSysInfo subSysInfo; AlignInfo alignInfo; if (id == SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_TGT) { subSysInfo = _SubSysInfo_Tgt; alignInfo = _Params.AlignInfo_Tgt; } else { subSysInfo = _SubSysInfo_Src; alignInfo = _Params.AlignInfo_Src; } var fovInfos = new Dictionary(); foreach (var fovInfo in VisionRecipeManager.GetFovInfos(subSysInfo)) { fovInfos.Add(fovInfo.FovId, fovInfo); } return SequenceAction.Ok(new AlignSeqParams_DryRun(alignInfo, fovInfos)); } #endregion private string CheckAlignResult_Tgt( SequenceContext context, string id, ICollection branches, SequenceActionResult param) { if (_Params.AlignResult_Tgt == null) { if (id.Contains("DRY_RUN")) { return SEQ_ID_DRY_RUN_UPDATE_UI_TGT; } else { return SEQ_ID_CLEAR_TGT; // 입력받은 Target 얼라인 보정량이 없으면, 얼라인 수행 } } else { if (id.Contains("DRY_RUN")) { return SEQ_ID_SET_DRY_RUN_SEQ_PARAMS_TGT; } else { return SEQ_ID_CHECK_STATUS_TGT; // 입력받은 Target 얼라인 보정량이 있으면, 얼라인 Skip } } } private SequenceActionResult SetAlignSeqParams( SequenceContext context, string id, SequenceActionResult param) { if (id == SEQ_ID_SET_ALIGN_SEQ_PARAMS_TGT) { return SequenceAction.Ok(new AlignSubSeqParams(_Params.AlignInfo_Tgt, _Params.FovPositions)); } else { return SequenceAction.Ok(new AlignSubSeqParams(_Params.AlignInfo_Src, _Params.FovPositions)); } } private string CheckStatus( SequenceContext context, string id, ICollection branches, SequenceActionResult param) { // Param : AlignResult var alignResult = param.Param as AlignResult; if (id == SEQ_ID_CHECK_ALIGN_RESULT_TGT) { return SEQ_ID_JOIN_ALIGN; // Target Align - Skip } else if (id == SEQ_ID_CHECK_STATUS_TGT) { if (alignResult != null) { _AlignResult_Tgt = alignResult; BindProcessData(context, id, param); if (_AlignResult_Tgt.Status == AlignStatus.NOT_FOUND) { return SEQ_ID_MARK_NG; } else if (_AlignResult_Tgt.Status == AlignStatus.DISTANCE_NG) { return SEQ_ID_DISTANCE_NG; } else { return SEQ_ID_JOIN_ALIGN; // 새로 수행한 Target Align - OK } } else { return SEQ_ID_JOIN_ALIGN; // InitParam으로 넘겨받은 Target Align - OK } } else { _AlignResult_Src = alignResult; // Source 자체의 Retry, Limit 체크는 의미가 없기 때문에, 항상 OK로 처리 if (_AlignResult_Src.Status == AlignStatus.RETRY || _AlignResult_Src.Status == AlignStatus.RETRY_COUNT_OVER || _AlignResult_Src.Status == AlignStatus.ALIGN_LIMIT_OVER) { _AlignResult_Src.Status = AlignStatus.OK; } BindProcessData(context, id, param); if (_AlignResult_Src.Status == AlignStatus.NOT_FOUND) { return SEQ_ID_MARK_NG; } else if (_AlignResult_Src.Status == AlignStatus.DISTANCE_NG) { return SEQ_ID_DISTANCE_NG; } else { if (_AlignResult_Mixed?.Status == AlignStatus.RETRY) { return SEQ_ID_MIX_ALIGN_RESULTS; // Retry Source Align - OK, Join으로 Target을 기다릴 필요가 없다. } else { return SEQ_ID_JOIN_ALIGN; // 최초 1번 Source Align - OK } } } } private SequenceActionResult MarkNG( SequenceContext context, string id, SequenceActionResult param) { return SequenceAction.Error(E1.GENERIC); } private SequenceActionResult DistanceNG( SequenceContext context, string id, SequenceActionResult param) { return SequenceAction.Error(E1.GENERIC); } private SequenceActionResult MixAlignResults( SequenceContext context, string id, SequenceActionResult param) { if (_AlignResult_Tgt != null && _AlignResult_Src != null) { _AlignResult_Mixed = _AlignResult_Src.Clone(); _AlignResult_Mixed.Delta.X += _AlignResult_Tgt.Delta.X; _AlignResult_Mixed.Delta.Y += _AlignResult_Tgt.Delta.Y; _AlignResult_Mixed.Delta.T += _AlignResult_Tgt.Delta.T; _AlignResult_Mixed.AlignName = "Mixed Align"; } return SequenceAction.Ok(); } private string CheckStatusMixed( SequenceContext context, string id, ICollection branches, SequenceActionResult param) { // Mixed는 Source의 레시피를 활용하지만, Source의 Status를 그대로 사용할 수 없다. // Mixed는 Aligner를 사용하지 않기 때문에, Mixed의 Status를 여기서 재정의 해주어야 한다. _AlignResult_Mixed.Status = AlignStatus.OK; if (_AlignLimitChecker_Mixed.IsEnabled()) { _AlignResult_Mixed.Status = _AlignLimitChecker_Mixed.GetAlignLimitStatus(_AlignResult_Mixed.Delta); if (_AlignResult_Mixed.Status == AlignStatus.ALIGN_LIMIT_OVER) { return SEQ_ID_ALIGN_LIMIT_NG; } } if (_AlignRetryChecker_Mixed.IsEnabled()) { _AlignResult_Mixed.Status = _AlignRetryChecker_Mixed.GetRetryStatus(_AlignResult_Mixed.Delta); if (_AlignResult_Mixed.Status == AlignStatus.RETRY_COUNT_OVER) { return SEQ_ID_RETRY_COUNT_NG; } } return SEQ_ID_FORK_MOVE; } private SequenceActionResult AlignLimitNG( SequenceContext context, string id, SequenceActionResult param) { BindProcessData(context, id, param); return SequenceAction.Error(E1.GENERIC); } private SequenceActionResult RetryCountNG( SequenceContext context, string id, SequenceActionResult param) { BindProcessData(context, id, param); return SequenceAction.Error(E1.GENERIC); } private void BindProcessData( SequenceContext context, string id, SequenceActionResult param) { UpdateUI(context, id, param); SaveCsv(context, id, param); SaveScreenShot(context, id, param); } private SequenceActionResult ApplyOffset( SequenceContext context, string id, SequenceActionResult param) { // Retry일 때는 오프셋을 반영하지 않는다. if (_AlignResult_Mixed.Status == AlignStatus.OK || _AlignResult_Mixed.Status == AlignStatus.F_OK) { if (_AlignRecipe_Src.AlignOffset.Enabled) { // Mixer 서브시스템이 따로 없으므로, Source의 얼라인 레시피를 사용하기로 한다. var alignOffset = _AlignRecipe_Src.AlignOffset; _AlignResult_Mixed.Delta.X += alignOffset.OffsetX; _AlignResult_Mixed.Delta.Y += alignOffset.OffsetY; _AlignResult_Mixed.Delta.T.Degree += alignOffset.OffsetT; } } return SequenceAction.Ok(); } private string CheckMoveEnabled( SequenceContext context, string id, ICollection branches, SequenceActionResult param) { if (_AlignRecipe_Src.ProcParams.AlignDeltaMoveEnabled && _Params.SeqMode == eSeqMode.Auto) { return SEQ_ID_MOVE_DELTA; } else { return SEQ_ID_MOVE_FINISHED; // Skip Move } } private SequenceActionResult MoveDelta( SequenceContext context, string id, SequenceActionResult param) { if (EquipAdapter.Instance.EquipMode.IsSimulMode()) { return SequenceAction.Ok(); } var alignDirector = AlignDirectorManager.Instance.GetDirector(_SubSysInfo_Src); var motionDelta = alignDirector.ApplyDirection(_AlignResult_Mixed.Delta.ToPosition()); var res = EquipAdapter.Instance.AlignMotionManager.RelMove(_SubSysInfo_Src, motionDelta); if (res != E1.OK) { LOG.E("Move Failed."); return SequenceAction.Error(E2.ABORTED); } return SequenceAction.Ok(); } private SequenceActionResult MoveFinished( SequenceContext context, string id, SequenceActionResult param) { // Join_Move를 위해 지나가는 노드 return SequenceAction.Ok(); } private SequenceActionResult UpdateUI( SequenceContext context, string id, SequenceActionResult param) { AlignResult alignResult; AlignInfo alignInfo; if (id.Contains("DRY_RUN")) { return SequenceAction.Ok(); } else if (id.Contains("TGT")) { alignResult = _AlignResult_Tgt; alignInfo = _Params.AlignInfo_Tgt; } else if (id.Contains("SRC")) { alignResult = _AlignResult_Src; alignInfo = _Params.AlignInfo_Src; } else { alignResult = _AlignResult_Mixed; alignInfo = _Params.AlignInfo_Src; } VisionEnv.AuroraEnv.EventPublisher.Publish( new EventMessage(alignInfo.ToSubSysInfo().GetKey(), VisionEventId.UPDATE_ALIGN_RESULT, alignResult)); PrintVisionLog(alignResult); return SequenceAction.Ok(); } private SequenceActionResult SaveCsv( SequenceContext context, string id, SequenceActionResult param) { if (EquipAdapter.Instance.EquipMode.IsNotDryrunAndTransferMode()) { if (id.Contains("TGT")) { _CsvManager.SaveCsv(_SubSysInfo_Tgt, AlignResultItemManager.GetAlignResultItem(_SubSysInfo_Tgt, _AlignResult_Tgt), eSaveDataType.Align); } else if (id.Contains("SRC")) { _CsvManager.SaveCsv(_SubSysInfo_Src, AlignResultItemManager.GetAlignResultItem(_SubSysInfo_Src, _AlignResult_Src), eSaveDataType.Align); } else { _CsvManager.SaveCsv(_SubSysInfo_Src, AlignResultItemManager.GetAlignResultItem(_SubSysInfo_Src, _AlignResult_Mixed), eSaveDataType.Align); } } return SequenceAction.Ok(); } private SequenceActionResult SaveScreenShot( SequenceContext context, string id, SequenceActionResult param) { if (EquipAdapter.Instance.EquipMode.IsNotDryrunAndTransferMode()) { var materialInfo = context.GetValue(VisionContextKeys.CNTXT_KEY_MATERIAL_INFO); VisionAligner.Aligner aligner; AlignResult alignResult; AlignInfo alignInfo; SubSysInfo subSysInfo; if (id.Contains("TGT")) { aligner = _Aligner_Tgt; alignResult = _AlignResult_Tgt; alignInfo = _Params.AlignInfo_Tgt; subSysInfo = _SubSysInfo_Tgt; } else if (id.Contains("SRC")) { aligner = _Aligner_Src; alignResult = _AlignResult_Src; alignInfo = _Params.AlignInfo_Src; subSysInfo = _SubSysInfo_Src; } else { aligner = _Aligner_Src; alignResult = _AlignResult_Mixed; alignInfo = _Params.AlignInfo_Src; subSysInfo = _SubSysInfo_Src; } if (aligner.IsAlignStatusNG(alignResult.Status) || VisionSettingManager.VisionSetting.SaveOkSceenShotImage) { var lastFovId = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_LAST_MEASURE_FOV_ID, alignInfo.GetKey())); _ImageManager.SaveScreenShot( VisionRecipeManager.GetFovInfo(subSysInfo, lastFovId), alignResult.Status.ToString(), materialInfo, _Params.DisplayType, AlignResultItemManager.GetTimeStamp(subSysInfo, alignResult)); } } return SequenceAction.Ok(); } private string CheckRetry( SequenceContext context, string id, ICollection branches, SequenceActionResult param) { if (_AlignRetryChecker_Mixed.IsEnabled()) { if (_AlignResult_Mixed.Status == AlignStatus.RETRY) { if (_AlignRecipe_Src.ProcParams.AlignDeltaMoveEnabled) { return SEQ_ID_CLEAR_SRC; // RETRY } else { return SEQ_ID_SEQ_COMPLETE; // RETRY, BUT NOT MOVE } } } return SEQ_ID_SEQ_COMPLETE; // OK } private SequenceActionResult SeqComplete( SequenceContext context, string id, SequenceActionResult param) { _TactTimeChecker.Stop(TactTimeType.ProcSeq, typeof(AlignMixerSeq).Name); // 택타임 측정 종료 _VisionLog.V("[END]"); return SequenceAction.Ok(_AlignResult_Mixed); } private void PrintVisionLog(AlignResult alignResult) { _VisionLog.I($"AlignCnt : {alignResult.AlignCnt}"); _VisionLog.I($"dX : {alignResult.Delta.X:F4}, dY : {alignResult.Delta.Y:F4}, dT : {alignResult.Delta.T.Degree:F4}"); _VisionLog.I($"Status : {alignResult.Status}"); _VisionLog.I($"Algorithm : {alignResult.AlignAlgorithm}"); } void OnStopped( SequenceContext context) { LOG.I($"{context.SequenceId} - STOP Handler"); HandleAbnormalFinish(context); } void OnForceError( SequenceContext context, string id) { LOG.E($"{context.SequenceId} - FORCE ERROR Handler"); HandleAbnormalFinish(context); } void HandleAbnormalFinish( SequenceContext context) { _TactTimeChecker.Stop(TactTimeType.ProcSeq, typeof(AlignMixerSeq).Name); // 택타임 측정 종료 } } }