/*
* Copyright (c) 2025 LG Electronics Inc.
* SPDX-License-Identifier: LicenseRef-LGE-Proprietary
*/
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) {
}
}
}