using Aurora; using Aurora.Api; using Aurora.Api.DataType; using Hermes; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using VisionCommon; using VisionCommonUI; using VisionCommonUI.Resources; #if USE_COGNEX using Cognex.VisionPro; using CogCamera; using CogCommon; // Cognex인 경우 여기에 구현 // ... #else using Matrox.MatroxImagingLibrary; using MilCamera; using MilCommon; namespace WeldingCommon.Vision.Camera { /// /// 장비 카메라 디바이스 A (클래스 이름 변경 금지) /// 장비에 맞게 변경하여 사용한다. /// 아래 코드는 레이저 스캐너 용접기 전용 카메라로 사용된다. /// public class MachineCamera_A : 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 MIL_ID[] MilProcImages = null; // SW트리거 콜백용 private GCHandle _UserDataHandle; private HookDataStruct _HookData = new HookDataStruct(); private MIL_DIG_HOOK_FUNCTION_PTR _HookFuncPtr = null; #endregion #region For RCC Flying private GCHandle _FlyingUserDataHandle; private HookDataStruct _FlyingHookData = new HookDataStruct(); private MIL_DIG_HOOK_FUNCTION_PTR _FlyingHookFuncPtr = null; #endregion public MachineCamera_A(string deviceId) : base(deviceId) { var subject = string.Format("MachineCamera_A[{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; } private void CheckImageBufferRotationChanged() { if (OldRatioX != CamConfig.ImageSizeRatioX || OldRatioY != CamConfig.ImageSizeRatioY || OldRotationType != CamConfig.FlipAndRotation.ImageRotation) { OldRatioX = CamConfig.ImageSizeRatioX; OldRatioY = CamConfig.ImageSizeRatioY; OldRotationType = CamConfig.FlipAndRotation.ImageRotation; VisionEnv.AuroraEnv.EventPublisher.Publish( new EventMessage(DeviceId, VisionEventId.UPDATE_GRAB_IMAGE_BUFFER)); } } #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 != StartGrabMdigProcess()) { 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); MIL.MdigControl(_Digitizer, MIL.M_AUTO_EXPOSURE, MIL.M_DISABLE); return E1.OK; } } catch (Exception e) { LOG.FuncE(e.Message); } return E1.GENERIC; } private void InitDigitizerWithDcf() { try { 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) { 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; } 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); } private int SetExposureTimeFromConfig() { try { SetInitialExposure(); SetExposure(CamConfig.ExposureTime_Ms); } catch (Exception e) { LOG.E(e.Message); return E1.GENERIC; } return E1.OK; } #endregion /// /// 이미지 버퍼 복제 /// /// 복제 버퍼 public override object CloneImageBuffer() { return MilEnvironment.CloneImage(_CroppedImage); } private int StartGrabMdigProcess() { try { lock (LockThis) { // Grab과 Live는 SW Trigger만 사용한다. SetSwTrigger(); _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.MdigProcess(_Digitizer, MilProcImages, (MIL_INT)MilProcImages.Length, MIL.M_START, MIL.M_DEFAULT, _HookFuncPtr, GCHandle.ToIntPtr(_UserDataHandle)); } } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private int SetSwTrigger() { try { lock (LockThis) { if (CamConfig.CamConnConfig.BoardType == eBoardType.MIL_CXP_RAPIXO) { //FlyingVision MIL.MdigControl(_Digitizer, MIL.M_TIMER1 + MIL.M_TIMER_STATE, MIL.M_DISABLE); MIL.MdigControl(_Digitizer, MIL.M_TIMER1 + MIL.M_TIMER_TRIGGER_SOURCE, MIL.M_SOFTWARE); MIL.MdigControl(_Digitizer, MIL.M_TIMER1 + MIL.M_TIMER_STATE, MIL.M_ENABLE); } else if (CamConfig.CamConnConfig.BoardType == eBoardType.MIL_GIGE) { // GigE MIL.MdigControl(_Digitizer, MIL.M_GRAB_TRIGGER_STATE, MIL.M_ENABLE); MIL.MdigControl(_Digitizer, MIL.M_GRAB_TRIGGER_SOURCE, MIL.M_SOFTWARE); } else { LOG.E("Check Cam BoardType."); } } } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private int SetHwTrigger() { try { lock (LockThis) { // TODO : HW Trigger 설정 확인 필요 //MIL.MdigControlFeature(_Digitizer, MIL.M_FEATURE_VALUE_AS_STRING, "TriggerSource", MIL.M_DEFAULT, "Line0"); //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, "On"); MIL.MdigControl(_Digitizer, MIL.M_TIMER1 + MIL.M_TIMER_STATE, MIL.M_DISABLE); MIL.MdigControl(_Digitizer, MIL.M_GRAB_ABORT, MIL.M_DEFAULT); MIL.MdigControl(_Digitizer, MIL.M_TIMER1 + MIL.M_TIMER_STATE, MIL.M_EDGE_RISING); MIL.MdigControl(_Digitizer, MIL.M_TIMER1 + MIL.M_TIMER_TRIGGER_SOURCE, MIL.M_AUX_IO2); MIL.MdigControl(_Digitizer, MIL.M_TIMER1 + MIL.M_TIMER_STATE, MIL.M_ENABLE); //MIL.MdigControl(_Digitizer, MIL.M_TIMER_TRIGGER_SOURCE, MIL.M_AUX_IO2); //MIL.MdigControl(_Digitizer, MIL.M_GRAB_TRIGGER_STATE, MIL.M_ENABLE); //MIL.MdigControl(_Digitizer, MIL.M_TIMER_TRIGGER_SOURCE, MIL.M_AUX_IO2); } } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private int AllocProcImage(ref MIL_ID[] milProcImages) { try { if (milProcImages == null) { milProcImages = new MIL_ID[CamConfig.HwTrigConfig.ProcBufSize]; } return AllocGrayProcImage(ref milProcImages); } catch (Exception e) { LOG.FuncE(e.Message); } return E1.GENERIC; } private int AllocGrayProcImage(ref MIL_ID[] milProcImages) { for (int i = 0; i < CamConfig.HwTrigConfig.ProcBufSize; i++) { MIL.MbufAlloc2d( MilEnvironment.Instance.GetSystem(CamConfig.CamConnConfig.BoardType, CamConfig.CamConnConfig.BoardSlotPos), CamConfig.ImageSize.Width, CamConfig.ImageSize.Height, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref milProcImages[i]); MIL.MbufClear(milProcImages[i], 0); } return E1.OK; } private void FreeProcImage(ref MIL_ID[] milProcImages) { try { if (milProcImages == null) { return; } for (int i = 0; i < milProcImages.Length; i++) { MilEnvironment.FreeImage(milProcImages[i]); milProcImages[i] = MIL.M_NULL; } milProcImages = null; } catch (Exception e) { LOG.FuncE(e.Message); return; } } protected override int DoDeactivate() { if (!IsActivated) { LOG.I("Already Deactivated!"); return E1.OK; } return Dispose(); } #region Dispose private int Dispose() { try { StopBufLive(); StopGrabMdigProcess(); #region For RCC Flying StopFlyingMdigProcess(); SetFlyingStart(false); #endregion FreeImages(); if (_Digitizer != MIL.M_NULL) { MIL.MdigFree(_Digitizer); _Digitizer = MIL.M_NULL; } // MIL Sys와 App은 할당 후, 런타임 중에 해지하지 않고 쓰기로 함. //MilEnvironment.Instance.Dispose(); return E1.OK; } catch (Exception e) { LOG.FuncE(e.Message); } return E1.GENERIC; } private void StopBufLive() { if (!IsActivated) return; try { lock (LockThis) { LiveOff(); MIL.MdigHalt(_Digitizer); } } catch (Exception e) { LOG.E(e.Message); } } private void StopGrabMdigProcess() { try { lock (LockThis) { if (MilProcImages?.Length > 0) { MIL.MdigControl(_Digitizer, MIL.M_PROCESS_TIMEOUT, 5); //MIL.MdigProcess(_Digitizer, MilProcImages, (MIL_INT)MilProcImages.Length, MIL.M_STOP + MIL.M_WAIT, MIL.M_DEFAULT, _HookFuncPtr, GCHandle.ToIntPtr(_UserDataHandle)); MIL.MdigProcess(_Digitizer, MilProcImages, (MIL_INT)MilProcImages.Length, MIL.M_STOP, MIL.M_DEFAULT, _HookFuncPtr, GCHandle.ToIntPtr(_UserDataHandle)); } } } catch (Exception e) { LOG.FuncE(e.Message); } } private void FreeImages() { try { MilEnvironment.FreeImage(_OriginalImage); MilEnvironment.FreeImage(_FilteredImage); MilEnvironment.FreeImage(_FilteredLiveImage); MilEnvironment.FreeImage(_CroppedImage); MilEnvironment.FreeImage(_CroppedLiveImage); FreeProcImage(ref MilProcImages); 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); } public override int GetGain() => throw new NotImplementedException(); public override int SetGain(int gain) => throw new NotImplementedException(); public override double GetMinExposure() { if (!IsActivated) { return E2.NOT_READY; } double exposureTime = 0; MIL.MdigInquire(_Digitizer, MIL.M_EXPOSURE_TIME + MIL.M_MIN_VALUE, ref exposureTime); return (int)(exposureTime / 1_000); } public override double GetMaxExposure() { if (!IsActivated) { return E2.NOT_READY; } //CXP 카메라 사용 시 ExposurteTime 못받아와서 하드코딩해둠 //double exposureTime = 5000 * 1000; double exposureTime = 0; MIL.MdigInquire(_Digitizer, MIL.M_EXPOSURE_TIME + MIL.M_MAX_VALUE, ref exposureTime); return (int)(exposureTime / 1_000); } #region For Custumizing private double _InitialExposureTime_Ms = 0; // 최초 노출시간 (ms) /// /// 카메라로부터 최초 노출시간 가져오기 /// 현재 노출시간은 아니고, 프로그램 시작시 DCF파일로부터 읽은 값임. /// /// 최초 노출시간(ms) public double 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 /// /// 카메라로부터 현재 노출시간 가져오기 /// /// 노출시간(ms) public override double GetExposure() { if (!IsActivated) { return E2.NOT_READY; } double exposureTime = 0; MIL.MdigInquire(_Digitizer, MIL.M_EXPOSURE_TIME, ref exposureTime); return (int)(exposureTime / 1_000); } /// /// 카메라 노출시간 쓰기. GigE 카메라가 아닌 경우 HW에서 설정해주어야 함. (MIL에서 지원X) /// /// 노출시간[ms] /// 성공 유무 public override int SetExposure(double exposureTime_ms) { if (!IsConnected()) { return E2.NOT_READY; } // [원래 코드] //MIL.MdigControl(_Digitizer, MIL.M_EXPOSURE_TIME, (double)(exposureTime * 1_000)); var exposureTime_us = MathUtil.ConvertUnitMilliToMicro(exposureTime_ms); if (CamConfig.CamConnConfig.BoardType == eBoardType.MIL_GIGE) { MIL.MdigControlFeature(_Digitizer, MIL.M_FEATURE_VALUE_AS_STRING, "ExposureTimeAbs", MIL.M_DEFAULT, exposureTime_us.ToString()); } else { 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에서는 동작하지 않았음. 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); return GrabSync_SwTrig(ref image); } private int GrabSync_SwTrig(ref object image) { try { lock (_SwTrigLock) { SetHookCompleted(false); SwTriggerOn(); return GetGrabImage(ref image, isExtTrig: false); } } catch (Exception e) { LOG.FuncE(e.Message); } 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) { // TODO : Live 실행 시 연속 Log 발생으로 주석 처리 //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(); Task.Run(() => { StartLive_SwTrig(); }); } } } public override void StopLive() { LiveOff(); } 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 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); //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); } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } // 플라잉 이미지 밀림 방지를 위해 Handler를 따로 씀. private MIL_INT FlyingHookHandler(MIL_INT HookType, MIL_ID HookId, IntPtr HookDataPtr) { try { //MIL_ID MilBufferId = MIL.M_NULL; //MIL.MdigGetHookInfo(HookId, MIL.M_MODIFIED_BUFFER + MIL.M_BUFFER_ID, ref MilBufferId); MIL_ID MilBufferIndex = MIL.M_NULL; MIL.MdigGetHookInfo(HookId, MIL.M_MODIFIED_BUFFER + MIL.M_BUFFER_INDEX, ref MilBufferIndex); LOG.D($"MilBufferIndex : {MilBufferIndex}"); } 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 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); } } } #region For RCC Flying private List AllocProcImages() { var results = new List(); AllocProcImage(ref MilProcImages); // SW트리거 콜백용 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)); } } return results; } private void AllocFlyingImage(ref MIL_ID flyingImage, int row, int col) { try { if (!IsFlyingEnabled()) { return; } if (flyingImage != MIL.M_NULL) { MilEnvironment.FreeImage(flyingImage); } int flyingImageWidth = CamConfig.ImageSize.Width * col; int flyingImageHeight = CamConfig.ImageSize.Height * row; MIL.MbufAlloc2d( MilEnvironment.Instance.GetSystem(CamConfig.CamConnConfig.BoardType, CamConfig.CamConnConfig.BoardSlotPos), flyingImageWidth, flyingImageHeight, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref flyingImage); if (flyingImage == MIL.M_NULL) { LOG.FuncE($"_FlyingImage is null"); return; } MIL.MbufClear(flyingImage, 0); } catch (Exception e) { LOG.FuncE(e.Message); return; } } private void FreeFlyingImage(ref MIL_ID flyingImage) { try { if (!IsFlyingEnabled()) { return; } MilEnvironment.FreeImage(flyingImage); flyingImage = MIL.M_NULL; } catch (Exception e) { LOG.FuncE(e.Message); return; } } private int AllocFlyingChildImages( MIL_ID flyingImage, ref MIL_ID[] flyingChildImages) { try { if (!IsFlyingEnabled()) { return E1.GENERIC; } if (IsFlyingStart()) { return E1.GENERIC; } if (flyingImage == MIL.M_NULL) { LOG.FuncE($"flyingImage is null"); return E1.GENERIC; } if (flyingChildImages == null) { flyingChildImages = new MIL_ID[CamConfig.ChildImage.ChildBufRowCols.Count]; for (int i = 0; i < flyingChildImages.Length; i++) { flyingChildImages[i] = MIL.M_NULL; } } int width = CamConfig.ImageSize.Width; int height = CamConfig.ImageSize.Height; for (int i = 0; i < CamConfig.ChildImage.ChildBufRowCols.Count; i++) { int col = CamConfig.ChildImage.ChildBufRowCols[i].ColIdx; int row = CamConfig.ChildImage.ChildBufRowCols[i].RowIdx; int offsetX = col * width; int offsetY = row * height; //LOG.D($"({row}, {col}) : {offsetX}, {offsetY}"); if (flyingChildImages[i] != MIL.M_NULL) { MilEnvironment.FreeImage(flyingChildImages[i]); } MIL.MbufChild2d(flyingImage, offsetX, offsetY, width, height, ref flyingChildImages[i]); } } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private void FreeFlyingChildImages(ref MIL_ID[] flyingChildImages) { try { if (!IsFlyingEnabled()) { return; } if (flyingChildImages == null) { return; } for (int i = 0; i < flyingChildImages.Length; i++) { MilEnvironment.FreeImage(flyingChildImages[i]); flyingChildImages[i] = MIL.M_NULL; } flyingChildImages = null; } catch (Exception e) { LOG.FuncE(e.Message); return; } } /// /// 플라잉 종료 /// /// 추가 파라미터 /// 성공 유무 public override int StopFlying(object param = null) { if (!IsActivated || !IsFlyingEnabled()) { return E1.GENERIC; } try { lock (LockThis) { if (!IsFlyingStart()) { return E1.OK; } StopFlyingMdigProcess(); StartGrabMdigProcess(); SetFlyingStart(false); var sw = Stopwatch.StartNew(); LOG.I($"T/T Flip Start"); for (int i = 0; i < _ProcImages.Length; i++) { var childImg = (MIL_ID)_ProcImages[i]; _MilImgFlipAndRotationTool.FlipAndRotation(childImg, ref childImg); // 회전 불필요 //var cropRoi = GetCropRoi(); // Crop 불필요 //childImg = MilEnvironment.CropImage(childImg, cropRoi); } sw.Stop(); LOG.I($"T/T Flip : {sw.ElapsedMilliseconds} ms"); } } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private int StopFlyingMdigProcess() { try { lock (LockThis) { if (!IsFlyingEnabled()) { return E1.GENERIC; } MIL.MdigControl(_Digitizer, MIL.M_PROCESS_TIMEOUT, 5); // [원래 코드] //MIL.MdigProcess(_Digitizer, _FlyingChildImages, (MIL_INT)_FlyingChildImages.Length, MIL.M_STOP + MIL.M_WAIT, MIL.M_SYNCHRONOUS, _FlyingHookFuncPtr, GCHandle.ToIntPtr(_FlyingUserDataHandle)); if (_ProcImages?.Length > 0) { //MIL.MdigProcess(_Digitizer, Array.ConvertAll(_ProcImages, item => (MIL_ID)item), (MIL_INT)_ProcImages.Length, MIL.M_STOP + MIL.M_WAIT, MIL.M_SYNCHRONOUS, _FlyingHookFuncPtr, GCHandle.ToIntPtr(_FlyingUserDataHandle)); MIL.MdigProcess(_Digitizer, Array.ConvertAll(_ProcImages, item => (MIL_ID)item), (MIL_INT)_ProcImages.Length, MIL.M_STOP, MIL.M_SYNCHRONOUS, _FlyingHookFuncPtr, GCHandle.ToIntPtr(_FlyingUserDataHandle)); } } } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } /// /// 플라잉 시작 /// /// FOV 정보 /// 추가 파라미터 /// 성공 유무 public override int StartFlying(FovInfo fovInfo, object param = null) { if (!IsActivated || !IsFlyingEnabled()) { return E1.GENERIC; } try { lock (LockThis) { var sw = Stopwatch.StartNew(); MilEnvironment.ClearImage(_ParentImage); sw.Stop(); LOG.I($"ClearImage {sw.ElapsedMilliseconds} msec"); sw.Restart(); StopLive(); sw.Stop(); LOG.I($"StopLive Process {sw.ElapsedMilliseconds} msec"); sw.Restart(); StopGrabMdigProcess(); sw.Stop(); LOG.I($"StopGrabMdigProcess Process {sw.ElapsedMilliseconds} msec"); sw.Restart(); StopFlyingMdigProcess(); sw.Stop(); LOG.I($"StopFlyingMdigProcess Process {sw.ElapsedMilliseconds} msec"); if (CamConfig.IsAlwaysSwTrigger()) { SetSwTrigger(); } else { SetHwTrigger(); } sw.Restart(); StartFlyingMdigProcess(); sw.Stop(); LOG.I($"StartFlyingMdigProcess {sw.ElapsedMilliseconds} msec"); SetFlyingStart(true); } } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private int StartFlyingMdigProcess() { try { lock (LockThis) { _FlyingHookData.HookDigitizer = _Digitizer; _FlyingHookData.HookImage = _OriginalImage; _FlyingHookData.HookCnt = 0; if (!_FlyingUserDataHandle.IsAllocated) { _FlyingUserDataHandle = GCHandle.Alloc(_FlyingHookData); } if (_FlyingHookFuncPtr == null) { _FlyingHookFuncPtr = new MIL_DIG_HOOK_FUNCTION_PTR(FlyingHookHandler); } if (_ProcImages != null) { MIL.MdigProcess( _Digitizer, Array.ConvertAll(_ProcImages, item => (MIL_ID)item), (MIL_INT)_ProcImages.Length, MIL.M_START, MIL.M_DEFAULT, _FlyingHookFuncPtr, GCHandle.ToIntPtr(_FlyingUserDataHandle)); } // 원래 코드 //MIL.MdigProcess(_Digitizer, _FlyingChildImages, (MIL_INT)_FlyingChildImages.Length, MIL.M_START, MIL.M_DEFAULT, _FlyingHookFuncPtr, GCHandle.ToIntPtr(_FlyingUserDataHandle)); } } catch (Exception e) { LOG.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } private bool IsFlyingEnabled() { return CamConfig.TriggerMode.IsFlying; } private bool IsFlyingStart() { lock (LockThis) { return IsActivated && IsFlyingEnabled() && _IsFlyingRunning; } } private void SetFlyingStart(bool isFlyingStart) { lock (LockThis) { if (IsFlyingEnabled()) { _IsFlyingRunning = isFlyingStart; } } } /// /// 호환되는 회색조 이미지 버퍼 가져오기 /// /// 이미지 사이즈 /// MIL 시스템 Id /// 이미지 버퍼 MIL ID public static MIL_ID GetCompatibleGrayImageBuffer(ImageSize imageSize, MIL_ID systemId) { if (!MilEnvironment.Instance.HasValidLicense()) { return MIL.M_NULL; } MIL_ID BufIdPtr = MIL.M_NULL; MIL.MbufAlloc2d(systemId, imageSize.Width, imageSize.Height, 8, 60L, ref BufIdPtr); MIL.MbufClear(BufIdPtr, 0.0); return BufIdPtr; } private void SwTriggerOn() { if (!IsActivated) { return; } try { if (CamConfig.CamConnConfig.BoardType == eBoardType.MIL_CXP_RAPIXO) { //FlyingVision MIL.MdigControl(_Digitizer, MIL.M_TIMER1 + MIL.M_TIMER_TRIGGER_SOFTWARE, 1); } else if (CamConfig.CamConnConfig.BoardType == eBoardType.MIL_GIGE) { // GigE MIL.MdigControl(_Digitizer, MIL.M_GRAB_TRIGGER_SOFTWARE, MIL.M_ACTIVATE); } else { LOG.FuncE("Check Cam BoardType"); } } catch (Exception e) { LOG.FuncE(e.Message); } } #endregion private class HookDataStruct { public MIL_ID HookDigitizer; public MIL_ID HookImage; public int HookCnt; } } } #endif