/* * Copyright (c) 2025 LG Electronics Inc. * SPDX-License-Identifier: LicenseRef-LGE-Proprietary */ using Aurora; using Aurora.Api; using Aurora.Api.DataType; using MilCommon; using VisionCommon; using Hermes; using Matrox.MatroxImagingLibrary; using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using VisionCommonUI; using VisionCommonUI.Resources; using Euresys.EGrabber; using System.Collections.Generic; namespace MilCamera { /// /// Euresys 모듈 정의 /// public enum eModule { Interface, Device, Remote, Stream } /// /// Euresys 카메라 디바이스 (영상처리는 MIL로 함) /// public class EuresysCamera : CameraBase { #region Euresys Global Variables private const uint GRAB_REQ_TIMEOUT_MS = 1000; // 1 sec private const uint REALLOC_BUFFER_COUNT = 5; private const ulong SINGLE_GRAB_COUNT = 1; private EGrabber _eGrabber; private Task _ProcessEventsTask; private CancellationTokenSource _CancellationTokenSource; private Euresys.EGrabber.Buffer _eLastBuffer; private readonly object _eLastBufferLock = new object(); private byte[] _LastBufferArray = null; private int _Width; private int _Height; private int _PixelSize; private int _PixelComponentCount; #endregion private MIL_ID _OriginalImage = MIL.M_NULL; // 원본 이미지 버퍼 private MIL_ID _FilteredImage = MIL.M_NULL; // Flip + Rotation 이미지 버퍼 private MIL_ID _FilteredLiveImage = MIL.M_NULL; // Flip + Rotation 라이브 이미지 버퍼 private MIL_ID _CroppedImage = MIL.M_NULL; // Flip + Rotation + Crop 이미지 버퍼 private MIL_ID _CroppedLiveImage = MIL.M_NULL; // Flip + Rotation + Crop 라이브 이미지 버퍼 private IMilCameraDisplay _MilCameraDisplay; // Fov별로 가지는 MilCameraDisplay를 호출시마다 받아서 사용 private MilImgFlipAndRotationTool _MilImgFlipAndRotationTool; /// /// 생성자 /// /// 디바이스 ID public EuresysCamera(string deviceId) : base(deviceId) { var subject = string.Format("EuresysCamera[{0}]", deviceId); LOG = new Logger(subject); TactTimeChecker = new TactTimeChecker(subject); } protected override int DoActivate(IDeviceConfig config) { if (IsActivated) { LOG.FuncI("Already Activated!"); return E1.OK; } CamConfig = config as CamConfig; eCamId camId = CamRecipe.GetCamIdFromDevId(DeviceId); _MilImgFlipAndRotationTool = new MilImgFlipAndRotationTool(camId, CamConfig); var res = Init(); if (res == E1.OK) { CheckImageBufferRotationChanged(); } return res; } #region Init private int Init() { MilEnvironment.Instance.AllocSys(CamConfig.CamConnConfig.BoardType, CamConfig.CamConnConfig.BoardSlotPos); if (E1.OK != InitDigitizer()) { MessageBoxUtil.ShowMessageBoxError(StrTokens.MSG_E_DigitizerInitError); return E1.GENERIC; } if (E1.OK != SetImageSize()) { MessageBoxUtil.ShowMessageBoxError(StrTokens.MSG_E_ImageSizeSettingError); return E1.GENERIC; } if (!MilEnvironment.Instance.HasValidLicense()) { MessageBoxUtil.ShowMessageBoxError(StrTokens.MSG_E_NoMilLicense); return E1.GENERIC; } if (E1.OK != AllocImages()) { MessageBoxUtil.ShowMessageBoxError(StrTokens.MSG_E_ImageNotAllocated); return E1.GENERIC; } if (E1.OK != SetTrigger()) { MessageBoxUtil.ShowMessageBoxError(StrTokens.MSG_E_TriggerSettingError); return E1.GENERIC; } if (E1.OK != SetExposureTimeFromConfig()) { MessageBoxUtil.ShowMessageBoxError(StrTokens.MSG_E_ExposureTimeSettingError); return E1.GENERIC; } StartStream(); return E1.OK; } private int InitDigitizer() { try { // Euresys 보드를 사용하더라도 MIL API를 사용할 것이므로 eBoardType을 MIL_DEFAULT로 설정한다. if (!EuresysEnvironment.IsEuresysBoardType(CamConfig.CamConnConfig.BoardType)) { LOG.FuncE("BoardType is not EURESYS."); return E1.ARGUMENT; } return InitDigitizerImpl(); } catch (Exception e) { LOG.FuncE(e.Message); } return E1.GENERIC; } private int InitDigitizerImpl() { try { InitEuresysGrabber(); if (EuresysGrabberManager.EGrabberDiscovery.CameraCount == 0) { LOG.FuncE("No Camera Discovered."); return E1.RESOURCE; } #region 참고 코드 // 방법 1 //int camIdx = 0; //_eGrabber = new EGrabber(_eGrabberDiscovery.Cameras[camIdx]); // 방법 2 //int grabberIdx = 0; //EGrabberInfo info = _eGrabberDiscovery.GetGrabber(grabberIdx); //_eGrabber = new EGrabber(info); #endregion int res = E1.GENERIC; if (CamConfig.CamConnConfig.BoardType == eBoardType.EURESYS_GIGELINK) { LOG.FuncI("Try connecting the camera by IP address."); res = ConnectCameraByIpAddress(); } if (res != E1.OK) { LOG.FuncI("Try connecting the camera by geni-cam device user id."); res = ConnectCameraByDeviceUserId(); } if (res != E1.OK) { LOG.FuncI("Try connecting the camera by slot and board pos."); res = ConnectCameraBySlotAndPortPos(); } if (res != E1.OK || _eGrabber == null) { LOG.FuncE("Failed to connect camera."); return E1.GENERIC; } string card = _eGrabber.Interface.Get("InterfaceID"); string dev = _eGrabber.Device.Get("DeviceID"); _Width = (int)_eGrabber.Remote.Get("Width"); _Height = (int)_eGrabber.Remote.Get("Height"); _PixelSize = (int)_eGrabber.Stream.Get("PixelSize"); _PixelComponentCount = (int)_eGrabber.Stream.Get("PixelComponentCount"); LOG.FuncI($"Interface : {card}"); LOG.FuncI($"Device : {dev}"); LOG.FuncI($"Resolution : {_Width}x{_Height}"); LOG.FuncI($"PixelSize : {_PixelSize}"); LOG.FuncI($"PixelComponentCount : {_PixelComponentCount}"); _eGrabber.ReallocBuffers(REALLOC_BUFFER_COUNT); _eGrabber.RegisterEventCallback(HookHandler); _LastBufferArray = new byte[_Width * _Height]; return E1.OK; } catch (Exception e) { LOG.FuncE(e.Message); } return E1.GENERIC; } private void InitEuresysGrabber() { if (EuresysGrabberManager.IsCreated()) { return; } if (!File.Exists(EuresysGrabberManager.CtiPath)) { SetCtiFilePath(); } if (File.Exists(EuresysGrabberManager.CtiPath)) { LOG.FuncV($"Set a digitizer with a cti file. [{EuresysGrabberManager.CtiPath}]"); if (EuresysGrabberManager.Create(EuresysGrabberManager.CtiPath) != E1.OK) { LOG.FuncE("Failed to create grabber."); return; } } else { LOG.FuncE($"cti file is exists not. [{EuresysGrabberManager.CtiPath}]"); return; } } private void SetCtiFilePath() { if (CamConfig.CamConnConfig.ConnFilePath != string.Empty) { EuresysGrabberManager.CtiPath = CamConfig.CamConnConfig.ConnFilePath; // Euresys의 경우 Cti 파일 경로 (Common Transport Interface) } else { string ctiFileName = "gigelink.cti"; // Default switch (CamConfig.CamConnConfig.BoardType) { case eBoardType.EURESYS_COAXLINK: ctiFileName = "coaxlink.cti"; break; case eBoardType.EURESYS_GRABLINK: ctiFileName = "grablink.cti"; break; } EuresysGrabberManager.CtiPath = string.Format(@"C:\\Program Files\\Euresys\\eGrabber\\cti\\x86_64\\{0}", ctiFileName); } } /// /// [방법1] GigE 카메라 고정 IP를 이용한 카메라 연결 /// private int ConnectCameraByIpAddress() { foreach (var grabberInfo in EuresysGrabberManager.EGrabberDiscovery.EGrabbers) { if (CamConfig.CamConnConfig.IpAddress != String.Empty) { _eGrabber = new EGrabber(grabberInfo); var ipAddress = _eGrabber.Interface.Get("DeviceCurrentIPAddress"); if (CamConfig.CamConnConfig.IpAddress == ipAddress) { return E1.OK; } else { _eGrabber?.Dispose(); _eGrabber = null; continue; } } } return E1.GENERIC; } /// /// [방법2] GenI캠 DeviceUserId를 이용한 카메라 연결 /// private int ConnectCameraByDeviceUserId() { foreach (var grabberInfo in EuresysGrabberManager.EGrabberDiscovery.EGrabbers) { if (CamConfig.Name != String.Empty) { if (CamConfig.Name == grabberInfo.DeviceUserID) { _eGrabber = new EGrabber(grabberInfo); return E1.OK; } } } return E1.GENERIC; } /// /// [방법3] SlotPos와 PortPos를 이용한 카메라 연결 /// private int ConnectCameraBySlotAndPortPos() { foreach (var grabberInfo in EuresysGrabberManager.EGrabberDiscovery.EGrabbers) { if ((int)CamConfig.CamConnConfig.BoardSlotPos == grabberInfo.InterfaceIndex && (int)CamConfig.CamConnConfig.BoardPortPos == grabberInfo.DeviceIndex) { LOG.FuncI($"InterfaceIndex(SlotPos) : {grabberInfo.InterfaceIndex}"); LOG.FuncI($"DeviceIndex(PortPos) : {grabberInfo.DeviceIndex}"); _eGrabber = new EGrabber(grabberInfo); return E1.OK; } } return E1.GENERIC; } private int SetImageSize() { try { CamConfig.ImageSize.Width = _Width; CamConfig.ImageSize.Height = _Height; CamConfig.ImageSize.Channel = _PixelComponentCount; CamConfig.ImageSize.BitDepth = _PixelSize; } catch (Exception e) { LOG.E(e.Message); return E1.GENERIC; } return E1.OK; } private int SetExposureTimeFromConfig() { try { SetExposure(CamConfig.ExposureTime_Ms); } catch (Exception e) { LOG.E(e.Message); return E1.GENERIC; } return E1.OK; } protected int AllocImages() { try { var results = new List(); results.Add(AllocOriginalImage(ref _OriginalImage)); results.Add(AllocFilteredImage(ref _FilteredImage)); results.Add(AllocFilteredImage(ref _FilteredLiveImage)); results.Add(AllocCroppedImage(ref _CroppedImage)); results.Add(AllocCroppedImage(ref _CroppedLiveImage)); results.AddRange(AllocProcImages()); foreach (int result in results) { if (result != E1.OK) { return E1.GENERIC; } } } catch (Exception e) { LOG.E(e.Message); return E1.GENERIC; } return E1.OK; } private int AllocOriginalImage(ref MIL_ID image) { return MilEnvironment.Instance.AllocOriginalImage(CamConfig, ref image); } private int AllocFilteredImage(ref MIL_ID image) { return MilEnvironment.Instance.AllocFilteredImage(CamConfig, ref image); } private int AllocCroppedImage(ref MIL_ID image) { return MilEnvironment.Instance.AllocCroppedImage(CamConfig, ref image); } /// /// 이미지 버퍼 복제 /// /// 복제 버퍼 public override object CloneImageBuffer() { return MilEnvironment.CloneImage(_CroppedImage); } private int SetTrigger() { try { SetImmediateTrigger(); if (CamConfig.TriggerMode.IsSoftware) { return SetSwTrigger(); } else { return SetHwTrigger(); } } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } } private void SetImmediateTrigger() { eCamId camId = CamRecipe.GetCamIdFromDevId(DeviceId); if (CameraFactory.GetBoardType(camId) != eBoardType.EURESYS_GIGELINK) { SetFeatureString(eModule.Device, "CameraControlMethod", "RC"); // 무조건 RG, RC이어야 Set이 됨. NC이면 잠겨서 설정 안됨. //SetFeatureString(eModule.Device, "CameraControlMethod", "RG"); // 무조건 RG, RC이어야 Set이 됨. NC이면 잠겨서 설정 안됨. SetFeatureString(eModule.Device, "ExposureReadoutOverlap", "True"); SetFeatureDouble(eModule.Device, "ExposureRecoveryTime", 100); // [us] } SetFeatureString(eModule.Remote, "TriggerMode", "On"); SetFeatureString(eModule.Remote, "TriggerSelector", "FrameStart"); //SetFeatureString(eModule.Remote, "TriggerOverlap", "ReadOut"); // "RG"일 때만 해당 SetFeatureString(eModule.Remote, "ExposureMode", "Timed"); // "RC"일 때만 해당. "Timed" : 카메라에서 노출 제어(RC) //SetFeatureString(eModule.Remote, "ExposureMode", "TriggerWidth"); // "RG"일 때만 해당. "TriggerWidth" : 보드에서 노출 제어(RG) } private int SetHwTrigger() { try { // 인터페이스 I/O 설정 SetFeatureString(eModule.Interface, "LineInputToolSelector", "LIN1"); SetFeatureString(eModule.Interface, "LineInputToolSource", "IIN11"); // 프레임 그래버 설정 eCamId camId = CamRecipe.GetCamIdFromDevId(DeviceId); if (CameraFactory.GetBoardType(camId) != eBoardType.EURESYS_GIGELINK) { SetFeatureString(eModule.Device, "CycleTriggerSource", "LIN1"); SetFeatureDouble(eModule.Device, "CycleMinimumPeriod", 4200); // 카메라 fps의 역수보다 약간 크게 입력해야 함 [us] } // 카메라 설정 SetFeatureString(eModule.Remote, "TriggerSource", CamConfig.TrigSrc.ToString()); // 카메라 제조사마다 다름.. Line0 ?? } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private int SetSwTrigger() { try { // 프레임 그래버 설정 eCamId camId = CamRecipe.GetCamIdFromDevId(DeviceId); if (CameraFactory.GetBoardType(camId) != eBoardType.EURESYS_GIGELINK) { SetFeatureString(eModule.Device, "CycleTriggerSource", "Immediate"); // 사이클 주기마다 반복 수행 //SetFeatureString(eModule.Device, "CycleTriggerSource", "StartCycle"); // 요청 올때만 수행 SetFeatureDouble(eModule.Device, "CycleMinimumPeriod", 30000); // 카메라 fps의 역수보다 약간 크게 입력해야 함 [us] } // 카메라 설정 SetFeatureString(eModule.Remote, "TriggerSource", CamConfig.TrigSrc.ToString()); SetFeatureString(eModule.Remote, "TriggerMode", "Off"); } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private void SetFeatureString(eModule module, string featureName, string writeValue) { try { string readValue = string.Empty; switch (module) { case eModule.Interface: _eGrabber.Interface.Set(featureName, writeValue); readValue = _eGrabber.Interface.Get(featureName); break; case eModule.Device: _eGrabber.Device.Set(featureName, writeValue); readValue = _eGrabber.Device.Get(featureName); break; case eModule.Remote: _eGrabber.Remote.Set(featureName, writeValue); readValue = _eGrabber.Remote.Get(featureName); break; case eModule.Stream: _eGrabber.Stream.Set(featureName, writeValue); readValue = _eGrabber.Stream.Get(featureName); break; } if (!writeValue.Equals(readValue)) { LOG.E($"module : {module} / featureName : {featureName} / writeValue: {writeValue} / readValue : {readValue}"); } else { LOG.I($"module : {module} / featureName : {featureName} / writeValue: {writeValue} / readValue : {readValue}"); } } catch (Exception e) { LOG.FuncE(e.Message); LOG.FuncE($"module : {module} / featureName : {featureName} / writeValue: {writeValue}"); } } private void SetFeatureDouble(eModule module, string featureName, double writeValue) { try { double readValue = -1; switch (module) { case eModule.Interface: _eGrabber.Interface.Set(featureName, writeValue); readValue = _eGrabber.Interface.Get(featureName); break; case eModule.Device: _eGrabber.Device.Set(featureName, writeValue); readValue = _eGrabber.Device.Get(featureName); break; case eModule.Remote: _eGrabber.Remote.Set(featureName, writeValue); readValue = _eGrabber.Remote.Get(featureName); break; case eModule.Stream: _eGrabber.Stream.Set(featureName, writeValue); readValue = _eGrabber.Stream.Get(featureName); break; } if (!writeValue.Equals(readValue)) { LOG.E($"module : {module} / featureName : {featureName} / writeValue: {writeValue} / readValue : {readValue}"); } else { LOG.I($"module : {module} / featureName : {featureName} / writeValue: {writeValue} / readValue : {readValue}"); } } catch (Exception e) { LOG.FuncE(e.Message); LOG.FuncE($"module : {module} / featureName : {featureName} / writeValue: {writeValue}"); } } private int StartStream() { try { _eGrabber?.EnableEvent(EventType.NewBufferData); _eGrabber?.Start(); _CancellationTokenSource = new CancellationTokenSource(); _ProcessEventsTask = _eGrabber.ProcessEventsAsync(EventType.NewBufferData, _CancellationTokenSource.Token); return E1.OK; } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } } private int StopStream() { try { _eGrabber?.Stop(); _eGrabber?.DisableEvent(EventType.NewBufferData); _CancellationTokenSource?.Cancel(); _ProcessEventsTask?.Wait(); _ProcessEventsTask = null; _CancellationTokenSource?.Dispose(); _CancellationTokenSource = null; if (_eLastBuffer != null) { _eLastBuffer.Push(); _eLastBuffer.Dispose(); _eLastBuffer = null; } _eGrabber?.FlushEvent(EventType.All); return E1.OK; } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } } #endregion protected override int DoDeactivate() { if (!IsActivated) { LOG.I("Already Deactivated!"); return E1.OK; } return Dispose(); } #region Dispose private int Dispose() { try { StopStream(); StopBufLive(); StopMdigProcess(); FreeImages(); _LastBufferArray = null; _eLastBuffer?.Dispose(); _eLastBuffer = null; _eGrabber?.Dispose(); _eGrabber = null; return E1.OK; } catch (Exception e) { LOG.FuncE(e.Message); } return E1.GENERIC; } private void StopBufLive() { if (!IsActivated) return; try { lock (LockThis) { StopLive(); } } catch (Exception e) { LOG.E(e.Message); } } private void FreeImages() { try { MilEnvironment.FreeImage(_OriginalImage); MilEnvironment.FreeImage(_FilteredImage); MilEnvironment.FreeImage(_FilteredLiveImage); MilEnvironment.FreeImage(_CroppedImage); MilEnvironment.FreeImage(_CroppedLiveImage); MilEnvironment.FreeImage(_ParentImage); int bufSize = CamConfig.ChildImage.Enabled ? CamConfig.ChildImage.ChildBufRowCols.Count : CamConfig.HwTrigConfig.ProcBufSize; MilEnvironment.FreeProcImages(bufSize, _ProcImages); } catch (Exception e) { LOG.E(e.Message); } } #endregion /// /// 카메라 연결 유무 /// /// 연결 유무 public override bool IsConnected() { //MIL_INT isConnected = 0; //MIL.MdigInquire(_Digitizer, MIL.M_CAMERA_PRESENT, ref isConnected); //return (isConnected == MIL.M_YES); return IsActivated; } /// /// 카메라 Gain 읽기 /// /// Gain 값 public override int GetGain() => throw new NotImplementedException(); /// /// 카메라 Gain 쓰기 /// /// Gain 값 /// 성공 유무 public override int SetGain(int gain) => throw new NotImplementedException(); /// /// 카메라 노출시간 읽기 /// /// 노출시간 public override double GetExposure() { if (_eGrabber == null) { return E2.NOT_READY; } var exposureTime = _eGrabber.Remote.Get("ExposureTime"); return exposureTime; } /// /// 카메라 최소 노출시간 읽기 /// /// 최소 노출시간 public override double GetMinExposure() => throw new NotImplementedException(); /// /// 카메라 최대 노출시간 읽기 /// /// 최대 노출시간 public override double GetMaxExposure() => throw new NotImplementedException(); /// /// 카메라 노출시간 쓰기 /// /// 노출시간[ms] /// 성공 유무 public override int SetExposure(double exposureTime_ms) { if (!IsConnected()) { return E2.NOT_READY; } var exposureTime_us = MathUtil.ConvertUnitMilliToMicro(exposureTime_ms); SetFeatureDouble(eModule.Remote, "ExposureTime", exposureTime_us); LOG.I($"Exposure Time Set : {exposureTime_ms} ms"); return E1.OK; } /// /// 이미지 반전 & 회전 Config 갱신 /// /// 이미지 반전 & 회전 Config /// 성공 유무 public override int ResetFlipAndRotation(FlipAndRotation flipAndRotation) { _MilImgFlipAndRotationTool?.ResetFlipAndRotation(flipAndRotation); return E1.OK; } /// /// 디스플레이 가져오기 /// /// 디스플레이 public override ICameraDisplay GetDisplay() { return _MilCameraDisplay; } /// /// 디스플레이 지정하기 /// /// 디스플레이 public override void SetDisplay(ICameraDisplay cameraDisplay) { _MilCameraDisplay = cameraDisplay as IMilCameraDisplay; _MilCameraDisplay.SetVisibility(); } /// /// 이미지 사이즈 가져오기 /// /// 이미지 사이즈 public override ImageSize GetImageSize() { return MilEnvironment.Instance.GetImageSize(CamConfig); } #region Grab /// /// 버퍼로부터 이미지를 꺼내온다. /// /// 디스플레이 /// 이미지 /// 성공 유무 public override int GetGrabImageFromBuffer(ICameraDisplay cameraDisplay, ref object image) { SetDisplay(cameraDisplay as IMilCameraDisplay); return GetGrabImage(ref image, isExtTrig : true); } /// /// 동기식 그랩 /// /// 디스플레이 /// 그랩 이미지 /// 성공 유무 public override int GrabSync(ICameraDisplay cameraDisplay, ref object image) { SetDisplay(cameraDisplay as IMilCameraDisplay); MilEnvironment.ClearImage(_OriginalImage); if (CamConfig.TriggerMode.IsSoftware) { return GrabSync_SwTrig(ref image); } else { return GrabSync_HwTrig(ref image); } } private int GrabSync_HwTrig(ref object image) { for (int i = 1; i <= CamConfig.GrabRetry; i++) { int res = GrabSync_HwTrig_Internal(ref image); if (res == E1.OK) { return E1.OK; } LOG.I($"Grab Retry : {i}"); } LOG.E("Grab Fail"); return E1.OK; } private int GrabSync_HwTrig_Internal(ref object image) { try { SetHookCompleted(false); TriggerOn(); Thread.Sleep(CamConfig.HwTrigConfig.OffDelay_Ms); TriggerOff(); return GetGrabImage(ref image, isExtTrig: false); } catch (Exception e) { LOG.FuncE(e.Message); } return E1.GENERIC; } /// /// SW 트리거의 경우 HookHandler에서 이미지가 계속 갱신되고 있으므로, 가져가기만 하면 됨 /// /// 이미지 /// 성공 유무 private int GrabSync_SwTrig(ref object image) { try { //SetHookCompleted(false); //ClearBuffers(); #region Grab 방식 //_eGrabber.ProcessEvent(EventType.NewBufferData, GRAB_REQ_TIMEOUT_MS); // [1] ProcessEvent (주문형 콜백) //NewBufferData data = _eGrabber.Pop(GRAB_REQ_TIMEOUT_MS); //PopOneOfResult rawData = _eGrabber.PopOneOf(EventType.NewBufferData, 3000); // [2] PopOneOf //NewBufferData data = rawData.EventData as NewBufferData; //NewBufferData data = _eGrabber.Pop(3000); // [3] Pop //NewBufferData data = _eGrabber.GetBufferData(REALLOC_BUFFER_COUNT - 1); // [4] GetBufferData #endregion //lock (_eLastBufferLock) { // if (_eLastBuffer != null) { // // push back the last buffer // _eLastBuffer.Push(); // _eLastBuffer.Dispose(); // _eLastBuffer = null; // } //} return GetGrabImage(ref image, isExtTrig: false); } catch (Exception e) { LOG.FuncE(e.Message); } //TactTimeChecker.Stop(TactTimeType.Grab, "GrabSyncWithFiltering"); return E1.GENERIC; } private void ClearBuffers() { int remainBuffers = (int)_eGrabber.Stream.GetInfo(STREAM_INFO_CMD.STREAM_INFO_NUM_AWAIT_DELIVERY); while (Convert.ToBoolean(remainBuffers)) { using (ScopedBuffer scopeBuffer = new ScopedBuffer(_eGrabber, 10)) { remainBuffers--; } } } private int GetGrabImage(ref object image, bool isExtTrig) { var stopWatch = Stopwatch.StartNew(); while (stopWatch.ElapsedMilliseconds < CamConfig.GrabTimeout_Ms) { bool hookCompleted = isExtTrig ? IsHookCompletedByExtTrig() : IsHookCompleted(); if (hookCompleted) { LOG.W($"Grab Completed : {stopWatch.ElapsedMilliseconds} ms"); FlipAndRotateImage(); CropImage(); SetDisplayImage(); image = IsLiveOn() ? _CroppedLiveImage : _CroppedImage; return E1.OK; } Thread.Sleep(10); } LOG.E($"HookHandler Time Out!! [{CamConfig.GrabTimeout_Ms} ms] "); return E1.GENERIC; } #endregion #region Live /// /// 라이브 시작 /// /// 디스플레이 public override void StartLive(ICameraDisplay cameraDisplay) { if (_FilteredLiveImage == MIL.M_NULL) { MessageBoxUtil.ShowMessageBoxError(string.Format(StrTokens.MSG_E_SomethingNotAllocated, "_FilteredLiveImage")); return; } lock (_LiveLock) { // 이미 라이브가 실행중이면, 디스플레이만 변경한다. SetDisplay(cameraDisplay); if (!IsLiveOn()) { LiveOn(); if (CamConfig.TriggerMode.IsSoftware) { Task.Run(() => { StartLive_SwTrig(); }); } else { Task.Run(() => { StartLive_HwTrig(); }); } } } } /// /// 라이브 종료 /// public override void StopLive() { lock (_LiveLock) { if (IsLiveOn()) { LiveOff(); } } } private void StartLive_HwTrig() { try { while (IsLiveOn()) { SetHookCompleted(false); TriggerOn(); Thread.Sleep(CamConfig.HwTrigConfig.OffDelay_Ms); TriggerOff(); var stopWatch = Stopwatch.StartNew(); while (stopWatch.ElapsedMilliseconds < CamConfig.GrabTimeout_Ms) { if (IsHookCompleted()) { FlipAndRotateImage(); CropImage(); SetDisplayImage(); break; } Thread.Sleep(10); } //Thread.Sleep(CamConfig.LiveDelay); } } catch (Exception e) { LOG.FuncE(e.Message); } } private void StartLive_SwTrig() { try { while (IsLiveOn()) { var image = (object)_FilteredLiveImage; GrabSync(_MilCameraDisplay as IMilCameraDisplay, ref image); _FilteredLiveImage = (MIL_ID)image; Thread.Sleep(CamConfig.LiveDelay_Ms); } } catch (Exception e) { LOG.FuncE(e.Message); } } #endregion private void TriggerOn() { try { EquipAdapter.Instance.EquipTrigger.TriggerOn(CamConfig); } catch (Exception e) { LOG.FuncE(e.Message); } } private void TriggerOff() { try { EquipAdapter.Instance.EquipTrigger.TriggerOff(CamConfig); } catch (Exception e) { LOG.FuncE(e.Message); } } /// /// 디스플레이에서 이미지 꺼내오기 /// /// 디스플레이 /// 디스플레이 이미지 public override object GetImageFromDisplay(ICameraDisplay cameraDisplay) { // 버퍼 그랩 이후, 디스플레이로부터 이미지를 꺼내오는 부분 // 이미지를 꺼내오기 전에 cameraDisplay와 동일한 _MilCameraDisplay가 사전에 Set되어 있어야 함. var milCameraDisplay = cameraDisplay as IMilCameraDisplay; if (milCameraDisplay.IsSetImage()) { return milCameraDisplay.GetImage(); } LOG.FuncE("Image of milCameraDisplay is null."); return MIL.M_NULL; } /// /// EventCallBack 함수 (SW 트리거 일때는 계속 들어오고, HW 트리거 일때는 신호가 터질 때만 들어옴) /// /// 그래버 /// Raw 데이터 private void HookHandler(EGrabber g, NewBufferData data) { try { PrintHookLog(); lock (_eLastBufferLock) { if (_eLastBuffer != null) { // push back the last buffer _eLastBuffer.Push(); _eLastBuffer.Dispose(); _eLastBuffer = null; } _eLastBuffer = new Euresys.EGrabber.Buffer(g, data); Marshal.Copy(_eLastBuffer.Pixels, _LastBufferArray, 0, _Width * _Height); MilEnvironment.ConvertByteArryToMil(_LastBufferArray, _Width, _Height, ref _OriginalImage); } SetHookCompleted(true); if (CamConfig.TriggerMode.IsExternal) { SetHookCompletedByExtTrig(true); } SetFlyingImage(); } catch (Exception e) { LOG.FuncE(e.Message); } } private void PrintHookLog() { if (!IsLiveOn()) { LOG.FuncV("[HW Trigger] HookHandler Called."); } } #region For Hw Trigger private List AllocProcImages() { var results = new List(); if (!CamConfig.IsAlwaysSwTrigger()) { if (_ProcImages?.Length > 0) { MilEnvironment.FreeProcImages(_ProcImages.Length, _ProcImages); } if (CamConfig.ChildImage.Enabled) { results.Add(MilEnvironment.Instance.AllocParentImage(CamConfig, ref _ParentImage)); results.Add(MilEnvironment.Instance.AllocChildImages(CamConfig, _ParentImage, ref _ProcImages)); } else { _ProcImages = new object[CamConfig.HwTrigConfig.ProcBufSize]; results.Add(MilEnvironment.Instance.AllocProcImages(CamConfig, CamConfig.HwTrigConfig.ProcBufSize, ref _ProcImages)); } } return results; } private void StartMdigProcess() { try { //_HookData.HookDigitizer = _Digitizer; //_HookData.HookImage = _OriginalImage; //_HookData.HookCnt = 0; //if (!_UserDataHandle.IsAllocated) { // _UserDataHandle = GCHandle.Alloc(_HookData); //} //if (_HookFuncPtr == null) { // _HookFuncPtr = new MIL_DIG_HOOK_FUNCTION_PTR(HookHandler); //} //MIL.MdigControlFeature(_Digitizer, MIL.M_FEATURE_VALUE_AS_STRING, "AcquisitionMode", MIL.M_DEFAULT, "Continuous"); // SingleFrame 아니어도 되나..? //MIL.MdigControlFeature(_Digitizer, MIL.M_FEATURE_VALUE_AS_STRING, "TriggerMode", MIL.M_DEFAULT, "On"); //MIL.MdigControlFeature(_Digitizer, MIL.M_FEATURE_VALUE_AS_STRING, "TriggerSource", MIL.M_DEFAULT, CamConfig.TrigSrc.ToString()); //if (_ProcImages != null) { // MIL.MdigProcess( // _Digitizer, Array.ConvertAll(_ProcImages, item => (MIL_ID)item), (MIL_INT)_ProcImages.Length, MIL.M_START, MIL.M_DEFAULT, _HookFuncPtr, GCHandle.ToIntPtr(_UserDataHandle)); //} } catch (Exception e) { LOG.FuncE(e.Message); } } private void StopMdigProcess() { try { //MIL.MdigControl(_Digitizer, MIL.M_PROCESS_TIMEOUT, 5); //if (_ProcImages?.Length > 0) { // //MIL.MdigProcess(_Digitizer, _ProcImages, (MIL_INT)_ProcImages.Length, MIL.M_STOP, MIL.M_DEFAULT, _HookFuncPtr, GCHandle.ToIntPtr(_UserDataHandle)); // 원래 코드 // MIL.MdigProcess( // _Digitizer, Array.ConvertAll(_ProcImages, item => (MIL_ID)item), (MIL_INT)_ProcImages.Length, MIL.M_STOP + MIL.M_WAIT, MIL.M_SYNCHRONOUS, _HookFuncPtr, GCHandle.ToIntPtr(_UserDataHandle)); //} //if (_UserDataHandle.IsAllocated) // _UserDataHandle.Free(); } catch (Exception e) { LOG.FuncE(e.Message); } } #endregion #region For Flying /// /// 플라잉 시작 /// /// FOV 정보 /// 추가 파라미터 /// 성공 유무 public override int StartFlying(FovInfo fovInfo, object param) { if (IsFlyingRunning()) { LOG.FuncW("Flying Already Running..."); return E1.GENERIC; } LOG.I("Flying Start."); try { var camDisplay = CameraDisplayManager.GetDisplay(fovInfo); SetDisplay(camDisplay); _FlyingIdx = 0; _IsFlyingRunning = true; StartMdigProcess(); } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } /// /// 플라잉 종료 /// /// 추가 파라미터 /// 성공 유무 public override int StopFlying(object param = null) { LOG.I("Flying Stopped."); try { _IsFlyingRunning = false; StopMdigProcess(); } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private void SetFlyingImage() { if (!CamConfig.TriggerMode.IsFlying) { return; } try { FlipAndRotateImage(); CropImage(); _FlyingIdx = (_FlyingIdx < _ProcImages.Length) ? _FlyingIdx : 0; MilEnvironment.CopyImage(_CroppedImage, (MIL_ID)_ProcImages[_FlyingIdx++]); SetDisplayImage(); } catch (Exception e) { LOG.FuncE(e.Message); } } #endregion #region For Child Image /// /// Child 이미지들로 Parent(원장) 이미지가 정상적으로 만들어지는지 검증 /// /// 디스플레이 /// 원장(부모) 이미지 /// 성공 유무 public override int VerifyChildImage(ICameraDisplay cameraDisplay, ref object image) { try { return E1.OK; } catch (Exception e) { LOG.FuncE(e.Message); } return E1.GENERIC; } #endregion private void SetDisplayImage() { if (_MilCameraDisplay == null) { return; } if (IsLiveOn()) { if (_CroppedLiveImage != null) { _MilCameraDisplay.SetImage(_CroppedLiveImage); } } else { if (_CroppedImage != null) { _MilCameraDisplay.SetImage(_CroppedImage); } } } private void FlipAndRotateImage() { if (_OriginalImage == null) { LOG.FuncE("_OriginalImage is null."); return; } if (IsLiveOn()) { _MilImgFlipAndRotationTool.FlipAndRotation(_OriginalImage, ref _FilteredLiveImage); } else { _MilImgFlipAndRotationTool.FlipAndRotation(_OriginalImage, ref _FilteredImage); } } private void CropImage() { var cropRoi = GetCropRoi(); if (IsLiveOn()) { if (_FilteredLiveImage != null) { _CroppedLiveImage = MilEnvironment.CropImage(_FilteredLiveImage, cropRoi); } } else { if (_FilteredImage != null) { _CroppedImage = MilEnvironment.CropImage(_FilteredImage, cropRoi); } } } private class HookDataStruct { public MIL_ID HookDigitizer; public MIL_ID HookImage; public int HookCnt; } } }