/********************************************************************************/
/*
* File name: milgige.cpp
* Location:  ...\Matrox Imaging\MILxxx\Examples\BoardSpecific\gigevision\C++\milgige
 *             
*
* Synopsis:  This program demonstrates new MIL features for managing GigE Vision(tm)
*            devices using the Matrox Driver for GigE Vision(tm).
*
* Note:      The camera features inquired by this example are taken from the GenICam(tm)
*            Standard Feature Naming Convention (SFNC). It can be downloaded from the
*            European Machine Vision Association's web site: http://www.emva.org/
*            Only a subset of defined SNFC features are used by this example for
*            illustrative purposes.
*
*            Some of the features accessed by this example might not be implemented
*            by your camera, therefore MIL error prints are temporarily disabled while
*            the process of feature enumeration is done. SFNC features not supported
*            by your camera will be marked with N/A. Because of this the code for
*            enumerating features is made more complex to account for varying
*            implementations by camera manufacturers.
*/

/* Headers. */
#include <mil.h>
#if M_MIL_USE_WINDOWS
#include <windows.h>
#endif

/* Set the PRINT_LOOKUP_TABLE define to 1 to print your camera's LUT       */
/* values (if present).                                                    */
#define PRINT_LOOKUP_TABLE       0

/* List of function prototypes used to enumerate and print camera features. */
void CameraPrintDeviceControls(MIL_ID MilDigitizer);
void CameraPrintTransportLayerControls(MIL_ID MilDigitizer);
void CameraPrintImageFormatControls(MIL_ID MilDigitizer);
void CameraPrintAcquisitionControls(MIL_ID MilDigitizer);
void CameraPrintIOControls(MIL_ID MilDigitizer);
void CameraPrintCounterAndTimerControls(MIL_ID MilDigitizer);
void CameraPrintEventControls(MIL_ID MilDigitizer);
void CameraPrintLUT(MIL_ID MilDigitizer);

/* List of function prototypes used to perform triggered acquisition. */
typedef enum {eSingleFrame=1, eMultiFrame, eContinuous} eTriggerType;
void SetTriggerControls(MIL_ID MilDigitizer, eTriggerType& Type, MIL_INT& NbFrames,
   MIL_TEXT_PTR oTriggerSelector, MIL_INT StringSize, bool &SoftwareTriggerSelected);
void SelectTriggerSource(MIL_ID MilDigitizer, bool& SoftwareTriggerSelected);
void ResetTriggerControls(MIL_ID MilDigitizer);
void DoTriggeredAcquisition(MIL_ID MilSystem, MIL_ID MilDigitizer, MIL_ID MilImageDisp);

/* Global variables used to store camera capabilities. */
bool ContinuousAMSupport = false;
bool SingleFrameAMSupport = false;
bool MultiFrameAMSupport = false;
MIL_INT MultipleAcquisitionModeSupport = 0;
bool CanTriggerAcquisitionStart = false;
bool CanTriggerFrameStart = false;

/* Main function. */
int MosMain(void)
   {
   MIL_ID MilApplication,  /* Application identifier.  */
          MilSystem,       /* System identifier.       */
          MilDisplay,      /* Display identifier.      */
          MilDigitizer,    /* Digitizer identifier.    */
          MilImage;        /* Image buffer identifier. */
   MIL_INT BoardType;
   MIL_INT Selection;

   /* Allocate defaults. */
   MappAllocDefault(M_DEFAULT, &MilApplication, &MilSystem, &MilDisplay, M_NULL, M_NULL);

   /* Allocate the digitizer controlling the camera. */
   MdigAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_DEFAULT, &MilDigitizer);
      
   /* In cases where the preferred method for device allocation requires allocating with     */
   /* a user-defined name the following code can be used. "MyCameraName" must be replaced    */
   /* with the actual camera name written in the camera.                                     */

   /* MdigAlloc(MilSystem, M_GC_CAMERA_ID(MIL_TEXT("MyCameraName")), MIL_TEXT("M_DEFAULT"),  */
   /*    M_GC_DEVICE_USER_NAME, &MilDigitizer);                                              */


   /* Allocate grab and display buffer. */
   MbufAllocColor(MilSystem,
                  MdigInquire(MilDigitizer, M_SIZE_BAND, M_NULL),
                  MdigInquire(MilDigitizer, M_SIZE_X, M_NULL),
                  MdigInquire(MilDigitizer, M_SIZE_Y, M_NULL),
                  MdigInquire(MilDigitizer, M_TYPE, M_NULL),
                  M_IMAGE+M_DISP+M_GRAB,
                  &MilImage);

   /* Get information on the system we are using and print a welcome message to the console. */
   MsysInquire(MilSystem, M_BOARD_TYPE, &BoardType);

   if(((BoardType & M_BOARD_TYPE_MASK) != M_GIGE_VISION))
      {
      /* Print error message. */
      MosPrintf(MIL_TEXT("This example program can only be used with the Matrox Driver for ")
                MIL_TEXT("GigE Vision.\n"));
      MosPrintf(MIL_TEXT("Please ensure that the default system type is set accordingly in ")
                MIL_TEXT("MIL Config.\n"));
      MosPrintf(MIL_TEXT("-------------------------------------------------------------\n\n"));
      MosPrintf(MIL_TEXT("Press <enter> to quit.\n"));
      MosGetch();
      MappFreeDefault(MilApplication, MilSystem, MilDisplay, M_NULL, M_NULL);
      return 1;
      }

   /* Print a message. */
   MosPrintf(MIL_TEXT("This example showcases GigE Vision specific features.\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to start.\n\n"));
   MosGetch();
#if M_MIL_USE_WINDOWS
   system("cls");
#endif
   MosPrintf(MIL_TEXT("------------------------------------------------------------\n\n"));
   MosPrintf(MIL_TEXT("                  Camera features summary.                  \n"));

   /* Disable error printing in case camera is not SFNC compliant with regard
      to some of the features it supports. */
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_DISABLE);

   /* Enumerate and print camera features. */
   CameraPrintDeviceControls(MilDigitizer);
   CameraPrintTransportLayerControls(MilDigitizer);
   CameraPrintImageFormatControls(MilDigitizer);
   CameraPrintAcquisitionControls(MilDigitizer);
   CameraPrintEventControls(MilDigitizer);
   CameraPrintIOControls(MilDigitizer);
   CameraPrintCounterAndTimerControls(MilDigitizer);
#if PRINT_LOOKUP_TABLE
   CameraPrintLUT(MilDigitizer);
#endif

   /* Re-enable error printing. */
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_ENABLE);

   /* Print a message. */
   MosPrintf(MIL_TEXT("\nPress <Enter> to continue.\n"));
   MosGetch();

   /* Clear the console text. */
#if M_MIL_USE_WINDOWS
   system("cls");

   /* Pop-up the camera feature browser GUI. */
   MdigControl(MilDigitizer, M_GC_FEATURE_BROWSER, M_OPEN+M_ASYNCHRONOUS);
   
   /* Print a message. */
   MosPrintf(MIL_TEXT("\nDisplaying the camera's feature browser.\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n"));
   MosGetch();
#endif
   /* Start a continuous acquisition. */
   MdispSelect(MilDisplay, MilImage);
   MdigGrabContinuous(MilDigitizer, MilImage);

   /* Print a message. */
   MosPrintf(MIL_TEXT("\nContinuous image grab in progress.\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to stop.\n"));
   MosGetch();
   
   /* Stop the continuous acquisition. */
   MdigHalt(MilDigitizer);

   /* If we can trigger AcquisitionStart or FrameStart events, ask if we should do
      triggered grabs. */
   if(CanTriggerAcquisitionStart || CanTriggerFrameStart)
      {
      //system("cls");
      MosPrintf(MIL_TEXT("\nYour camera supports acquisition triggers.\n"));
      MosPrintf(MIL_TEXT("Do you want to test triggered acquisition (Y/N)? "));
      Selection = MosGetch();
      MosPrintf(MIL_TEXT("\n"));
      if((Selection == 'Y') || (Selection == 'y'))
         DoTriggeredAcquisition(MilSystem, MilDigitizer, MilImage);
      }
   else
      {
      MosPrintf(MIL_TEXT("\nPress <Enter> to quit.\n"));
      MosGetch();
      }


   MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, MilImage);

   return 0;
   }

/* Prints SFNC features */
void CameraPrintDeviceControls(MIL_ID MilDigitizer)
   {
   MIL_INT Len = 0;
   MIL_TEXT_PTR CameraVendor = M_NULL;
   MIL_TEXT_PTR CameraModel = M_NULL;
   MIL_TEXT_PTR CameraSerialNumber = M_NULL;
   MIL_TEXT_PTR CameraUserName = M_NULL;
   MIL_TEXT_PTR CameraScanType = M_NULL;
   MIL_TEXT_PTR InterfaceName = M_NULL;
   MIL_TEXT_PTR IpAddress = M_NULL;

   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE+M_STRING_SIZE, MIL_TEXT("DeviceVendorName"), M_DEFAULT, &Len);
   if(Len)
      {
      CameraVendor = new MIL_TEXT_CHAR[Len];
      MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("DeviceVendorName"), M_TYPE_STRING, CameraVendor);
      }

   Len = 0;
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE+M_STRING_SIZE, MIL_TEXT("DeviceModelName"), M_DEFAULT, &Len);
   if(Len)
      {
      CameraModel = new MIL_TEXT_CHAR[Len];
      MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("DeviceModelName"), M_TYPE_STRING, CameraModel);
      }

   Len = 0;
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE+M_STRING_SIZE, MIL_TEXT("DeviceID"), M_DEFAULT, &Len);
   if(Len)
      {
      CameraSerialNumber = new MIL_TEXT_CHAR[Len];
      MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("DeviceID"), M_TYPE_STRING, CameraSerialNumber);
      }

   Len = 0;
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE+M_STRING_SIZE, MIL_TEXT("DeviceUserID"), M_DEFAULT, &Len);
   if(Len)
      {
      CameraUserName = new MIL_TEXT_CHAR[Len];
      MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("DeviceUserID"), M_TYPE_STRING, CameraUserName);
      }

   Len = 0;
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING+M_STRING_SIZE, MIL_TEXT("DeviceScanType"), M_DEFAULT, &Len);
   if(Len)
      {
      CameraScanType = new MIL_TEXT_CHAR[Len];
      MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("DeviceScanType"), M_TYPE_ENUMERATION, CameraScanType);
      }


   MosPrintf(MIL_TEXT("\n------------------ Camera Device Controls ------------------\n\n"));
   MosPrintf(MIL_TEXT("%30s %s %s\n"), MIL_TEXT("Camera:"), CameraVendor?CameraVendor:MIL_TEXT("N/A"), CameraModel?CameraModel:MIL_TEXT("N/A"));
   MosPrintf(MIL_TEXT("%30s %s\n"), MIL_TEXT("Serial number:"), CameraSerialNumber?CameraSerialNumber:MIL_TEXT("N/A"));
   MosPrintf(MIL_TEXT("%30s %s\n"), MIL_TEXT("User-defined name:"), CameraUserName?CameraUserName:MIL_TEXT("N/A"));
   MosPrintf(MIL_TEXT("%30s %s\n"), MIL_TEXT("Device scan type:"), CameraScanType?CameraScanType:MIL_TEXT("N/A"));

   Len = 0;
   MdigInquire(MilDigitizer, M_GC_NIC_IP_ADDRESS_STRING_SIZE, &Len);
   if(Len)
      {
      IpAddress = new MIL_TEXT_CHAR[Len];
      MdigInquire(MilDigitizer, M_GC_NIC_IP_ADDRESS_STRING, IpAddress);
      }
   
   MdigInquire(MilDigitizer, M_GC_INTERFACE_NAME_SIZE, &Len);
   if(Len)
      {
      InterfaceName = new MIL_TEXT_CHAR[Len];
      MdigInquire(MilDigitizer, M_GC_INTERFACE_NAME, InterfaceName);
      MosPrintf(MIL_TEXT("%30s %s (%s)\n"), MIL_TEXT("Camera is connected to:"), InterfaceName,
         IpAddress);
      }

   if(CameraVendor)
      delete [] CameraVendor;
   if(CameraModel)
      delete [] CameraModel;
   if(CameraSerialNumber)
      delete [] CameraSerialNumber;
   if(CameraUserName)
      delete [] CameraUserName;
   if(CameraScanType)
      delete [] CameraScanType;
   if(IpAddress)
      delete [] IpAddress;
   if(InterfaceName)
      delete [] InterfaceName;
   }

/* Prints SFNC features */
void CameraPrintImageFormatControls(MIL_ID MilDigitizer)
   {
   MIL_INT64 SensorWidth = 0, SensorHeight = 0;
   MIL_INT64 Width = 0, Height = 0, WidthMax = 0, HeightMax = 0, WidthMin = 0, HeightMin = 0;
   MIL_TEXT_PTR* PixelFormats = NULL;
   MIL_INT PixFrmtCount = 0, Len;
   MIL_BOOL ReverseX = M_FALSE, ReverseY = M_FALSE;

   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE,  MIL_TEXT("SensorWidth"),   M_TYPE_INT64, &SensorWidth);
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE,  MIL_TEXT("SensorHeight"),  M_TYPE_INT64, &SensorHeight);
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE,  MIL_TEXT("Width"),         M_TYPE_INT64, &Width);
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE,  MIL_TEXT("Height"),        M_TYPE_INT64, &Height);
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE,  MIL_TEXT("ReverseX"),      M_TYPE_BOOLEAN, &ReverseX);
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE,  MIL_TEXT("ReverseY"),      M_TYPE_BOOLEAN, &ReverseY);
   MdigInquireFeature(MilDigitizer, M_FEATURE_MAX,    MIL_TEXT("Width"),         M_TYPE_INT64, &WidthMax);
   MdigInquireFeature(MilDigitizer, M_FEATURE_MAX,    MIL_TEXT("Height"),        M_TYPE_INT64, &HeightMax);
   MdigInquireFeature(MilDigitizer, M_FEATURE_MIN,    MIL_TEXT("Width"),         M_TYPE_INT64, &WidthMin);
   MdigInquireFeature(MilDigitizer, M_FEATURE_MIN,    MIL_TEXT("Height"),        M_TYPE_INT64, &HeightMin);

   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("PixelFormat"), M_DEFAULT, &PixFrmtCount);
   if(PixFrmtCount)
      {
      PixelFormats = new MIL_TEXT_PTR [PixFrmtCount];
      for(MIL_INT i=0; i<PixFrmtCount; i++)
         {
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+M_STRING_SIZE+i, MIL_TEXT("PixelFormat"), M_DEFAULT, &Len);
         PixelFormats[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+i, MIL_TEXT("PixelFormat"), M_DEFAULT, PixelFormats[i]);
         }
      }

   MosPrintf(MIL_TEXT("\n------------------- Image Format Controls ------------------\n\n"));
   MosPrintf(MIL_TEXT("%30s %4lld x %-4lld\n"), MIL_TEXT("Sensor size:"), SensorWidth?SensorWidth:Width, SensorHeight?SensorHeight:Height);
   MosPrintf(MIL_TEXT("%30s %4lld x %-4lld\n"), MIL_TEXT("Current ROI:"), Width, Height);
   MosPrintf(MIL_TEXT("%30s %4lld x %-4lld;%4lld x %-4lld\n"), MIL_TEXT("Maximum and Minimum ROI:"), WidthMax, HeightMax, WidthMin, HeightMin);
   MosPrintf(MIL_TEXT("\n%30s %s\n"), MIL_TEXT("Image Reverse X:"), ReverseX ? MIL_TEXT("true") : MIL_TEXT("false"));
   MosPrintf(MIL_TEXT("%30s %s\n"), MIL_TEXT("Image Reverse Y:"), ReverseY ? MIL_TEXT("true") : MIL_TEXT("false"));
   MosPrintf(MIL_TEXT("\n%30s %s\n"), MIL_TEXT("Supported pixel formats:"), PixelFormats?PixelFormats[0]:MIL_TEXT("N/A"));
   for(MIL_INT i=1; i<PixFrmtCount; i++)
      MosPrintf(MIL_TEXT("                               %s\n"), PixelFormats?PixelFormats[i]:MIL_TEXT("N/A"));

   if(PixelFormats)
      {
      for(MIL_INT i=0; i<PixFrmtCount; i++)
         delete [] PixelFormats[i];

      delete [] PixelFormats;
      }
   }

/* Prints SFNC features */
void CameraPrintAcquisitionControls(MIL_ID MilDigitizer)
   {
   MIL_TEXT_PTR* AcquisitionModes = NULL;
   MIL_TEXT_PTR* TriggerSelectors = NULL;
   MIL_TEXT_PTR* ExposureModes = NULL;
   MIL_DOUBLE ExposureTime = 0.0;
   MIL_INT AcMdCount = 0;
   MIL_INT TgSelCount = 0;
   MIL_INT ExMdCount = 0;
   MIL_INT Len;

   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("AcquisitionMode"), M_DEFAULT, &AcMdCount);
   if(AcMdCount)
      {
      AcquisitionModes = new MIL_TEXT_PTR [AcMdCount];
      if(AcMdCount > 1)
         MultipleAcquisitionModeSupport = 1;

      for(MIL_INT i=0; i<AcMdCount; i++)
         {
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+M_STRING_SIZE+i, MIL_TEXT("AcquisitionMode"), M_DEFAULT, &Len);
         AcquisitionModes[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+i, MIL_TEXT("AcquisitionMode"), M_DEFAULT, AcquisitionModes[i]);

         if(MosStrcmp(MIL_TEXT("Continuous"), AcquisitionModes[i]) == 0)
            ContinuousAMSupport = true;
         else if(MosStrcmp(MIL_TEXT("SingleFrame"), AcquisitionModes[i]) == 0)
            SingleFrameAMSupport = true;
         else if(MosStrcmp(MIL_TEXT("MultiFrame"), AcquisitionModes[i]) == 0)
            MultiFrameAMSupport = true;
         }
      }

   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("TriggerSelector"), M_DEFAULT, &TgSelCount);
   if(TgSelCount)
      {
      TriggerSelectors = new MIL_TEXT_PTR [TgSelCount];
      for(MIL_INT i=0; i<TgSelCount; i++)
         {
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+M_STRING_SIZE+i, MIL_TEXT("TriggerSelector"), M_DEFAULT, &Len);
         TriggerSelectors[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+i, MIL_TEXT("TriggerSelector"), M_DEFAULT, TriggerSelectors[i]);

         if(MosStrcmp(MIL_TEXT("AcquisitionStart"), TriggerSelectors[i]) == 0)
            CanTriggerAcquisitionStart = true;
         else if(MosStrcmp(MIL_TEXT("FrameStart"), TriggerSelectors[i]) == 0)
            CanTriggerFrameStart = true;
         }
      }

   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("ExposureMode"), M_DEFAULT, &ExMdCount);
   if(ExMdCount)
      {
      ExposureModes = new MIL_TEXT_PTR [ExMdCount];
      for(MIL_INT i=0; i<ExMdCount; i++)
         {
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+M_STRING_SIZE+i, MIL_TEXT("ExposureMode"), M_DEFAULT, &Len);
         ExposureModes[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+i, MIL_TEXT("ExposureMode"), M_DEFAULT, ExposureModes[i]);
         }
      }

   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("ExposureTime"), M_TYPE_MIL_DOUBLE, &ExposureTime);

   MosPrintf(MIL_TEXT("\n------------------- Acquisition Controls -------------------\n\n"));
   
   MosPrintf(MIL_TEXT("%30s %s\n"), MIL_TEXT("Supported acquisition modes:"), AcquisitionModes?AcquisitionModes[0]:MIL_TEXT("N/A"));
   for(MIL_INT i=1; i<AcMdCount; i++)
      MosPrintf(MIL_TEXT("%30s %s\n"), MIL_TEXT(""), AcquisitionModes?AcquisitionModes[i]:MIL_TEXT("N/A"));

   MosPrintf(MIL_TEXT("\n%30s %s\n"), MIL_TEXT("Supported trigger selectors:"), TriggerSelectors?TriggerSelectors[0]:MIL_TEXT("N/A"));
   for(MIL_INT i=1; i<TgSelCount; i++)
      MosPrintf(MIL_TEXT("%30s %s\n"), MIL_TEXT(""), TriggerSelectors?TriggerSelectors[i]:MIL_TEXT("N/A"));

   MosPrintf(MIL_TEXT("\n%30s %s\n"), MIL_TEXT("Supported exposure modes:"), ExposureModes?ExposureModes[0]:MIL_TEXT("N/A"));
   for(MIL_INT i=1; i<ExMdCount; i++)
      MosPrintf(MIL_TEXT("%30s %s\n"), MIL_TEXT(""), ExposureModes?ExposureModes[i]:MIL_TEXT("N/A"));

   if(ExposureTime == 0.0)
      MosPrintf(MIL_TEXT("\n%30s %s\n"), MIL_TEXT("Exposure time:"), MIL_TEXT("N/A"));
   else
      MosPrintf(MIL_TEXT("\n%30s %.1f us\n"), MIL_TEXT("Exposure time:"), ExposureTime);


   if(ExposureModes)
      {
      for(MIL_INT i=0; i<ExMdCount; i++)
         delete [] ExposureModes[i];

      delete [] ExposureModes;
      }

   if(TriggerSelectors)
      {
      for(MIL_INT i=0; i<TgSelCount; i++)
         delete [] TriggerSelectors[i];

      delete [] TriggerSelectors;
      }

   if(AcquisitionModes)
      {
      for(MIL_INT i=0; i<AcMdCount; i++)
         delete [] AcquisitionModes[i];

      delete [] AcquisitionModes;
      }
   }

/* Prints SFNC features */
void CameraPrintIOControls(MIL_ID MilDigitizer)
   {
   MIL_TEXT_PTR* Lines = NULL;
   MIL_TEXT_PTR* LineFormats = NULL;
   MIL_TEXT_PTR* LineModes = NULL;
   MIL_INT LineCnt = 0;
   MIL_INT Len;

   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("LineSelector"), M_DEFAULT, &LineCnt);
   if(LineCnt)
      {
      Lines = new MIL_TEXT_PTR [LineCnt];
      LineFormats = new MIL_TEXT_PTR [LineCnt];
      LineModes = new MIL_TEXT_PTR [LineCnt];
      for(MIL_INT i=0; i<LineCnt; i++)
         {
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+M_STRING_SIZE+i, MIL_TEXT("LineSelector"), M_DEFAULT, &Len);
         Lines[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+i, MIL_TEXT("LineSelector"), M_DEFAULT, Lines[i]);
         LineFormats[i] = NULL;
         LineModes[i] = NULL;
         }

      for(MIL_INT i=0; i<LineCnt; i++)
         {
         MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("LineSelector"), M_TYPE_ENUMERATION, Lines[i]);

         Len=0;
         MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING+M_STRING_SIZE, MIL_TEXT("LineMode"), M_TYPE_ENUMERATION, &Len);
         if(Len)
            {
            LineModes[i] = new MIL_TEXT_CHAR[Len];
            MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("LineMode"), M_TYPE_ENUMERATION, LineModes[i]);
            }

         Len=0;
         MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING+M_STRING_SIZE, MIL_TEXT("LineFormat"), M_TYPE_ENUMERATION, &Len);
         if(Len)
            {
            LineFormats[i] = new MIL_TEXT_CHAR[Len];
            MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("LineFormat"), M_TYPE_ENUMERATION, LineFormats[i]);
            }
         }
      }
   
   MosPrintf(MIL_TEXT("\n------------------- Digital I/O Controls -------------------\n\n"));

   MosPrintf(MIL_TEXT("%7s%-18s%-18s%-18s%7s\n\n"), MIL_TEXT(""), MIL_TEXT("Name"), MIL_TEXT("Mode"), MIL_TEXT("Format"), MIL_TEXT(""));

   if(LineCnt == 0)
      MosPrintf(MIL_TEXT("%7s%-18s%-18s%-18s%7s\n"), MIL_TEXT(""), MIL_TEXT("N/A"), MIL_TEXT("N/A"), MIL_TEXT("N/A"), MIL_TEXT(""));
   else
      {
      for(MIL_INT i=0; i<LineCnt; i++)
         MosPrintf(MIL_TEXT("%7s%-18s%-18s%-18s%7s\n"), MIL_TEXT(""), Lines?Lines[i]:MIL_TEXT("N/A"), LineModes[i]?LineModes[i]:MIL_TEXT("N/A"), LineFormats[i]?LineFormats[i]:MIL_TEXT("N/A"), MIL_TEXT(""));
      }

   if(LineFormats)
      {
      for(MIL_INT i=0; i<LineCnt; i++)
         if(LineFormats[i])
            delete [] LineFormats[i];
      delete [] LineFormats;
      }

   if(LineModes)
      {
      for(MIL_INT i=0; i<LineCnt; i++)
         if(LineModes[i])
            delete [] LineModes[i];
      delete [] LineModes;
      }

   if(Lines)
      {
      for(MIL_INT i=0; i<LineCnt; i++)
         delete [] Lines[i];
      delete [] Lines;
      }

   }

/* Prints SFNC features */
void CameraPrintTransportLayerControls(MIL_ID MilDigitizer)
   {
   MIL_INT64 GigEMajorVersion = 0;
   MIL_INT64 GigEMinorVersion = 0;
   MIL_INT64 StreamChannelPacketSize = 0;
   MIL_INT64 CurrentIp = -1;
   MIL_INT64 MAC = -1;
   MIL_UINT8* Ip = (MIL_UINT8*)&CurrentIp;
   MIL_UINT8* pMAC = (MIL_UINT8*)&MAC;

   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("GevVersionMajor"),      M_TYPE_INT64, &GigEMajorVersion);
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("GevVersionMinor"),      M_TYPE_INT64, &GigEMinorVersion);
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("GevSCPSPacketSize"),    M_TYPE_INT64, &StreamChannelPacketSize);
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("GevMACAddress"),        M_TYPE_INT64, &MAC);
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("GevCurrentIPAddress"),  M_TYPE_INT64, &CurrentIp);

   MosPrintf(MIL_TEXT("\n-------------- Camera Transport Layer Controls -------------\n\n"));
   if(GigEMajorVersion == 0)
      MosPrintf(MIL_TEXT("%30s N/A\n"), MIL_TEXT("GigE Vision Version:"));
   else 
      MosPrintf(MIL_TEXT("%30s %d.%d\n"), MIL_TEXT("GigE Vision Version:"), GigEMajorVersion, GigEMinorVersion);

   if(MAC == -1)
      MosPrintf(MIL_TEXT("%30s N/A\n"), MIL_TEXT("MAC Address:"));
   else
      MosPrintf(MIL_TEXT("%30s %.2X-%.2X-%.2X-%.2X-%.2X-%.2X\n"), MIL_TEXT("MAC Address:"), pMAC[5], pMAC[4], pMAC[3], pMAC[2], pMAC[1], pMAC[0]);

   if(CurrentIp == -1)
      MosPrintf(MIL_TEXT("%30s N/A\n"), MIL_TEXT("Current IP Address:"));
   else
      MosPrintf(MIL_TEXT("%30s %d.%d.%d.%d\n"), MIL_TEXT("Current IP Address:"), Ip[3], Ip[2], Ip[1], Ip[0]);
   
   MosPrintf(MIL_TEXT("%30s %d\n"), MIL_TEXT("Packet size:"), StreamChannelPacketSize);
   }

/* Prints SFNC features */
void CameraPrintCounterAndTimerControls(MIL_ID MilDigitizer)
   {
   MIL_TEXT_PTR* Counters = NULL;
   MIL_TEXT_PTR* CountersStatus = NULL;
   MIL_TEXT_PTR* Timers = NULL;
   MIL_TEXT_PTR* TimersStatus = NULL;
   MIL_INT CountersCnt = 0;
   MIL_INT TimersCnt = 0;
   MIL_INT Len = 0;

   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("CounterSelector"), M_DEFAULT, &CountersCnt);
   if(CountersCnt)
      {
      Counters = new MIL_TEXT_PTR [CountersCnt];
      CountersStatus = new MIL_TEXT_PTR [CountersCnt];
      for(MIL_INT i=0; i<CountersCnt; i++)
         {
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+M_STRING_SIZE+i, MIL_TEXT("CounterSelector"), M_DEFAULT, &Len);
         Counters[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+i, MIL_TEXT("CounterSelector"), M_DEFAULT, Counters[i]);
         CountersStatus[i] = NULL;
         }
      }

   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("TimerSelector"), M_DEFAULT, &TimersCnt);
   if(TimersCnt)
      {
      Timers = new MIL_TEXT_PTR [TimersCnt];
      TimersStatus = new MIL_TEXT_PTR [TimersCnt];
      for(MIL_INT i=0; i<TimersCnt; i++)
         {
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+M_STRING_SIZE+i, MIL_TEXT("TimerSelector"), M_DEFAULT, &Len);
         Timers[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+i, MIL_TEXT("TimerSelector"), M_DEFAULT, Timers[i]);
         TimersStatus[i] = NULL;
         }
      }

   for(MIL_INT i=0; i<CountersCnt; i++)
      {
      MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("CounterSelector"), M_TYPE_ENUMERATION, Counters[i]);
      Len = 0;
      MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING+M_STRING_SIZE, MIL_TEXT("CounterStatus"), M_TYPE_ENUMERATION, &Len);
      if(Len)
         {
         CountersStatus[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("CounterStatus"), M_TYPE_ENUMERATION, CountersStatus[i]);
         }
      }

   for(MIL_INT i=0; i<TimersCnt; i++)
      {
      MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TimerSelector"), M_TYPE_ENUMERATION, Timers[i]);
      Len = 0;
      MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING+M_STRING_SIZE, MIL_TEXT("TimerStatus"), M_TYPE_ENUMERATION, &Len);
      if(Len)
         {
         TimersStatus[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TimerStatus"), M_TYPE_ENUMERATION, TimersStatus[i]);
         }
      }

   MosPrintf(MIL_TEXT("\n---------------- Counter and Timer Controls ----------------\n\n"));

   MosPrintf(MIL_TEXT("%20s%-15s%-15s%20s\n\n"), MIL_TEXT(""), MIL_TEXT("Name"), MIL_TEXT("Status"), MIL_TEXT(""));

   if(CountersCnt == 0)
      MosPrintf(MIL_TEXT("%20s%-15s%-15s%20s\n"), MIL_TEXT(""), MIL_TEXT("N/A"), MIL_TEXT("N/A"), MIL_TEXT(""));
   else
      {
      for(MIL_INT i=0; i<CountersCnt; i++)
         MosPrintf(MIL_TEXT("%20s%-15s%-15s%20s\n"), MIL_TEXT(""), Counters?Counters[i]:MIL_TEXT("N/A"), CountersStatus[i]?CountersStatus[i]:MIL_TEXT("N/A"), MIL_TEXT(""));
      }

   if(TimersCnt == 0)
      MosPrintf(MIL_TEXT("%20s%-15s%-15s%20s\n"), MIL_TEXT(""), MIL_TEXT("N/A"), MIL_TEXT("N/A"), MIL_TEXT(""));
   else
      {
      for(MIL_INT i=0; i<TimersCnt; i++)
         MosPrintf(MIL_TEXT("%20s%-15s%-15s%20s\n"), MIL_TEXT(""), Timers?Timers[i]:MIL_TEXT("N/A"), TimersStatus[i]?TimersStatus[i]:MIL_TEXT("N/A"), MIL_TEXT(""));
      }

   if(CountersStatus)
      {
      for(MIL_INT i=0; i<CountersCnt; i++)
         {
         if(CountersStatus[i])
            delete [] CountersStatus[i];
         }
      delete [] CountersStatus;
      }

   if(TimersStatus)
      {
      for(MIL_INT i=0; i<TimersCnt; i++)
         {
         if(TimersStatus[i])
            delete [] TimersStatus[i];
         }
      delete [] TimersStatus;
      }

   if(Timers)
      {
      for(MIL_INT i=0; i<TimersCnt; i++)
         delete [] Timers[i];
      
      delete [] Timers;
      }

   if(Counters)
      {
      for(MIL_INT i=0; i<CountersCnt; i++)
         delete [] Counters[i];
      
      delete [] Counters;
      }
   }

/* Prints SFNC features */
void CameraPrintEventControls(MIL_ID MilDigitizer)
   {
   MIL_TEXT_PTR* Events = NULL;
   MIL_INT EventCnt = 0;
   MIL_INT Len = 0;

   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("EventSelector"), M_DEFAULT, &EventCnt);
   if(EventCnt)
      {
      Events = new MIL_TEXT_PTR [EventCnt];
      for(MIL_INT i=0; i<EventCnt; i++)
         {
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+M_STRING_SIZE+i, MIL_TEXT("EventSelector"), M_DEFAULT, &Len);
         Events[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+i, MIL_TEXT("EventSelector"), M_DEFAULT, Events[i]);
         }
      }
   
   MosPrintf(MIL_TEXT("\n---------------------- Event Controls ----------------------\n\n"));
   MosPrintf(MIL_TEXT("%30s "), MIL_TEXT("Supported events:"));

   if(EventCnt == 0)
      MosPrintf(MIL_TEXT("N/A\n"));
   else
      {
      MosPrintf(MIL_TEXT("%s\n"), Events[0]);
      for(MIL_INT i = 1; i < EventCnt; i++)
         MosPrintf(MIL_TEXT("                               %s\n"), Events[i]);
      }

   if(Events)
      {
      for(MIL_INT i = 0; i < EventCnt; i++)
         delete [] Events[i];

      delete[] Events;
      }
   }

void CameraPrintLUT(MIL_ID MilDigitizer)
   {
   MIL_INT64 MinIndex = 0, MaxIndex = 0, LutValue = 0;
   MIL_INT Len = 0, LutSelCount = 0;
   MIL_TEXT_PTR* LutSelectors = NULL;
   MIL_TEXT_CHAR Str[16];

   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("LUTSelector"), M_DEFAULT, &LutSelCount);
   if(LutSelCount)
      {
      LutSelectors = new MIL_TEXT_PTR [LutSelCount];
      for(MIL_INT i=0; i<LutSelCount; i++)
         {
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+M_STRING_SIZE+i, MIL_TEXT("LUTSelector"), M_DEFAULT, &Len);
         LutSelectors[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+i, MIL_TEXT("LUTSelector"), M_DEFAULT, LutSelectors[i]);

         MosPrintf(MIL_TEXT("\nPress <Enter> to print %s Lookup table.\n"), LutSelectors[i]);
         MosGetch();
#if M_MIL_USE_WINDOWS
         system("cls");
#endif

         MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("LUTSelector"), M_TYPE_ENUMERATION, LutSelectors[i]);

         MosPrintf(MIL_TEXT("\n------- Printing (%s) lookup table contents -----\n"), LutSelectors[i]);

         MdigInquireFeature(MilDigitizer, M_FEATURE_MIN, MIL_TEXT("LUTIndex"), M_TYPE_INT64, &MinIndex);
         MdigInquireFeature(MilDigitizer, M_FEATURE_MAX, MIL_TEXT("LUTIndex"), M_TYPE_INT64, &MaxIndex);
         
         for(MIL_INT64 j=MinIndex; j<=MaxIndex; j++)
            {
            MdigControlFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("LUTIndex"), M_TYPE_INT64, &j);
            MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("LUTValue"), M_TYPE_INT64, &LutValue);

            if((j % 5) == 0)
               MosPrintf(MIL_TEXT("\n"));

            MosSprintf(Str, 16, MIL_TEXT("[%d]"), j);
            MosPrintf(MIL_TEXT("%7s : %-6d"), Str, LutValue);
            }
         }

      for(MIL_INT i=0; i<LutSelCount; i++)
         delete [] LutSelectors[i];

      delete [] LutSelectors;
      }
   }


/* Set the camera in triggered mode according to the user's input. */
void SetTriggerControls(MIL_ID MilDigitizer, eTriggerType& Type, MIL_INT& NbFrames, MIL_TEXT_PTR oTriggerSelector, MIL_INT StringSize, bool &SoftwareTriggerSelected)
   {
   MIL_INT Done = 0;
   MIL_INT64 Tmp = 0;

   if(CanTriggerAcquisitionStart && MultipleAcquisitionModeSupport)
      {
      MIL_INT Selection;

      do
         {
         MosPrintf(MIL_TEXT("\n\n%-35s"), MIL_TEXT("Do you want to trigger a:")); 

         if(ContinuousAMSupport)
            MosPrintf(MIL_TEXT("(C) %-30s\n%35s"), MIL_TEXT("Continuous acquisition"), MIL_TEXT(""));
         if(MultiFrameAMSupport)
            MosPrintf(MIL_TEXT("(M) %-30s\n%35s"), MIL_TEXT("MultiFrame acquisition"), MIL_TEXT(""));
         if(SingleFrameAMSupport)
            MosPrintf(MIL_TEXT("(S) %-30s\n%35s"), MIL_TEXT("SingleFrame acquisition"), MIL_TEXT(""));

         MosPrintf(MIL_TEXT("\n"));
         Selection = MosGetch();
         Done = 1;
         switch(Selection)
            {
            case 'c':
            case 'C':
               MosStrcpy(oTriggerSelector, StringSize, MIL_TEXT("AcquisitionStart"));
               MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("AcquisitionMode"), M_TYPE_ENUMERATION, MIL_TEXT("Continuous"));
               MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerSelector"), M_TYPE_ENUMERATION, MIL_TEXT("AcquisitionStart"));
               MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerMode"), M_TYPE_ENUMERATION, MIL_TEXT("On"));
               MosPrintf(MIL_TEXT("Continuous acquisition trigger selected.\n"));
               SelectTriggerSource(MilDigitizer, SoftwareTriggerSelected);
               Type = eContinuous;
               break;
            case 'm':
            case 'M':
               MosStrcpy(oTriggerSelector, StringSize, MIL_TEXT("AcquisitionStart"));
               MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("AcquisitionMode"), M_TYPE_ENUMERATION, MIL_TEXT("MultiFrame"));
               MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerSelector"), M_TYPE_ENUMERATION, MIL_TEXT("AcquisitionStart"));
               MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerMode"), M_TYPE_ENUMERATION, MIL_TEXT("On"));
               MosPrintf(MIL_TEXT("Multi Frame acquisition trigger selected.\n"));
               SelectTriggerSource(MilDigitizer, SoftwareTriggerSelected);

               MosPrintf(MIL_TEXT("\n%-30s"), MIL_TEXT("\nHow many frames per trigger?"), MIL_TEXT(""));
#if M_MIL_USE_WINDOWS
               scanf_s("%d", &NbFrames);
#else
               scanf("%ld", &NbFrames);
#endif
               MosPrintf(MIL_TEXT("%d Frames will be acquired per trigger.\n"), NbFrames);
               Tmp = NbFrames;
               MdigControlFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("AcquisitionFrameCount"), M_TYPE_INT64, &Tmp);
               Type = eMultiFrame;
               break;
            case 's':
            case 'S':
               if(CanTriggerFrameStart)
                  {
                  MosStrcpy(oTriggerSelector, StringSize, MIL_TEXT("FrameStart"));
                  MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("AcquisitionMode"), M_TYPE_ENUMERATION, MIL_TEXT("Continuous"));
                  MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerSelector"), M_TYPE_ENUMERATION, MIL_TEXT("FrameStart"));
                  MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerMode"), M_TYPE_ENUMERATION, MIL_TEXT("On"));
                  }
               else
                  {
                  MosStrcpy(oTriggerSelector, StringSize, MIL_TEXT("AcquisitionStart"));
                  MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("AcquisitionMode"), M_TYPE_ENUMERATION, MIL_TEXT("SingleFrame"));
                  MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerSelector"), M_TYPE_ENUMERATION, MIL_TEXT("AcquisitionStart"));
                  MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerMode"), M_TYPE_ENUMERATION, MIL_TEXT("On"));
                  }

               MosPrintf(MIL_TEXT("Single Frame acquisition trigger selected.\n"));
               SelectTriggerSource(MilDigitizer, SoftwareTriggerSelected);
               Type = eSingleFrame;
               break;
            default:
               MosPrintf(MIL_TEXT("Invalid selection."));
               Done = 0;
               break;
            }
         }
      while(!Done);
      }
   else if(CanTriggerFrameStart)
      {
      MosStrcpy(oTriggerSelector, StringSize, MIL_TEXT("FrameStart"));
      MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerSelector"), M_TYPE_ENUMERATION, MIL_TEXT("FrameStart"));
      MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerMode"), M_TYPE_ENUMERATION, MIL_TEXT("On"));
      MosPrintf(MIL_TEXT("\n\nFrame start trigger will be performed.\n"));
      SelectTriggerSource(MilDigitizer, SoftwareTriggerSelected);
      Type = eSingleFrame;
      }
   }

/* Set the source of the trigger (software, input pin, ... according to the user's input */
void SelectTriggerSource(MIL_ID MilDigitizer, bool& SoftwareTriggerSelected)
   {
   MIL_TEXT_PTR* TriggerSource = NULL;
   MIL_INT Cnt = 0;
   MIL_INT Len = 0;
   MIL_INT Selection = 0;
   MIL_INT Done = 0;

   SoftwareTriggerSelected = false;
   MosPrintf(MIL_TEXT("%-35s"), MIL_TEXT("Please select the trigger source:"));

   MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_COUNT, MIL_TEXT("TriggerSource"), M_DEFAULT, &Cnt);
   if(Cnt)
      {
      TriggerSource = new MIL_TEXT_PTR [Cnt];
      for(MIL_INT i=0; i<Cnt; i++)
         {
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+M_STRING_SIZE+i, MIL_TEXT("TriggerSource"), M_DEFAULT, &Len);
         TriggerSource[i] = new MIL_TEXT_CHAR[Len];
         MdigInquireFeature(MilDigitizer, M_FEATURE_ENUM_ENTRY_NAME+i, MIL_TEXT("TriggerSource"), M_DEFAULT, TriggerSource[i]);
         }

      MosPrintf(MIL_TEXT("(%d) %-30s\n"), 0, TriggerSource[0]);
      for(MIL_INT i=1; i<Cnt; i++)
         {
         MosPrintf(MIL_TEXT("%-35s(%d) %-20s\n"), MIL_TEXT(""), i, TriggerSource[i]);
         }

      do
         {
#if M_MIL_USE_WINDOWS
         scanf_s("%d", &Selection);
#else
         scanf("%ld", &Selection);
#endif
         if(Selection >= 0 && Selection < Cnt)
            {
            MosPrintf(MIL_TEXT("%s selected\n"), TriggerSource[Selection]);
            Done = 1;
            }
         else
            MosPrintf(MIL_TEXT("Invalid selection.\n"));
         }
      while(!Done);

      MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerSource"), M_TYPE_ENUMERATION, TriggerSource[Selection]);
      if(MosStrcmp(TriggerSource[Selection], MIL_TEXT("Software")) == 0)
         SoftwareTriggerSelected = true;
      }

   if(TriggerSource)
      {
      for(MIL_INT i = 0; i < Cnt; i++)
         delete [] TriggerSource[i];

      delete[] TriggerSource;
      }
   }

/* Puts the camera back in non-triggered mode. */
void ResetTriggerControls(MIL_ID MilDigitizer)
   {
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_DISABLE);
   MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerSelector"), M_TYPE_ENUMERATION, MIL_TEXT("FrameStart"));
   MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerMode"), M_TYPE_ENUMERATION, MIL_TEXT("Off"));

   MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerSelector"), M_TYPE_ENUMERATION, MIL_TEXT("AcquisitionStart"));
   MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerMode"), M_TYPE_ENUMERATION, MIL_TEXT("Off"));
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_ENABLE);
   }

/* User's processing function hook data structure. */
typedef struct
   {
   MIL_ID  MilDigitizer;
   MIL_ID  MilImageDisp;
   MIL_INT ProcessedImageCount;
   } HookDataStruct;

/* User's processing function prototype. */
MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType,
                                  MIL_ID HookId,
                                  void* HookDataPtr);

void DoTriggeredAcquisition(MIL_ID MilSystem, MIL_ID MilDigitizer, MIL_ID MilImageDisp)
   {
   eTriggerType TriggerType = (eTriggerType)0;
   bool SoftwareTriggerSelected = false;
   MIL_INT NbFrames = 10;
   MIL_INT MilGrabBufferListSize;
   MIL_INT Done = 0;
   MIL_INT Ch = 0;
   MIL_ID* MilGrabBufferList = NULL;
   MIL_TEXT_CHAR TriggerSelector[256] = {'\0'};;
   HookDataStruct UserHookData;
   MIL_INT StartOp = M_START;

   /*Set-up the camera in triggered mode according to the user's input. */
   SetTriggerControls(MilDigitizer, TriggerType, NbFrames, TriggerSelector, 255, SoftwareTriggerSelected);

   MilGrabBufferList = new MIL_INT[(NbFrames==M_INFINITE) ? 10 : NbFrames];

   /* Allocate the grab buffers and clear them. */
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_DISABLE);
   for(MilGrabBufferListSize = 0; 
            MilGrabBufferListSize<NbFrames; MilGrabBufferListSize++)
      {
      MbufAlloc2d(MilSystem,
                  MdigInquire(MilDigitizer, M_SIZE_X, M_NULL),
                  MdigInquire(MilDigitizer, M_SIZE_Y, M_NULL),
                  8+M_UNSIGNED,
                  M_IMAGE+M_GRAB+M_PROC,
                  &MilGrabBufferList[MilGrabBufferListSize]);

      if (MilGrabBufferList[MilGrabBufferListSize])
         {
         MbufClear(MilGrabBufferList[MilGrabBufferListSize], 0xFF);
         }
      else
         break;
      }
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_ENABLE);

   /* Initialize the User's processing function data structure. */
   UserHookData.MilDigitizer        = MilDigitizer;
   UserHookData.MilImageDisp        = MilImageDisp;
   UserHookData.ProcessedImageCount = 0;

   /* Set the grab timeout to infinite for triggered grab. */
   MdigControl(MilDigitizer, M_GRAB_TIMEOUT, M_INFINITE);

   /* Print a message and wait for a key press after a minimum number of frames. */
   if(SoftwareTriggerSelected)
      MosPrintf(MIL_TEXT("\n\nPress <t> to do a software trigger.\n"));
   else
      MosPrintf(MIL_TEXT("\n\nWaiting for a input trigger signal.\n"));
   MosPrintf(MIL_TEXT("Press any other key to quit.\n\n"));

   if(TriggerType == eMultiFrame)
      StartOp = M_SEQUENCE+M_COUNT(NbFrames);

   do
      {
      /* Start the processing. The processing function is called for every frame grabbed. */
      MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize,
                     StartOp, M_ASYNCHRONOUS, ProcessingFunction, &UserHookData);

      /* If trigger mode is software, send a software trigger when the user presses the <T> key. */
      if(SoftwareTriggerSelected)
         {
         do
            {
            Ch =  MosGetch();
            if(Ch == 'T' || Ch == 't')
               {
               MdigControlFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("TriggerSelector"), M_TYPE_ENUMERATION, TriggerSelector);
               MdigControlFeature(MilDigitizer, M_FEATURE_EXECUTE, MIL_TEXT("TriggerSoftware"), M_TYPE_COMMAND, M_NULL);
               if(TriggerType == eMultiFrame)
                  break;
               }
            else
               Done = 1;
            }
         while(!Done);
         }
      else if(TriggerType != eMultiFrame)
         Done = MosGetch();
      else if(MosKbhit())
         Done = 1;

      /* Stop the processing. */
      MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize,
                                 Done ? M_STOP : M_STOP+M_WAIT, M_DEFAULT, ProcessingFunction, &UserHookData);
      }
   while(!Done);

   /* Reset the camera to non-triggered mode. */
   ResetTriggerControls(MilDigitizer);
   
   /* Free the grab buffers. */
   while(MilGrabBufferListSize > 0)
      MbufFree(MilGrabBufferList[--MilGrabBufferListSize]);
   
   delete [] MilGrabBufferList;
   }

/* User's processing function called every time a grab buffer is modified. */
/* -----------------------------------------------------------------------*/

/* Local defines. */
#define STRING_LENGTH_MAX  20
#define STRING_POS_X       20
#define STRING_POS_Y       20

MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType,
                                  MIL_ID HookId,
                                  void* HookDataPtr)
   {
   HookDataStruct *UserHookDataPtr = (HookDataStruct *)HookDataPtr;
   MIL_ID ModifiedBufferId;
   MIL_TEXT_CHAR Text[STRING_LENGTH_MAX]= {MIL_TEXT('\0'),};

   /* Retrieve the MIL_ID of the grabbed buffer. */
   MdigGetHookInfo(HookId, M_MODIFIED_BUFFER+M_BUFFER_ID, &ModifiedBufferId);

   /* Print and draw the frame count. */
   UserHookDataPtr->ProcessedImageCount++;
   MosPrintf(MIL_TEXT("Processing frame #%d.\r"), UserHookDataPtr->ProcessedImageCount);
   MosSprintf(Text, STRING_LENGTH_MAX, MIL_TEXT("%ld"), 
                                       UserHookDataPtr->ProcessedImageCount);
   MgraText(M_DEFAULT, ModifiedBufferId, STRING_POS_X, STRING_POS_Y, Text);

   /* Perform the processing and update the display. */
   MbufCopy(ModifiedBufferId, UserHookDataPtr->MilImageDisp);
   
   return 0;
   }