/*
* 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 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 _CalibratedImage = 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 IDictionary _MilCalibrationContexts = new Dictionary();
private eCamId _CamId;
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;
_CamId = CamRecipe.GetCamIdFromDevId(DeviceId);
_MilImgFlipAndRotationTool = new MilImgFlipAndRotationTool(_CamId, CamConfig);
var res = Init();
if (res == E1.OK) {
CheckImageBufferRotationChanged();
SetMilCalibrationContexts();
}
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 {
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(AllocOriginalImage(ref _CalibratedImage));
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;
}
#endregion
private void SetMilCalibrationContexts() {
bool isRecipeChanged = false;
foreach (FovInfo fovInfo in VisionRecipeManager.GetFovInfos(_CamId)) {
_MilCalibrationContexts[fovInfo] = MIL.M_NULL;
var fovRecipe = VisionRecipeManager.GetFovRecipe(fovInfo);
if (fovRecipe.CamCalibrationRecipe.Enabled && fovRecipe.CamCalibrationRecipe.Used) {
var filename = Path.Combine(ApplicationExt.DataPath, "_config_vision", "CalData", $@"/{fovInfo}.mca");
if (File.Exists(filename)) {
MIL_ID calId = MIL.M_NULL;
var systemId = MilEnvironment.Instance.GetSystem(CamConfig.CamConnConfig.BoardType, CamConfig.CamConnConfig.BoardSlotPos);
MIL.McalRestore(filename, systemId, MIL.M_DEFAULT, ref calId);
_MilCalibrationContexts[fovInfo] = calId;
if (calId != MIL.M_NULL) {
if (!fovRecipe.CamCalibrationRecipe.Loaded) { // 기존 값과 다른 경우에만 갱신
fovRecipe.CamCalibrationRecipe.Loaded = true;
isRecipeChanged = true;
}
}
else {
if (fovRecipe.CamCalibrationRecipe.Loaded) { // 기존 값과 다른 경우에만 갱신
fovRecipe.CamCalibrationRecipe.Loaded = false;
isRecipeChanged = true;
}
}
}
}
}
if (isRecipeChanged) {
RecipeFileManager.SaveVisionRecipe();
}
}
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 {
FreeMilCalibrationContexts();
MilEnvironment.FreeImage(_OriginalImage);
MilEnvironment.FreeImage(_CalibratedImage);
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);
}
}
private void FreeMilCalibrationContexts() {
bool isRecipeChanged = false;
foreach (FovInfo fovInfo in VisionRecipeManager.GetFovInfos(_CamId)) {
var fovRecipe = VisionRecipeManager.GetFovRecipe(fovInfo);
if (MilEnvironment.IsValidContextId(_MilCalibrationContexts[fovInfo])) {
MIL.McalFree(_MilCalibrationContexts[fovInfo]);
}
if (fovRecipe.CamCalibrationRecipe.Loaded) {
fovRecipe.CamCalibrationRecipe.Loaded = false;
isRecipeChanged = true;
}
}
_MilCalibrationContexts.Clear();
if (isRecipeChanged) {
RecipeFileManager.SaveVisionRecipe();
}
}
#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 double 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 double 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 double 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);
}
///
/// 카메라 노출시간 쓰기. GigE 카메라가 아닌 경우 HW에서 설정해주어야 함. (MIL에서 지원X)
///
/// 노출시간[ms]
/// 성공 유무
public override int SetExposure(double 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에서는 동작하지 않았음.
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);
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 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);
ApplyImagePreProcessing();
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");
ApplyImagePreProcessing();
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()) {
ApplyImagePreProcessing();
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 {
ApplyImagePreProcessing();
_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 ApplyImagePreProcessing() {
TransformCalImage();
FlipAndRotateImage();
CropImage();
}
private void TransformCalImage() {
if (_OriginalImage == null) {
LOG.FuncE("_OriginalImage is null.");
return;
}
if (_CalibratedImage == null) {
LOG.FuncE("_CalibratedImage is null.");
return;
}
FovInfo fovInfo = _MilCameraDisplay.GetDisplayFovInfo();
var fovRecipe = VisionRecipeManager.GetFovRecipe(fovInfo);
// 캘 기능이 켜져있고, 캘 Context가 존재하면 이미지 왜곡 보정
if (fovRecipe.CamCalibrationRecipe.Enabled &&
MilEnvironment.IsValidContextId(_MilCalibrationContexts[fovInfo])) {
MIL.McalTransformImage(
_OriginalImage,
_CalibratedImage,
_MilCalibrationContexts[fovInfo],
MIL.M_BILINEAR,
MIL.M_FULL_CORRECTION,
MIL.M_WARP_IMAGE
);
}
// 그렇지 않으면, 원본 이미지 그대로 사용. 택타임 개선을 위해 Copy 명령은 따로 하지 않음.
else {
_CalibratedImage = _OriginalImage;
}
}
private void FlipAndRotateImage() {
if (_CalibratedImage == null) {
LOG.FuncE("_CalibratedImage is null.");
return;
}
if (IsLiveOn()) {
_MilImgFlipAndRotationTool.FlipAndRotation(_CalibratedImage, ref _FilteredLiveImage);
}
else {
_MilImgFlipAndRotationTool.FlipAndRotation(_CalibratedImage, 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;
}
}
}