/* * Copyright (c) 2025 LG Electronics Inc. * SPDX-License-Identifier: LicenseRef-LGE-Proprietary */ using Aurora.Api; using System; using System.Collections.Generic; using System.Linq; using VisionCalibrator; using VisionCommon; using VisionCommonUI; using VisionCommonUI.Resources; namespace VisionSequence.Setup { /// /// 캘리브레이션 시퀀스 요청 파라미터 /// public class CalSeqParams { /// 서브 시스템 정보 public SubSysInfo SubSysInfo { get; set; } /// 메인 화면 타입 public eDisplayType DisplayType { get; set; } /// 캘리브레이션 플랜아이템 리스트 public List PlanItems { get; set; } /// 캘리브레이션 셋업 레시피 public CalSetupRecipe CalSetupRecipe { get; set; } } /// /// 캘리브레이션 시퀀스 수행 결과 /// public class CalSeqResult { /// X축 캘리브레이션 패턴 검출 이미지 좌표 리스트 public List XPoints; /// Y축 캘리브레이션 패턴 검출 이미지 좌표 리스트 public List YPoints; /// 회전 원점 캘리브레이션 패턴 검출 이미지 좌표 리스트 public List ThetaPoints; /// X축으로 1회 이동 거리 public double LengthX; /// Y축으로 1회 이동 거리 public double LengthY; /// T축으로 1회 회전 각도(Degree) public double LengthTheta; } /// /// 캘리브레이션 시퀀스 수행 결과 파라미터 /// public class CalSeqResultParams { /// 캘리브레이션 시퀀스 수행 결과 리스트 public List Results { get; set; } } /// /// 캘리브레이션 계산 결과 /// public class CalCalculateResult { /// 카메라 회전 오차 public Angle ErrorRot; /// 이미지 픽셀 크기 X public double PixelSizeX; /// 이미지 픽셀 크기 Y public double PixelSizeY; /// 모터 회전 중심 public StaticPoint ThetaOrigin; } /// /// 캘리브레이션 계산 결과 파라미터 /// public class CalCalculateResultParams { /// 캘리브레이션 계산 결과 리스트 public List Results { get; set; } } /// /// 캘리브레이션 셋업 시퀀스 /// public class SetupSeq_Calibration : SubSeqBase { private readonly Logger LOG = new Logger(typeof(SetupSeq_Calibration).Name); private CalSeqParams _Params; private List _CalResult; private List _FovInfos; private List _FovRecipes; private SubSysInfo _SubSysInfo; private List _CamIds; private int _CamCnt; private string _SeqName; private int _CurrentFov; private bool _IsPatternNotFound; private CalSetupRecipe _CalSetupRecipe; #region Sequence Id private const string SEQ_ID_INIT = "INIT"; private const string SEQ_ID_CHECK_PATTERN = "CHECK_PATTERN"; private const string SEQ_ID_UNDEFINED_PATTERN_NG = "SEQ_ID_UNDEFINED_PATTERN_NG"; private const string SEQ_ID_PREPARE = "PREPARE"; private const string SEQ_ID_RUN_CALIBRATION_SEQ = "RUN_CALIBRATION_SEQ"; private const string SEQ_ID_APPLY_DATA = "ApplyData"; private const string SEQ_ID_SEQ_COMPLETE = "SEQ_COMPLETE"; private const string SEQ_ID_CALCULATE = "CALCULATE"; #endregion 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_CHECK_PATTERN] > sb[SEQ_ID_PREPARE] > sb[SEQ_ID_RUN_CALIBRATION_SEQ] > sb[SEQ_ID_APPLY_DATA] ); sb.Flows( sb[SEQ_ID_CHECK_PATTERN] > sb[SEQ_ID_UNDEFINED_PATTERN_NG] > sb[SEQ_ID_SEQ_COMPLETE] ); sb.Flows( sb[SEQ_ID_APPLY_DATA] > sb[SEQ_ID_PREPARE] ); sb.Flows( sb[SEQ_ID_APPLY_DATA] > sb[SEQ_ID_SEQ_COMPLETE] > sb[SEQ_ID_CALCULATE] > sb.Final ); #if SEQ_DRAW sb.Draw($"{GetType().Name}.pdf"); #endif return sb; } private void SetActions(SequenceBuilder sb) { var CalSubSeqBuilder = SubSeqManager.CreateSubSeqBuilder(new SetupSeq_CalSub(), "SetupSeq_CalSub"); sb.AddAction(SEQ_ID_INIT, Init); sb.AddDecision(SEQ_ID_CHECK_PATTERN, CheckPattern); sb.AddAction(SEQ_ID_UNDEFINED_PATTERN_NG, UndefinedPatternNG); sb.AddAction(SEQ_ID_PREPARE, Prepare); sb.AddSubsequence(SEQ_ID_RUN_CALIBRATION_SEQ, CalSubSeqBuilder); sb.AddDecision(SEQ_ID_APPLY_DATA, ApplyData); sb.AddAction(SEQ_ID_SEQ_COMPLETE, SeqComplete); sb.AddAction(SEQ_ID_CALCULATE, Calculate); } private SequenceActionResult Init(SequenceContext context, string id, SequenceActionResult param) { if (param.Param != null) { _Params = param.Param as CalSeqParams; } _CalResult = new List(); //_FovInfo = _Params.FovInfo; _SubSysInfo = _Params.SubSysInfo; _FovInfos = VisionRecipeManager.GetFovInfos(_SubSysInfo); _FovRecipes = VisionRecipeManager.GetFovRecipes(_FovInfos); _CamIds = VisionRecipeManager.GetCamIds(_FovRecipes); _CamCnt = _CamIds.Count(); _CurrentFov = 0; _IsPatternNotFound = false; _CalSetupRecipe = _Params.CalSetupRecipe; if (_SubSysInfo != null) return SequenceAction.Ok(); else return SequenceAction.Error(); } private string CheckPattern(SequenceContext context, string id, ICollection branches, SequenceActionResult param) { var subsysRecipe = VisionRecipeManager.GetSubSysRecipe(_SubSysInfo); foreach (var fovRecipe in subsysRecipe.FovRecipes) { if (fovRecipe.CalFovRecipe.PatternId == -1) { _IsPatternNotFound = true; return SEQ_ID_UNDEFINED_PATTERN_NG; } } return SEQ_ID_PREPARE; } private SequenceActionResult UndefinedPatternNG( SequenceContext context, string id, SequenceActionResult param) { string errMsg = "Undefined PatternID Available"; PrintErrorLogAndShowErrorToast(LOG, errMsg); _CalResult = new List(); return SequenceAction.Ok(); } private SequenceActionResult Prepare(SequenceContext context, string id, SequenceActionResult param) { List fovInfos = new List(); fovInfos.Clear(); for (int i = 0; i < _CamCnt; i++) { fovInfos.Add(_FovInfos[_CurrentFov + i]); } List planItems = new List(); planItems.Clear(); planItems = _Params.PlanItems.ConvertAll(item => new PlanItem(item.Step, item.Purpose, item.Target, item.Action, item.X, item.Y, item.T)); return SequenceAction.Ok( new CalSubSeqParams() { FovInfos = fovInfos, PlanItems = planItems, DisplayType = _Params.DisplayType, CamCnt = _CamCnt }); ; } private string ApplyData(SequenceContext context, string id, ICollection branches, SequenceActionResult param) { var calResult = param.Param as CalSubSeqResultParams; if (calResult.IsPatternNotFound) { LOG.E($"FOV {_CurrentFov} Calibration : Pattern Not Found"); _IsPatternNotFound = true; return SEQ_ID_SEQ_COMPLETE; } _CalResult.Add(calResult); _CurrentFov = _CamCnt + _CurrentFov; if (_CurrentFov == _FovInfos.Count) return SEQ_ID_SEQ_COMPLETE; return SEQ_ID_PREPARE; } private SequenceActionResult SeqComplete( SequenceContext context, string id, SequenceActionResult param) { if (_IsPatternNotFound) { return SequenceAction.Error(); } CalSeqResultParams returnParam = new CalSeqResultParams(); if (returnParam.Results == null) { returnParam.Results = new List(); } foreach (var calSubSeqResult in _CalResult) { for (int i = 0; i < _CamCnt; i++) { CalSeqResult calFovResult = new CalSeqResult(); if (_CalSetupRecipe.isX()) { calFovResult.XPoints = calSubSeqResult.XPoints[i]; calFovResult.LengthX = calSubSeqResult.LengthX[i]; } if (_CalSetupRecipe.isY()) { calFovResult.YPoints = calSubSeqResult.YPoints[i]; calFovResult.LengthY = calSubSeqResult.LengthY[i]; } if (_CalSetupRecipe.isT()) { calFovResult.ThetaPoints = calSubSeqResult.ThetaPoints[i]; calFovResult.LengthTheta = calSubSeqResult.LengthTheta[i]; } returnParam.Results.Add(calFovResult); } } return SequenceAction.Ok(returnParam); } private SequenceActionResult Calculate( SequenceContext context, string id, SequenceActionResult param) { CalCalculateResultParams returnParam = new CalCalculateResultParams(); returnParam.Results = new List(); List fovCalResults = new List(); var calSeqResult = param.Param as CalSeqResultParams; if (_IsPatternNotFound) { return SequenceAction.Error(); } var calResults = calSeqResult.Results; for (int fovId = 0; fovId < calResults.Count(); fovId++) { CalCalculateResult fovCalResult = new CalCalculateResult(); var calibrator = CalibratorManager.GetCalibrator(_FovInfos[fovId]); var cameraAgent = CameraAgentManager.GetCameraAgent(_FovInfos[fovId]); var imageSize = cameraAgent.GetImageSize(); Angle errorRotX = Angle.CreateWithDegree(0); Angle errorRotY = Angle.CreateWithDegree(0); double imgPixelX = 0; double imgPixelY = 0; if (_CalSetupRecipe.isX() && _CalSetupRecipe.isY()) { if (calResults[fovId].XPoints != null) { errorRotX = calibrator.FindStaticErrorRotation(calResults[fovId].XPoints, 'x'); imgPixelX = Math.Abs(calibrator.GetPixelSize(calResults[fovId].XPoints, calResults[fovId].LengthX, errorRotX.Radian, 'x')); } if (calResults[fovId].YPoints != null) { errorRotY = calibrator.FindStaticErrorRotation(calResults[fovId].YPoints, 'y'); imgPixelY = Math.Abs(calibrator.GetPixelSize(calResults[fovId].YPoints, calResults[fovId].LengthY, errorRotY.Radian, 'y')); } fovCalResult.ErrorRot = Angle.CreateWithDegree((errorRotX.Degree + errorRotY.Degree) / 2); fovCalResult.PixelSizeX = imgPixelX; fovCalResult.PixelSizeY = imgPixelY; // 3. theta origin if (_CalSetupRecipe.isT()) { StaticPoint thetaOrigin; if (calResults[fovId].ThetaPoints != null) { var thetaStaticPoints = new List(); foreach (var point in calResults[fovId].ThetaPoints) { thetaStaticPoints.Add(calibrator.TransformToCalibratedSpace(point, imageSize, fovCalResult.ErrorRot, new StaticPoint(imgPixelX, imgPixelY))); } thetaOrigin = calibrator.GetThetaOriginPoint(thetaStaticPoints, Angle.CreateWithDegree(calResults[fovId].LengthTheta)); } else { thetaOrigin = new StaticPoint(0, 0); } fovCalResult.ThetaOrigin = thetaOrigin; } } else if (_CalSetupRecipe.isX()) { if (calResults[fovId].XPoints != null) { errorRotX = calibrator.FindStaticErrorRotation(calResults[fovId].XPoints, 'x'); imgPixelX = Math.Abs(calibrator.GetPixelSize(calResults[fovId].XPoints, calResults[fovId].LengthX, errorRotX.Radian, 'x')); } fovCalResult.ErrorRot = errorRotX; fovCalResult.PixelSizeX = imgPixelX; if (_CalSetupRecipe.isT()) { StaticPoint thetaOrigin; if (calResults[fovId].ThetaPoints != null) { var thetaStaticPoints = new List(); foreach (var point in calResults[fovId].ThetaPoints) { thetaStaticPoints.Add(calibrator.TransformToCalibratedSpaceWithPixelX(point, imageSize, fovCalResult.ErrorRot, fovCalResult.PixelSizeX)); } thetaOrigin = calibrator.GetThetaOriginPoint(thetaStaticPoints, Angle.CreateWithDegree(calResults[fovId].LengthTheta)); } else { thetaOrigin = new StaticPoint(0, 0); } fovCalResult.ThetaOrigin = thetaOrigin; } } else if (_CalSetupRecipe.isY()) { if (calResults[fovId].YPoints != null) { errorRotY = calibrator.FindStaticErrorRotation(calResults[fovId].YPoints, 'y'); imgPixelY = Math.Abs(calibrator.GetPixelSize(calResults[fovId].YPoints, calResults[fovId].LengthY, errorRotY.Radian, 'y')); } fovCalResult.ErrorRot = errorRotY; fovCalResult.PixelSizeY = imgPixelY; if (_CalSetupRecipe.isT()) { StaticPoint thetaOrigin; if (calResults[fovId].ThetaPoints != null) { var thetaStaticPoints = new List(); foreach (var point in calResults[fovId].ThetaPoints) { thetaStaticPoints.Add(calibrator.TransformToCalibratedSpaceWithPixelY(point, imageSize, fovCalResult.ErrorRot, fovCalResult.PixelSizeY)); } thetaOrigin = calibrator.GetThetaOriginPoint(thetaStaticPoints, Angle.CreateWithDegree(calResults[fovId].LengthTheta)); } else { thetaOrigin = new StaticPoint(0, 0); } fovCalResult.ThetaOrigin = thetaOrigin; } } else if (_CalSetupRecipe.isT()) { // 3. theta origin StaticPoint thetaOrigin; if (calResults[fovId].ThetaPoints != null) { var thetaStaticPoints = new List(); foreach (var point in calResults[fovId].ThetaPoints) { thetaStaticPoints.Add(calibrator.TransformToCalibratedSpace(point, imageSize)); } thetaOrigin = calibrator.GetThetaOriginPoint(thetaStaticPoints, Angle.CreateWithDegree(calResults[fovId].LengthTheta)); } else { thetaOrigin = new StaticPoint(0, 0); } fovCalResult.ThetaOrigin = thetaOrigin; } fovCalResults.Add(fovCalResult); } List thetaOrigins = new List(); StaticPoint averageThetaOrigin; if (_CalSetupRecipe.isT()) { for (int fovId = 0; fovId < calResults.Count(); fovId++) { thetaOrigins.Add(fovCalResults[fovId].ThetaOrigin); } averageThetaOrigin = new StaticPoint(thetaOrigins.Average(item => item.X), thetaOrigins.Average(item => item.Y)); } else { averageThetaOrigin = new StaticPoint(0, 0); } foreach (var camId in _CamIds) { CalCalculateResult camCalResult = new CalCalculateResult(); List errorRots = new List(); List pixelSizesX = new List(); List pixelSizesY = new List(); if (_CalSetupRecipe.isX() && _CalSetupRecipe.isY()) { for (int fovId = 0; fovId < calResults.Count(); fovId++) { if (_FovRecipes[fovId].CamRecipe.CamId == camId) { errorRots.Add(fovCalResults[fovId].ErrorRot); pixelSizesX.Add(fovCalResults[fovId].PixelSizeX); pixelSizesY.Add(fovCalResults[fovId].PixelSizeY); } } camCalResult.ErrorRot = Angle.CreateWithDegree(errorRots.Average(item => item.Degree)); camCalResult.PixelSizeX = pixelSizesX.Average(); camCalResult.PixelSizeY = pixelSizesY.Average(); } else if (_CalSetupRecipe.isX()) { for (int fovId = 0; fovId < calResults.Count(); fovId++) { if (_FovRecipes[fovId].CamRecipe.CamId == camId) { errorRots.Add(fovCalResults[fovId].ErrorRot); pixelSizesX.Add(fovCalResults[fovId].PixelSizeX); } } camCalResult.ErrorRot = Angle.CreateWithDegree(errorRots.Average(item => item.Degree)); camCalResult.PixelSizeX = pixelSizesX.Average(); } else if (_CalSetupRecipe.isY()) { for (int fovId = 0; fovId < calResults.Count(); fovId++) { if (_FovRecipes[fovId].CamRecipe.CamId == camId) { errorRots.Add(fovCalResults[fovId].ErrorRot); pixelSizesY.Add(fovCalResults[fovId].PixelSizeY); } } camCalResult.ErrorRot = Angle.CreateWithDegree(errorRots.Average(item => item.Degree)); camCalResult.PixelSizeY = pixelSizesY.Average(); } if (_CalSetupRecipe.isT()) { camCalResult.ThetaOrigin = averageThetaOrigin; } returnParam.Results.Add(camCalResult); } return SequenceAction.Ok(returnParam); } void OnStopped( SequenceContext context) { LOG.I($"{context.SequenceId} - STOP Handler"); HandleAbnormalFinish(context); } void OnForceError( SequenceContext context, string id) { LOG.I($"{context.SequenceId} - FORCE ERROR Handler"); HandleAbnormalFinish(context); } void HandleAbnormalFinish( SequenceContext context) { } } }