/* * Copyright (c) 2025 LG Electronics Inc. * SPDX-License-Identifier: LicenseRef-LGE-Proprietary */ using Aurora.Api; using Hermes; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace VisionCommon { public abstract class PatternAgentBase : IDisposable { protected object _lockObj = new object(); protected Logger _logger = null; protected FovInfo _fovInfo; protected eCamId _camId; protected Dictionary _patterns = new Dictionary(); public PatternAgentBase(FovInfo fovInfo) { _fovInfo = fovInfo; _camId = VisionRecipeManager.GetCamRecipe(fovInfo).CamId; if (VisionSettingManager.VisionSetting.UseCamPattern) { _logger = new Logger($"PA_{fovInfo.GetKey()}"); } else { _logger = new Logger($"PA_{_camId}"); } } public abstract Dictionary GetPatterns(); public List GetMainPatterns(PatternInfo patternInfo) { var fovIdxPrefix = patternInfo.GetPrefix(); return GetMainPatterns(fovIdxPrefix); } public List GetMainPatterns(PatternInfo patternInfo, ePatternType patternType) { var fovIdxPrefix = patternInfo.GetPrefix(); return GetMainPatterns(fovIdxPrefix, patternType); } public abstract List GetMainPatterns(SubSysInfo subSysInfo, eFovId fovId); public abstract List GetMainPatterns(FovInfo fovInfo); public List GetMainPatterns(string fovIdxPrefix) { return GetPatterns() .Where(pi => pi.Key.IsMainPattern(fovIdxPrefix)) .Select(pi => pi.Key) .OrderBy(pi => pi.GetFullId()) // 패턴 ID를 기준으로 오름차순 정렬 .ToList(); } public List GetMainPatterns(string fovIdxPrefix, ePatternType patternType) { return GetPatterns() .Where(pi => pi.Key.IsMainPattern(fovIdxPrefix)) .Where(pi => pi.Key.PatternType == patternType) .Select(pi => pi.Key) .OrderBy(pi => pi.GetFullId()) // 패턴 ID를 기준으로 오름차순 정렬 .ToList(); } public PatternInfo GetMainPatternInfo(PatternInfo subPatternInfo) { return GetMainPatternInfo(subPatternInfo.GetPatternIndex()); } public PatternInfo GetMainPatternInfo(PatternIndex subPatternIndex) { var mainPatternIndex = subPatternIndex.Clone() as PatternIndex; mainPatternIndex.PatternSubId = PatternConstants.MAIN_PATTERN_INDEX; return GetPatternInfo(mainPatternIndex); } public bool HasSubPattern(PatternInfo patternInfo) { var subPatternInfos = GetSubPatterns(patternInfo); return subPatternInfos?.Count > 0; } public List GetSubPatterns(PatternInfo patternInfo) { var patternIdPrefix = patternInfo.GetPatternIdPrefix(); return GetSubPatterns(patternIdPrefix); } public List GetSubPatterns(string patternIdPrefix) { return GetPatterns() .Where(pi => pi.Key.IsSubPattern(patternIdPrefix)) .Select(pi => pi.Key) .OrderBy(pi => pi.GetFullId()) // 패턴 ID를 기준으로 오름차순 정렬 .ToList(); } public List GetMainSubPatterns(PatternInfo patternInfo) { var fovIdxPrefix = patternInfo.GetPrefix(); return GetPatterns() .Where(pi => pi.Key.GetFullId().StartsWith(fovIdxPrefix)) .Where(pi => pi.Key.GetPatternId() == patternInfo.GetPatternId()) .Select(pi => pi.Key) .OrderBy(pi => pi.GetPatternSubId()) // PatternSubId를 기준으로 오름차순 정렬 .ToList(); } public List GetAllPatternsForFov(PatternInfo patternInfo) { var fovIdxPrefix = patternInfo.GetPrefix(); return GetAllPatternsForFov(fovIdxPrefix); } public List GetAllPatternsForFov(string fovIdxPrefix) { return GetPatterns() .Where(pi => pi.Key.GetFullId().StartsWith(fovIdxPrefix)) .Select(pi => pi.Key) .OrderBy(pi => pi.GetPatternId()) // 패턴 ID를 기준으로 오름차순 정렬 .ThenBy(pi => pi.GetPatternSubId()) .ToList(); } public List GetFixturePatternIndexListForFov(PatternInfo patternInfo) { var patterns = GetMainPatterns(patternInfo); return patterns .Where(pi => PatternConstants.IsFixturePatternType(pi.PatternType)) .Where(pi => pi.GetPatternIdPrefix() != patternInfo.GetPatternIdPrefix()) .Select(pi => pi.GetPatternId()).ToList(); } public bool IsPatternExist(FovInfo fovInfo, int patternId) { return GetPatternInfo(fovInfo, patternId) != null; } public bool IsPatternExist(PatternIndex patternIndex) { return GetPatternInfo(patternIndex) != null; } public abstract PatternInfo GetPatternInfo(FovInfo fovInfo, int patternId); public PatternInfo GetPatternInfo(PatternIndex patternIndex) { try { var selectedInfos = GetPatterns().Where( pi => pi.Key.GetFullId().Equals(patternIndex.GetFullId())).ToList(); if (selectedInfos.Count > 0) { return selectedInfos[0].Key; } } catch (Exception e) { _logger.E(e.Message); } return null; } public IPatternFinder GetPattern(PatternIndex patternIndex) { var patternInfo = GetPatternInfo(patternIndex); return (patternInfo == null) ? null : GetPattern(patternInfo); } public IPatternFinder GetPattern(PatternInfo patternInfo) { if (GetPatterns().ContainsKey(patternInfo)) { return GetPatterns()[patternInfo]; } return null; } public int ReloadPattern(PatternInfo patternInfo) { if (GetPatterns().ContainsKey(patternInfo)) { ((IDisposable)GetPatterns()[patternInfo])?.Dispose(); } var pattern = CreatePattern(patternInfo); if (pattern != null) { GetPatterns()[patternInfo] = pattern; return E1.OK; } return E2.NOT_FOUND; } public int ClearPatterns(Dictionary patterns) { foreach (var pattern in patterns) { ((IDisposable)pattern.Value)?.Dispose(); } patterns.Clear(); return E1.OK; } public int MovePattern(PatternInfo sourcePatternInfo, PatternInfo dstPatternInfo) { return MovePattern(sourcePatternInfo.GetPatternIndex(), dstPatternInfo.GetPatternIndex()); } public int MovePattern(PatternIndex sourcePatternIndex, PatternIndex dstPatternIndex) { if (E1.OK != CopyPattern(sourcePatternIndex, dstPatternIndex)) { _logger.FuncE($"CopyPattern error"); return E1.GENERIC; } return Remove(sourcePatternIndex); } public int CopyPattern(PatternInfo sourcePatternInfo, PatternInfo dstPatternInfo) { return CopyPattern(sourcePatternInfo.GetPatternIndex(), dstPatternInfo.GetPatternIndex()); } public int CopyPatternFiles(string oldPatternPath, string newPatternPath) { try { if (!Directory.Exists(newPatternPath)) { Directory.CreateDirectory(newPatternPath); } var sourceFiles = Directory.GetFiles(oldPatternPath, "*", SearchOption.TopDirectoryOnly); foreach (var sourceFile in sourceFiles) { var dstFile = Path.Combine( newPatternPath, Path.GetFileName(sourceFile)); File.Copy(sourceFile, dstFile); _logger.I($"{sourceFile} -> {dstFile}"); } } catch (Exception e) { _logger.E(e.Message); return E1.GENERIC; } return E1.OK; } public abstract string GetPatternInfoPath(); public abstract int LoadPatterns(); public int LoadPatterns(Dictionary patterns) { var patternInfoList = GetPatternInfoListFromFiles( GetPatternInfoPath()); foreach (var patternInfo in patternInfoList) { patterns[patternInfo] = CreatePattern(patternInfo); } return E1.OK; } public abstract int CopyPattern(PatternIndex sourcePatternIndex, PatternIndex dstPatternIndex); public int Train(PatternInfo patternInfo, ITrainParam trainParam) { if (GetPatterns().ContainsKey(patternInfo)) { ((PatternBase)GetPatterns()[patternInfo]).SetDirty(); return ((IPatternTrain)GetPatterns()[patternInfo]).Train(trainParam); } return E2.NOT_FOUND; } public ITrainParam GetTrainParam(PatternInfo patternInfo) { if (GetPatterns().ContainsKey(patternInfo)) { if (GetPatterns()[patternInfo] is IPatternTrain) { return (GetPatterns()[patternInfo] as IPatternTrain).GetTrainParam(); } } return null; } public abstract object GetTool(PatternInfo patternInfo); public ROI GetFindROI(PatternInfo patternInfo) { if (GetPatterns().ContainsKey(patternInfo)) { return (GetPatterns()[patternInfo]).GetFindROI(); } return null; } public int SetFindROI(PatternInfo patternInfo, ROI findRoi) { if (GetPatterns().ContainsKey(patternInfo)) { ((PatternBase)GetPatterns()[patternInfo]).SetDirty(); return (GetPatterns()[patternInfo]).SetFindROI(findRoi); } return E1.GENERIC; } public IRunParam GetRunParam(PatternInfo patternInfo) { var patterns = GetPatterns(); if (GetPatterns().ContainsKey(patternInfo)) { return (GetPatterns()[patternInfo]).GetRunParam(); } return null; } public int SetRunParam(PatternInfo patternInfo, IRunParam runParam) { if (GetPatterns().ContainsKey(patternInfo)) { ((PatternBase)GetPatterns()[patternInfo]).SetDirty(); return (GetPatterns()[patternInfo]).SetRunParam(runParam); } return E1.GENERIC; } #region Multi Patterns public List FindMulti( PatternInfo masterPatternInfo, object image, ICameraDisplay cameraDisplay, FixtureResult fixtureResult = null) { List slavePatternInfos = GetMainSubPatterns(masterPatternInfo); var patternResults = new List(); foreach (PatternInfo curPatternInfo in slavePatternInfos) { if (curPatternInfo.IsActivated) { // Multi Pattern의 경우 Slave 패턴들로만 찾아야 한다. if (PatternConstants.IsMainPattern(curPatternInfo.PatternIndex)) { continue; } patternResults.Add(FindPattern(curPatternInfo, image, cameraDisplay, fixtureResult)); } } List sortedPatternResults = patternResults.OrderBy(result => result.PatternIndex.PatternSubId).ToList(); // 0 : Master 패턴, 1~9 : Slave 패턴 (Master 패턴은 실제 패턴이 아니고, Slave에 대한 대표자 역할만 해준다.) // *Rule : Slave 패턴 중에 하나라도 NG이면, Master에 NG Status를 할당한다. var masterPatternResult = new PatternResult() { PatternIndex = masterPatternInfo.GetPatternIndex().Clone() as PatternIndex, Status = eFoundStatus.FoundOK, Results = new List() }; foreach (var patternResult in sortedPatternResults) { if (patternResult.Status == eFoundStatus.NotFound) { masterPatternResult.Status = eFoundStatus.NotFound; break; } } sortedPatternResults.Insert(0, masterPatternResult); return sortedPatternResults; } #endregion #region Single Pattern public PatternResult Find( PatternInfo patternInfo, object image, ICameraDisplay cameraDisplay, FixtureResult fixtureResult = null) { if (PatternConstants.IsPatternBlock(patternInfo.PatternType)) { return FindPatternBlock(patternInfo, image, cameraDisplay, fixtureResult); } var allPatternInfos = GetMainSubPatterns(patternInfo); var patternResult = new PatternResult(); foreach (var curPatternInfo in allPatternInfos) { patternResult = FindPattern(curPatternInfo, image, cameraDisplay, fixtureResult); if (patternResult.Status == eFoundStatus.FoundOK) { break; } } return patternResult; } #endregion public ROI GetFixtureROI(PatternInfo patternInfo, FixtureResult fixtureResult) { if (fixtureResult != null) { var curFindRoi = GetFindROI(patternInfo); var fixtureRoi = Fixture.GetFixtureROI(patternInfo.PatternType, curFindRoi, fixtureResult); _logger.I($"curFindRoi : {curFindRoi}"); _logger.I($"fixtureRoi : {fixtureRoi}"); return fixtureRoi; } return GetFindROI(patternInfo); } public PatternResult FindPatternBlock( PatternInfo patternInfo, object image, ICameraDisplay cameraDisplay, FixtureResult fixtureResult = null) { var patternResult = new PatternResult(); var allPatternInfos = GetMainSubPatterns(patternInfo); foreach (var curPatternInfo in allPatternInfos) { var pattern = GetPatterns()[curPatternInfo]; if (!curPatternInfo.IsActivated) { _logger.I($"{curPatternInfo} : NOT activated"); patternResult = pattern.GetNotFoundPatternResult(); } else { var patternBlock = pattern as IPatternBlock; var patternBlockResults = new List(); double tatcTime = 0; foreach (var patternId in patternBlock.GetPatternBlockIds()) { var curPatternIndex = patternInfo.GetPatternIndex().Clone() as PatternIndex; curPatternIndex.PatternId = patternId; var patterBlockPatternInfo = GetPatternInfo(curPatternIndex); var allSubPatternInfos = GetMainSubPatterns(patterBlockPatternInfo); PatternResult curSubPatternResult = new PatternResult(); foreach (var curSubPatternInfo in allSubPatternInfos) { curSubPatternResult = FindPattern(curSubPatternInfo, image, cameraDisplay, fixtureResult); if (curSubPatternResult.Status == eFoundStatus.FoundOK) { break; } } patternBlockResults.Add(curSubPatternResult); //tatcTime += curSubPatternResult.TactTimeInMilliSec; tatcTime = curSubPatternResult.TactTimeInMilliSec; // 누적 택타임이 아닌 마지막 택타임으로 사용 } patternResult = patternBlock.Find(patternBlockResults); patternResult.TactTimeInMilliSec = tatcTime; } if (patternResult.Status == eFoundStatus.FoundOK) { foreach (var reuslt in patternResult.Results) { _logger.I($"{curPatternInfo} : {patternResult.Status} : {reuslt}"); } break; } else { _logger.I($"{curPatternInfo} : {patternResult.Status}"); } } SetPatternParams(patternInfo, patternResult); return patternResult; } public PatternResult Find(PatternInfo patternInfo, object image, ROI findRoi = null) { if (PatternConstants.IsPatternBlock(patternInfo.PatternType)) { return FindPatternBlock(patternInfo, image, findRoi); } return FindPattern(patternInfo, image, findRoi); } // TestFind for Pattern Registration public abstract PatternResult FindPattern(PatternInfo patternInfo, object image, ROI findRoi = null); // Find for Sequence public abstract PatternResult FindPattern(PatternInfo patternInfo, object image, ICameraDisplay cameraDisplay, FixtureResult fixtureResult); public abstract PatternResult FindPatternBlock(PatternInfo patternInfo, object image, ROI findRoi = null); public void SetPatternParams(PatternInfo patternInfo, PatternResult patternResult) { patternResult.PatternType = patternInfo.PatternType; patternResult.PatternName = patternInfo.PatternName; patternResult.PatternIndex = patternInfo.GetPatternIndex().Clone() as PatternIndex; } public abstract void Draw(PatternInfo patternInfo, PatternResult patternResult, ICameraDisplay cameraDisplay, bool isFixturePattern = false); public int Save(PatternInfo patternInfo) { if (GetPatterns().ContainsKey(patternInfo)) { ReplacePatternInfo(patternInfo); SavePatternInfoToFile( patternInfo, PatternConstants.GetSubPatternInfoFilePath(patternInfo)); if (GetPatterns()[patternInfo] != null) { return GetPatterns()[patternInfo].Save(); } } return E2.NOT_FOUND; } public void ReplacePatternInfo(PatternInfo patternInfo) { if (GetPatterns().ContainsKey(patternInfo)) { var pattern = GetPatterns()[patternInfo]; GetPatterns().Remove(patternInfo); GetPatterns().Add(patternInfo, pattern); } } public int Create(PatternInfo patternInfo) { if (GetPatterns().ContainsKey(patternInfo)) { _logger.I($"Already exist : {patternInfo}"); return E1.GENERIC; } var pattern = CreatePattern(patternInfo); if (pattern != null) { GetPatterns()[patternInfo] = pattern; return E1.OK; } return E2.NOT_FOUND; } public int Remove(PatternIndex patternIndex) { if (!IsPatternExist(patternIndex)) { _logger.I($"Pattern does NOT exist : {patternIndex}"); return E1.OK; } return Remove(GetPatternInfo(patternIndex)); } public int Remove(PatternInfo patternInfo) { if (patternInfo != null) { if (GetPatterns().ContainsKey(patternInfo)) { // MIL 프로그램 등에서 패턴 파일을 열어 두면 폴더 삭제 중 예외가 나므로, 먼저 확인하고 아무것도 지우지 않는다. if (IsPatternFileLocked(patternInfo, out string lockedFilePath)) { _logger.E($"Pattern file is locked : {lockedFilePath}"); return E1.GENERIC; } try { GetPatterns()[patternInfo].Remove(); // Remove Sub Pattern Directory GetPatterns().Remove(patternInfo); DeletePatternDirectory(patternInfo); } catch (Exception e) { _logger.E($"Remove pattern error : {patternInfo}, {e.Message}"); // 삭제 도중 실패하면 남은 파일로 패턴을 다시 불러와 둔다. try { if (GetPatterns().ContainsKey(patternInfo)) { ReloadPattern(patternInfo); } } catch (Exception reloadException) { _logger.E($"Reload pattern error : {patternInfo}, {reloadException.Message}"); } return E1.GENERIC; } return E1.OK; } } return E2.NOT_FOUND; } // 패턴(메인 또는 서브 하나) 폴더의 파일 중 다른 프로그램이 열고 있는 파일이 있는지 확인한다. public bool IsPatternFileLocked(PatternInfo patternInfo, out string lockedFilePath) { lockedFilePath = string.Empty; var patternDirPath = Path.GetDirectoryName(PatternConstants.GetSubPatternInfoFilePath(patternInfo)); if (!Directory.Exists(patternDirPath)) { return false; } foreach (var filePath in Directory.GetFiles(patternDirPath, "*", SearchOption.AllDirectories)) { try { using (File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { } } catch (IOException) { lockedFilePath = filePath; return true; } catch (UnauthorizedAccessException) { lockedFilePath = filePath; return true; } } return false; } // 메인과 서브 패턴 전체 중 다른 프로그램이 열고 있는 파일이 있는지 확인한다. public bool IsMainSubPatternFileLocked(PatternInfo patternInfo, out string lockedFilePath) { lockedFilePath = string.Empty; foreach (var curPatternInfo in GetMainSubPatterns(patternInfo)) { if (IsPatternFileLocked(curPatternInfo, out lockedFilePath)) { return true; } } return false; } public static string GetPatternFileLockedMessage(string lockedFilePath) { return "패턴 파일이 다른 프로그램에서 열려 있어 삭제할 수 없습니다.\n" + "MIL 프로그램 등에서 파일을 닫은 뒤 다시 삭제하세요.\n\n" + lockedFilePath; } public abstract IPatternFinder CreatePattern(PatternInfo patternInfo); public void DeletePatternDirectory(PatternInfo patternInfo) { var patternDirPath = PatternConstants.GetPatternDirPath(patternInfo); if (!Directory.Exists(patternDirPath)) { return; } if (IsDirectoryEmpty(patternDirPath)) { Directory.Delete(patternDirPath, recursive: true); } } public bool IsDirectoryEmpty(string dirPath) { var dirInfo = new DirectoryInfo(dirPath); var dirs = dirInfo?.GetDirectories(); var files = dirInfo?.GetFiles(); return (dirs?.Length == 0 && files?.Length == 0); } public abstract List GetPatternInfoListFromFiles(string curRecipePath); public int SavePatternInfoToFile(PatternInfo patternInfo, string filePath) { var patternDirPath = Path.GetDirectoryName(filePath); if (!Directory.Exists(patternDirPath)) { Directory.CreateDirectory(patternDirPath); } using (var fs = File.Create(filePath)) using (var sw = new StreamWriter(fs)) using (var jtw = new JsonTextWriter(sw) { Formatting = Formatting.Indented, Indentation = 2, }) { (new JsonSerializer()).Serialize(jtw, patternInfo); } return E1.OK; } public static PatternIndex GetSubPatternIndex(PatternInfo mainPatternInfo, int subPatternId) { var subPatternIndex = mainPatternInfo.GetPatternIndex().Clone() as PatternIndex; subPatternIndex.PatternSubId = subPatternId; return subPatternIndex; } public System.Drawing.Bitmap GetThumbnail(PatternInfo patternInfo) { if (_patterns.ContainsKey(patternInfo)) { if (PatternConstants.IsPatternBlock(patternInfo.PatternType)) { return GetPatternBlockThumbnail(patternInfo); } var pattern = _patterns[patternInfo]; if (pattern != null) { return pattern.GetThumbnail(); } } return ThumbnailUtil.GetDefaultImage(); } public System.Drawing.Bitmap GetPatternBlockThumbnail(PatternInfo patternInfo) { if (_patterns.ContainsKey(patternInfo)) { if (PatternConstants.IsPatternBlock(patternInfo.PatternType)) { var patternBlock = _patterns[patternInfo] as IPatternBlock; var findRois = new List(); foreach (var patternId in patternBlock.GetPatternBlockIds()) { var curPatternIndex = patternInfo.GetPatternIndex().Clone() as PatternIndex; curPatternIndex.PatternId = patternId; var patterBlockPatternInfo = GetPatternInfo(curPatternIndex); var findRoi = GetFindROI(patterBlockPatternInfo); findRois.Add(findRoi); } return patternBlock.GetThumbnail(findRois); } } return ThumbnailUtil.GetDefaultImage(); } /// /// C2_F4 or C4_F4에서 라인 교점으로 얼라인 하는 경우, 각 FOV들 간에 상관관계를 이용하여 라인 교점 알고리즘을 보상해주기 위해 라인 중점 저장 /// /// 라인 결과 /// 패턴 정보 /// 카메라 디스플레이 public void SetLineCenterPoint(PatternResult patternResult, PatternInfo patternInfo, ICameraDisplay cameraDisplay) { var fovInfo = cameraDisplay.GetDisplayFovInfo(); var alignInfo = VisionRecipeManager.GetAlignInfo(fovInfo.UnitId, fovInfo.SubSysId, eAlignId.Align1); var alignRecipe = VisionRecipeManager.GetAlignRecipe(alignInfo); if (patternResult.Status == eFoundStatus.ForceOK) { // 라인 교점 if (patternResult.PatternType == ePatternType.LineIntersection) { var results = patternResult.Results.ToList(); var baseResult = results[0] as BaseResult; var calibrator = CalAdapter.Instance.CalManager.GetCalibrator(fovInfo); var cameraAgent = CameraAgentManager.GetCameraAgent(fovInfo); var imageSize = cameraAgent.GetImageSize(); var calibratedPoint = CalAdapter.Instance.CalManager.TransformToCalibratedSpace(fovInfo, baseResult.ImagePosition, imageSize); LineFittingManager.SetLineCenterPoint(fovInfo, eLineDirection.H, calibratedPoint); LineFittingManager.SetLineCenterPoint(fovInfo, eLineDirection.V, calibratedPoint); } else { // 싱글 라인 var results = patternResult.Results.ToList(); if (results.Count > 0) { var baseResult = results[0] as BaseResult; var lineType = Line.GetLineDirection(baseResult.Angle); var calibrator = CalAdapter.Instance.CalManager.GetCalibrator(fovInfo); var cameraAgent = CameraAgentManager.GetCameraAgent(fovInfo); var imageSize = cameraAgent.GetImageSize(); var calibratedPoint = CalAdapter.Instance.CalManager.TransformToCalibratedSpace(fovInfo, baseResult.ImagePosition, imageSize); LineFittingManager.SetLineCenterPoint(fovInfo, lineType, calibratedPoint); } } } } public abstract int GetMilSystemId(eCamId camId); public abstract int GetFindNumber(PatternInfo patternInfo); public abstract bool IsTrainPattern(PatternInfo patternInfo); public abstract bool IsDeletable(PatternInfo patternInfo, int patternIndex); public void Dispose() { ClearPatterns(_patterns); } } }