/*************************************************************************************/
/*
 * File name: MulticastMaster.cpp
 * Location:  ...\Matrox Imaging\MILxxx\Examples\BoardSpecific\gigevision\C++\multicastmaster
 *             
 *
 * Synopsis:  This program shows how to perform IP Multicast with GigE Vision devices.
 *
 *            To do this you must have a network capable of delivering a Multicast
 *            service over IPv4. This requires the use of routers and LAN switches
 *            that support the Internet Group Management Protocol (IGMP). Some manual
 *            configuration of you LAN switches might be required. More information
 *            can be found in the IP Multicast section of Matrox GigE Vision
 *            Assistant's help file.
 *
 *      Note: This example must be used along with the MulticastSlave program
 *            connected to the same GigE Vision device and running on another PC.
 */
#include <mil.h>
#if M_MIL_USE_WINDOWS
#include <Windows.h>
#endif

 /* Number of images in the buffering grab queue.
    Generally, increasing this number gives better real-time grab.
  */
#define BUFFERING_SIZE_MAX 22

// Host to network byte swap long
#define htonl(x)  ( ( ( ( x ) & 0x000000ff ) << 24 ) | \
                    ( ( ( x ) & 0x0000ff00 ) <<  8 ) | \
                    ( ( ( x ) & 0x00ff0000 ) >>  8 ) | \
                    ( ( ( x ) & 0xff000000 ) >> 24 ) )

// Host to network byte swap short
#define htons(x)  ( ( ( ( x ) & 0xff00 ) >> 8 ) | \
                    ( ( ( x ) & 0x00ff ) << 8 ) )

// Network to host byte swap long
#define ntohl(x)  htonl(x)

// Network to host byte swap short
#define ntohs(x)  htons(x)

#define IPV4_ADDRESS_SIZE  20

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

/* Function prototypes.                  */
void PrintCameraInfo(MIL_ID MilDigitizer);
MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType,
                                  MIL_ID HookId,
                                  void* HookDataPtr);

/* Main function. */
/* ---------------*/

int MosMain(void)
{
   MIL_ID  MilApplication;
   MIL_ID  MilSystem     ;
   MIL_ID  MilDigitizer;
   MIL_ID  MilDisplay;
   MIL_ID  MilImageDisp;
   MIL_ID  MilGrabBufferList[BUFFERING_SIZE_MAX];
   MIL_INT MilGrabBufferListSize;
   MIL_INT ProcessFrameCount  = 0;
   MIL_INT DigProcessInProgress = M_FALSE;
   MIL_INT BoardType = 0;
   MIL_DOUBLE ProcessFrameRate = 0;
   MIL_INT64 SourceDataFormat;
   MIL_TEXT_CHAR MulticastAddr[IPV4_ADDRESS_SIZE] = {'\0'};
   HookDataStruct UserHookData;
   MIL_INT n = 0;

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

   /* This example only runs on a MIL GigE Vision system type. */
   MsysInquire(MilSystem, M_BOARD_TYPE, &BoardType);
   if(((BoardType & M_BOARD_TYPE_MASK) != M_GIGE_VISION))
      {
      MosPrintf(MIL_TEXT("This example requires a M_GIGE_VISION system type.\n"));
      MosPrintf(MIL_TEXT("Please change system type in milconfig.\n"));
      MappFreeDefault(MilApplication, MilSystem, M_NULL, M_NULL, M_NULL);
      return 0;
      }

   /* Allocate a master Multicast digitizer. */
   MdigAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_GC_MULTICAST_MASTER,
      &MilDigitizer);
   /* The default Multicast address can be changed if a conflict exists with:  */
   // MosSprintf(MulticastAddr, IPV4_ADDRESS_SIZE, MIL_TEXT("%s"), MIL_TEXT("239.255.16.16"));
   // MdigControl(MilDigitizer, M_GC_STREAM_CHANNEL_MULTICAST_ADDRESS_STRING,
   //    M_PTR_TO_DOUBLE(MulticastAddr));
   // MdigControl(MilDigitizer, M_GC_UPDATE_MULTICAST_INFO, M_DEFAULT);

   /* Note that the above IP address 239.255.16.16 is specified for illustrative purposes
      only. */

   /* Allocate a display. */
   MdispAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_DEFAULT, &MilDisplay);
   /* Inquire the buffer format compatible with the camera's current pixel format. */
   MdigInquire(MilDigitizer, M_SOURCE_DATA_FORMAT, &SourceDataFormat);

   /* Allocate the display buffer clear it and associate it to the display. */
   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+M_PROC+SourceDataFormat,
      &MilImageDisp);
   MbufClear(MilImageDisp, M_COLOR_BLACK);
   MdispSelect(MilDisplay, MilImageDisp);

   /* Allocate the grab buffers and clear them. */
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_DISABLE);
   for(MilGrabBufferListSize = 0; 
      MilGrabBufferListSize<BUFFERING_SIZE_MAX; MilGrabBufferListSize++)
      {
      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_GRAB+M_PROC+SourceDataFormat,
         &MilGrabBufferList[MilGrabBufferListSize]);

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

   /* Free buffers to leave space for possible temporary buffers. */
   for (n=0; n<2 && MilGrabBufferListSize; n++)
      {
      MilGrabBufferListSize--;
      MbufFree(MilGrabBufferList[MilGrabBufferListSize]);
      }

   MosPrintf(MIL_TEXT("This example demonstrates the use of IP Multicast with GigE Vision"));
   MosPrintf(MIL_TEXT(" devices.\n"));
   MosPrintf(MIL_TEXT("It allocates a Multicast master digitizer that can read, write and"));
   MosPrintf(MIL_TEXT(" grab from\n"));
   MosPrintf(MIL_TEXT("a GigE Vision device.\n\n"));
   MosPrintf(MIL_TEXT("This example must be used along with MulticastSlave.cpp connected to"));
   MosPrintf(MIL_TEXT(" the same\n"));
   MosPrintf(MIL_TEXT("GigE Vision device and running on another PC.\n\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to continue."));
   MosGetch();

   /* Print info related to the device we are connected to. */
   PrintCameraInfo(MilDigitizer);

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

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

   /* NOTE: Now the main() is free to perform other tasks 
                                                    while the processing is executing. */
   /* -------------------------------------------------------------------------------- */
   MosPrintf(MIL_TEXT("If the MulticastSlave program is already running on the other PC, it"));
   MosPrintf(MIL_TEXT(" should\n"));
   MosPrintf(MIL_TEXT("have detected that this device is controlled by a multicast master"));
   MosPrintf(MIL_TEXT(" digitizer\n"));
   MosPrintf(MIL_TEXT("and have started image acquisition.\n\n"));
   MosPrintf(MIL_TEXT("If the MulticastSlave program is not yet started then it should be"));
   MosPrintf(MIL_TEXT(" started now.\n"));
   MosPrintf(MIL_TEXT("\nPress <Enter> to stop.\n"));
   MosGetch();

   /* Stop the processing. */
   MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize,
               M_STOP, M_DEFAULT, ProcessingFunction, &UserHookData);

   /* Print statistics. */
   MdigInquire(MilDigitizer, M_PROCESS_FRAME_COUNT,  &ProcessFrameCount);
   MdigInquire(MilDigitizer, M_PROCESS_FRAME_RATE,   &ProcessFrameRate);
   MosPrintf(MIL_TEXT("\n\n%ld frames grabbed at %.1f frames/sec (%.1f ms/frame).\n"),
                          ProcessFrameCount, ProcessFrameRate, 1000.0/ProcessFrameRate);
   MosPrintf(MIL_TEXT("Press <Enter> to end.\n\n"));
   MosGetch();

   while(MilGrabBufferListSize > 0)
      MbufFree(MilGrabBufferList[--MilGrabBufferListSize]);

   /* Release defaults. */
   MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, MilImageDisp);

   return 0;
}

/* Prints information regarding the device this master digitizer is connected to. */
/* -----------------------------------------------------------------------       */
void PrintCameraInfo(MIL_ID MilDigitizer)
   {
   MIL_TEXT_PTR DeviceVendor = NULL;
   MIL_TEXT_PTR DeviceModel  = NULL;
   MIL_TEXT_PTR PixelFormat  = NULL;
   MIL_INT64 Width = 0, Height = 0;
   MIL_INT Port = 0;
   MIL_INT Len = 0;
   MIL_TEXT_PTR MulticastAddress;

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

   /* Inquire camera vendor name. */
   MdigInquire(MilDigitizer, M_CAMERA_VENDOR_SIZE, &Len);
   DeviceVendor = new MIL_TEXT_CHAR[Len];
   MdigInquire(MilDigitizer, M_CAMERA_VENDOR, DeviceVendor);
   
   /* Inquire camera model name. */
   MdigInquire(MilDigitizer, M_CAMERA_MODEL_SIZE, &Len);
   DeviceModel = new MIL_TEXT_CHAR[Len];
   MdigInquire(MilDigitizer, M_CAMERA_MODEL, DeviceModel);

   /* Inquire camera pixel format. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING+M_STRING_SIZE,
      MIL_TEXT("PixelFormat"), M_DEFAULT, &Len);
   PixelFormat = new MIL_TEXT_CHAR[Len];
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE_AS_STRING, MIL_TEXT("PixelFormat"),
      M_DEFAULT, PixelFormat);

   /* Inquire camera width and height. */
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("Width"), M_TYPE_INT64,
      &Width);
   MdigInquireFeature(MilDigitizer, M_FEATURE_VALUE, MIL_TEXT("Height"), M_TYPE_INT64,
      &Height);

   /* Inquire the Multicast address used. */
   MdigInquire(MilDigitizer, M_GC_STREAM_CHANNEL_MULTICAST_ADDRESS_STRING_SIZE,
      &Len);
   MulticastAddress = new MIL_TEXT_CHAR[Len];
   MdigInquire(MilDigitizer, M_GC_STREAM_CHANNEL_MULTICAST_ADDRESS_STRING,
      MulticastAddress);

   MdigInquire(MilDigitizer, M_GC_STREAM_PORT, &Port);

   /* Print camera info. */
   MosPrintf(MIL_TEXT("\n--------------------- Master digitizer connection status. "));
   MosPrintf(MIL_TEXT("---------------------\n\n"), DeviceVendor, DeviceModel);
   MosPrintf(MIL_TEXT("Connected to             %s %s\n"), DeviceVendor, DeviceModel);
   MosPrintf(MIL_TEXT("Device pixel format:     %s\n"), PixelFormat);
   MosPrintf(MIL_TEXT("Device AOI:              %d x %d\n"), Width, Height);
   MosPrintf(MIL_TEXT("IPv4 Multicast address:  %s\n"), MulticastAddress);
   MosPrintf(MIL_TEXT("Stream port:             %d\n\n"), Port);

   /* Cleanup. */
   if(DeviceVendor)
      delete [] DeviceVendor;
   if(DeviceModel)
      delete [] DeviceModel;
   if(PixelFormat)
      delete [] PixelFormat;
   if(MulticastAddress)
      delete [] MulticastAddress;
   }

/* 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'),};
   MIL_INT IsFrameCorrupt = M_FALSE;

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


   /* Print and draw the frame count. */
   UserHookDataPtr->ProcessedImageCount++;
   if(IsFrameCorrupt)
      UserHookDataPtr->CorruptImageCount++;
   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);
   
   #if M_MIL_USE_CE
   /* Give execution time to user interface when the digitizer processing queue is full.
      If necessary, the Sleep value can be increased to give more execution time to user
      interface.
   */
   if(MdigInquire(UserHookDataPtr->MilDigitizer, M_PROCESS_PENDING_GRAB_NUM, M_NULL) <= 1)
      {
      if ((UserHookDataPtr->ProcessedImageCount%10) == 0)
         Sleep(2);
      }
   #endif

   return 0;
   }