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 System.Collections.Generic; namespace MilCamera { /// /// MIL 카메라 디바이스 /// public class MilCamera : CameraBase { private MIL_ID _Digitizer = MIL.M_NULL; 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; private readonly object _SwTrigLock = new object(); #region For Hw Trigger private const int GRAB_HANDLER_WAITING_TIME = 250; // GrabHandler에 들어올 때까지 기다리는 최대 시간 (ms) -> 버퍼 그랩의 경우 기다릴 필요X private GCHandle _UserDataHandle; private HookDataStruct _HookData = new HookDataStruct(); private MIL_DIG_HOOK_FUNCTION_PTR _HookFuncPtr; #endregion /// /// 생성자 /// /// public MilCamera(string deviceId) : base(deviceId) { var subject = string.Format("MilCamera[{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 (!MilEnvironment.Instance.HasValidLicense()) { MessageBoxUtil.ShowMessageBoxError(StrTokens.MSG_E_NoMilLicense); return E1.GENERIC; } 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 (E1.OK != AllocImages()) { MessageBoxUtil.ShowMessageBoxError(StrTokens.MSG_E_ImageNotAllocated); return E1.GENERIC; } if (E1.OK != SetExposureTimeFromConfig()) { MessageBoxUtil.ShowMessageBoxError(StrTokens.MSG_E_ExposureTimeSettingError); return E1.GENERIC; } if (E1.OK != SetTrigger()) { MessageBoxUtil.ShowMessageBoxError(StrTokens.MSG_E_TriggerSettingError); return E1.GENERIC; } return E1.OK; } private int InitDigitizer() { try { if (CamConfig.CamConnConfig.ConnFilePath != string.Empty) { if (File.Exists(CamConfig.CamConnConfig.ConnFilePath)) { LOG.FuncV($"Set a digitizer with a dcf file. [{CamConfig.CamConnConfig.ConnFilePath}]"); InitDigitizerWithDcf(); } else { LOG.FuncE("Dcf file is exists not."); return E2.NOT_FOUND; } } else { LOG.FuncV("Set a digitizer without a dcf file."); InitDigitizerWithoutDcf(); // DcfFilePath가 공백이면, Dcf파일 없이 Digitizer 할당. } if (_Digitizer != MIL.M_NULL) { MIL.MdigControl(_Digitizer, MIL.M_GRAB_ABORT, MIL.M_DEFAULT); MIL.MdigControl(_Digitizer, MIL.M_GRAB_MODE, MIL.M_ASYNCHRONOUS); return E1.OK; } } catch (Exception e) { LOG.FuncE(e.Message); } return E1.GENERIC; } private void InitDigitizerWithDcf() { try { if (_Digitizer == MIL.M_NULL || _Digitizer == 0) { MIL.MdigAlloc( MilEnvironment.Instance.GetSystem(CamConfig.CamConnConfig.BoardType, CamConfig.CamConnConfig.BoardSlotPos), (int)CamConfig.CamConnConfig.BoardPortPos, CamConfig.CamConnConfig.ConnFilePath, MIL.M_DEFAULT, ref _Digitizer); } } catch (Exception e) { LOG.FuncE(e.Message); } } private void InitDigitizerWithoutDcf() { try { if (CamConfig.CamConnConfig.BoardType == eBoardType.MIL_GIGE) { if (_Digitizer == MIL.M_NULL || _Digitizer == 0) { MIL.MdigAlloc( MilEnvironment.Instance.GetSystem(CamConfig.CamConnConfig.BoardType, CamConfig.CamConnConfig.BoardSlotPos), MIL.M_GC_CAMERA_ID(CamConfig.CamConnConfig.IpAddress), "M_DEFAULT", MIL.M_GC_DEVICE_IP_ADDRESS, ref _Digitizer); } } } catch (Exception e) { LOG.FuncE(e.Message); } } private int SetImageSize() { MIL_INT width = 0; MIL_INT height = 0; MIL_INT band = 0; MIL_INT bitDepth = 0; try { MIL.MdigInquire(_Digitizer, MIL.M_SIZE_X, ref width); MIL.MdigInquire(_Digitizer, MIL.M_SIZE_Y, ref height); MIL.MdigInquire(_Digitizer, MIL.M_SIZE_BAND, ref band); MIL.MdigInquire(_Digitizer, MIL.M_SIZE_BIT, ref bitDepth); CamConfig.ImageSize.Width = (int)width; CamConfig.ImageSize.Height = (int)height; CamConfig.ImageSize.Channel = (int)band; CamConfig.ImageSize.BitDepth = (int)bitDepth; } catch (Exception e) { LOG.E(e.Message); return E1.GENERIC; } return E1.OK; } private int SetExposureTimeFromConfig() { try { SetInitialExposure(); 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 { StopMdigProcess(); if (CamConfig.TriggerMode.IsSoftware) { return SetSwTrigger(); } else { return SetHwTrigger(); } } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } } private int SetHwTrigger() { try { StartMdigProcess(); } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private int SetSwTrigger() { try { MIL.MdigControlFeature(_Digitizer, MIL.M_FEATURE_VALUE_AS_STRING, "AcquisitionMode", MIL.M_DEFAULT, "Continuous"); MIL.MdigControlFeature(_Digitizer, MIL.M_FEATURE_VALUE_AS_STRING, "TriggerMode", MIL.M_DEFAULT, "Off"); MIL.MdigControlFeature(_Digitizer, MIL.M_FEATURE_VALUE_AS_STRING, "TriggerSource", MIL.M_DEFAULT, CamConfig.TrigSrc.ToString()); MIL.MdigControl(_Digitizer, MIL.M_GRAB_TRIGGER, MIL.M_DISABLE); } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private void SetTriggerMode_On() { // "TriggerMode"를 설정을 지원하는 카메라일 경우 사용 가능 MIL.MdigControlFeature(_Digitizer, MIL.M_FEATURE_VALUE_AS_STRING, "TriggerMode", MIL.M_DEFAULT, "On"); } private void SetTriggerMode_Off() { // "TriggerMode"를 설정을 지원하는 카메라일 경우 사용 가능 MIL.MdigControlFeature(_Digitizer, MIL.M_FEATURE_VALUE_AS_STRING, "TriggerMode", MIL.M_DEFAULT, "Off"); } #endregion protected override int DoDeactivate() { if (!IsActivated) { LOG.I("Already Deactivated!"); return E1.OK; } return Dispose(); } #region Dispose private int Dispose() { try { StopBufLive(); StopMdigProcess(); FreeImages(); if (_Digitizer != MIL.M_NULL) { MIL.MdigFree(_Digitizer); _Digitizer = MIL.M_NULL; } return E1.OK; } catch (Exception e) { LOG.FuncE(e.Message); } return E1.GENERIC; } private void StopBufLive() { if (!IsActivated) return; try { lock (LockThis) { StopLive(); MIL.MdigHalt(_Digitizer); } } 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); } /// /// 카메라 Gain 읽기 /// /// Gain 값 public override int GetGain() { if (!IsConnected()) { return E2.NOT_READY; } MIL_INT gain = 0; MIL.MdigInquire(_Digitizer, MIL.M_GAIN, ref gain); return (int)gain; } /// /// 카메라 Gain 쓰기 /// /// Gain 값 /// 성공 유무 public override int SetGain(int gain) { if (!IsConnected()) { return E2.NOT_READY; } MIL.MdigControl(_Digitizer, MIL.M_GAIN, gain); return E1.OK; } /// /// 카메라 노출시간 읽기 /// /// 노출시간 public override int GetExposure() { if (!IsConnected()) { return E2.NOT_READY; } double exposureTime = 0; MIL.MdigInquire(_Digitizer, MIL.M_EXPOSURE_TIME, ref exposureTime); return (int)(exposureTime / 1000); } /// /// 카메라 최소 노출시간 읽기 /// /// 최소 노출시간 public override int GetMinExposure() { if (!IsConnected()) { return E2.NOT_READY; } double exposureTime = 0; MIL.MdigInquire(_Digitizer, MIL.M_EXPOSURE_TIME + MIL.M_MIN_VALUE, ref exposureTime); return (int)(exposureTime / 1000); } /// /// 카메라 최대 노출시간 읽기 /// /// 최대 노출시간 public override int GetMaxExposure() { if (!IsConnected()) { return E2.NOT_READY; } double exposureTime = 0; MIL.MdigInquire(_Digitizer, MIL.M_EXPOSURE_TIME + MIL.M_MAX_VALUE, ref exposureTime); return (int)(exposureTime / 1000); } #region For Custumizing private int _InitialExposureTime_Ms = 0; // 최초 노출시간 (ms) /// /// 카메라로부터 최초 노출시간 가져오기 /// 현재 노출시간은 아니고, 프로그램 시작시 DCF파일로부터 읽은 값임. /// /// 최초 노출시간(ms) public override int GetInitialExposure() { if (!IsActivated) { return E2.NOT_READY; } return _InitialExposureTime_Ms; } /// /// 카메라로부터 최초 노출시간 가져오기 (현재 노출시간은 아님) /// /// 성공 유무 private int SetInitialExposure() { if (!IsActivated) { return E2.NOT_READY; } _InitialExposureTime_Ms = GetExposure(); return E1.OK; } #endregion /// /// 카메라 노출시간 쓰기. GigE 카메라가 아닌 경우 HW에서 설정해주어야 함. (MIL에서 지원X) /// /// 노출시간[ms] /// 성공 유무 public override int SetExposure(int exposureTime_ms) { if (!IsConnected()) { return E2.NOT_READY; } var exposureTime_us = MathUtil.ConvertUnitMilliToMicro(exposureTime_ms); //MIL.MdigControlFeature(_Digitizer, MIL.M_FEATURE_VALUE_AS_STRING, "ExposureTime", MIL.M_DEFAULT, exposureTime_us.ToString()); //MIL.MdigControl(_Digitizer, MIL.M_EXPOSURE_TIME, exposureTime_us); // GigE에만 해당, 특정 HW에서는 동작하지 않았음. MIL.MdigControl(_Digitizer, MIL.M_EXPOSURE_TIME, exposureTime_us * 1000); // GigE에만 해당, 특정 HW에서는 1000배의 숫자가 들어가야만 함. 특정 HW에서는 동작하지 않았음. 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; } /// /// 이미지 사이즈 가져오기 /// /// 이미지 사이즈 public override ImageSize GetImageSize() { return MilEnvironment.Instance.GetImageSize(CamConfig); } #region Grab /// /// 버퍼로부터 이미지를 꺼내온다. /// /// 디스플레이 /// 이미지 /// 성공 유무 public override int GetGrabImageFromBuffer(ICameraDisplay cameraDisplay, ref object image) { SetDisplay(cameraDisplay as IMilCameraDisplay); MilEnvironment.ClearImage(image); MilEnvironment.ClearImage(_OriginalImage); return GetGrabImage(ref image, isExtTrig : true); } /// /// 동기식 그랩 /// /// 디스플레이 /// 그랩 이미지 /// 성공 유무 public override int GrabSync(ICameraDisplay cameraDisplay, ref object image) { SetDisplay(cameraDisplay as IMilCameraDisplay); MilEnvironment.ClearImage(image); 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 Failed!"); 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; } private int GrabSync_SwTrig(ref object image) { try { //TactTimeChecker.Start(TactTimeType.Grab, "GrabSyncWithFiltering"); lock (_SwTrigLock) { MIL.MdigGrab(_Digitizer, _OriginalImage); MIL.MdigGrabWait(_Digitizer, MIL.M_GRAB_END); FlipAndRotateImage(); CropImage(); SetDisplayImage(); image = IsLiveOn() ? _CroppedLiveImage : _CroppedImage; } //TactTimeChecker.Stop(TactTimeType.Grab, "GrabSyncWithFiltering"); return E1.OK; } catch (Exception e) { LOG.FuncE(e.Message); } //TactTimeChecker.Stop(TactTimeType.Grab, "GrabSyncWithFiltering"); return E1.GENERIC; } 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 (_CroppedLiveImage == MIL.M_NULL) { MessageBoxUtil.ShowMessageBoxError(string.Format(StrTokens.MSG_E_SomethingNotAllocated, "_CroppedLiveImage")); 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 { object tmpImage = null; // 라이브때 리턴 이미지는 필요X while (IsLiveOn()) { GrabSync(_MilCameraDisplay, ref tmpImage); 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; } private MIL_INT HookHandler(MIL_INT HookType, MIL_ID HookId, IntPtr HookDataPtr) { try { PrintHookLog(HookId); if (!CamConfig.ChildImage.Enabled) { //MIL_ID MilBufferId = MIL.M_NULL; MIL.MdigGetHookInfo(HookId, MIL.M_MODIFIED_BUFFER + MIL.M_BUFFER_ID, ref _OriginalImage); //MIL.MbufCopy(MilBufferId, _OriginalImage); // 중복 Copy 제거 SetHookCompleted(true); if (CamConfig.TriggerMode.IsExternal) { SetHookCompletedByExtTrig(true); } SetFlyingImage(); #region 미사용 코드 (참고용) //if (HookDataPtr == null) { // LOG.FuncE("HookDataPtr is null."); // return E1.GENERIC; //} //if (!IntPtr.Zero.Equals(HookDataPtr)) { // GCHandle userDataHandle = GCHandle.FromIntPtr(HookDataPtr); // HookDataStruct userData = userDataHandle.Target as HookDataStruct; // userData.HookCnt++; // MIL.MdigGetHookInfo(HookId, MIL.M_MODIFIED_BUFFER + MIL.M_BUFFER_ID, ref MilBufferId); // MIL.MbufCopy(MilBufferId, _OriginalImage); // //MIL.MbufCopy(MilBufferId, userData.HookImage); // SetHookCompleted(true); // SetGrabCompletedByExtTrig(); //} //else { // LOG.FuncE("IntPtr of HookDataPtr is Zero."); // return E1.GENERIC; //} #endregion } } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private void PrintHookLog(MIL_ID hookId) { if (!IsLiveOn()) { MIL_ID hookBufferIndex = MIL.M_NULL; MIL.MdigGetHookInfo(hookId, MIL.M_MODIFIED_BUFFER + MIL.M_BUFFER_INDEX, ref hookBufferIndex); LOG.D($"Hook Buffer Index : {hookBufferIndex}"); } } private void PrintFrameLog() { try { //MIL_INT frameCnt = 0; //MIL.MdigInquire(_Digitizer, MIL.M_PROCESS_FRAME_COUNT, ref frameCnt); double frameRate = 0; MIL.MdigInquire(_Digitizer, MIL.M_PROCESS_FRAME_RATE, ref frameRate); LOG.FuncI(string.Format("Frame Rate : {0:0.0} [f/s]", frameRate)); } catch (Exception e) { LOG.FuncE(e.Message); } } #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 { MIL.MdigControl(_Digitizer, MIL.M_GRAB_ABORT, MIL.M_DEFAULT); _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이 맞는 것 같으나, 아래 코드로 강제 트리거 여러 번 발생시 HookHandler에 무조건 1번만 들어오는 이슈가 있었음.. // *강제 트리거 코드 : MIL.MdigControl(_Digitizer, MIL.M_GRAB_TRIGGER_SOFTWARE, MIL.M_ACTIVATE) //MIL.MdigControlFeature(_Digitizer, MIL.M_FEATURE_VALUE_AS_STRING, "AcquisitionMode", MIL.M_DEFAULT, "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, Array.ConvertAll(_ProcImages, item => (MIL_ID)item), (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 = null) { 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 { SetDisplay(cameraDisplay); foreach (ChildBufRowCol childBufRowCol in CamConfig.ChildImage.ChildBufRowCols) { if (!childBufRowCol.Enabled) { continue; } SetExposure((childBufRowCol.PosIdx + 1) * 5); Thread.Sleep(50); object childImage = null; GrabSync(_MilCameraDisplay, ref childImage); if (_ProcImages.Length > childBufRowCol.PosIdx) { MilEnvironment.CopyImage((MIL_ID)childImage, (MIL_ID)_ProcImages[childBufRowCol.PosIdx]); } else { LOG.FuncE("[ERROR] _ProcImages.Length <= childBufRowCol.PosIdx"); } } image = GetParentImage(); 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; } } }