using System; using System.Collections.Generic; using System.Linq; using Aurora.Api; using VisionCommon; using VisionInspector; using Hermes; using VisionCommonUI; using VisionCommonUI.Resources; namespace VisionSequence.Inspector { /// /// 패턴 간의 거리 검사 시퀀스 파라미터 /// public class InspSeqParams_PatternDistance { /// 얼라인 키 정보 public AlignInfo AlignInfo { get; set; } /// 패턴 검출 결과 public Dictionary MeasureResults { get; set; } /// /// 생성자 /// /// 얼라인 키 정보 /// 패턴 검출 결과 public InspSeqParams_PatternDistance( AlignInfo alignInfo, Dictionary measureResults) { this.AlignInfo = alignInfo; this.MeasureResults = measureResults; } } /// /// 서로 다른 FOV에서 찾은 두 패턴 간에 거리 검사 (서브시퀀스로 호출해서 사용) /// public class InspSeq_PatternDistance : SubSeqBase { private readonly Logger LOG = new Logger(typeof(InspSeq_PatternDistance).Name); private Logger _VisionLog = new Logger("InspSeq_PatternDistance", VisionKeys.LogKey); private InspSeqParams_PatternDistance _Params; private SubSysInfo _SubSysInfo; private SubSysRecipe _SubSysRecipe; private PatternDistanceParam _PatternDistanceParam; private CsvManager _CsvManager = new CsvManager(); private string _SeqName; #region Sequence Id private const string SEQ_ID_INIT = "INIT"; private const string SEQ_ID_CLEAR = "CLEAR"; private const string SEQ_ID_MEASURE_DISTANCE = "MEASURE_DISTANCE"; private const string SEQ_ID_UPDATE_UI = "UPDATE_UI"; private const string SEQ_ID_SAVE_CSV = "SAVE_CSV"; private const string SEQ_ID_SEQ_COMPLETE = "SEQ_COMPLETE"; #endregion /// /// 생성자 /// public InspSeq_PatternDistance() { } private void SetActions(SequenceBuilder sb) { sb.AddAction(SEQ_ID_INIT, Init); sb.AddAction(SEQ_ID_CLEAR, Clear); sb.AddAction(SEQ_ID_MEASURE_DISTANCE, MeasureDistance); sb.AddAction(SEQ_ID_UPDATE_UI, UpdateUI); sb.AddAction(SEQ_ID_SAVE_CSV, SaveCsv); sb.AddAction(SEQ_ID_SEQ_COMPLETE, SeqComplete); } public override SequenceBuilder Builder(string seqName) { _SeqName = 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_MEASURE_DISTANCE] > sb[SEQ_ID_UPDATE_UI] > sb[SEQ_ID_SAVE_CSV] > sb[SEQ_ID_SEQ_COMPLETE] ); 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) { if (param.Param != null) { _Params = param.Param as InspSeqParams_PatternDistance; } else { MessageBoxUtil.ShowMessageBoxError( string.Format(StrTokens.MSG_E_ParamIsNull)); return SequenceAction.Error(E1.ARGUMENT); } _SubSysInfo = _Params.AlignInfo.ToSubSysInfo(); var alignRecipe = VisionRecipeManager.GetAlignRecipe(_Params.AlignInfo); _PatternDistanceParam = alignRecipe.InspParams.PatternDistanceParam; // Spec, ErrRange if (!IsValidMeasureResults()) { return null; } return SequenceAction.Ok(); } private bool IsValidMeasureResults() { if (_Params.MeasureResults == null) { LOG.E("_Params.MeasureResults is null."); return false; } else if (_Params.MeasureResults.Count < 2) { LOG.E($"_Params.MeasureResults.Count is less than 2."); return false; } return true; } private SequenceActionResult Clear( SequenceContext context, string id, SequenceActionResult param) { PatternDistanceResultItemManager.Clear(_SubSysInfo); return SequenceAction.Ok(); } private SequenceActionResult MeasureDistance( SequenceContext context, string id, SequenceActionResult param) { List fovList = _Params.MeasureResults.Keys.ToList(); List fovIdPairs = VisionRecipeManager.GetFovIdPairs(fovList); var patternDistanceResults = new List(); foreach (eFovIdPair fovIdPair in fovIdPairs) { var result = new PatternDistanceResult(); switch (fovIdPair) { case eFovIdPair.Fov1_2: MeasureDistanceInternal(eFovId.Fov1, eFovId.Fov2, ref result); break; case eFovIdPair.Fov1_3: MeasureDistanceInternal(eFovId.Fov1, eFovId.Fov3, ref result); break; case eFovIdPair.Fov1_4: MeasureDistanceInternal(eFovId.Fov1, eFovId.Fov4, ref result); break; case eFovIdPair.Fov2_3: MeasureDistanceInternal(eFovId.Fov2, eFovId.Fov3, ref result); break; case eFovIdPair.Fov2_4: MeasureDistanceInternal(eFovId.Fov2, eFovId.Fov4, ref result); break; case eFovIdPair.Fov3_4: MeasureDistanceInternal(eFovId.Fov3, eFovId.Fov4, ref result); break; } patternDistanceResults.Add(result); } context.PutValue(string.Format(VisionContextKeys.CNTXT_KEY_PATTERN_DISTANCE_RESULTS, _Params.AlignInfo.GetKey()), patternDistanceResults); return SequenceAction.Ok(); } private void MeasureDistanceInternal(eFovId startFov, eFovId endFov, ref PatternDistanceResult result) { if (IsValidFovIds(startFov, endFov)) { double distance = MathUtil.CalculateDistance( _Params.MeasureResults[startFov].MaterialPosition, _Params.MeasureResults[endFov].MaterialPosition); double spec = GetSpec(startFov, endFov); InspStatus status = GetStatus(startFov, endFov, spec, distance); result.StartFov = startFov; result.EndFov = endFov; result.Distance = distance; result.ErrRange = distance - spec; result.InspStatus = status; } else { LOG.E($"_Params.MeasureResults does not contain all fov keys. [{startFov}, {endFov}]"); } } private bool IsValidFovIds(eFovId startFov, eFovId endFov) { if (_Params.MeasureResults.ContainsKey(startFov) && _Params.MeasureResults.ContainsKey(endFov)) { return true; } else { return false; } } private double GetSpec(eFovId startFov, eFovId endFov) { return GetRefDistance(startFov, endFov); } private InspStatus GetStatus(eFovId startFov, eFovId endFov, double spec, double measuredDistance) { double measureDiff = measuredDistance - spec; if (Math.Abs(measureDiff) <= Math.Abs(_PatternDistanceParam.ErrRange)) { return InspStatus.OK; } else { return InspStatus.NG; } } // FOV RefPoint(기준점) 간에 거리를 Spec으로 활용한다. RefPointOffset이 0이면, Spec은 FovCenter 간에 거리가 된다. private double GetRefDistance(eFovId startFov, eFovId endFov) { var startFovInfo = VisionRecipeManager.GetFovInfo(_SubSysInfo, startFov); var endFovInfo = VisionRecipeManager.GetFovInfo(_SubSysInfo, endFov); var startRefPoint = VisionRecipeManager.GetRefPoint(startFovInfo); var endRefPoint = VisionRecipeManager.GetRefPoint(endFovInfo); return MathUtil.CalculateDistance(startRefPoint, endRefPoint); } private SequenceActionResult UpdateUI( SequenceContext context, string id, SequenceActionResult param) { var patternDistanceResults = context.GetValue>(string.Format(VisionContextKeys.CNTXT_KEY_PATTERN_DISTANCE_RESULTS, _Params.AlignInfo.GetKey())); var subSysInfo = _Params.AlignInfo.ToSubSysInfo(); VisionEnv.AuroraEnv.EventPublisher.Publish( new EventMessage(subSysInfo.GetKey(), VisionEventId.UPDATE_PATTERN_DISTANCE_RESULTS, patternDistanceResults)); PrintVisionLog(patternDistanceResults); return SequenceAction.Ok(); } private SequenceActionResult SaveCsv( SequenceContext context, string id, SequenceActionResult param) { if (EquipAdapter.Instance.EquipMode.IsNotDryrunAndTransferMode()) { var patternDistanceResults = context.GetValue>(string.Format(VisionContextKeys.CNTXT_KEY_PATTERN_DISTANCE_RESULTS, _Params.AlignInfo.GetKey())); foreach (PatternDistanceResult result in patternDistanceResults) { _CsvManager.SaveCsvSync( _SubSysInfo, PatternDistanceResultItemManager.GetPatternDistanceResultItem(_SubSysInfo, result), eSaveDataType.PatternDistance); } } return SequenceAction.Ok(); } private SequenceActionResult SeqComplete( SequenceContext context, string id, SequenceActionResult param) { var patternDistanceResults = context.GetValue>(string.Format(VisionContextKeys.CNTXT_KEY_PATTERN_DISTANCE_RESULTS, _Params.AlignInfo.GetKey())); return SequenceAction.Ok(patternDistanceResults); } private void PrintVisionLog(List patternDistanceResults) { foreach (PatternDistanceResult result in patternDistanceResults) { _VisionLog.I($"StartFov : {result.StartFov}"); _VisionLog.I($"EndFov : {result.EndFov}"); _VisionLog.I($"Distance : {result.Distance:F3}"); _VisionLog.I($"ErrRange : {result.ErrRange:F3}"); _VisionLog.I($"Status : {result.InspStatus}"); } } private void OnStopped(SequenceContext context) { LOG.I($"{context.SequenceId} - STOP Handler"); HandleAbnormalFinish(context); } private void OnForceError(SequenceContext context, string id) { LOG.E($"{context.SequenceId} - FORCE ERROR Handler"); HandleAbnormalFinish(context); } void HandleAbnormalFinish(SequenceContext context) { } } }