/* * Copyright (c) 2025 LG Electronics Inc. * SPDX-License-Identifier: LicenseRef-LGE-Proprietary */ 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; using System.Runtime.Remoting.Contexts; using VisionCommon.DB.Align; using VisionCommon.DB; using System; namespace VisionSequence.Aligner { /// /// 얼라인 공정 시퀀스 파라미터 /// public class AlignProcSeqParams : SeqParamsBase { /// 얼라인 키 정보 public AlignInfo AlignInfo { 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; } /// /// DB 사용시에만 할당해서 사용 /// internal VisionAlignDB AlignDB { get; set; } /// /// 얼라인 시퀀스 파라미터를 얻어온다. /// /// 얼라인 키 정보 /// FOV별로 이동시킬 절대위치. null일 경우, 자동으로 Adapter에 정의된 티칭위치로 이동 /// 메인 화면 타입 /// 얼라인 횟수 /// 시퀀스 모드 public AlignProcSeqParams( AlignInfo alignInfo, Dictionary fovPositions = null, eDisplayType displayType = eDisplayType.Main_1, bool alignCntClear = true, eSeqMode seqMode = eSeqMode.Auto) : base(seqMode) { this.AlignInfo = alignInfo; this.FovPositions = fovPositions; this.DisplayType = displayType; this.AlignCntClear = alignCntClear; AlignDB = VisionSettingManager.VisionSetting.UseDBService ? new VisionAlignDB(alignInfo, seqMode) : null; } } /// /// 얼라인 공정 시퀀스 /// public class AlignProcSeq : SubSeqBase { private readonly Logger LOG = new Logger(typeof(AlignProcSeq).Name); private Logger _VisionLog = new Logger(typeof(AlignProcSeq).Name, VisionKeys.LogKey); private TactTimeChecker _TactTimeChecker; private VisionAligner.Aligner _Aligner; private AlignRecipe _AlignRecipe; private SubSysInfo _SubSysInfo; private AlignProcSeqParams _Params; private CsvManager _CsvManager = new CsvManager(); private ImageManager _ImageManager = new ImageManager(); private string _SeqName; #region Sequence Id private const string SEQ_ID_INIT = "INIT"; private const string SEQ_ID_MOVE_FIRST_TEACHING_POS = "SEQ_ID_MOVE_FIRST_TEACHING_POS"; // Dry Run private const string SEQ_CHECK_DRY_RUN = "CHECK_DRY_RUN"; private const string SEQ_ID_SET_DRY_RUN_SEQ_PARAMS = "SET_DRY_RUN_SEQ_PARAMS"; private const string SEQ_ID_DRY_RUN_SEQ = "DRY_RUN_SEQ"; private const string SEQ_ID_DRY_RUN_UPDATE_UI = "DRY_RUN_UPDATE_UI"; private const string SEQ_ID_CLEAR = "CLEAR"; private const string SEQ_ID_CHECK_PROC_PATTERN = "SEQ_ID_CHECK_PROC_PATTERN"; private const string SEQ_ID_UNDEFINED_PATTERN_NG = "SEQ_ID_UNDEFINED_PATTERN_NG"; private const string SEQ_ID_SET_ALIGN_SEQ_PARAMS = "SET_ALIGN_SEQ_PARAMS"; private const string SEQ_ID_ALIGN_SEQ = "ALIGN_SEQ"; private const string SEQ_ID_CHECK_STATUS = "CHECK_STATUS"; private const string SEQ_ID_MARK_NG = "MARK_NG"; private const string SEQ_ID_DISTANCE_NG = "DISTANCE_NG"; private const string SEQ_ID_ALIGN_LIMIT_NG = "ALIGN_LIMIT_NG"; private const string SEQ_ID_RETRY_COUNT_NG = "RETRY_COUNT_NG"; private const string SEQ_ID_ALIGN_RESULT_NULL = "ALIGN_RESULT_NULL"; private const string SEQ_ID_FORK = "FORK"; 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 = "JOIN"; private const string SEQ_ID_CHECK_RETRY = "CHECK_RETRY"; private const string SEQ_ID_SEQ_COMPLETE = "SEQ_COMPLETE"; #endregion /// /// 생성자 /// /// 시퀀스 이름 /// 시퀀스 키 정보 public AlignProcSeq(string seqName, AlignInfo alignInfo) { _SeqName = seqName; _AlignRecipe = VisionRecipeManager.GetAlignRecipe(alignInfo); _SubSysInfo = alignInfo?.ToSubSysInfo(); } private void SetActions(SequenceBuilder sb) { SequenceBuilder dryRunSeqBuilder = SubSeqManager.CreateSubSeqBuilder(new AlignSeq_DryRun(), typeof(AlignSeq_DryRun).Name); SequenceBuilder alignSeqBuilder = SubSeqManager.CreateSubSeqBuilder(new AlignSubSeq(), typeof(AlignSubSeq).Name); sb.AddAction(SEQ_ID_INIT, Init); sb.AddAction(SEQ_ID_MOVE_FIRST_TEACHING_POS, MoveFirstTeachingPos); // Dry Run sb.AddDecision(SEQ_CHECK_DRY_RUN, CheckDryRun); sb.AddAction(SEQ_ID_SET_DRY_RUN_SEQ_PARAMS, SetDryRunSeqParams); sb.AddSubsequence(SEQ_ID_DRY_RUN_SEQ, dryRunSeqBuilder); sb.AddAction(SEQ_ID_DRY_RUN_UPDATE_UI, UpdateUI); sb.AddAction(SEQ_ID_CLEAR, Clear); sb.AddDecision(SEQ_ID_CHECK_PROC_PATTERN, CheckProcPattern); sb.AddAction(SEQ_ID_UNDEFINED_PATTERN_NG, UndefinedPatternNG); sb.AddAction(SEQ_ID_SET_ALIGN_SEQ_PARAMS, SetAlignSeqParams); sb.AddSubsequence(SEQ_ID_ALIGN_SEQ, alignSeqBuilder); sb.AddDecision(SEQ_ID_CHECK_STATUS, CheckStatus); sb.AddAction(SEQ_ID_MARK_NG, MarkNG); sb.AddAction(SEQ_ID_DISTANCE_NG, DistanceNG); sb.AddAction(SEQ_ID_ALIGN_LIMIT_NG, AlignLimitNG); sb.AddAction(SEQ_ID_RETRY_COUNT_NG, RetryCountNG); sb.AddAction(SEQ_ID_ALIGN_RESULT_NULL, AlignResultNull); sb.AddFork(SEQ_ID_FORK); 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); 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_CLEAR] > sb[SEQ_ID_MOVE_FIRST_TEACHING_POS] > sb[SEQ_CHECK_DRY_RUN] > sb[SEQ_ID_CHECK_PROC_PATTERN] > sb[SEQ_ID_SET_ALIGN_SEQ_PARAMS] > sb[SEQ_ID_ALIGN_SEQ] > sb[SEQ_ID_CHECK_STATUS] > sb[SEQ_ID_FORK] > sb[SEQ_ID_APPLY_OFFSET] > sb[SEQ_ID_CHECK_MOVE_ENABLED] > sb[SEQ_ID_MOVE_DELTA] > sb[SEQ_ID_MOVE_FINISHED] ); sb.Flows( sb[SEQ_ID_CHECK_PROC_PATTERN] > sb[SEQ_ID_UNDEFINED_PATTERN_NG] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_CHECK_DRY_RUN] // Dry Run > sb[SEQ_ID_SET_DRY_RUN_SEQ_PARAMS] > sb[SEQ_ID_DRY_RUN_SEQ] > sb[SEQ_ID_DRY_RUN_UPDATE_UI] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_FORK] > sb[SEQ_ID_UPDATE_UI] ); sb.Flows( sb[SEQ_ID_CHECK_STATUS] // Mark NG > sb[SEQ_ID_MARK_NG] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_STATUS] // Distance NG > sb[SEQ_ID_DISTANCE_NG] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_STATUS] // Align Limit NG > sb[SEQ_ID_ALIGN_LIMIT_NG] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_STATUS] // Retry Count NG > sb[SEQ_ID_RETRY_COUNT_NG] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_STATUS] // Align Result Null > sb[SEQ_ID_ALIGN_RESULT_NULL] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_MOVE_ENABLED] > sb[SEQ_ID_MOVE_FINISHED] ); sb.Flows( sb[SEQ_ID_MOVE_FINISHED] > sb[SEQ_ID_JOIN] ); sb.Flows( sb[SEQ_ID_UPDATE_UI] > sb[SEQ_ID_SAVE_CSV] > sb[SEQ_ID_SAVE_SCREENSHOT] > sb[SEQ_ID_JOIN] > sb[SEQ_ID_CHECK_RETRY] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_CHECK_RETRY] // Retry > sb[SEQ_ID_CLEAR] ); 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, VisionKeys.AlignProcSeqKey); if (param.Param != null) { _Params = param.Param as AlignProcSeqParams; } else { LOG.E($"Param is null."); return null; } _Aligner = AlignAgentManager.GetAlignAgent(_Params.AlignInfo); _AlignRecipe = VisionRecipeManager.GetAlignRecipe(_Params.AlignInfo); _SubSysInfo = VisionRecipeManager.GetSubSysInfo(_Params.AlignInfo.UnitId, _Params.AlignInfo.SubSysId); _VisionLog = new Logger(_SubSysInfo.SubSysName, VisionKeys.LogKey); _VisionLog.V("[START]"); context.PutValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_PROC_PARAMS, _SubSysInfo), _AlignRecipe.ProcParams); if (_Params.AlignCntClear) { _Aligner.Clear(); } if (VisionDBAgentManager.IsUseDB) { VisionDBAgentManager.GetAlignDBAgent().AllocNextSeqId(_Params.AlignDB); } return SequenceAction.Ok(); } private SequenceActionResult Clear( SequenceContext context, string id, SequenceActionResult param) { GrabFindSeqUtil.Clear(_SubSysInfo); AlignResultItemManager.Clear(_SubSysInfo); PatternResultItemManager.Clear(_SubSysInfo); return SequenceAction.Ok(); } private SequenceActionResult MoveFirstTeachingPos( SequenceContext context, string id, SequenceActionResult param) { if (EquipAdapter.Instance.EquipMode.IsSimulMode()) { return SequenceAction.Ok(); } AlignResult alignResult = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT, _Params.AlignInfo.GetKey())); if (_AlignRecipe.ProcParams.FirstTeachingMoveEnabled || // 첫번째 티칭 위치로 이동 설정이 True인 경우 첫 Fov로 이동한다. (alignResult != null && alignResult.Status == AlignStatus.RETRY && _AlignRecipe.AlignRetry.Enabled)) { // Align Retry 상황의 경우 첫 Fov로 이동한다. int res = E1.GENERIC; var fov1Info = new FovInfo(_SubSysInfo, eFovId.Fov1); if (_Params.FovPositions != null && _Params.FovPositions.ContainsKey(eFovId.Fov1)) { // 파라미터로 넘겨받은 절대위치로 이동 res = EquipAdapter.Instance.TeachingMotionManager.AbsMove(fov1Info, _Params.FovPositions[eFovId.Fov1]); } else { // Adapter에 정의된 티칭위치로 이동 res = EquipAdapter.Instance.TeachingMotionManager.MovePosition(fov1Info, ePositionType.Teaching); } if (res == E1.OK) { ApplyGrabDelay(_SubSysInfo); return SequenceAction.Ok(); } else { return SequenceAction.Error(E2.ABORTED); } } 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_SET_DRY_RUN_SEQ_PARAMS; } else { return SEQ_ID_CHECK_PROC_PATTERN; } } private SequenceActionResult SetDryRunSeqParams( SequenceContext context, string id, SequenceActionResult param) { var fovInfos = new Dictionary(); foreach (var fovInfo in VisionRecipeManager.GetFovInfos(_SubSysInfo)) { fovInfos.Add(fovInfo.FovId, fovInfo); } return SequenceAction.Ok(new AlignSeqParams_DryRun(_Params.AlignInfo, fovInfos, displayType: _Params.DisplayType)); } #endregion private string CheckProcPattern( SequenceContext context, string id, ICollection branches, SequenceActionResult param) { var subsysRecipe = VisionRecipeManager.GetSubSysRecipe(_SubSysInfo); foreach (var fovRecipe in subsysRecipe.FovRecipes) { if((int)fovRecipe.FovId >= ProcParams.GetFovNum(_AlignRecipe.ProcParams.ProcFovType)) { // 얼라인에 사용하는 Fov의 수만큼만 확인한다. continue; } foreach (var procPattern in fovRecipe.PatternRecipe.ProcPatternInfos) { if (_Params.AlignInfo.AlignId.ToString() == procPattern.ProcId) { if((int)procPattern.ProcPatternId >= ProcParams.GetProcNum(_AlignRecipe.ProcParams.ProcFovType)) { // 얼라인에 사용하는 Pattern의 수만큼만 확인한다. continue; } if (procPattern.PatternId == -1) { return SEQ_ID_UNDEFINED_PATTERN_NG; } } } } return SEQ_ID_SET_ALIGN_SEQ_PARAMS; } private SequenceActionResult UndefinedPatternNG( SequenceContext context, string id, SequenceActionResult param) { string errMsg = "Undefined PatternID Available"; PrintErrorLogAndShowErrorToast(LOG, errMsg); context.PutValue( string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT, _Params.AlignInfo.GetKey()), new AlignResult() { Status = AlignStatus.UNDEFINED_PATTERN, DisplayType = _Params.DisplayType, AlignAlgorithm = _AlignRecipe.ProcParams.AlignAlgorithm, }); return SequenceAction.Ok(); } private SequenceActionResult SetAlignSeqParams( SequenceContext context, string id, SequenceActionResult param) { return SequenceAction.Ok(new AlignSubSeqParams(_Params.AlignInfo, _Params.FovPositions, _Params.DisplayType, _Params.SeqMode)); } private string CheckStatus( SequenceContext context, string id, ICollection branches, SequenceActionResult param) { // Param : AlignResult var alignResult = param.Param as AlignResult; alignResult.DisplayType = _Params.DisplayType; if (alignResult == null) { return SEQ_ID_ALIGN_RESULT_NULL; } if (alignResult.Status == AlignStatus.NOT_FOUND) { return SEQ_ID_MARK_NG; } else if (alignResult.Status == AlignStatus.DISTANCE_NG) { return SEQ_ID_DISTANCE_NG; } else if (alignResult.Status == AlignStatus.ALIGN_LIMIT_OVER) { return SEQ_ID_ALIGN_LIMIT_NG; } else if (alignResult.Status == AlignStatus.RETRY_COUNT_OVER) { return SEQ_ID_RETRY_COUNT_NG; } else { return SEQ_ID_FORK; } } private SequenceActionResult MarkNG( SequenceContext context, string id, SequenceActionResult param) { DoNgActions(context, id, param); return SequenceAction.Error(E1.GENERIC); } private SequenceActionResult DistanceNG( SequenceContext context, string id, SequenceActionResult param) { DoNgActions(context, id, param); return SequenceAction.Error(E1.GENERIC); } private SequenceActionResult AlignLimitNG( SequenceContext context, string id, SequenceActionResult param) { DoNgActions(context, id, param); return SequenceAction.Error(E1.GENERIC); } private SequenceActionResult RetryCountNG( SequenceContext context, string id, SequenceActionResult param) { DoNgActions(context, id, param); return SequenceAction.Error(E1.GENERIC); } private SequenceActionResult AlignResultNull( SequenceContext context, string id, SequenceActionResult param) { return SequenceAction.Ok(); } private void DoNgActions( SequenceContext context, string id, SequenceActionResult param) { AlignResult alignResult = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT, _Params.AlignInfo.GetKey())); context.PutValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT_WITH_OFFSET, _Params.AlignInfo.GetKey()), alignResult.Clone()); UpdateUI(context, id, param); SaveCsv(context, id, param); SaveScreenShot(context, id, param); } private SequenceActionResult ApplyOffset( SequenceContext context, string id, SequenceActionResult param) { AlignResult alignResult = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT, _Params.AlignInfo.GetKey())); AlignResult alignResultWithOffset = alignResult.Clone(); // Retry일 때는 오프셋을 반영하지 않는다. if (alignResult.Status == AlignStatus.OK || alignResult.Status == AlignStatus.F_OK) { if (_AlignRecipe.AlignOffset.Enabled) { var alignOffset = _AlignRecipe.AlignOffset; alignResultWithOffset.Delta.X += alignOffset.OffsetX; alignResultWithOffset.Delta.Y += alignOffset.OffsetY; alignResultWithOffset.Delta.T.Degree += alignOffset.OffsetT; } } context.PutValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT_WITH_OFFSET, _Params.AlignInfo.GetKey()), alignResultWithOffset); return SequenceAction.Ok(); } private string CheckMoveEnabled( SequenceContext context, string id, ICollection branches, SequenceActionResult param) { if (_AlignRecipe.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(); } AlignResult alignResultWithOffset = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT_WITH_OFFSET, _Params.AlignInfo.GetKey())); var alignDirector = AlignDirectorManager.Instance.GetDirector(_SubSysInfo); var motionDelta = alignDirector.ApplyDirection(alignResultWithOffset.Delta.ToPosition()); var res = EquipAdapter.Instance.AlignMotionManager.RelMove(_SubSysInfo, 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을 위해 지나가는 노드 return SequenceAction.Ok(); } private SequenceActionResult UpdateUI( SequenceContext context, string id, SequenceActionResult param) { AlignResult alignResult = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT, _Params.AlignInfo.GetKey())); alignResult.DisplayType = _Params.DisplayType; // 개별 AlignSequence 에서 진행해 주어야하는 부분인데, 놓쳤을 시 방어코드개념으로 추가 var subSysInfo = _Params.AlignInfo.ToSubSysInfo(); VisionEnv.AuroraEnv.EventPublisher.Publish( new EventMessage(_Params.AlignInfo.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()) { AlignResult alignResult = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT, _Params.AlignInfo.GetKey())); _CsvManager.SaveCsv( _Params.AlignInfo, AlignResultItemManager.GetAlignResultItem(_Params.AlignInfo, alignResult), eSaveDataType.Align); if (VisionDBAgentManager.IsUseDB) { _Params.AlignDB.AllocAlignResult(alignResult); _Params.AlignDB.SetSeqEnd(); VisionDBAgentManager.GetAlignDBAgent().SetDataAsync(_Params.AlignDB); } } return SequenceAction.Ok(); } private SequenceActionResult SaveScreenShot( SequenceContext context, string id, SequenceActionResult param) { if (EquipAdapter.Instance.EquipMode.IsNotDryrunAndTransferMode()) { AlignResult alignResult = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT, _Params.AlignInfo.GetKey())); var materialInfo = context.GetValue(VisionContextKeys.CNTXT_KEY_MATERIAL_INFO); var fovInfo = VisionRecipeManager.GetFovInfo(_SubSysInfo.UnitId, _SubSysInfo.SubSysId, eFovId.Fov1); if (_Aligner.IsAlignStatusNG(alignResult.Status) || VisionSettingManager.VisionSetting.SaveOkSceenShotImage) { var lastFovId = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_LAST_MEASURE_FOV_ID, _Params.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) { AlignResult alignResult = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT, _Params.AlignInfo.GetKey())); if (_Params.SeqMode == eSeqMode.Auto) { if (_AlignRecipe.AlignRetry.Enabled) { if (alignResult.Status == AlignStatus.RETRY) { if (_AlignRecipe.ProcParams.AlignDeltaMoveEnabled) { return SEQ_ID_CLEAR; // 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) { AlignResult alignResultWithOffset = new AlignResult(); if (EquipAdapter.Instance.EquipMode.IsNotDryrunAndTransferMode()) { alignResultWithOffset = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT_WITH_OFFSET, _Params.AlignInfo.GetKey())); } else { // DryRun의 경우 Offset이 없는 얼라인 결과를 전달 alignResultWithOffset = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_RESULT, _Params.AlignInfo.GetKey())); } SetPatternResults(context, alignResultWithOffset); _TactTimeChecker.Stop(TactTimeType.ProcSeq, VisionKeys.AlignProcSeqKey); // 택타임 측정 종료 _VisionLog.V("[END]"); if (context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_PROC_PARAMS, _SubSysInfo)) != null) { context.RemoveValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_PROC_PARAMS, _SubSysInfo)); } return SequenceAction.Ok(alignResultWithOffset); } private void SetPatternResults(SequenceContext context, AlignResult alignResult) { var fovInfos = VisionRecipeManager.GetFovInfos(_SubSysInfo); foreach (FovInfo fovInfo in fovInfos) { var patternResult = context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_PATTERN_RESULT, fovInfo.GetKey())); if (!alignResult.PatternResults.ContainsKey(fovInfo)) { alignResult.PatternResults.Add(fovInfo, patternResult); } else { alignResult.PatternResults[fovInfo] = patternResult; } } } 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, VisionKeys.AlignProcSeqKey); // 택타임 측정 종료 if(context.GetValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_PROC_PARAMS, _SubSysInfo)) != null) { context.RemoveValue(string.Format(VisionContextKeys.CNTXT_KEY_ALIGN_PROC_PARAMS, _SubSysInfo)); } } } }