Buffer Interface
CircHoldSimple.cpp

This console application demonstrates the holding of buffers.

// CircHoldSimple.cpp : Defines the entry point for the console application.
//
// This console example demonstrates how to hold a buffer using the
// CircularInterface class. (The BiAPI can also be used to hold
// circular buffers.) This example is a cpp file but usies procedural
// programming to keep the example simple understand.
//
// In main, the application prompts the user for the board to open and
// the number of buffers to create. There are two threads that are created,
// WorkerThread and WaitForFrameThread. The WaitForFrameThread waits for
// new frames to be acquired into memory. The WorkerThead processes the
// image data.
// In the WaitForFrameThread, when a new frame has been acquired, the buffer
// number of that frame is compared to a random number. If the random number
// and buffer number match, the buffer is held and the buffer handle gets
// stored in a buffer handle queue. Now that the buffer handle is in the
// queue, the WorkerThread will be notified that there is a buffer to
// process. The processing could include anything, a histogram, edge
// detection, and so on. For this example, the processing being done is
// displaying the held buffer to screen. While acquiring images, main
// continues to refresh the display surface with the PeekMessage function.
// The application will run until the user presses any key on the keyboard
// or if an error occurs in a thread. When the user presses a key, acquisition
// is stopped, we wait for the threads to end and the user allocated resources
// are cleaned up. There is no specific clean up call for the
// CircularInterface instance, because the destructor of the class will handle
// freeing resources and closing the board.
//
// When using the CircularInterface class or the BiAPI for holding buffers,
// we would recommended using the multi-threaded approach presented in this
// example, where one thread efficiently waits for frames, while the other
// processes the selected buffers.
#include "stdafx.h"
#include "CircHoldSimple.h"
#include "BFErApi.h" // open dialog
#include <queue>
#include <conio.h>
#include "DSapi.h"
#include "BFIOApi.h"
#include <iomanip>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// The one and only application object
CWinApp theApp;
using namespace std;
using namespace BufferAcquisition;
UINT WorkerThread(LPVOID lpdwParam); // Processes buffers by displaying them
UINT WaitForFrameThread(LPVOID lpdwParam); // Waits for newly acquired frames
// Data structure that is shared between all threads.
typedef struct _DataStruct
{
CircularInterface* pCirc; // Pointer to the circular instance
BFBOOL* pThreadDone; // Pointer to the thead done flag
queue<BiCirHandle>* pBufferHandleQueue; // Buffer handle queue
HANDLE* pNewHandleSemaphore; // Pointer to the semaphore
HANDLE* pBufferQueueMutex; // Pointer to the mutex
BFS32* phDspSrf; // Pointer to the display surface
BFU32 numBuffers; // Number of buffers allocated
}DataStruct;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
#if defined(_MSC_VER) && defined(_DEBUG)
AfxEnableMemoryLeakDump(false);
DispSurfAfxCrtWorkaround_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(BFNULL), BFNULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
nRetCode = 1;
}
else
{
BFU32 boardType, boardNum, init, serNum; // Open dialog variables
BFU32 numBuffers; // Number of buffers allocated by the user
BFU32 cirSetupOptions = 0; // use default setup
BFU32 errorMode = CirErStop; // stop if we run out of buffers
queue<BiCirHandle> BufferHandleQueue; // A queue of buffer handles
BFBOOL ThreadDone = FALSE; // TRUE <=> End worker thread
CWinThread* pWorkerThread; // Pointer to the worker thread
CWinThread* pWaitFrameThread; // Pointer to the wait for frame thread
BFS32 hDspSrf = -1; // handle to display surface
MSG Msg; // Used for updating the display surface
CircularInterface* circ; // Pointer to the Circular instance
// Prompt the user for the board to open. If there is only one board,
// that board will be selected.
if( DoBrdOpenDialog(BOD_BRD_NUM_NON_FAMILY|BOD_HIDEJUSTOPEN,
FF_BITFLOW_MODERN, &boardType, &boardNum, &init, &serNum))
{
return -1;
}
// Prompt the user for the number of buffers
cout << "Enter the number of buffers to allocate: ";
scanf_s("%d", &numBuffers);
// The mutex will protect the buffer handle queue from reads/writes from
// different threads.
HANDLE BufferQueueMutex = CreateMutex(BFNULL, FALSE, BFNULL);
// The semaphore will notify the worker thread that there is
// a new handle in the buffer handle queue.
HANDLE NewHandleSemaphore = CreateSemaphore(BFNULL, 0, 0x7FFFFFFF, BFNULL);
// Compare the buffer number to this random number.
srand( (unsigned)time( BFNULL ) );
BFU32 randNum = (BFU32)(numBuffers * rand() / (RAND_MAX + 1.0));
// Try creating a CircularInterface instance and starting acquisition.
try
{
// create circular instance
circ = new CircularInterface(boardNum, numBuffers, errorMode, cirSetupOptions);
// Create display surface to view held buffers.
if(!DispSurfCreate(&hDspSrf, circ->getBrdInfo(BiCamInqXSize),
{
cout << "Couldn't create display surface" << endl;
CloseHandle(BufferQueueMutex);
CloseHandle(NewHandleSemaphore);
return 1;
}
cout << "\nHit any key to end application." << endl;
// Fill out the data structure being passed into the threads
DataStruct data;
data.pBufferQueueMutex = &BufferQueueMutex;
data.pCirc = circ;
data.pBufferHandleQueue = &BufferHandleQueue;
data.phDspSrf = &hDspSrf;
data.pNewHandleSemaphore = &NewHandleSemaphore;
data.pThreadDone = &ThreadDone;
data.numBuffers = numBuffers;
// Start the threads
pWorkerThread = AfxBeginThread(WorkerThread, (LPVOID)&data, THREAD_PRIORITY_NORMAL);
pWaitFrameThread = AfxBeginThread(WaitForFrameThread, (LPVOID)&data, THREAD_PRIORITY_NORMAL);
// Start acquisition of images
}
catch(BFException e)
{
nRetCode = -1;
}
// Loop here until the user hits a key or
// there is an error in a thread.
while(!BFkbhit() && !ThreadDone)
{
if(PeekMessage(&Msg,BFNULL,0,0,PM_REMOVE))
DispatchMessage(&Msg);
else
Sleep(10);
}
// Signal worker thread to exit
ThreadDone = TRUE;
// Incase we are waiting in the worker thread, release the semaphore.
ReleaseSemaphore(NewHandleSemaphore, 1, BFNULL);
// Start acquisition of images
// Wait for worker thread to end.
DWORD exitCode;
while(GetExitCodeThread(pWorkerThread->m_hThread, &exitCode) &&
exitCode == STILL_ACTIVE)
{
Sleep(10);
}
// Wait for wait frame thread to end
while(GetExitCodeThread(pWaitFrameThread->m_hThread, &exitCode) &&
exitCode == STILL_ACTIVE)
{
Sleep(10);
}
// Close Display window
DispSurfClose(hDspSrf);
CloseHandle(BufferQueueMutex);
CloseHandle(NewHandleSemaphore);
delete circ;
if (BFkbhit())
BFgetch();
cout << "\nHit any key to continue." << endl;
while(!BFkbhit())
{
Sleep(1);
}
}
return nRetCode;
}
UINT WorkerThread(LPVOID lpdwParam)
{
DataStruct* pData = (DataStruct*)lpdwParam;
BiCirHandle cirHandle;
// Loop until notified not too.
while(*pData->pThreadDone == FALSE)
{
// Wait for the main thread notification that a new buffer was held
WaitForSingleObject(*pData->pNewHandleSemaphore, INFINITE);
// If the semaphore was bumped when the main thread was exiting,
// end this thread.
if(*pData->pThreadDone)
break;
// Get the buffer handle from the buffer handle queue.
// This is done safely with a mutex.
WaitForSingleObject(pData->pBufferQueueMutex, INFINITE);
cirHandle = pData->pBufferHandleQueue->front();
pData->pBufferHandleQueue->pop();
ReleaseMutex(pData->pBufferQueueMutex);
try
{
// Process the buffer. For our case, the process is
// copying the buffer to the display.
if(!DispSurfFormatBlit(*pData->phDspSrf, cirHandle.pBufData,
pData->pCirc->getBrdInfo(BiCamInqBitsPerPix), BFDISP_FORMAT_NORMAL))
{
cout << "Error displaying the held buffer." << endl;
*pData->pThreadDone = TRUE;
}
// Done processing this buffer, mark it available
pData->pCirc->setBufferStatus(cirHandle, BIAVAILABLE);
}
catch(BFException ex)
{
*pData->pThreadDone = TRUE;
}
}
return 0;
}
UINT WaitForFrameThread(LPVOID lpdwParam)
{
DataStruct* pData = (DataStruct*)lpdwParam;
BiCirHandle cirHandle;
BFU32 rv;
srand( (unsigned)time( BFNULL ) );
BFU32 randNum = (BFU32)(pData->numBuffers * rand() / (RAND_MAX + 1.0));
try
{
while(*pData->pThreadDone == FALSE)
{
// Wait for a frame to be acquired into memory.
rv = pData->pCirc->waitDoneFrame(INFINITE, &cirHandle);
if(rv != BI_OK)
{
// Acquisition has been stopped or aborted, check the
// error stack for any errors.
BFU32 er = pData->pCirc->getCirError();
while(er != BI_OK)
{
pData->pCirc->showError(er);
er = pData->pCirc->getCirError();
}
*pData->pThreadDone = TRUE;
return -1;
}
// Determine if this frame should be one to process
// In this case, if the buffer number matches the
// random number, process it.
if(cirHandle.BufferNumber == randNum)
{
// Hold this buffer. Buffers can be held based on the handle
// or buffer number. Here the handle is being used.
rv = pData->pCirc->setBufferStatus(cirHandle, BIHOLD);
if(rv == BI_OK)
{
// Put the held buffer handle on the queue.
// This is done safely with the mutex.
WaitForSingleObject(*pData->pBufferQueueMutex, INFINITE);
pData->pBufferHandleQueue->push(cirHandle);
ReleaseMutex(*pData->pBufferQueueMutex);
// Notify the worker thread that there is a buffer to process
ReleaseSemaphore(*pData->pNewHandleSemaphore, 1, BFNULL);
}
else
{
// Couldn't hold the buffer, so make it available.
// Check documentation for reseaons why a buffer
// would not be held.
pData->pCirc->setBufferStatus(cirHandle, BIAVAILABLE);
}
// Generate a new random number
randNum = (BFU32)(pData->numBuffers * rand() / (RAND_MAX + 1.0));
}
else
{
// Were not processing this buffer so make it available
// to be acquired into.
pData->pCirc->setBufferStatus(cirHandle, BIAVAILABLE);
}
cout << "Acquired Buffer = " << setw(8) << cirHandle.BufferNumber << "\r";
}
}
catch(BFException ex)
{
*pData->pThreadDone = TRUE;
}
return 0;
}