/******************************************************************************/
/*
 * File name: MultiCamera.cpp
 * Location: See Matrox Example Launcher in the MIL Control Center
 * 
 *
 * Synopsis:  This program detects the number of cameras attached
 *            to a GigE or USB3 Vision system, prints camera vendor information
 *            and starts grabbing from all cameras found using
 *            MdigProcess. It also handles camera removal and addition.
 *
 * Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
 * All Rights Reserved
 */


#include <mil.h>
#include <vector>

using namespace std;

#if M_MIL_USE_WINDOWS
#include <conio.h>
#include <stdlib.h>
#include <windows.h>
#endif

#define WINDOW_TITLE          MIL_TEXT("Camera: ")
#define MAX_CAM               32
#define BUFFERING_SIZE_MAX    3
#define STRING_LENGTH_MAX     128
#define STRING_POS_X          20
#define STRING_POS_Y          20
#define STATS_PRINT_PERIOD    1000
#define USE_FEATURE_BROWSER   0

/* User's processing function and camera detect hook data structure. */
class DigHookDataStruct
   {
   public:
      DigHookDataStruct()
         {
         Clear();
         }

      void Clear()
         {
         MilSystem = M_NULL;
         MilDigitizer = M_NULL;
         MilDisplay = M_NULL;
         MilImageDisp = M_NULL;
         memset(MilGrabBufferList, 0, sizeof(MIL_ID) * BUFFERING_SIZE_MAX);
         MilGrabBufferListSize = 0;
         ProcessedImageCount = 0;
         FrameRate = 0;
         ResendRequests = 0;
         PacketSize = 0;
         PacketsMissed = 0;
         CorruptImageCount = 0;
         GrabInProgress = 0;
         PayloadSize = 0;
         IsConnected = false;
         SystemType = 0;
         }

      MIL_ID MilSystem;
      MIL_ID MilDigitizer;
      MIL_ID MilDisplay;
      MIL_ID MilImageDisp;
      MIL_ID MilGrabBufferList[BUFFERING_SIZE_MAX];
      MIL_INT MilGrabBufferListSize;
      MIL_INT ProcessedImageCount;
      MIL_DOUBLE FrameRate;
      MIL_INT ResendRequests;
      MIL_INT PacketSize;
      MIL_INT PacketsMissed;
      MIL_INT CorruptImageCount;
      MIL_INT GrabInProgress;
      MIL_INT PayloadSize;
      MIL_STRING CamVendor;
      MIL_STRING CamModel;
      MIL_STRING CamUniqueId;
      MIL_STRING AdapterName;
      bool IsConnected;
      MIL_INT SystemType;
   };

/* User's system camera detect hook data structure. */
#define MAX_ADAPTER_DESCRIPTION_LENGTH  512
class SysHookDataStruct
   {
   public:
      SysHookDataStruct()
         {
         MilSystem = 0;
         NbCameras = 0;
         DigHookDataStrutPtr = NULL;
         PrintAdapterInfo = false;
         SystemType = 0;
         }

      MIL_ID MilSystem;
      MIL_INT NbCameras;
      DigHookDataStruct* DigHookDataStrutPtr;
      bool PrintAdapterInfo;
      vector<MIL_STRING> Adapters;
      MIL_INT SystemType;
   };

/* User's processing and camera detect hook functions. */
MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType,
   MIL_ID HookId,
   void* HookDataPtr);
MIL_INT MFTYPE CamPresentFunction(MIL_INT HookType,
   MIL_ID HookId,
   void* HookDataPtr);

/* Digitizer allocation and acquisition routines.
   Used to make the example more compact. */
void DigAllocResources(MIL_ID MilSystem, MIL_INT DeviceNum,
   DigHookDataStruct* UserSt,
   bool PrintErrors = true);
void DigFreeResources(DigHookDataStruct* UserSt);
void DigStartAcquisition(DigHookDataStruct* UserSt);
void DigStopAcquisition(DigHookDataStruct* UserSt);
void PrintCameraInfo(SysHookDataStruct* UserSt);
void AddAdapterToList(SysHookDataStruct* UserSt, const MIL_STRING& AdapterName);

int MosMain(void)
   {
   MIL_ID MilApplication,
      MilSystem;
   MIL_INT SystemType,
      DevNb,
      Done = M_FALSE,
      ProcessFrameCount = 0,
      MaxCam = MAX_CAM;
   MIL_DOUBLE ProcessFrameRate = 0.0;
   SysHookDataStruct SysUserHookData;
   DigHookDataStruct DigUserHookData[MAX_CAM];

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

   MsysInquire(MilSystem, M_SYSTEM_TYPE, &SystemType);

   SysUserHookData.MilSystem = MilSystem;
   SysUserHookData.NbCameras = 0;
   SysUserHookData.DigHookDataStrutPtr = DigUserHookData;
   SysUserHookData.SystemType = SystemType;

   if((SystemType != M_SYSTEM_GIGE_VISION_TYPE) &&
      (SystemType != M_SYSTEM_USB3_VISION_TYPE))
      {
      MosPrintf(MIL_TEXT("This example requires a M_GIGE_VISION or M_USB3_VISION system type.\n"));
      MosPrintf(MIL_TEXT("Please change system type in milconfig.\n"));
      MappFreeDefault(MilApplication, MilSystem, M_NULL, M_NULL, M_NULL);
      MosGetch();
      return -1;
      }

   /* Inquire the number of cameras attached. */
   MsysInquire(MilSystem, M_NUM_CAMERA_PRESENT, &SysUserHookData.NbCameras);
   if (SysUserHookData.NbCameras > MaxCam)
      SysUserHookData.NbCameras = MaxCam;

   /* Allocate and start acquisition on all cameras found at MsysAlloc time. */
   if (SysUserHookData.NbCameras)
      {
      MIL_INT CamerasAllocated = M_FALSE;
      /* Allocate digitizers and other resources. */
      for (DevNb = M_DEV0; DevNb < MAX_CAM; DevNb++)
         {
         DigUserHookData[DevNb].SystemType = SystemType;
         DigAllocResources(MilSystem, DevNb, &DigUserHookData[DevNb], false);
         if (DigUserHookData[DevNb].MilDigitizer)
            {
            CamerasAllocated = M_TRUE;
            if(SystemType == M_SYSTEM_GIGE_VISION_TYPE)
               {
               AddAdapterToList(&SysUserHookData, DigUserHookData[DevNb].AdapterName);
               }
            }
         }

      if (CamerasAllocated == M_TRUE)
         {
         /* Start acquisition. */
         for (DevNb = M_DEV0; DevNb < MAX_CAM; DevNb++)
            DigStartAcquisition(&DigUserHookData[DevNb]);
         }
      else
         {
         /* Cameras might already be allocated by another process on this or */
         /* another PC. Do a license check to determine if allocation        */
         /* failure was caused by lack of proper license.                    */

         MIL_ID MilRemoteApplication = M_NULL;
         MIL_INT LicenseModules = 0;
         MsysInquire(MilSystem, M_OWNER_APPLICATION, &MilRemoteApplication);

         MappInquire(MilRemoteApplication, M_LICENSE_MODULES, &LicenseModules);
         if (!(LicenseModules & (M_LICENSE_INTERFACE)))
            {
            MosPrintf(MIL_TEXT("Need a GigE Vision license to run this example.\n"));
            MosPrintf(MIL_TEXT("Press <Enter> to end.\n"));
            MosGetch();

            MappFreeDefault(MilApplication, MilSystem, M_NULL, M_NULL, M_NULL);
            return 0;
            }
         }
      }

   /* Register a hook function to the system's camera present event. */
   /* Used to:                                                        */
   /* 1- Allocate and start acquisition on a newly attached camera.   */
   /* 2- Stop acquisition on a camera that has been removed.          */
   /* 3- Resume acquisition on a camera that has been re-connected.   */
   MsysHookFunction(MilSystem, M_CAMERA_PRESENT, CamPresentFunction, &SysUserHookData);

   /* At this point digitizers have been allocated and acquisition started on all      */
   /* cameras found at MsysAlloc time (if any). The example now waits for the user to  */
   /* add / remove cameras to the system. The camera present hook will then get called */
   /* and everything will get handled from there.                                      */

   while (!MosKbhit())
      PrintCameraInfo(&SysUserHookData);

   MosGetch();

   /* The user is stopping the example, stop acquisition and free everything. */
   for (DevNb = M_DEV0; DevNb < MAX_CAM; DevNb++)
      DigStopAcquisition(&DigUserHookData[DevNb]);

   PrintCameraInfo(&SysUserHookData);

   MosPrintf(MIL_TEXT("\nFreeing:\n"));
   for (DevNb = M_DEV0; DevNb < MAX_CAM; DevNb++)
      DigFreeResources(&DigUserHookData[DevNb]);

   MsysHookFunction(MilSystem, M_CAMERA_PRESENT + M_UNHOOK, CamPresentFunction,
      &SysUserHookData);
   MsysFree(MilSystem);
   MappFree(MilApplication);

   return 0;
   }

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

MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType,
   MIL_ID HookId,
   void* HookDataPtr)
   {
   DigHookDataStruct *UserHookDataPtr = (DigHookDataStruct *)HookDataPtr;
   MIL_ID ModifiedBufferId = 0;
   MIL_INT ResendRequests = 0, PacketsMissed = 0, IsCorrupt = 0;

   /* Retrieve the MIL_ID of the grabbed buffer and camera statistics. */
   MdigGetHookInfo(HookId, M_MODIFIED_BUFFER + M_BUFFER_ID, &ModifiedBufferId);
   MdigGetHookInfo(HookId, M_CORRUPTED_FRAME, &IsCorrupt);
   if(UserHookDataPtr->SystemType == M_SYSTEM_GIGE_VISION_TYPE)
      {
      MdigGetHookInfo(HookId, M_GC_PACKETS_RESENDS_NUM, &ResendRequests);
      MdigGetHookInfo(HookId, M_GC_PACKETS_MISSED, &PacketsMissed);
      }

   UserHookDataPtr->ResendRequests += ResendRequests;
   UserHookDataPtr->PacketsMissed += PacketsMissed;

   /* Copy the grabbed frame to display. */
   if (IsCorrupt)
      UserHookDataPtr->CorruptImageCount++;
   else
      {
      UserHookDataPtr->ProcessedImageCount++;
      MbufCopy(ModifiedBufferId, UserHookDataPtr->MilImageDisp);
      }

   return 0;
   }

/* User's camera present function called every time a camera connection state */
/* changes.                                                                   */
/* -------------------------------------------------------------------------- */

MIL_INT MFTYPE CamPresentFunction(MIL_INT HookType,
   MIL_ID HookId,
   void* HookDataPtr)
   {
   SysHookDataStruct *UserHookDataPtr = (SysHookDataStruct *)HookDataPtr;
   MIL_INT IsCamPresent, Number;

   /* Inquire the camera present state (present or not present). */
   MsysGetHookInfo(UserHookDataPtr->MilSystem, HookId, M_CAMERA_PRESENT, &IsCamPresent);
   /* Inquire the camera's digitizer device number . */
   MsysGetHookInfo(UserHookDataPtr->MilSystem, HookId, M_NUMBER, &Number);
   /* Update the number of camera present variable. */
   MsysInquire(UserHookDataPtr->MilSystem, M_NUM_CAMERA_PRESENT,
      &UserHookDataPtr->NbCameras);

   if (IsCamPresent)
      {
      MIL_STRING UniqueId;

      /* Inquire the camera's Unique Id. */
      MsysGetHookInfo(UserHookDataPtr->MilSystem, HookId, M_GC_UNIQUE_ID_STRING, UniqueId);

      /* Camera is present. */
      if (UserHookDataPtr->DigHookDataStrutPtr[Number].MilDigitizer == 0)
         {
         /* Newly attached camera; allocate it. */
         DigAllocResources(UserHookDataPtr->MilSystem, Number,
            &UserHookDataPtr->DigHookDataStrutPtr[Number]);
         }
      else if (UserHookDataPtr->DigHookDataStrutPtr[Number].CamUniqueId != UniqueId)
         {
         /* New camera added in place of another one, free old dig */
         DigFreeResources(&UserHookDataPtr->DigHookDataStrutPtr[Number]);

         /* Find out if camera was previously allocated on a different device number. */
         for (MIL_INT i = 0; i < MAX_CAM; i++)
            {
            if ((i != Number) && (UserHookDataPtr->DigHookDataStrutPtr[Number].CamUniqueId == UniqueId))
               DigFreeResources(&UserHookDataPtr->DigHookDataStrutPtr[i]);
            }

         /* Allocate resources. */
         DigAllocResources(UserHookDataPtr->MilSystem, Number,
            &UserHookDataPtr->DigHookDataStrutPtr[Number]);
         }

      if (UserHookDataPtr->DigHookDataStrutPtr[Number].MilDigitizer)
         {
         if(UserHookDataPtr->SystemType == M_SYSTEM_GIGE_VISION_TYPE)
            AddAdapterToList(UserHookDataPtr, UserHookDataPtr->DigHookDataStrutPtr[Number].AdapterName);
         }
      UserHookDataPtr->DigHookDataStrutPtr[Number].IsConnected = true;
      /* Start acquisition. */
      DigStartAcquisition(&UserHookDataPtr->DigHookDataStrutPtr[Number]);
      }
   else
      {
      /* Camera is not present. */
      if (UserHookDataPtr->DigHookDataStrutPtr[Number].MilDigitizer)
         {
         /* Stop acquisition. */
         DigStopAcquisition(&UserHookDataPtr->DigHookDataStrutPtr[Number]);
         }

      UserHookDataPtr->DigHookDataStrutPtr[Number].IsConnected = false;
      }

   return 0;
   }

void DigAllocGigEVisionResources(DigHookDataStruct* UserSt, bool PrintErrors)
   {
   MdigInquire(UserSt->MilDigitizer, M_GC_PACKET_SIZE, &UserSt->PacketSize);
   MdigInquire(UserSt->MilDigitizer, M_GC_INTERFACE_NAME, UserSt->AdapterName);
   }

/* Allocates digitizer plus all other resources needed for image acquisition and */
/* camera state changes.                                                         */
/* ------------------------------------------------------------------------------*/

void DigAllocResources(MIL_ID MilSystem, MIL_INT DeviceNum, DigHookDataStruct* UserSt,
   bool PrintErrors)
   {
   MIL_INT SizeBand = 0, BufType = 0, SizeBit = 0;
   MIL_INT64 BufFormat = 0;

   /* 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.                                     */

   /* MIL_TEXT_CHAR MyCameraName[64] = {'\0'};                                               */

   /* MosSprintf(MyCameraName, 64, MIL_TEXT("MyCameraName%d"), DeviceNum);                   */
   /* MdigAlloc(MilSystem, M_GC_CAMERA_ID(MIL_TEXT("MyCameraName")), MIL_TEXT("M_DEFAULT"),  */
   /*    M_GC_DEVICE_NAME, &MilDigitizer);                                              */

   if (PrintErrors)
      MdigAlloc(MilSystem, DeviceNum, MIL_TEXT("M_DEFAULT"), M_DEV_NUMBER,
      &UserSt->MilDigitizer);
   else
      {
      MappControl(M_DEFAULT, M_ERROR, M_PRINT_DISABLE);
      MdigAlloc(MilSystem, DeviceNum, MIL_TEXT("M_DEFAULT"), M_DEV_NUMBER,
         &UserSt->MilDigitizer);
      MappControl(M_DEFAULT, M_ERROR, M_PRINT_ENABLE);
      }

   if (UserSt->MilDigitizer)
      {
      MIL_INT Len = 0;
      UserSt->MilSystem = MilSystem;
      UserSt->IsConnected = true;

      /* Disable MdigProcess grab monitor since disconnecting a camera will result in an  */
      /* error message.                                                                   */
      MdigControl(UserSt->MilDigitizer, M_PROCESS_GRAB_MONITOR, M_DISABLE);
      /* Disable corrupted frame errors as they are handled from the MdigProcess hook     */
      /* function.                                                                        */
      MdigControl(UserSt->MilDigitizer, M_CORRUPTED_FRAME_ERROR, M_DISABLE);

      /* Inquire information related to the camera being allocated. */
      MdigInquire(UserSt->MilDigitizer, M_CAMERA_VENDOR, UserSt->CamVendor);
      MdigInquire(UserSt->MilDigitizer, M_CAMERA_MODEL, UserSt->CamModel);
      MdigInquire(UserSt->MilDigitizer, M_GC_UNIQUE_ID_STRING, UserSt->CamUniqueId);

      if(UserSt->SystemType == M_SYSTEM_GIGE_VISION_TYPE)
         DigAllocGigEVisionResources(UserSt, PrintErrors);

      /* Pop-up the MIL feature browser; exposes GigE device features.                */
#if (USE_FEATURE_BROWSER == 1)
      MdigControl(UserSt->MilDigitizer, M_GC_FEATURE_BROWSER, M_OPEN + M_ASYNCHRONOUS);
#endif

      /* Allocate displays and buffers; everything necessary to run MdigProcess. */
      MdispAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_DEFAULT,
         &UserSt->MilDisplay);
#if (MIL_COMPILE_VERSION >= 1030)
      MdispControl(UserSt->MilDisplay, M_TITLE, UserSt->CamModel);
#else
      MdispControl(UserSt->MilDisplay, M_TITLE, UserSt->CamModel.c_str());
#endif

      /* Allocate a buffer format that matches the camera's pixel format. */
      MdigInquire(UserSt->MilDigitizer, M_SIZE_BAND, &SizeBand);
      MdigInquire(UserSt->MilDigitizer, M_TYPE, &BufType);
      MdigInquire(UserSt->MilDigitizer, M_SOURCE_DATA_FORMAT, &BufFormat);
      MdigInquire(UserSt->MilDigitizer, M_SIZE_BIT, &SizeBit);

      MbufAllocColor(MilSystem,
         SizeBand,
         MdigInquire(UserSt->MilDigitizer, M_SIZE_X, M_NULL),
         MdigInquire(UserSt->MilDigitizer, M_SIZE_Y, M_NULL),
         BufType,
         M_IMAGE + M_GRAB + M_DISP + BufFormat,
         &UserSt->MilImageDisp);

      MbufClear(UserSt->MilImageDisp, M_COLOR_BLACK);
      if(SizeBit > 8)
         {
         MdispControl(UserSt->MilDisplay, M_VIEW_MODE, M_BIT_SHIFT);
         MdispControl(UserSt->MilDisplay, M_VIEW_BIT_SHIFT, SizeBit - 8);
         }

      MdispSelect(UserSt->MilDisplay, UserSt->MilImageDisp);

      for (MIL_INT i = 0; i < BUFFERING_SIZE_MAX; i++)
         {
         MbufAllocColor(MilSystem,
            SizeBand,
            MdigInquire(UserSt->MilDigitizer, M_SIZE_X, M_NULL),
            MdigInquire(UserSt->MilDigitizer, M_SIZE_Y, M_NULL),
            BufType,
            M_GRAB + M_IMAGE + BufFormat,
            &UserSt->MilGrabBufferList[i]);
         if (UserSt->MilGrabBufferList[i])
            {
            UserSt->MilGrabBufferListSize++;
            MbufClear(UserSt->MilGrabBufferList[i], 0);
            }
         }
      }
   }

/* Free digitizer and all other resources allocated.  */
/* -------------------------------------------------- */

void DigFreeResources(DigHookDataStruct* UserSt)
   {
   if (UserSt->MilDigitizer)
      {
      for (MIL_INT i = 0; i < UserSt->MilGrabBufferListSize; i++)
         MbufFree(UserSt->MilGrabBufferList[i]);

      MbufFree(UserSt->MilImageDisp);
      MdispFree(UserSt->MilDisplay);

#if (USE_FEATURE_BROWSER == 1)
      /* Close the MIL feature browser; exposes GigE device features.                */
      MdigControl(UserSt->MilDigitizer, M_GC_FEATURE_BROWSER, M_CLOSE);
#endif

      MdigFree(UserSt->MilDigitizer);

      UserSt->Clear();
      }
   }

/* Starts MdigProcess. */
/* ------------------- */

void DigStartAcquisition(DigHookDataStruct* UserSt)
   {
   if (UserSt->MilDigitizer)
      {
      UserSt->GrabInProgress = M_TRUE;
      MdigProcess(UserSt->MilDigitizer, UserSt->MilGrabBufferList,
         UserSt->MilGrabBufferListSize, M_START, M_DEFAULT,
         ProcessingFunction, UserSt);

      MdigInquire(UserSt->MilDigitizer, M_GC_PAYLOAD_SIZE, &UserSt->PayloadSize);
      }
   }

/* Stops MdigProcess. */
/* ------------------ */

void DigStopAcquisition(DigHookDataStruct* UserSt)
   {
   if (UserSt->GrabInProgress)
      {
      MdigProcess(UserSt->MilDigitizer, UserSt->MilGrabBufferList,
         UserSt->MilGrabBufferListSize, M_STOP, M_DEFAULT,
         ProcessingFunction, UserSt);
      UserSt->GrabInProgress = M_FALSE;
      }
   }

/* Print camera state information. */
/* ------------------ */
void PrintCameraInfo(SysHookDataStruct* UserSt)
   {
   if (UserSt->NbCameras)
      {
      MIL_TEXT_CHAR Str[] = MIL_TEXT("                           ");
      MosSleep(STATS_PRINT_PERIOD);
#if M_MIL_USE_WINDOWS
      system("cls");
#endif
      MosPrintf(MIL_TEXT("This example shows how to handle camera connect / ")
         MIL_TEXT("disconnect events.\n\n"));
      MosPrintf(MIL_TEXT("%d camera%s detected.\n"), (int)UserSt->NbCameras,
         UserSt->NbCameras > 1 ? MIL_TEXT("s") : MIL_TEXT(""));
      MosPrintf(MIL_TEXT("You can proceed to add / remove cameras to your ")
         MIL_TEXT("system at anytime.\n\n"));
      MosPrintf(MIL_TEXT("%s----------------------------------------------------\n"), Str);
      MosPrintf(MIL_TEXT("%s                  Camera statistics                 \n"), Str);
      MosPrintf(MIL_TEXT("%s-------------------------------------+--------------\n"), Str);
      MosPrintf(MIL_TEXT("%s                Frame                |    Packet    \n"), Str);
      MosPrintf(MIL_TEXT("%s-------------------------------------+--------------\n"), Str);
      MosPrintf(MIL_TEXT("%-14s%-13s%9s%8s%11s%8s%8s%8s\n"), MIL_TEXT("Model"),
         MIL_TEXT("State"), MIL_TEXT("Grabbed"), MIL_TEXT("Rate"),
         MIL_TEXT("Bandwidth"), MIL_TEXT("Corrupt"), MIL_TEXT("|  Size"),
         MIL_TEXT("Resends"));
      MosPrintf(MIL_TEXT("----------------------------")
         MIL_TEXT("------------------------------------+--------------\n"));

      for (MIL_INT i = 0; i < MAX_CAM; i++)
         {
         if (UserSt->DigHookDataStrutPtr[i].MilDigitizer)
            {
            DigHookDataStruct* DigStPtr = &UserSt->DigHookDataStrutPtr[i];

            if (DigStPtr->IsConnected)
               MdigInquire(DigStPtr->MilDigitizer, M_PROCESS_FRAME_RATE, &DigStPtr->FrameRate);
            else
               DigStPtr->FrameRate = 0;

            MosPrintf(MIL_TEXT("%-14.13s%-13.12s%9d%8.1f%11.1f%8d%8d%8d\n"),
               DigStPtr->CamModel.c_str(),
               DigStPtr->IsConnected ? MIL_TEXT("Connected") : MIL_TEXT("Disconnected"),
               DigStPtr->ProcessedImageCount,
               DigStPtr->FrameRate,
               (DigStPtr->PayloadSize*DigStPtr->FrameRate / 1e6),
               DigStPtr->CorruptImageCount,
               DigStPtr->PacketSize,
               DigStPtr->ResendRequests);
            }
         }

      MosPrintf(MIL_TEXT("----------------------------")
         MIL_TEXT("---------------------------------------------------\n\n"));

      if(UserSt->SystemType == M_SYSTEM_GIGE_VISION_TYPE)
         {
         MosPrintf(MIL_TEXT("Network adapter statistics\n\n"));
         MIL_DOUBLE AdapterBandwidth = 0;
         for (size_t i = 0; i < UserSt->Adapters.size(); i++)
            {
            AdapterBandwidth = 0;
            DigHookDataStruct* DigStPtr = NULL;
            for (MIL_INT j = 0; j < MAX_CAM; j++)
               {
               DigStPtr = &(UserSt->DigHookDataStrutPtr[j]);
               if (DigStPtr->MilDigitizer && DigStPtr->AdapterName == UserSt->Adapters[i])
                  {
                  AdapterBandwidth += (DigStPtr->PayloadSize*DigStPtr->FrameRate / 1e6);
                  }
               }

            MosPrintf(MIL_TEXT("\n%-50.49s%.1f (MB/s) connected to:\n"), UserSt->Adapters[i].c_str(),
               AdapterBandwidth);
            MosPrintf(MIL_TEXT("----------------------------")
               MIL_TEXT("---------------------------------------------------\n"));

            for (MIL_INT j = 0; j < MAX_CAM; j++)
               {
               DigStPtr = &(UserSt->DigHookDataStrutPtr[j]);
               if (DigStPtr->MilDigitizer && DigStPtr->AdapterName == UserSt->Adapters[i])
                  MosPrintf(MIL_TEXT("%s %s\n"), DigStPtr->CamVendor.c_str(), DigStPtr->CamModel.c_str());
               }
            }
         }
      }
   else
      {
#if M_MIL_USE_WINDOWS
      system("cls");
#endif
      MosPrintf(MIL_TEXT("This example shows how to handle camera connect / ")
         MIL_TEXT("disconnect events.\n\n"));
      MosPrintf(MIL_TEXT("%d camera detected.\n"), (int)UserSt->NbCameras);
      MosPrintf(MIL_TEXT("You can proceed to add / remove cameras to your system ")
         MIL_TEXT("anytime.\n\n"));
      MosPrintf(MIL_TEXT("\r|"));
      MosSleep(STATS_PRINT_PERIOD / 4);
      MosPrintf(MIL_TEXT("\r/"));
      MosSleep(STATS_PRINT_PERIOD / 4);
      MosPrintf(MIL_TEXT("\r-"));
      MosSleep(STATS_PRINT_PERIOD / 4);
      MosPrintf(MIL_TEXT("\r\\"));
      MosSleep(STATS_PRINT_PERIOD / 4);
      }
   }

void AddAdapterToList(SysHookDataStruct* UserSt, const MIL_STRING& AdapterName)
   {
   /* Start by searching if adapter is already in list. */
   for (size_t i = 0; i < UserSt->Adapters.size(); i++)
      {
      if (UserSt->Adapters[i] == AdapterName)
         return;
      }

   UserSt->Adapters.push_back(AdapterName);
   }