/***************************************************************************************/
/*
* File name: RTdigProcess.cpp
* Location: See Matrox Example Launcher in the MIL Control Center
 * 
*
* Synopsis:  This program shows the use of the MdigProcess() function and its multiple
*            buffering acquisition to do robust real-time processing.
*
*            The user's processing code to execute is located in a callback function 
*            that will be called for each frame acquired (see ProcessingFunction()).
*
*      Note: The average processing time must be shorter than the grab time or some
*            frames will be missed. Also, if the processing results are not displayed
*            and the frame count is not drawn, the CPU usage is reduced significantly.
*
*            This program can be use with Real-Time sub-system or windows to compare
*            the result.
*
* Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
* All Rights Reserved
*/

#include <mil.h>
#include <Windows.h>
#include <psapi.h>


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

#include "..\..\RTDisplayMonitor\C++\rtdisplaymonitor.h"

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

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

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

int MosMain(void)
   {
   MIL_ID MilApplication;
   MIL_ID MilSystem     ;
   MIL_ID MilDigitizer  ;
   MIL_ID MilImageDisp = M_NULL;
   MIL_ID MilGrabBufferList[BUFFERING_SIZE_MAX] = { 0 };
   MIL_INT MilGrabBufferListSize;
   MIL_INT ProcessFrameCount;
   MIL_INT ProcessFrameMissed;
   MIL_DOUBLE ProcessFrameRate;
   MIL_INT NbFrames = 0, n = 0;

   HookDataStruct UserHookData;
   HANDLE DispMilEvent  = NULL;
   HANDLE ShareMemEvent = NULL;
   void * SharePrt      = NULL;

   /* Allocate defaults. */
   MappAlloc(M_DEFAULT, &MilApplication);
   MsysAlloc(M_SYSTEM_DEFAULT, M_DEFAULT, M_DEFAULT, &MilSystem);

   MdigAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_DEFAULT, &MilDigitizer);

   MbufAllocColor(MilSystem,
      MdigInquire(MilDigitizer, M_SIZE_BAND, 0),
      MdigInquire(MilDigitizer, M_SIZE_X, 0),
      MdigInquire(MilDigitizer, M_SIZE_X, 0),
      8, M_IMAGE + M_GRAB + M_PROC + M_NON_PAGED, &MilImageDisp);

#ifdef UNDER_RTSS
   DispMilEvent = RtOpenEvent(EVENT_ALL_ACCESS,TRUE, DISP_EVENT_MIL_NAMED);
   if (DispMilEvent)
      {
      ShareMemEvent =  RtOpenSharedMemory(PAGE_READONLY,FALSE,DISP_SHARE_MEM_NAMED, &SharePrt);
      if(ShareMemEvent)
         {
         MIL_INT SizeX        = MdigInquire(MilDigitizer, M_SIZE_X, 0);
         MIL_INT SizeY        = MdigInquire(MilDigitizer, M_SIZE_Y, 0);
         MIL_INT Pitch        = MbufInquire(MilImageDisp, M_PITCH, 0);
         MIL_INT64 Format     = MbufInquire(MilImageDisp, M_FORMAT, 0);

         RTX_DISP_BUFFER *RtxPtr = (RTX_DISP_BUFFER *) SharePrt;
         RtxPtr->Cmd             = DISP_ALLOC_CMD;
         RtxPtr->DisplayIdx      = 1;
         RtxPtr->Band            = MdigInquire(MilDigitizer, M_SIZE_BAND, 0);
         RtxPtr->SizeX           = SizeX;
         RtxPtr->SizeY           = SizeY;
         RtxPtr->Pitch           = Pitch;
         RtxPtr->Format          = Format;

         MIL_UINT64 PhyAddr = 0;
         if (RtxPtr->Band == 1)
            {
            MbufInquire(MilImageDisp, M_PHYSICAL_ADDRESS, &PhyAddr);
            RtxPtr->PhysicalAddress[0] = PhyAddr;
            RtxPtr->PhysicalAddress[1] = RtxPtr->PhysicalAddress[2] = 0;
            }
         else if (RtxPtr->Band == 3)
            {
            
            for (MIL_INT64 Iter = 0; Iter < 3; Iter++)
               {
               MIL_ID ChildBand = 0;
               MbufChildColor(MilImageDisp, Iter, &ChildBand);
               if (ChildBand != 0)
                  {
                  MbufInquire(ChildBand, M_PHYSICAL_ADDRESS, &PhyAddr);
                  RtxPtr->PhysicalAddress[Iter] = PhyAddr;
                  MbufFree(ChildBand);
                  }
               }
            
            }
         
         RtSetEvent(DispMilEvent);
         }
      }
#endif
   /* 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, 0),
         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);

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


   /* Print a message. */
   MosPrintf(MIL_TEXT("MIL RTOS grab example.\n"));
   MosPrintf(MIL_TEXT("---------------------------\n\n"));

   MosPrintf(MIL_TEXT("This example show how to use MdigProcess with a RTOS sub-system.\n\n"));

   MosPrintf(MIL_TEXT("In order to see the grabbed images you must run RTDisplayMonitor\n"));
   MosPrintf(MIL_TEXT("under Windows before starting RTDigProcess.\n"));

   /* Initialize the user's processing function data structure. */
   UserHookData.MilDigitizer        = MilDigitizer;
   UserHookData.MilImageDisp        = MilImageDisp;
   UserHookData.ProcessedImageCount = 0;
   UserHookData.ShareMemPtr         = SharePrt;
   UserHookData.WindowDispHandle    = DispMilEvent;

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

   MosPrintf(MIL_TEXT("\nPress <Enter> to stop.\n"));
   MosPrintf(MIL_TEXT("\n\n"));

   /* Here the main() is free to perform other tasks while the processing is executing. */
   /* --------------------------------------------------------------------------------- */

   while(!MosKbhit())
      {
      MosSleep(1000);
      MdigInquire(MilDigitizer, M_PROCESS_FRAME_COUNT, &ProcessFrameCount);
      MdigInquire(MilDigitizer, M_PROCESS_FRAME_RATE, &ProcessFrameRate);
      MdigInquire(MilDigitizer, M_PROCESS_FRAME_MISSED, &ProcessFrameMissed);
      MosPrintf(MIL_TEXT("\r%ld frames grabbed at %.1f frames/sec (%.1f ms/frame) Frame missed:%lld."),
         ProcessFrameCount, ProcessFrameRate, 1000.0/ProcessFrameRate, ProcessFrameMissed);
      }; 

   MosGetch();

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

   /* Free the grab buffers. */
   while(MilGrabBufferListSize > 0)
      MbufFree(MilGrabBufferList[--MilGrabBufferListSize]);

#ifdef UNDER_RTSS
   if(DispMilEvent)
      {
      RTX_DISP_BUFFER *RtxPtr = (RTX_DISP_BUFFER *)SharePrt;
      RtxPtr->Cmd = DISP_FREE_CMD;
      RtSetEvent(DispMilEvent);
      RtCloseHandle(DispMilEvent);
      }

   if(ShareMemEvent)
      RtCloseHandle(ShareMemEvent);
#endif

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

   return 0;
   }


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

MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType, MIL_ID HookId, void* HookDataPtr)
   {
   HookDataStruct *UserHookDataPtr = (HookDataStruct *)HookDataPtr;
   MIL_ID ModifiedBufferId;

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

   /* Increment the frame counter. */
   UserHookDataPtr->ProcessedImageCount++;

   /* Update the display. */
   MbufCopy(ModifiedBufferId, UserHookDataPtr->MilImageDisp);

#ifdef UNDER_RTSS
   if(UserHookDataPtr->WindowDispHandle)
      {
      RTX_DISP_BUFFER *RtxPtr = (RTX_DISP_BUFFER *) UserHookDataPtr->ShareMemPtr;
      RtxPtr->Cmd             = DISP_UPATE_CMD;
      RtSetEvent(UserHookDataPtr->WindowDispHandle);
      }
#endif

   return 0;
   }